├── .gitignore ├── experiments └── README.md ├── data └── README.md ├── src └── sota │ ├── cub │ ├── tester.sh │ ├── generate_dataset.sh │ ├── train_gan.sh │ ├── benchmark.sh │ └── architecture │ │ ├── fullyconnected.json │ │ └── cycle_wgan.json │ ├── flo │ ├── tester.sh │ ├── generate_dataset.sh │ ├── train_gan.sh │ ├── benchmark.sh │ └── architecture │ │ ├── fullyconnected.json │ │ └── cycle_wgan.json │ ├── sun │ ├── tester.sh │ ├── generate_dataset.sh │ ├── train_gan.sh │ ├── benchmark.sh │ └── architecture │ │ ├── fullyconnected.json │ │ └── cycle_wgan.json │ └── awa1 │ ├── tester.sh │ ├── generate_dataset.sh │ ├── train_gan.sh │ ├── benchmark.sh │ └── architecture │ ├── fullyconnected.json │ └── cycle_wgan.json ├── options ├── __init__.py ├── test.py ├── dtype.py ├── gan.py ├── generator.py ├── benchmark.py ├── models.py └── base.py ├── models ├── __init__.py ├── generators.py ├── discriminators.py ├── supporttypes.py ├── wgan.py ├── classifier.py ├── rwgan.py ├── regressor.py ├── gan.py ├── modelobj.py └── base.py ├── requirements.txt ├── routines ├── aux.py ├── augment_features.py ├── tester.py └── benchmark.py ├── README.md ├── train.py └── notebooks └── dataset └── processing.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | src/data/* 2 | util 3 | *.pyc -------------------------------------------------------------------------------- /experiments/README.md: -------------------------------------------------------------------------------- 1 | #Introduction 2 | 3 | When runned the scripts from ../src/sota/ 4 | 5 | the folder will be created in this directory. -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | structure file required: 2 | 3 | ./cub 4 | ./cub/data.h5 5 | ./cub/knn.h5 6 | 7 | ./sun 8 | ./sun/data.h5 9 | ./sun/knn.h5 10 | 11 | ./flo 12 | ./flo/data.h5 13 | ./flo/knn.h5 14 | 15 | ./awa1 16 | ./awa1/data.h5 17 | ./awa1/knn.h5 18 | -------------------------------------------------------------------------------- /src/sota/cub/tester.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | _DBDIR_=./data/ 3 | _DATABASE_=cub 4 | _CONFERENCE_=sota 5 | _EPOCH_=epoch_80_80 6 | _MODEL_=./experiments/$_CONFERENCE_/$_DATABASE_/cycle_wgan/classifier/checkpoint/$_EPOCH_/architecture.json 7 | 8 | python -m routines.tester -db $_DATABASE_ -rc 1 --load_model $_MODEL_ --dataroot $_DBDIR_ 9 | -------------------------------------------------------------------------------- /src/sota/flo/tester.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | _DBDIR_=./data/ 3 | _DATABASE_=flo 4 | _CONFERENCE_=sota 5 | _EPOCH_=epoch_18_18 6 | _MODEL_=./experiments/$_CONFERENCE_/$_DATABASE_/cycle_wgan/classifier/checkpoint/$_EPOCH_/architecture.json 7 | 8 | python -m routines.tester -db $_DATABASE_ -rc 1 --load_model $_MODEL_ --dataroot $_DBDIR_ 9 | -------------------------------------------------------------------------------- /src/sota/sun/tester.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | _DBDIR_=./data/ 3 | _DATABASE_=sun 4 | _CONFERENCE_=sota 5 | _EPOCH_=epoch_85_85 6 | _MODEL_=./experiments/$_CONFERENCE_/$_DATABASE_/cycle_wgan/classifier/checkpoint/$_EPOCH_/architecture.json 7 | 8 | python -m routines.tester -db $_DATABASE_ -rc 1 --load_model $_MODEL_ --dataroot $_DBDIR_ 9 | -------------------------------------------------------------------------------- /src/sota/awa1/tester.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | _DBDIR_=./data/ 3 | _DATABASE_=awa1 4 | _CONFERENCE_=sota 5 | _EPOCH_=epoch_37_37 6 | _MODEL_=./experiments/$_CONFERENCE_/$_DATABASE_/cycle_wgan/classifier/checkpoint/$_EPOCH_/architecture.json 7 | 8 | python -m routines.tester -db $_DATABASE_ -rc 1 --load_model $_MODEL_ --dataroot $_DBDIR_ 9 | -------------------------------------------------------------------------------- /options/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "Rafael Felix, Vijay Kumar and Gustavo Carneiro" 2 | __copyright__ = "MIT License" 3 | __credits__ = ["Rafael Felix", "Gustavo Carneiro", "Vijay Kumar", "Ian Reid"] 4 | __license__ = "MIT License" 5 | __version__ = "1.0.3" 6 | __maintainer__ = "Rafael Felix" 7 | __email__ = "rafael.felixalves@adelaide.edu.au" 8 | __status__ = "production" 9 | __all__=["base", "gan", "generator", "models"] -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "Rafael Felix, Vijay Kumar and Gustavo Carneiro" 2 | __copyright__ = "MIT License" 3 | __credits__ = ["Rafael Felix", "Gustavo Carneiro", "Vijay Kumar", "Ian Reid"] 4 | __license__ = "MIT License" 5 | __version__ = "1.0.1" 6 | __maintainer__ = "Rafael Felix" 7 | __email__ = "rafael.felixalves@adelaide.edu.au" 8 | __status__ = "production" 9 | __all__ = ["base", "classifier", "discriminators", "generators", "gan", "regressor", "rwgan", "wgan", "supporttypes"] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ## python=3.5.5 2 | # Name Version Build Channel 3 | cudatoolkit 8.0 3 4 | cudnn 7.0.5 cuda8.0_0 5 | h5py 2.7.1 py35h8d53cdc_0 6 | hdf5 1.10.1 h9caa474_1 7 | imageio 2.3.0 py35_0 8 | numpy 1.14.2 py35hdbf6ddf_1 9 | pillow 5.0.0 py35h3deb7b8_0 10 | scikit-image 0.13.1 py35h14c3975_1 11 | scikit-learn 0.19.1 py35hbf1f462_0 12 | scipy 1.0.1 py35hfc37229_0 13 | tensorboard 1.5.1 py35_1 conda-forge 14 | tensorflow 1.5.0 py35_0 conda-forge 15 | tensorflow-gpu 1.4.1 0 16 | -------------------------------------------------------------------------------- /src/sota/flo/generate_dataset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | 8 | _GPU_DEV_="0" 9 | _GPU_MEM_=0.9 10 | _CONFERENCE_=sota 11 | _DATABASE_=flo 12 | 13 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 14 | _BASE_FOLDER_=$_BASE_FOLDER_/gan-model/ 15 | 16 | #Architecture and checkpoint for generator 17 | _ARCHITECTURE_=$_BASE_FOLDER_/checkpoint/generator/ 18 | 19 | _OUTDIR_=$_BASE_FOLDER_/source/data 20 | echo $_OUTDIR_ 21 | mkdir --parents $_OUTDIR_ 22 | rm $_OUTDIR_/* 23 | 24 | _MERGE_=0 25 | # domain to be generated, can be: unseen, seen or [unseen,seen] 26 | _DOMAIN_=unseen 27 | # Number of features per class, can be either: Number or [N_U, N_S] 28 | _NUM_FEATURES_=300 29 | _SAVENPY_=0 30 | 31 | # Routine 32 | python -m routines.augment_features --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --outdir $_OUTDIR_ --merge $_MERGE_ --num_features $_NUM_FEATURES_ --domain $_DOMAIN_ --save_numpy $_SAVENPY_ --dataroot $_DBDIR_ 33 | -------------------------------------------------------------------------------- /src/sota/sun/generate_dataset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | 8 | _GPU_DEV_="0" 9 | _GPU_MEM_=0.9 10 | _CONFERENCE_=sota 11 | _DATABASE_=sun 12 | 13 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 14 | _BASE_FOLDER_=$_BASE_FOLDER_/gan-model/ 15 | 16 | #Architecture and checkpoint for generator 17 | _ARCHITECTURE_=$_BASE_FOLDER_/checkpoint/generator/ 18 | 19 | _OUTDIR_=$_BASE_FOLDER_/source/data 20 | echo $_OUTDIR_ 21 | mkdir --parents $_OUTDIR_ 22 | rm $_OUTDIR_/* 23 | 24 | _MERGE_=0 25 | # domain to be generated, can be: unseen, seen or [unseen,seen] 26 | _DOMAIN_=unseen 27 | # Number of features per class, can be either: Number or [N_U, N_S] 28 | _NUM_FEATURES_=141 29 | _SAVENPY_=0 30 | 31 | # Routine 32 | python -m routines.augment_features --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --outdir $_OUTDIR_ --merge $_MERGE_ --num_features $_NUM_FEATURES_ --domain $_DOMAIN_ --save_numpy $_SAVENPY_ --dataroot $_DBDIR_ 33 | -------------------------------------------------------------------------------- /src/sota/cub/generate_dataset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Dataset root 3 | _DBDIR_=./data/ 4 | # Model type 5 | _MODEL_TYPE_=cycle_wgan 6 | 7 | _GPU_DEV_="0" 8 | _GPU_MEM_=0.9 9 | _CONFERENCE_=sota 10 | _DATABASE_=cub 11 | 12 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 13 | _BASE_FOLDER_=$_BASE_FOLDER_/gan-model/ 14 | 15 | #Architecture and checkpoint for generator 16 | _ARCHITECTURE_=$_BASE_FOLDER_/checkpoint/generator/ 17 | 18 | _OUTDIR_=$_BASE_FOLDER_/source/data 19 | echo $_OUTDIR_ 20 | mkdir --parents $_OUTDIR_ 21 | rm $_OUTDIR_/* 22 | 23 | _MERGE_=0 24 | # domain to be generated, can be: unseen, seen or [unseen,seen] 25 | _DOMAIN_=unseen 26 | # Number of features per class, can be either: Number or [N_U, N_S] 27 | _NUM_FEATURES_=200 28 | _SAVENPY_=0 29 | 30 | # Routine 31 | python -m routines.augment_features --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --outdir $_OUTDIR_ --merge $_MERGE_ --num_features $_NUM_FEATURES_ --domain $_DOMAIN_ --save_numpy $_SAVENPY_ --dataroot $_DBDIR_ 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/sota/awa1/generate_dataset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | 8 | _GPU_DEV_="0" 9 | _GPU_MEM_=0.9 10 | _CONFERENCE_=sota 11 | _DATABASE_=awa1 12 | 13 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 14 | _BASE_FOLDER_=$_BASE_FOLDER_/gan-model/ 15 | 16 | #Architecture and checkpoint for generator 17 | _ARCHITECTURE_=$_BASE_FOLDER_/checkpoint/generator/ 18 | 19 | _OUTDIR_=$_BASE_FOLDER_/source/data 20 | echo $_OUTDIR_ 21 | mkdir --parents $_OUTDIR_ 22 | rm $_OUTDIR_/* 23 | 24 | _MERGE_=0 25 | # domain to be generated, can be: unseen, seen or [unseen,seen] 26 | _DOMAIN_=[unseen,seen] 27 | # Number of features per class, can be either: Number or [N_U, N_S] 28 | _NUM_FEATURES_=[1200,300] 29 | _SAVENPY_=0 30 | 31 | # Routine 32 | python -m routines.augment_features --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --outdir $_OUTDIR_ --merge $_MERGE_ --num_features $_NUM_FEATURES_ --domain $_DOMAIN_ --save_numpy $_SAVENPY_ --dataroot $_DBDIR_ 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/sota/awa1/train_gan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Dataset root 3 | _DBDIR_=./data/ 4 | # Model type 5 | _MODEL_TYPE_=cycle_wgan 6 | # GPU assign 7 | _GPU_DEV_="0" 8 | # GPU memory use 9 | _GPU_MEM_=0.9 10 | # conference flag 11 | _CONFERENCE_=sota 12 | # DB type. (requires a folder at _DBDIR_) 13 | _DATABASE_=awa1 14 | 15 | # Set up of base folder for running GAN 16 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 17 | mkdir $_BASE_FOLDER_ --parents 18 | 19 | # Architecture file. Provided in the code 20 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_MODEL_TYPE_.json 21 | 22 | _TRAIN_GAN_=1 23 | _TRAIN_CLS_=1 24 | _TRAIN_REG_=1 25 | # Saving points for REG, CLS and GAN 26 | _SAVE_POINTS_="[30,40,285]" 27 | # Saving option for every epoch 0 switch off 28 | _SAVE_=0 29 | 30 | # Training routine for GAN 31 | python train.py --dbname $_DATABASE_ --baseroot $_BASE_FOLDER_ --train_gan $_TRAIN_GAN_ --train_cls $_TRAIN_CLS_ --train_reg $_TRAIN_REG_ --architecture_file $_ARCHITECTURE_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --save_model $_SAVE_ --savepoints $_SAVE_POINTS_ --dataroot $_DBDIR_ 32 | -------------------------------------------------------------------------------- /src/sota/flo/train_gan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Dataset root 3 | _DBDIR_=./data/ 4 | # Model type 5 | _MODEL_TYPE_=cycle_wgan 6 | # GPU assign 7 | _GPU_DEV_="0" 8 | # GPU memory use 9 | _GPU_MEM_=0.9 10 | # conference flag 11 | _CONFERENCE_=sota 12 | # DB type. (requires a folder at _DBDIR_) 13 | _DATABASE_=flo 14 | 15 | # Set up of base folder for running GAN 16 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 17 | mkdir $_BASE_FOLDER_ --parents 18 | 19 | # Architecture file. Provided in the code 20 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_MODEL_TYPE_.json 21 | 22 | _TRAIN_GAN_=1 23 | _TRAIN_CLS_=1 24 | _TRAIN_REG_=1 25 | # Saving points for REG, CLS and GAN 26 | _SAVE_POINTS_="[15,100,926]" 27 | # Saving option for every epoch 0 switch off 28 | _SAVE_=0 29 | 30 | # Training routine for GAN 31 | python train.py --dbname $_DATABASE_ --baseroot $_BASE_FOLDER_ --train_gan $_TRAIN_GAN_ --train_cls $_TRAIN_CLS_ --train_reg $_TRAIN_REG_ --architecture_file $_ARCHITECTURE_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --save_model $_SAVE_ --savepoints $_SAVE_POINTS_ --dataroot $_DBDIR_ 32 | -------------------------------------------------------------------------------- /src/sota/sun/train_gan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Dataset root 3 | _DBDIR_=./data/ 4 | # Model type 5 | _MODEL_TYPE_=cycle_wgan 6 | # GPU assign 7 | _GPU_DEV_="0" 8 | # GPU memory use 9 | _GPU_MEM_=0.9 10 | # conference flag 11 | _CONFERENCE_=sota 12 | # DB type. (requires a folder at _DBDIR_) 13 | _DATABASE_=sun 14 | 15 | # Set up of base folder for running GAN 16 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 17 | mkdir $_BASE_FOLDER_ --parents 18 | 19 | # Architecture file. Provided in the code 20 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_MODEL_TYPE_.json 21 | 22 | _TRAIN_GAN_=1 23 | _TRAIN_CLS_=1 24 | _TRAIN_REG_=1 25 | # Saving points for REG, CLS and GAN 26 | _SAVE_POINTS_="[15,100,985]" 27 | # Saving option for every epoch 0 switch off 28 | _SAVE_=0 29 | 30 | # Training routine for GAN 31 | python train.py --dbname $_DATABASE_ --baseroot $_BASE_FOLDER_ --train_gan $_TRAIN_GAN_ --train_cls $_TRAIN_CLS_ --train_reg $_TRAIN_REG_ --architecture_file $_ARCHITECTURE_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --save_model $_SAVE_ --savepoints $_SAVE_POINTS_ --dataroot $_DBDIR_ 32 | -------------------------------------------------------------------------------- /src/sota/cub/train_gan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Dataset root 3 | _DBDIR_=./data/ 4 | # Model type 5 | _MODEL_TYPE_=cycle_wgan 6 | # GPU assign 7 | _GPU_DEV_="0" 8 | # GPU memory use 9 | _GPU_MEM_=0.9 10 | # conference flag 11 | _CONFERENCE_=sota 12 | # DB type. (requires a folder at _DBDIR_) 13 | _DATABASE_=cub 14 | 15 | # Set up of base folder for running GAN 16 | _BASE_FOLDER_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 17 | mkdir $_BASE_FOLDER_ --parents 18 | 19 | # Architecture file. Provided in the code 20 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_MODEL_TYPE_.json 21 | 22 | _TRAIN_GAN_=1 23 | 24 | #Note: the classifier is not used to train, only to evaluate the fake samples, as an stop criteria. 25 | _TRAIN_CLS_=1 26 | _TRAIN_REG_=1 27 | # Saving points for REG, CLS and GAN 28 | _SAVE_POINTS_="[30,40,926]" 29 | # Saving option for every epoch 0 switch off 30 | _SAVE_=0 31 | 32 | # Training routine for GAN 33 | python train.py --dbname $_DATABASE_ --baseroot $_BASE_FOLDER_ --train_gan $_TRAIN_GAN_ --train_cls $_TRAIN_CLS_ --train_reg $_TRAIN_REG_ --architecture_file $_ARCHITECTURE_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --save_model $_SAVE_ --savepoints $_SAVE_POINTS_ --dataroot $_DBDIR_ 34 | -------------------------------------------------------------------------------- /src/sota/flo/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | # Trainable architecture 8 | _TRAINABLE_=fullyconnected 9 | 10 | _GPU_DEV_="0" 11 | _GPU_MEM_=0.9 12 | _CONFERENCE_=sota 13 | _DATABASE_=flo 14 | 15 | _BASE_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 16 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_TRAINABLE_.json 17 | _GAN_FOLDER_=$_BASE_/gan-model/ 18 | 19 | _BASE_FOLDER_=$_BASE_/ 20 | 21 | mkdir --parents $_BASE_FOLDER_ 22 | 23 | 24 | # Fake features directory 25 | _DATADIR_=$_GAN_FOLDER_/source/data/ 26 | # Fake features H5file 27 | _AUG_FILE_=$_DATADIR_/data.h5 28 | 29 | _SAVEPOINTS_="[18]" 30 | _SAVE_=0 31 | # Every N epochs, perform evaluation on validation set 32 | _EVERY_=1 33 | # validation split from training [0,1] 34 | _VAL_SPLIT_=0.1 35 | 36 | # Data augmentation option (merge or replace real dataset) 37 | _AUG_OP_=replace 38 | # domain: openset or zsl 39 | _DOMAIN_=openset 40 | 41 | _DESCRIPTION_=classifier 42 | python -m routines.benchmark --description $_DESCRIPTION_ --baseroot $_BASE_FOLDER_ --augm_file $_AUG_FILE_ --augm_operation $_AUG_OP_ --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --save_model $_SAVE_ --savepoints $_SAVEPOINTS_ --every $_EVERY_ --domain $_DOMAIN_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --validation_split $_VAL_SPLIT_ --dataroot $_DBDIR_ -------------------------------------------------------------------------------- /src/sota/sun/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | # Trainable architecture 8 | _TRAINABLE_=fullyconnected 9 | 10 | _GPU_DEV_="0" 11 | _GPU_MEM_=0.9 12 | _CONFERENCE_=sota 13 | _DATABASE_=sun 14 | 15 | _BASE_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 16 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_TRAINABLE_.json 17 | _GAN_FOLDER_=$_BASE_/gan-model/ 18 | 19 | _BASE_FOLDER_=$_BASE_/ 20 | 21 | mkdir --parents $_BASE_FOLDER_ 22 | 23 | 24 | # Fake features directory 25 | _DATADIR_=$_GAN_FOLDER_/source/data/ 26 | # Fake features H5file 27 | _AUG_FILE_=$_DATADIR_/data.h5 28 | 29 | _SAVEPOINTS_="[85]" 30 | _SAVE_=0 31 | # Every N epochs, perform evaluation on validation set 32 | _EVERY_=1 33 | # validation split from training [0,1] 34 | _VAL_SPLIT_=0.1 35 | 36 | # Data augmentation option (merge or replace real dataset) 37 | _AUG_OP_=merge 38 | # domain: openset or zsl 39 | _DOMAIN_=openset 40 | 41 | _DESCRIPTION_=classifier 42 | python -m routines.benchmark --description $_DESCRIPTION_ --baseroot $_BASE_FOLDER_ --augm_file $_AUG_FILE_ --augm_operation $_AUG_OP_ --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --save_model $_SAVE_ --savepoints $_SAVEPOINTS_ --every $_EVERY_ --domain $_DOMAIN_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --validation_split $_VAL_SPLIT_ --dataroot $_DBDIR_ 43 | 44 | -------------------------------------------------------------------------------- /src/sota/awa1/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | # Trainable architecture 8 | _TRAINABLE_=fullyconnected 9 | 10 | _GPU_DEV_="0" 11 | _GPU_MEM_=0.9 12 | _CONFERENCE_=sota 13 | _DATABASE_=awa1 14 | 15 | _BASE_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 16 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_TRAINABLE_.json 17 | _GAN_FOLDER_=$_BASE_/gan-model/ 18 | 19 | _BASE_FOLDER_=$_BASE_/ 20 | 21 | mkdir --parents $_BASE_FOLDER_ 22 | 23 | 24 | # Fake features directory 25 | _DATADIR_=$_GAN_FOLDER_/source/data/ 26 | # Fake features H5file 27 | _AUG_FILE_=$_DATADIR_/data.h5 28 | 29 | _SAVEPOINTS_="[37]" 30 | _SAVE_=0 31 | # Every N epochs, perform evaluation on validation set 32 | _EVERY_=1 33 | # validation split from training [0,1] 34 | _VAL_SPLIT_=0.1 35 | 36 | # Data augmentation option (merge or replace real dataset) 37 | _AUG_OP_=replace 38 | # domain: openset or zsl 39 | _DOMAIN_=openset 40 | 41 | _DESCRIPTION_=classifier 42 | python -m routines.benchmark --description $_DESCRIPTION_ --baseroot $_BASE_FOLDER_ --augm_file $_AUG_FILE_ --augm_operation $_AUG_OP_ --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --save_model $_SAVE_ --savepoints $_SAVEPOINTS_ --every $_EVERY_ --domain $_DOMAIN_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --validation_split $_VAL_SPLIT_ --dataroot $_DBDIR_ 43 | 44 | -------------------------------------------------------------------------------- /src/sota/cub/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Dataset root 4 | _DBDIR_=./data/ 5 | # Model type 6 | _MODEL_TYPE_=cycle_wgan 7 | # Trainable architecture 8 | _TRAINABLE_=fullyconnected 9 | 10 | _GPU_DEV_="0" 11 | _GPU_MEM_=0.9 12 | _CONFERENCE_=sota 13 | _DATABASE_=cub 14 | 15 | _BASE_=./experiments/$_CONFERENCE_/$_DATABASE_/$_MODEL_TYPE_/ 16 | _ARCHITECTURE_=./src/$_CONFERENCE_/$_DATABASE_/architecture/$_TRAINABLE_.json 17 | _GAN_FOLDER_=$_BASE_/gan-model/ 18 | 19 | _BASE_FOLDER_=$_BASE_/ 20 | 21 | mkdir --parents $_BASE_FOLDER_ 22 | 23 | 24 | # Fake features directory 25 | _DATADIR_=$_GAN_FOLDER_/source/data/ 26 | # Fake features H5file 27 | _AUG_FILE_=$_DATADIR_/data.h5 28 | 29 | _SAVEPOINTS_="[1,80]" 30 | _SAVE_=0 31 | # Every N epochs, perform evaluation on validation set 32 | _EVERY_=1 33 | # validation split from training [0,1] 34 | _VAL_SPLIT_=0.1 35 | 36 | # Data augmentation option (merge or replace real dataset) 37 | _AUG_OP_=merge 38 | # domain: openset or zsl 39 | _DOMAIN_=openset 40 | 41 | _DESCRIPTION_=classifier 42 | python -m routines.benchmark --description $_DESCRIPTION_ --baseroot $_BASE_FOLDER_ --augm_file $_AUG_FILE_ --augm_operation $_AUG_OP_ --dbname $_DATABASE_ --architecture_file $_ARCHITECTURE_ --save_model $_SAVE_ --savepoints $_SAVEPOINTS_ --every $_EVERY_ --domain $_DOMAIN_ --gpu_devices $_GPU_DEV_ --gpu_memory $_GPU_MEM_ --validation_split $_VAL_SPLIT_ --dataroot $_DBDIR_ 43 | 44 | -------------------------------------------------------------------------------- /models/generators.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import BaseModel 25 | import tensorflow as tf 26 | class Generator(BaseModel): 27 | 28 | def __init__(self, hparams=None): 29 | if hparams != None: 30 | if 'namespace' not in hparams: 31 | hparams['namespace'] = 'generator/' 32 | super(Generator, self).__init__(hparams) 33 | 34 | __MODEL__=Generator 35 | -------------------------------------------------------------------------------- /src/sota/cub/architecture/fullyconnected.json: -------------------------------------------------------------------------------- 1 | {"namespace":"classifier", 2 | "x_dim": 2048, 3 | "y_dim": 200, 4 | "lmbda": 10, 5 | "wdecay": 1e-4, 6 | "epochs": 80, 7 | "batch": 4096, 8 | "y_dim": 200, 9 | "beta":0.01, 10 | "placeholder_input": ["x"], 11 | "placeholder_input": ["x","y"], 12 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 13 | {"name": "a", "shape": [null, 1024], "dtype": "float32"}, 14 | {"name": "y", "shape": [null, 200], "dtype": "float32"}, 15 | {"name": "y_pred", "shape": [null, 200], "dtype": "float32"}, 16 | {"name": "a_pred", "shape": [null, 1024], "dtype": "float32"}, 17 | {"name": "a_dict", "shape": [null, 1024], "dtype": "float32"}, 18 | {"name": "z", "shape": [null, 1024], "dtype": "float32"}], 19 | "placeholder_call": ["x"], 20 | "architecture": 21 | [{"name": "c_fc1", "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, "shape":[2048, 200]}, 22 | {"name": "c_bc1", "initializer": {"name":"constant", "params": {"value": 0.9999}}, "shape":[200]}], 23 | 24 | "operators": 25 | [{"op": "placeholder", "input":"x"}, 26 | {"op":"matmul", "input": "layers/c_fc1"}, 27 | {"op":"bias_add", "input": "layers/c_bc1"}, 28 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 29 | {"op": "activation", "type": "softmax"}], 30 | 31 | "optimizer": 32 | {"name":"adam", 33 | "lr": 1e-4, 34 | "decay": 1e-3} 35 | } -------------------------------------------------------------------------------- /src/sota/flo/architecture/fullyconnected.json: -------------------------------------------------------------------------------- 1 | 2 | {"namespace":"classifier", 3 | "x_dim": 2048, 4 | "y_dim": 102, 5 | 6 | "lmbda": 10, 7 | "epochs": 18, 8 | 9 | "beta":0.01, 10 | "batch": 2048, 11 | "wdecay": 1e-4, 12 | 13 | "placeholder_input": ["x"], 14 | "placeholder_input": ["x","y"], 15 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 16 | {"name": "a", "shape": [null, 1024], "dtype": "float32"}, 17 | {"name": "y", "shape": [null, 102], "dtype": "float32"}, 18 | {"name": "y_pred", "shape": [null, 102], "dtype": "float32"}, 19 | {"name": "a_pred", "shape": [null, 1024], "dtype": "float32"}, 20 | {"name": "a_dict", "shape": [null, 1024], "dtype": "float32"}, 21 | {"name": "z", "shape": [null, 1024], "dtype": "float32"}], 22 | "placeholder_call": ["x"], 23 | "architecture": 24 | [{"name": "c_fc1", "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, "shape":[2048, 102]}, 25 | {"name": "c_bc1", "initializer": {"name":"constant", "params": {"value": 0.9999}}, "shape":[102]}], 26 | 27 | "operators": 28 | [{"op": "placeholder", "input":"x"}, 29 | {"op":"matmul", "input": "layers/c_fc1"}, 30 | {"op":"bias_add", "input": "layers/c_bc1"}, 31 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 32 | {"op": "activation", "type": "softmax"}], 33 | 34 | "optimizer": 35 | {"name":"adam", 36 | "lr": 9.9701e-5, 37 | "decay": 3.988e-3} 38 | } -------------------------------------------------------------------------------- /models/discriminators.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import BaseModel 25 | import tensorflow as tf 26 | 27 | class Discriminator(BaseModel): 28 | 29 | def __init__(self, hparams=None): 30 | if hparams != None: 31 | if 'namespace' not in hparams: 32 | hparams['namespace'] = 'discriminator/' 33 | 34 | super(Discriminator, self).__init__(hparams) 35 | 36 | __MODEL__=Discriminator -------------------------------------------------------------------------------- /src/sota/sun/architecture/fullyconnected.json: -------------------------------------------------------------------------------- 1 | {"namespace":"classifier", 2 | "x_dim": 2048, 3 | "y_dim": 717, 4 | "lmbda": 10, 5 | 6 | "wdecay": 1e-4, 7 | "epochs": 85, 8 | "batch": 2048, 9 | 10 | "placeholder_input": ["x"], 11 | "placeholder_input": ["x","y"], 12 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 13 | {"name": "a", "shape": [null, 102], "dtype": "float32"}, 14 | {"name": "y", "shape": [null, 717], "dtype": "float32"}, 15 | {"name": "y_pred", "shape": [null, 717], "dtype": "float32"}, 16 | {"name": "a_pred", "shape": [null, 102], "dtype": "float32"}, 17 | {"name": "a_dict", "shape": [null, 102], "dtype": "float32"}, 18 | {"name": "z", "shape": [null, 102], "dtype": "float32"}], 19 | 20 | "placeholder_call": ["x"], 21 | 22 | "architecture": 23 | [{"name": "c_fc1", 24 | "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, 25 | "shape":[2048, 717]}, 26 | {"name": "c_bc1", 27 | "initializer": {"name":"constant", "params": {"value": 0.9999}}, 28 | "shape":[717]}], 29 | 30 | "operators": 31 | [{"op": "placeholder", "input":"x"}, 32 | {"op":"matmul", "input": "layers/c_fc1"}, 33 | {"op":"bias_add", "input": "layers/c_bc1"}, 34 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 35 | {"op": "activation", "type": "softmax"}], 36 | 37 | "optimizer": 38 | {"name":"adam", 39 | "lr": 1e-4, 40 | "decay": 12e-3} 41 | } -------------------------------------------------------------------------------- /src/sota/awa1/architecture/fullyconnected.json: -------------------------------------------------------------------------------- 1 | {"namespace":"classifier", 2 | "x_dim": 2048, 3 | "y_dim": 50, 4 | "lmbda": 10, 5 | "epochs": 37, 6 | "batch": 9128, 7 | "y_dim": 50, 8 | "beta":0.01, 9 | 10 | "wdecay": 0.0001, 11 | "placeholder_input": ["x"], 12 | "placeholder_input": ["x","y"], 13 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 14 | {"name": "a", "shape": [null, 85], "dtype": "float32"}, 15 | {"name": "y", "shape": [null, 50], "dtype": "float32"}, 16 | {"name": "y_pred", "shape": [null, 50], "dtype": "float32"}, 17 | {"name": "a_pred", "shape": [null, 85], "dtype": "float32"}, 18 | {"name": "a_dict", "shape": [null, 85], "dtype": "float32"}, 19 | {"name": "z", "shape": [null, 85], "dtype": "float32"}], 20 | 21 | "placeholder_call": ["x"], 22 | "architecture": 23 | [{"name": "c_fc1", "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, "shape":[2048, 50]}, 24 | {"name": "c_bc1", "initializer": {"name":"constant", "params": {"value": 0.9999}}, "shape":[50]}], 25 | 26 | "operators": 27 | [ 28 | {"op": "placeholder", "input":"x"}, 29 | {"op":"matmul", "input": "layers/c_fc1"}, 30 | {"op":"bias_add", "input": "layers/c_bc1"}, 31 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 32 | {"op": "activation", "type": "softmax"} 33 | ], 34 | 35 | "optimizer": 36 | {"name":"adam", 37 | "lr": 9.9800e-5, 38 | "decay": 2.84e-3} 39 | } -------------------------------------------------------------------------------- /models/supporttypes.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | __INITIALIZERS__ = {'truncated': tf.truncated_normal_initializer, 26 | 'constant': tf.constant_initializer, 27 | 'zeros': tf.zeros_initializer, 28 | 'xavier': tf.contrib.layers.xavier_initializer, 29 | 'batch_norm': tf.layers.BatchNormalization} 30 | 31 | __OPERATORS__ = {'matmul': tf.matmul, 32 | 'bias_add': tf.nn.bias_add, 33 | 'concat': tf.concat, 34 | 'relu': tf.nn.relu, 35 | 'softmax': tf.nn.softmax, 36 | 'leaky_relu': tf.nn.leaky_relu, 37 | 'sigmoid': tf.nn.sigmoid, 38 | 'dropout': tf.nn.dropout} 39 | 40 | __OPTIMIZERS__ = {'adam': tf.train.AdamOptimizer, 41 | 'sgd': tf.train.GradientDescentOptimizer, 42 | 'rms': tf.train.RMSPropOptimizer} -------------------------------------------------------------------------------- /options/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .models import ModelsBase 25 | from .dtype import * 26 | 27 | class TestOptions(ModelsBase): 28 | def __init__(self): 29 | super(TestOptions, self).__init__() 30 | 31 | def initialize(self): 32 | super(TestOptions, self).initialize() 33 | self.parser.add_argument('-s', '--scores', type=str2strlist, default=False, help='Get ensemble scores') 34 | self.parser.add_argument('-d', '--dropout', type=str2bool, default=False, help='Test mcmc dropout') 35 | self.parser.add_argument('-o', '--output', type=str, default='/tmp/', help='Test mcmc dropout') 36 | self.parser.add_argument('-rc', '--recycle_folder', type=str2bool, default=True, help='Recycle load_model path? ../../') 37 | 38 | __OPTION__=TestOptions 39 | 40 | if __name__ == '__main__': 41 | print('-'*100) 42 | print(':: Testing file: {}'.format(__file__)) 43 | print('-'*100) 44 | 45 | params = __OPTION__() 46 | params.parse() 47 | -------------------------------------------------------------------------------- /models/wgan.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | 26 | from .gan import GAN 27 | from .classifier import Classifier 28 | from copy import deepcopy 29 | 30 | import numpy as np 31 | class WGAN(GAN): 32 | 33 | def __init__(self, hparams): 34 | if 'namespace' not in hparams: 35 | hparams['namespace'] = 'wgan' 36 | 37 | if '__list_models__' not in hparams: 38 | hparams['__list_models__'] = ['generator', 'discriminator'] 39 | super(WGAN, self).__init__(hparams) 40 | 41 | def cwgan_loss(self): 42 | alpha = tf.random_uniform(shape=tf.shape(self.generator.output), minval=0., maxval=1.) 43 | interpolation = alpha * self.discriminator.x + (1. - alpha) * self.generator.output 44 | 45 | d_input = tf.concat([interpolation, self.generator.a], -1) 46 | grad = tf.gradients(self.discriminator.forward(d_input), [interpolation])[0] 47 | grad_norm = tf.norm(grad, axis=1, ord='euclidean') 48 | self.grad_pen = self.lmbda * tf.reduce_mean(tf.square(grad_norm - 1)) 49 | 50 | return self.d_real - self.d_fake + self.grad_pen 51 | 52 | __MODEL__=WGAN 53 | -------------------------------------------------------------------------------- /options/dtype.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import argparse 25 | def str2list(v): 26 | import re 27 | return list([var.replace('\'', '') for var in re.search(r'\[(.*)\]', v).group(1).split(',')]) 28 | 29 | def str2strlist(v): 30 | values = ''.join(''.join(v.split("[")).split("]")) 31 | values = ''.join(''.join(values.split("(")).split(")")) 32 | values = ','.join(values.split(', ')).split(',') 33 | return list(values) 34 | 35 | def str2intlist(v): 36 | values = ''.join(''.join(v.split("[")).split("]")) 37 | values = ''.join(''.join(values.split("(")).split(")")) 38 | values = ','.join(values.split(', ')).split(',') 39 | return list([int(value) for value in values]) 40 | 41 | def str2array(v, rtype=int): 42 | import re 43 | import numpy as np 44 | return np.array([var.replace('\'', '') for var in re.search(r'\[(.*)\]', v).group(1).split(',')]).astype(rtype) 45 | 46 | def str2bool(v): 47 | if v.lower() in ('yes', 'true', 't', 'y', '1'): 48 | return True 49 | elif v.lower() in ('no', 'false', 'f', 'n', '0'): 50 | return False 51 | else: 52 | raise argparse.ArgumentTypeError('Boolean value expected.') -------------------------------------------------------------------------------- /options/gan.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .models import ModelsBase 25 | from .dtype import * 26 | 27 | class GANOptions(ModelsBase): 28 | def __init__(self): 29 | super(GANOptions, self).__init__() 30 | 31 | def initialize(self): 32 | super(GANOptions, self).initialize() 33 | self.parser.add_argument('--architecture_file', type=str, default='./src/architecture/gan_awa.json', 34 | help='Type of attribute to assess') 35 | 36 | self.parser.add_argument('--exp_directories', type=list, help='Directories on experiments_eccv18 folders "["list of names"]"', 37 | default=['checkpoint', 'results', 'source', 'logs']) 38 | 39 | self.parser.add_argument('--train_cls', type=str2bool, help='Pre-train classifier?', 40 | default=False) 41 | 42 | self.parser.add_argument('--train_reg', type=str2bool, help='Pre-train regressor?', 43 | default=False) 44 | 45 | self.parser.add_argument('--train_gan', type=str2bool, help='Train GAN', 46 | default=False) 47 | 48 | # Dataset 49 | self.parser.add_argument('--att_type', type=str, default='continuous', help='Type of attribute') 50 | 51 | 52 | __OPTION__=GANOptions 53 | 54 | if __name__ == '__main__': 55 | print('-'*100) 56 | print(':: Testing file: {}'.format(__file__)) 57 | print('-'*100) 58 | 59 | params = GANOptions() 60 | params.parse() 61 | -------------------------------------------------------------------------------- /models/classifier.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import BaseModel 25 | import tensorflow as tf 26 | 27 | class Classifier(BaseModel): 28 | 29 | def __init__(self, hparams=None): 30 | if hparams != None: 31 | if 'namespace' not in hparams: 32 | hparams['namespace'] = 'classifier' 33 | super(Classifier, self).__init__(hparams) 34 | self.loss_function = self.bce_loss 35 | 36 | def loss(self): 37 | return self.bce_loss(self.outlogit, self.y) 38 | 39 | def __build_specs__(self, training=True): 40 | self.output, out = self.__forward__(ret_all=True) 41 | self.outlogit = out['last_logit'] 42 | self.output_test = self.__forward__(training=False) 43 | self.c_accuracy = tf.metrics.mean_per_class_accuracy(self.y, 44 | self.y_pred, 45 | self.y_dim) 46 | if training: 47 | self.set_loss(self.loss()) 48 | 49 | def bce_loss(self, y_pred, y_true): 50 | out = tf.nn.softmax_cross_entropy_with_logits(logits=y_pred, labels=y_true) 51 | return tf.reduce_mean(out) 52 | 53 | def evaluate(self, data): 54 | y_pred = self(data) 55 | return {'{}_loss'.format(self.get_name()) : self.get_loss(data), 56 | '{}_acc'.format(self.get_name()): self.accuracy({'y_pred': y_pred.argmax(-1), 57 | 'y': data['y'].argmax(-1)})} 58 | 59 | 60 | __MODEL__=Classifier 61 | -------------------------------------------------------------------------------- /models/rwgan.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | from .wgan import WGAN 26 | from .classifier import Classifier 27 | from copy import deepcopy 28 | 29 | import numpy as np 30 | 31 | class RWGAN(WGAN): 32 | def __init__(self, hparams): 33 | if hparams != None: 34 | if 'namespace' not in hparams: 35 | hparams['namespace'] = 'rwgan' 36 | if '__list_models__' not in hparams: 37 | hparams['__list_models__'] = ['regressor', 'classifier', 'generator', 'discriminator'] 38 | super(RWGAN, self).__init__(hparams) 39 | 40 | def __build_specs__(self): 41 | 42 | # Generator step 43 | d_input = tf.concat([self.generator.output, self.generator.a], -1) 44 | self.generator.set_loss(tf.reduce_mean(self.discriminator.forward(d_input))) 45 | 46 | a_out = self.regressor.forward(self.generator.output, ret_all=True)[0] 47 | y_logit = self.classifier.forward(self.generator.output, ret_all=True)[1]['last_logit'] 48 | 49 | self.regularization = self.regressor.loss_function(a_out, self.generator.a) 50 | self.aux_loss = self.regularization * self.regressor.beta 51 | 52 | self.generator.add2loss(self.aux_loss) 53 | 54 | # Discriminator step 55 | self.d_real = tf.reduce_mean(self.discriminator.output) 56 | d_input_fake = tf.concat([self.generator.output, self.discriminator.a], -1) 57 | self.d_fake = tf.reduce_mean(self.discriminator.forward(d_input_fake)) 58 | 59 | self.discriminator.set_loss(self.cwgan_loss()) 60 | 61 | 62 | __MODEL__=RWGAN 63 | -------------------------------------------------------------------------------- /options/generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import Base 25 | from .dtype import * 26 | 27 | class GeneratorOptions(Base): 28 | def __init__(self): 29 | super(GeneratorOptions, self).__init__() 30 | 31 | def initialize(self): 32 | super(GeneratorOptions, self).initialize() 33 | self.parser.add_argument('--architecture_file', type=str2strlist, default='', 34 | help='Architecture json file') 35 | 36 | self.parser.add_argument('--num_features', type=str2intlist, default=300, help='Num of features to generate') 37 | self.parser.add_argument('--iter', type=str2intlist, default=10, help='Num of features to generate') 38 | self.parser.add_argument('--domain', type=str2strlist, default='unseen', help='Generate from given domain: (seen, unseen, openset)') 39 | self.parser.add_argument('--outdir', type=str, default='/tmp/', help='Default directory to save features') 40 | self.parser.add_argument('--outfile', type=str, default='data.h5', help='Filename to save generated features') 41 | 42 | self.parser.add_argument('--verbose', type=str2bool, default=False, help='Print progress?') 43 | self.parser.add_argument('--specs', type=str, default=False, help='Specification json file') 44 | 45 | self.parser.add_argument('--merge', type=str2bool, default=False, help='Option to merge fake to original dataset') 46 | self.parser.add_argument('--save_numpy', type=str2bool, default=False, help='Option to save numpy matrix obj') 47 | 48 | 49 | __OPTION__=GeneratorOptions 50 | 51 | if __name__ == '__main__': 52 | print('-'*100) 53 | print(':: Testing file: {}'.format(__file__)) 54 | print('-'*100) 55 | 56 | params = GeneratorOptions() 57 | params.parse() 58 | -------------------------------------------------------------------------------- /options/benchmark.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .models import ModelsBase 25 | from .dtype import * 26 | 27 | class Benchmark(ModelsBase): 28 | def __init__(self): 29 | super(Benchmark, self).__init__() 30 | 31 | def initialize(self): 32 | super(Benchmark, self).initialize() 33 | self.parser.add_argument('--metric_list', type=str2list, help='Type of attribute', 34 | default=['acc', 35 | 'classifier_loss', 36 | 'h_mean']) 37 | self.parser.add_argument('--exp_directories', type=list, help='Directories on experiments_eccv18 folders "["list of names"]"', 38 | default=['checkpoint', 'results', 'logs']) 39 | 40 | self.parser.add_argument('-arch', '--architecture_file', type=str, default='', 41 | help='Type of attribute to assess') 42 | 43 | self.parser.add_argument('-d', '--domain', type=str, default='openset', 44 | help='[openset, openval, zsl]') 45 | 46 | self.parser.add_argument('--every', type=int, default=5, help='Number of epochs') 47 | 48 | self.parser.add_argument('--merge', type=str2bool, default=False, help='Number of epochs') 49 | self.parser.add_argument('--replace', type=str2bool, default=True, help='Number of epochs') 50 | 51 | self.parser.add_argument('--augm_file', type=str, default=False, help='Filename and path to *.h5') 52 | self.parser.add_argument('--augm_operation', type=str, default='replace', help="How to augmente the dataset? [replace, merge]") 53 | 54 | 55 | 56 | __OPTION__=Benchmark 57 | 58 | if __name__ == '__main__': 59 | print('-'*100) 60 | print(':: Testing file: {}'.format(__file__)) 61 | print('-'*100) 62 | 63 | params = GANOptions() 64 | params.parse() 65 | -------------------------------------------------------------------------------- /options/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import Base 25 | from .dtype import * 26 | 27 | 28 | class ModelsBase(Base): 29 | 30 | def __init__(self): 31 | super(ModelsBase, self).__init__() 32 | 33 | def initialize(self): 34 | super(ModelsBase, self).initialize() 35 | 36 | 37 | self.parser.add_argument('--savepoints', type=str2array, help='Epochs to save checkpoints [1,100,150]', 38 | default="[-99]") 39 | # User setup directories 40 | self.parser.add_argument('--setup', type=bool, help='Setup experimental directory', default=True) 41 | # Saving options 42 | self.parser.add_argument('--save_model', type=str2bool, help='Save model', default=False) 43 | self.parser.add_argument('--save_from', type=int, help='Save from given epoch', default=0) 44 | self.parser.add_argument('--save_every', type=int, help='Save data every N epoch', default=False) 45 | self.parser.add_argument('--saveall', type=bool, help='Save all (careful high disk consuming)', default=False) 46 | 47 | # 48 | self.parser.add_argument('-v', '--verbose', type=str2bool, default=False, help='Verbose') 49 | self.parser.add_argument('--description', type=str, help='Experiment description', default='TEST') 50 | self.parser.add_argument('--plusinfo', type=str, help='Any other information that is relevant') 51 | self.parser.add_argument('--sideinfo', type=str2list, help='Side information to create folder "["list of names"]"', 52 | default=None) 53 | 54 | 55 | # Model 56 | self.parser.add_argument('--load_model', type=str, default=False, help='Load model previously created') 57 | 58 | #Experiments 59 | self.parser.add_argument('--checkpoint', type=int, default=1, help='Number of checkpoint every N epochs') 60 | self.parser.add_argument('--checkpoints_max', type=int, default=3, help='Number max of checkpoints') 61 | self.parser.add_argument('--validation_split', type=float, default=0.1, help='Proportion of validation samples') 62 | 63 | 64 | self.initialized = True 65 | 66 | _OPTION_=ModelsBase 67 | 68 | if __name__ == '__main__': 69 | print('-'*100) 70 | print(':: Testing file: {}'.format(__file__)) 71 | print('-'*100) 72 | 73 | -------------------------------------------------------------------------------- /models/regressor.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .base import BaseModel 25 | import tensorflow as tf 26 | 27 | class Regressor(BaseModel): 28 | 29 | def __init__(self, hparams=None): 30 | if hparams != None: 31 | if 'namespace' not in hparams: 32 | hparams['namespace'] = 'regressor' 33 | super(Regressor, self).__init__(hparams) 34 | self.loss_function = self.mse_loss 35 | 36 | def mse_loss(self, a_pred, a_true): 37 | return tf.reduce_mean(tf.squared_difference(a_pred, a_true)) 38 | 39 | def loss(self): 40 | return self.mse_loss(self.output, self.a) 41 | 42 | def __build_specs__(self, training=True): 43 | self.output = self.__forward__() 44 | self.output_test = self.__forward__(training=False) 45 | self.distances = tf.reduce_sum(tf.abs(tf.subtract(self.a_dict, 46 | tf.expand_dims(self.a_pred,1))), axis=2) 47 | self.knn = tf.argmin(self.distances, 1) 48 | 49 | if self.contains('test'): 50 | if 'mcmc_dropout' in self.test: 51 | self.output_mcmc = self.__forward__() 52 | if training: 53 | self.set_loss(self.loss()) 54 | 55 | def get_knn(self, a_pred, a_dict): 56 | return self.sess.run([self.knn], 57 | feed_dict={self.a_pred: a_pred, self.a_dict: a_dict}) 58 | 59 | def get_distances(self, data): 60 | tdata = {'output':[self.distances], 61 | 'placeholder_batch':'a_pred', 62 | 'placeholders':['a_pred']} 63 | tdata.update(data) 64 | return self.run(tdata) 65 | 66 | def get_y_pred(self, a_pred, data): 67 | import numpy as np 68 | a_masked = np.ma.array(a_pred, mask=np.array([data['y_classes']] * a_pred.shape[0])) 69 | if 'op' in data: 70 | if data['op'] == 'min': 71 | return a_masked.argmin(-1) 72 | if data['op'] == 'max': 73 | return a_masked.argmax(-1) 74 | else: 75 | return a_masked.argmin(-1) 76 | 77 | def evaluate(self, data): 78 | a_pred = self(data) 79 | data['a_pred'] = a_pred 80 | y_prob = self.get_distances(data) 81 | 82 | y_pred = self.get_y_pred(y_prob, data) 83 | return {'{}_loss'.format(self.get_name()) : self.get_loss(data), 84 | '{}_acc'.format(self.get_name()): self.accuracy({'y_pred': y_pred, 'y': data['y'].argmax(-1)})} 85 | 86 | def predict(self, data): 87 | from numpy import array 88 | from util.metrics import softmax 89 | _data = {'a_pred': self(data)} 90 | _data.update(data) 91 | return softmax(-self.get_distances(_data), -1) 92 | 93 | 94 | __MODEL__=Regressor 95 | -------------------------------------------------------------------------------- /routines/aux.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | train.py: Training routine. 25 | Reference: 26 | Felix, R., Vijay Kumar, B. G., Reid, I., & Carneiro, G. (2018). Multi-modal Cycle-consistent Generalized Zero-Shot Learning. arXiv preprint arXiv:1808.00136. 27 | (download paper)[http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf] 28 | """ 29 | _repeat_=100 30 | def __add_parent__(): 31 | import os 32 | import sys 33 | sys.path.append(os.path.abspath('../')) 34 | sys.path.append(os.path.abspath('./')) 35 | 36 | 37 | def load_model(architecture_file, mtype='base'): 38 | import models 39 | from tensorflow import GPUOptions, ConfigProto, Session 40 | checkdir = '/'.join(architecture_file.split('/')[:-1]) + '/' 41 | 42 | print('\n'*2, '-'*_repeat_, '\n:: Open Session\n', '-'*_repeat_, '\n') 43 | gpu_options = GPUOptions(per_process_gpu_memory_fraction=0.5) 44 | config=ConfigProto(allow_soft_placement=True, gpu_options=gpu_options) 45 | sess = Session(config=config) 46 | print('\n', '-'*_repeat_) 47 | 48 | model = models.__dict__[mtype].__MODEL__() 49 | pkg= {'model':model, 50 | 'architecture': architecture_file, 51 | 'dir':checkdir} 52 | models.base.__MODEL__.load_architecture(pkg) 53 | model.set_session(sess) 54 | model.build(training=False) 55 | model.load(pkg) 56 | return model 57 | 58 | def __tensorboard_script__(fname='/tmp/tensorboard_script.sh', logidr='/tmp/'): 59 | with open(fname, 'w') as out: 60 | out.write('#!/usr/bin/env bash\n') 61 | out.write('tensorboard --logdir=\'{}\' --port=6006\n'.format(logidr)) 62 | 63 | def __seed__(): 64 | from tensorflow import set_random_seed 65 | from numpy.random import seed 66 | _seed = 53 67 | seed(_seed) 68 | set_random_seed(_seed) 69 | 70 | def __git_version__(): 71 | import inspect, os 72 | try: 73 | with open('./.git/refs/heads/master') as f: 74 | committag = f.readline() 75 | file_ = inspect.getfile(inspect.currentframe()) # script filename (usually with path) 76 | dir_ = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 77 | 78 | return {'commit': committag, 'script': file_, 'project': dir_} 79 | except: 80 | raise Exception('Not possible to reach project commit versioning!') 81 | 82 | 83 | def update_metric(model, mflag, _set, answer): 84 | for key, item in answer.items(): 85 | if key in _set.__dict__: 86 | _set.__dict__[key].update(item) 87 | try: 88 | model.set_summary(tag='{}/{}/metric/{}'.format(model.namespace, mflag, key), 89 | value=item) 90 | except: 91 | pass 92 | 93 | def get_basic_model(value): 94 | import models 95 | return {'classifier': models.classifier, 96 | 'regressor': models.regressor, 97 | 'generator': models.generators, 98 | 'discriminator': models.discriminators}[value] -------------------------------------------------------------------------------- /options/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .dtype import * 25 | 26 | import argparse 27 | 28 | class Base(): 29 | 30 | def __init__(self): 31 | self.parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) 32 | self.initialized = False 33 | 34 | def initialize(self): 35 | from time import strftime 36 | # Code setupt directories 37 | self.parser.add_argument('-r', '--root', type=str, help='Setup experiment folder (full)', default='/tmp') 38 | self.parser.add_argument('-n', '--namespace', type=str, help='Experiment folder', default='') 39 | self.parser.add_argument('-broot','--baseroot', type=str, help='Parent directory for experimenta folder', default='/tmp') 40 | self.parser.add_argument('-aroot', '--auxroot', type=str, help='Extra directory or file', default='/tmp/') 41 | 42 | # Computer setup 43 | self.parser.add_argument('--gpu_devices', type=str, help='Number of GPU cores required', default=False) 44 | self.parser.add_argument('--gpu_memory', type=float, help='Memory to required for each core', default=0.9) 45 | 46 | # Utils 47 | self.parser.add_argument('--timestamp', type=str, help='Timestamp', 48 | default='{}'.format(strftime("%d%m%y_%H%M%S"))) 49 | 50 | self.parser.add_argument('-db', '--dbname', type=str, help='Dataset root', default='AWA1') 51 | self.parser.add_argument('-droot', '--dataroot', type=str, help='Dataset root', default='/var/scientific/data/eccv18/') 52 | self.parser.add_argument('-ddir', '--datadir', type=str, help='Dataset root path for file', default='') 53 | 54 | self.initialized = True 55 | 56 | 57 | def parse(self, verbose=False): 58 | if not self.initialized: 59 | self.initialize() 60 | 61 | self.opt = self.parser.parse_args() 62 | 63 | if self.opt.datadir == '': 64 | self.opt.datadir = '{}/{}/'.format(self.opt.dataroot, self.opt.dbname).lower() 65 | 66 | args = vars(self.opt) 67 | 68 | if verbose: 69 | self.print() 70 | 71 | return self.opt 72 | 73 | def save(self, root): 74 | try: 75 | from util.storage import Json 76 | Json().save(self.as_dict(), root) 77 | except: 78 | raise Exception('Error: Json:save: not possible to save') 79 | 80 | def print(self): 81 | args = vars(self.opt) 82 | print('\n\n','------------ Options -------------') 83 | for k, v in sorted(args.items()): 84 | print('%s: %s' % (str(k), str(v))) 85 | print('-------------- End ----------------','\n\n') 86 | 87 | def as_dict(self): 88 | if self.initialized: 89 | return self.opt.__dict__ 90 | 91 | def keys(self): 92 | return list(self.__dict__.keys()) 93 | 94 | 95 | _OPTION_=Base 96 | 97 | if __name__ == '__main__': 98 | print('-'*100) 99 | print(':: Testing file: {}'.format(__file__)) 100 | print('-'*100) 101 | 102 | -------------------------------------------------------------------------------- /src/sota/cub/architecture/cycle_wgan.json: -------------------------------------------------------------------------------- 1 | {"namespace":"rwgan", 2 | "x_dim": 2048, 3 | "y_dim": 50, 4 | "lmbda": 10, 5 | "placeholder_input": ["a", "x"], 6 | "__list_models__":["generator", "discriminator", "classifier", "regressor"], 7 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 8 | {"name": "a", "shape": [null, 1024], "dtype": "float32"}, 9 | {"name": "y", "shape": [null, 200], "dtype": "float32"}, 10 | {"name": "y_classes", "shape": [200], "dtype": "float32"}, 11 | {"name": "y_pred", "shape": [null, 200], "dtype": "float32"}, 12 | {"name": "a_pred", "shape": [null, 1024], "dtype": "float32"}, 13 | {"name": "a_dict", "shape": [null, 1024], "dtype": "float32"}, 14 | {"name": "z", "shape": [null, 1024], "dtype": "float32"}], 15 | 16 | "gan":{"epochs":926, 17 | "batch": 64, 18 | "c_beta": 0.01, 19 | "r_beta": 0.01}, 20 | 21 | "generator":{"placeholder_input": ["a", "z"], 22 | "placeholder_call": ["a", "z"], 23 | "architecture": 24 | [{"name": "g_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 4096]}, 25 | {"name": "g_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 26 | {"name": "g_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 2048]}, 27 | {"name": "g_bc2", "initializer": {"name":"zeros"}, "shape":[2048]}], 28 | "operators": 29 | [{"op":"concat", "inputs": ["a", "z"], "axis":-1}, 30 | {"op":"matmul", "input": "layers/g_fc1"}, 31 | {"op":"bias_add", "input": "layers/g_bc1"}, 32 | {"op": "activation", "type": "leaky_relu"}, 33 | {"op":"matmul", "input": "layers/g_fc2"}, 34 | {"op":"bias_add", "input": "layers/g_bc2"}, 35 | {"op": "activation", "type": "relu"}], 36 | 37 | "optimizer": {"name":"adam", 38 | "lr": 1e-4}}, 39 | 40 | "discriminator":{ "placeholder_input": ["x", "a", "generator/a"], 41 | "placeholder_call": ["x", "a"], 42 | "architecture": 43 | [{"name": "d_fc1", "initializer": {"name":"xavier"}, "shape":[3072, 4096]}, 44 | {"name": "d_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 45 | {"name": "d_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 1]}, 46 | {"name": "d_bc2", "initializer": {"name":"zeros"}, "shape":[1]}], 47 | 48 | "operators": 49 | [{"op":"concat", "inputs": ["x", "a"], "axis":-1}, 50 | {"op":"matmul", "input": "layers/d_fc1"}, 51 | {"op":"bias_add", "input": "layers/d_bc1"}, 52 | 53 | {"op": "activation", "type": "leaky_relu"}, 54 | 55 | {"op":"matmul", "input": "layers/d_fc2"}, 56 | {"op":"bias_add", "input": "layers/d_bc2"}], 57 | 58 | "optimizer": 59 | {"name":"adam", 60 | "lr": 1e-3} 61 | }, 62 | 63 | "classifier":{ "placeholder_input": ["x","y"], 64 | "placeholder_call": ["x"], 65 | "epochs": 30, 66 | "batch": 2048, 67 | "y_dim": 200, 68 | "beta":0.01, 69 | "architecture": 70 | [{"name": "c_fc1", "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, "shape":[2048, 200]}, 71 | {"name": "c_bc1", "initializer": {"name":"constant", "params": {"value": 0.9999}}, "shape":[200]}], 72 | 73 | "operators": 74 | [{"op": "placeholder", "input":"x"}, 75 | {"op":"matmul", "input": "layers/c_fc1"}, 76 | {"op":"bias_add", "input": "layers/c_bc1"}, 77 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 78 | {"op": "activation", "type": "softmax"}], 79 | 80 | "optimizer": 81 | {"name":"adam", 82 | "lr": 0.05} 83 | }, 84 | "regressor":{ "placeholder_input": ["x","a", "y"], 85 | "fix_variables": ["a_dict", "y_classes"], 86 | "placeholder_call": ["x"], 87 | "epochs": 40, 88 | "batch": 64, 89 | "x_dim":2048, 90 | "y_dim": 200, 91 | "a_dim":1024, 92 | "wdecay":0.0, 93 | "beta":0.01, 94 | "architecture": 95 | [{"name": "r_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 1024]}, 96 | {"name": "r_bc1", "initializer": {"name":"zeros"}, "shape":[1024]}], 97 | 98 | "operators": 99 | [{"op": "placeholder", "input":"x"}, 100 | {"op":"matmul", "input": "layers/r_fc1"}, 101 | {"op":"bias_add", "input": "layers/r_bc1"}], 102 | 103 | "optimizer": 104 | {"name":"adam", 105 | "lr": 9.9978e-5, 106 | "decay":8.75e-5} 107 | } 108 | } -------------------------------------------------------------------------------- /src/sota/awa1/architecture/cycle_wgan.json: -------------------------------------------------------------------------------- 1 | {"namespace":"rwgan", 2 | "x_dim": 2048, 3 | "y_dim": 50, 4 | "lmbda": 10, 5 | "loss_name":"wgan_loss", 6 | "placeholder_input": ["a", "x"], 7 | "__list_models__":["generator", "discriminator", "classifier", "regressor"], 8 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 9 | {"name": "a", "shape": [null, 85], "dtype": "float32"}, 10 | {"name": "y", "shape": [null, 50], "dtype": "float32"}, 11 | {"name": "y_classes", "shape": [50], "dtype": "float32"}, 12 | {"name": "y_pred", "shape": [null, 50], "dtype": "float32"}, 13 | {"name": "a_pred", "shape": [null, 85], "dtype": "float32"}, 14 | {"name": "a_dict", "shape": [null, 85], "dtype": "float32"}, 15 | {"name": "z", "shape": [null, 85], "dtype": "float32"}], 16 | 17 | "gan":{"epochs":350, 18 | "batch": 64, 19 | "c_beta": 0.01, 20 | "r_beta": 0.01}, 21 | 22 | "generator":{"placeholder_input": ["a", "z"], 23 | "placeholder_call": ["a", "z"], 24 | "architecture": 25 | [{"name": "g_fc1", "initializer": {"name":"xavier"}, "shape":[170, 4096]}, 26 | {"name": "g_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 27 | {"name": "g_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 2048]}, 28 | {"name": "g_bc2", "initializer": {"name":"zeros"}, "shape":[2048]}], 29 | "operators": 30 | [{"op":"concat", "inputs": ["a", "z"], "axis":-1}, 31 | {"op":"matmul", "input": "layers/g_fc1"}, 32 | {"op":"bias_add", "input": "layers/g_bc1"}, 33 | {"op": "activation", "type": "leaky_relu"}, 34 | {"op":"matmul", "input": "layers/g_fc2"}, 35 | {"op":"bias_add", "input": "layers/g_bc2"}, 36 | {"op": "activation", "type": "relu"}], 37 | 38 | "optimizer": {"name":"adam", 39 | "lr": 1e-4}}, 40 | 41 | "discriminator":{ "placeholder_input": ["x", "a", "generator/a"], 42 | "placeholder_call": ["x", "a"], 43 | "architecture": 44 | [{"name": "d_fc1", "initializer": {"name":"xavier"}, "shape":[2133, 4096]}, 45 | {"name": "d_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 46 | {"name": "d_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 1]}, 47 | {"name": "d_bc2", "initializer": {"name":"zeros"}, "shape":[1]}], 48 | 49 | "operators": 50 | [{"op":"concat", "inputs": ["x", "a"], "axis":-1}, 51 | {"op":"matmul", "input": "layers/d_fc1"}, 52 | {"op":"bias_add", "input": "layers/d_bc1"}, 53 | 54 | {"op": "activation", "type": "leaky_relu"}, 55 | 56 | {"op":"matmul", "input": "layers/d_fc2"}, 57 | {"op":"bias_add", "input": "layers/d_bc2"}], 58 | 59 | "optimizer": 60 | {"name":"adam", 61 | "lr": 1e-3} 62 | }, 63 | 64 | "classifier":{ "placeholder_input": ["x","y"], 65 | "placeholder_call": ["x"], 66 | "epochs": 30, 67 | "batch": 2048, 68 | "y_dim": 50, 69 | "beta":0.01, 70 | "architecture": 71 | [{"name": "c_fc1", "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, "shape":[2048, 50]}, 72 | {"name": "c_bc1", "initializer": {"name":"constant", "params": {"value": 0.9999}}, "shape":[50]}], 73 | 74 | "operators": 75 | [{"op": "placeholder", "input":"x"}, 76 | {"op":"matmul", "input": "layers/c_fc1"}, 77 | {"op":"bias_add", "input": "layers/c_bc1"}, 78 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 79 | {"op": "activation", "type": "softmax"}], 80 | 81 | "optimizer": 82 | {"name":"adam", 83 | "lr": 0.05} 84 | }, 85 | "regressor":{ "placeholder_input": ["x","a", "y"], 86 | "fix_variables": ["a_dict", "y_classes"], 87 | "placeholder_call": ["x"], 88 | "epochs": 40, 89 | "batch": 64, 90 | "x_dim":2048, 91 | "y_dim": 50, 92 | "a_dim":85, 93 | "wdecay":0.0, 94 | "beta":0.01, 95 | "architecture": 96 | [{"name": "r_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 85]}, 97 | {"name": "r_bc1", "initializer": {"name":"zeros"}, "shape":[85]}], 98 | 99 | "operators": 100 | [{"op": "placeholder", "input":"x"}, 101 | {"op":"matmul", "input": "layers/r_fc1"}, 102 | {"op":"bias_add", "input": "layers/r_bc1"}], 103 | 104 | "optimizer": 105 | {"name":"adam", 106 | "lr": 9.9945e-5, 107 | "decay":2.645e-4} 108 | } 109 | } -------------------------------------------------------------------------------- /src/sota/flo/architecture/cycle_wgan.json: -------------------------------------------------------------------------------- 1 | {"namespace":"rwgan", 2 | "x_dim": 2048, 3 | "y_dim": 102, 4 | "lmbda": 10, 5 | "placeholder_input": ["a", "x"], 6 | "__list_models__":["generator", "discriminator", "classifier", "regressor"], 7 | 8 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 9 | {"name": "a", "shape": [null, 1024], "dtype": "float32"}, 10 | {"name": "y", "shape": [null, 102], "dtype": "float32"}, 11 | {"name": "y_classes", "shape": [102], "dtype": "float32"}, 12 | {"name": "y_pred", "shape": [null, 102], "dtype": "float32"}, 13 | {"name": "a_pred", "shape": [null, 1024], "dtype": "float32"}, 14 | {"name": "a_dict", "shape": [null, 1024], "dtype": "float32"}, 15 | {"name": "z", "shape": [null, 1024], "dtype": "float32"}], 16 | 17 | "gan":{"epochs":1000, 18 | "batch": 64, 19 | "c_beta": 0.01, 20 | "r_beta": 0.01}, 21 | 22 | "generator":{"placeholder_input": ["a", "z"], 23 | "placeholder_call": ["a", "z"], 24 | "architecture": 25 | [{"name": "g_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 4096]}, 26 | {"name": "g_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 27 | {"name": "g_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 2048]}, 28 | {"name": "g_bc2", "initializer": {"name":"zeros"}, "shape":[2048]}], 29 | "operators": 30 | [{"op":"concat", "inputs": ["a", "z"], "axis":-1}, 31 | {"op":"matmul", "input": "layers/g_fc1"}, 32 | {"op":"bias_add", "input": "layers/g_bc1"}, 33 | {"op": "activation", "type": "leaky_relu"}, 34 | {"op":"matmul", "input": "layers/g_fc2"}, 35 | {"op":"bias_add", "input": "layers/g_bc2"}, 36 | {"op": "activation", "type": "relu"}], 37 | 38 | "optimizer": {"name":"adam", 39 | "lr": 1e-4}}, 40 | 41 | "discriminator":{ "placeholder_input": ["x", "a", "generator/a"], 42 | "placeholder_call": ["x", "a"], 43 | "architecture": 44 | [{"name": "d_fc1", "initializer": {"name":"xavier"}, "shape":[3072, 4096]}, 45 | {"name": "d_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 46 | {"name": "d_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 1]}, 47 | {"name": "d_bc2", "initializer": {"name":"zeros"}, "shape":[1]}], 48 | 49 | "operators": 50 | [{"op":"concat", "inputs": ["x", "a"], "axis":-1}, 51 | {"op":"matmul", "input": "layers/d_fc1"}, 52 | {"op":"bias_add", "input": "layers/d_bc1"}, 53 | 54 | {"op": "activation", "type": "leaky_relu"}, 55 | 56 | {"op":"matmul", "input": "layers/d_fc2"}, 57 | {"op":"bias_add", "input": "layers/d_bc2"}], 58 | 59 | "optimizer": 60 | {"name":"adam", 61 | "lr": 1e-3} 62 | }, 63 | 64 | "classifier":{ "placeholder_input": ["x","y"], 65 | "placeholder_call": ["x"], 66 | "epochs": 100, 67 | "batch": 2048, 68 | "y_dim": 102, 69 | "beta":0.01, 70 | "architecture": 71 | [{"name": "c_fc1", 72 | "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, 73 | "shape":[2048, 102]}, 74 | 75 | {"name": "c_bc1", 76 | "initializer": {"name":"constant", "params": {"value": 0.9999}}, 77 | "shape":[102]}], 78 | 79 | "operators": 80 | [{"op": "placeholder", "input":"x"}, 81 | {"op":"matmul", "input": "layers/c_fc1"}, 82 | {"op":"bias_add", "input": "layers/c_bc1"}, 83 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 84 | {"op": "activation", "type": "softmax"}], 85 | 86 | "optimizer": 87 | {"name":"adam", 88 | "lr": 1e-3, 89 | "decay": 4.8e-3} 90 | }, 91 | "regressor":{ "placeholder_input": ["x","a", "y"], 92 | "fix_variables": ["a_dict", "y_classes"], 93 | "placeholder_call": ["x"], 94 | "epochs": 40, 95 | "batch": 64, 96 | "x_dim":2048, 97 | "y_dim": 102, 98 | "a_dim":1024, 99 | 100 | "wdecay":0.0, 101 | "beta":0.01, 102 | "architecture": 103 | [{"name": "r_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 1024]}, 104 | {"name": "r_bc1", "initializer": {"name":"zeros"}, "shape":[1024]}], 105 | 106 | "operators": 107 | [{"op": "placeholder", "input":"x"}, 108 | {"op":"matmul", "input": "layers/r_fc1"}, 109 | {"op":"bias_add", "input": "layers/r_bc1"}], 110 | 111 | "optimizer": 112 | {"name":"adam", 113 | "lr": 9.99e-5, 114 | "decay":2.28e-05} 115 | } 116 | } -------------------------------------------------------------------------------- /src/sota/sun/architecture/cycle_wgan.json: -------------------------------------------------------------------------------- 1 | {"namespace":"rwgan", 2 | "x_dim": 2048, 3 | "y_dim": 717, 4 | "lmbda": 10, 5 | "placeholder_input": ["a", "x"], 6 | "__list_models__":["generator", "discriminator", "classifier", "regressor"], 7 | "placeholders":[{"name": "x", "shape": [null, 2048], "dtype": "float32"}, 8 | 9 | {"name": "y", "shape": [null, 717], "dtype": "float32"}, 10 | {"name": "y_classes", "shape": [717], "dtype": "float32"}, 11 | {"name": "y_pred", "shape": [null, 717], "dtype": "float32"}, 12 | 13 | {"name": "a", "shape": [null, 102], "dtype": "float32"}, 14 | {"name": "a_pred", "shape": [null, 102], "dtype": "float32"}, 15 | {"name": "a_dict", "shape": [null, 102], "dtype": "float32"}, 16 | {"name": "z", "shape": [null, 102], "dtype": "float32"}], 17 | 18 | "gan":{"epochs":985, 19 | "batch": 64, 20 | "c_beta": 0.01, 21 | "r_beta": 0.01}, 22 | 23 | "generator":{"placeholder_input": ["a", "z"], 24 | "placeholder_call": ["a", "z"], 25 | "architecture": 26 | [{"name": "g_fc1", "initializer": {"name":"xavier"}, "shape":[204, 4096]}, 27 | {"name": "g_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 28 | {"name": "g_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 2048]}, 29 | {"name": "g_bc2", "initializer": {"name":"zeros"}, "shape":[2048]}], 30 | "operators": 31 | [{"op":"concat", "inputs": ["z", "a"], "axis":-1}, 32 | {"op":"matmul", "input": "layers/g_fc1"}, 33 | {"op":"bias_add", "input": "layers/g_bc1"}, 34 | {"op": "activation", "type": "leaky_relu"}, 35 | {"op":"matmul", "input": "layers/g_fc2"}, 36 | {"op":"bias_add", "input": "layers/g_bc2"}, 37 | {"op": "activation", "type": "relu"}], 38 | 39 | "optimizer": {"name":"adam", 40 | "lr": 6.1508e-4, 41 | "decay": 1.8e-2}}, 42 | 43 | "discriminator":{ "placeholder_input": ["x", "a", "generator/a"], 44 | "placeholder_call": ["x", "a"], 45 | "architecture": 46 | [{"name": "d_fc1", "initializer": {"name":"xavier"}, "shape":[2150, 4096]}, 47 | {"name": "d_bc1", "initializer": {"name":"zeros"}, "shape":[4096]}, 48 | {"name": "d_fc2", "initializer": {"name":"xavier"}, "shape":[4096, 1]}, 49 | {"name": "d_bc2", "initializer": {"name":"zeros"}, "shape":[1]}], 50 | 51 | "operators": 52 | [{"op":"concat", "inputs": ["x", "a"], "axis":-1}, 53 | {"op":"matmul", "input": "layers/d_fc1"}, 54 | {"op":"bias_add", "input": "layers/d_bc1"}, 55 | 56 | {"op": "activation", "type": "leaky_relu"}, 57 | 58 | {"op":"matmul", "input": "layers/d_fc2"}, 59 | {"op":"bias_add", "input": "layers/d_bc2"}], 60 | 61 | "optimizer": 62 | {"name":"adam", 63 | "lr": 8.6763e-3, 64 | "decay": 2.53e-3} 65 | }, 66 | 67 | "classifier":{ "placeholder_input": ["x","y"], 68 | "placeholder_call": ["x"], 69 | "epochs": 15, 70 | "batch": 2048, 71 | "y_dim": 717, 72 | "beta":0.01, 73 | 74 | "architecture": 75 | [{"name": "c_fc1", 76 | "initializer": {"name":"truncated", "params":{"stddev": 0.01 }}, 77 | "shape":[2048, 717]}, 78 | 79 | {"name": "c_bc1", 80 | "initializer": {"name":"constant", "params": {"value": 0.9999}}, 81 | "shape":[717]}], 82 | 83 | "operators": 84 | [{"op": "placeholder", "input":"x"}, 85 | {"op":"matmul", "input": "layers/c_fc1"}, 86 | {"op":"bias_add", "input": "layers/c_bc1"}, 87 | {"op": "activation", "type": "relu", "out": 1, "ref":"last_logit"}, 88 | {"op": "activation", "type": "softmax"}], 89 | 90 | "optimizer": 91 | {"name":"adam", 92 | "lr": 9.6154e-4, 93 | "decay": 4.45e-2} 94 | }, 95 | "regressor":{ "placeholder_input": ["x","a", "y"], 96 | "fix_variables": ["a_dict", "y_classes"], 97 | "placeholder_call": ["x"], 98 | "y_dim": 717, 99 | 100 | "epochs": 100, 101 | "batch": 64, 102 | "wdecay": 1e-3, 103 | "beta": 0.01, 104 | 105 | "architecture": 106 | [{"name": "r_fc1", "initializer": {"name":"xavier"}, "shape":[2048, 102]}, 107 | {"name": "r_bc1", "initializer": {"name":"zeros"}, "shape":[102]}], 108 | 109 | "operators": 110 | [{"op": "placeholder", "input":"x"}, 111 | {"op":"matmul", "input": "layers/r_fc1"}, 112 | {"op":"bias_add", "input": "layers/r_bc1"}], 113 | 114 | "optimizer": 115 | {"name":"adam", 116 | "lr": 9.6469e-5, 117 | "decay":1.38e-2} 118 | } 119 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-modal Cycle-consistent Generalized Zero-Shot Learning 2 | ## cycle-WGAN ECCV 18 3 | 4 | **Paper**: [download paper](http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf) 5 | 6 | Code for model presented on our paper accepted on European Conference on Computer Vision 2018. 7 | 8 | **Abstract**: In generalized zero shot learning (GZSL), the set of classes are split into seen and unseen classes, where training relies on the semantic features of the seen and unseen classes and the visual representations of only the seen classes, while testing uses the visual representations of the seen and unseen classes. Current methods address GZSL by learning a transformation from the visual to the semantic space, exploring the assumption that the distribution of classes in the semantic and visual spaces is relatively similar. Such methods tend to transform unseen testing visual representations into one of the seen classes' semantic features instead of the semantic features of the correct unseen class, resulting in low accuracy GZSL classification. Recently, generative adversarial networks (GAN) have been explored to synthesize visual representations of the unseen classes from their semantic features - the synthesized representations of the seen and unseen classes are then used to train the GZSL classifier. This approach has been shown to boost GZSL classification accuracy, but there is one important missing constraint: there is no guarantee that synthetic visual representations can generate back their semantic feature in a multi-modal cycle-consistent manner. This missing constraint can result in synthetic visual representations that do not represent well their semantic features, which means that the use of this constraint can improve GAN-based approaches. In this paper, we propose the use of such constraint based on a new regularization for the GAN training that forces the generated visual features to reconstruct their original semantic features. Once our model is trained with this multi-modal cycle-consistent semantic compatibility, we can then synthesize more representative visual representations for the seen and, more importantly, for the unseen classes. Our proposed approach shows the best GZSL classification results in the field in several publicly available datasets. 9 | 10 | ## Citation 11 | ``` 12 | @inproceedings{felix2018multi, 13 | title={Multi-modal Cycle-Consistent Generalized Zero-Shot Learning}, 14 | author={Felix, Rafael and Kumar, BG Vijay and Reid, Ian and Carneiro, Gustavo}, 15 | booktitle={European Conference on Computer Vision}, 16 | pages={21--37}, 17 | year={2018}, 18 | organization={Springer} 19 | } 20 | ``` 21 | 22 | ## Dependencies 23 | 24 | In order to reproduce the code, please check the `requirements.txt` file. 25 | Attach: [requirements.txt](./requirements.txt). 26 | 27 | In addition, it is necessary to clone the repository [util](https://github.com/rfelixmg/util), which is an extra dependency for this project with some functionalities. 28 | 29 | **Package**: [util](https://github.com/rfelixmg/util) -- https://github.com/rfelixmg/util.git 30 | 31 | 32 | # Basic Usage: 33 | 34 | The main file for training cycle-WGAN is [train.py](https://github.com/rfelixmg/frwgan-eccv18/blob/master/train.py) 35 | 36 | ``` 37 | # Help: 38 | python train.py --help 39 | 40 | # Example 41 | python train.py --baseroot /tmp/test/ --train_cls 1 --train_reg 1 --train_gan 1 --architecture_file ./src/bash/eccv18/awa/architecture_rwgan.json --gpu_devices "0" --gpu_memory 0.8 --save_model 0 --savepoints "[30,40,300]" 42 | 43 | usage: train.py [-h] [--root ROOT] [--namespace NAMESPACE] 44 | [--gpu_devices GPU_DEVICES] [--gpu_memory GPU_MEMORY] 45 | [--savepoints SAVEPOINTS] [--setup SETUP] 46 | [--save_model SAVE_MODEL] [--save_from SAVE_FROM] 47 | [--save_every SAVE_EVERY] [--saveall SAVEALL] 48 | [--dbname DBNAME] [--dataroot DATAROOT] [--datadir DATADIR] 49 | [--baseroot BASEROOT] [--description DESCRIPTION] 50 | [--plusinfo PLUSINFO] [--sideinfo SIDEINFO] 51 | [--exp_directories EXP_DIRECTORIES] [--auxroot AUXROOT] 52 | [--timestamp TIMESTAMP] [--load_model LOAD_MODEL] 53 | [--checkpoint CHECKPOINT] [--checkpoints_max CHECKPOINTS_MAX] 54 | [--validation_split VALIDATION_SPLIT] 55 | [--metric_list METRIC_LIST] 56 | [--checkpoint_metric CHECKPOINT_METRIC] 57 | [--architecture_file ARCHITECTURE_FILE] 58 | [--train_cls TRAIN_CLS] [--train_reg TRAIN_REG] 59 | [--train_gan TRAIN_GAN] [--att_type ATT_TYPE] 60 | 61 | ``` 62 | 63 | ## Pre-defined experiments 64 | 65 | We added a few routines that running the entire training, which reproduces State-of-the-art results reported in our paper. You will find these experiments in ./src/sota/{dataset_name}/. For each dataset, we defined: 66 | > 1. **train_gan.sh** -- script to perform the training of cycle-WGAN. 67 | > 2. **generate_dataset.sh** -- script which uses the trained cycle-WGAN to generate pseudo samples. 68 | > 3. **benchmark.sh** -- script to train the fully connected classifier to perform GZSL classification. 69 | > 4. **tester.sh** -- script which uses the trained cycle-WGAN to generate pseudo samples. 70 | > 5. **architecture/cycle-wgan.json** -- architecture file for cycle-WGAN. 71 | > 6. **architecture/fully-connected.json** -- architecture file for classifier. 72 | 73 | **Running** 74 | ``` 75 | # make sure to adjust the paths and gpu usage parameters 76 | bash ./src/sota/train_gan.sh 77 | ``` 78 | 79 | # Datasets 80 | 81 | ## Original files 82 | **Download**: [dataset](https://cvml.ist.ac.at/AwA2/) ~ https://cvml.ist.ac.at/AwA2 83 | 84 | This dataset provides a platform to benchmark transfer-learning algorithms, in particular, attribute base classification and zero-shot learning. It can act as a drop-in replacement to the original Animals with Attributes (AwA) dataset, as it has the same class structure and almost the same characteristics. In addition, on this website, you will find CUB, SUN, FLO. 85 | 86 | ## Dataset in H5File 87 | 88 | In order to run our experiments, and facilitate visualization of the dataset, we introduce the GZSL datasets in H5file format. You can download the datasets CUB, SUN, FLO, and AWA1 in the h5file format, which can be downloaded [dataset.zip](https://drive.google.com/open?id=1cJ-Hl5F9LOn4l-53vhu3-zfv0in2ahYI) (~3GB in zip file). Alternativelly, run the following bash lines: 89 | ``` 90 | !wget https://drive.google.com/open?id=1cJ-Hl5F9LOn4l-53vhu3-zfv0in2ahYI -O ./data/datasets.zip 91 | unzip ./data/datasets.zip -d ./data/ 92 | ``` 93 | **HDF5 file:** The current code uses a HDF5 structure to organize the dataset. In order to facilitate the display of the dataset you might want to use HDFView 2.9 (for linux users) 94 | 95 | 96 | 97 | 98 | # Initial setup 99 | 100 | The general setup for running the experiments can be achieve running the following bash lines. 101 | ``` 102 | git clone https://github.com/rfelixmg/frwgan-eccv18.git 103 | cd frwgan-eccv18/ 104 | git clone https://github.com/rfelixmg/util.git 105 | conda install requirements.txt 106 | 107 | wget https://drive.google.com/open?id=1cJ-Hl5F9LOn4l-53vhu3-zfv0in2ahYI -O ./data/datasets.zip 108 | unzip ./data/datasets.zip -d ./data/ 109 | ``` 110 | -------------------------------------------------------------------------------- /routines/augment_features.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | train.py: Training routine. 25 | Reference: 26 | Felix, R., Vijay Kumar, B. G., Reid, I., & Carneiro, G. (2018). Multi-modal Cycle-consistent Generalized Zero-Shot Learning. arXiv preprint arXiv:1808.00136. 27 | (download paper)[http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf] 28 | """ 29 | _repeat_=100 30 | 31 | from .aux import load_model, __add_parent__ 32 | 33 | def get_new_features(model, domain, num_features): 34 | from numpy import array 35 | answer = {'X': array([]), 36 | 'Y': array([]), 37 | 'A': {'continuous' : array([])}} 38 | 39 | for (input_a, input_y) in domain: 40 | batch_a = array([input_a] * num_features) 41 | batch_y = array([input_y] * num_features) 42 | batch_z = model.get_noise(shape=[num_features, input_a.shape[0]]) 43 | data = {'a': batch_a, 44 | 'z': batch_z} 45 | _out = model(data) 46 | answer['X'] = model.merge_array(answer['X'], _out) 47 | answer['Y'] = model.merge_array(answer['Y'], batch_y) 48 | answer['A']['continuous'] = model.merge_array(answer['A']['continuous'], batch_a) 49 | out = answer 50 | 51 | return out 52 | 53 | def save_data(_dataset): 54 | new_dataset = _dataset['train'] 55 | from numpy import savetxt 56 | from util.storage import DataH5py 57 | from util.tensors import merge_array 58 | 59 | try: 60 | if not options.merge: 61 | DataH5py().save_dict_to_hdf5(_dataset, '{}/{}'.format(options.outdir, options.outfile)) 62 | else: 63 | DataH5py().save_dict_to_hdf5(new_dataset, '{}/_generated_{}'.format(options.outdir, options.outfile)) 64 | dataset.train.X = merge_array(dataset.train.X, 65 | new_dataset['X']) 66 | 67 | dataset.train.Y = merge_array(dataset.train.Y, 68 | new_dataset['Y']) 69 | 70 | dataset.train.A.continuous = merge_array(dataset.train.A.continuous, 71 | new_dataset['A']['continuous']) 72 | dataset.info = _dataset['info'] 73 | DataH5py().save_dict_to_hdf5(dataset, '{}/{}'.format(options.outdir, options.outfile)) 74 | 75 | if options.save_numpy: 76 | savetxt('{}/X.npy'.format(options.outdir), new_dataset['X']) 77 | savetxt('{}/Y.npy'.format(options.outdir), new_dataset['Y']) 78 | savetxt('{}/A.npy'.format(options.outdir), new_dataset['A']['continuous']) 79 | 80 | try: 81 | from os import symlink 82 | symlink('{}/knn.h5'.format(options.datadir),'{}/knn.h5'.format(options.outdir)) 83 | except Exception as e: 84 | from warnings import warn 85 | warn("\n:: [warning] Link already exist") 86 | print(e) 87 | 88 | except Exception as e: 89 | import sys, traceback 90 | traceback.print_exc(file=sys.stdout) 91 | 92 | def get_architecture(architecture_file): 93 | 94 | if architecture_file[-4:] == 'json': 95 | return options.architecture_file 96 | else: 97 | from util.files import list_directories 98 | _epochs = list_directories(architecture_file + '/', False) 99 | if 'last_epoch' in _epochs: 100 | _epochs.remove('last_epoch') 101 | if len(_epochs) > 1: 102 | from numpy import sort 103 | 104 | available_epochs = list(sort([int(_value.split('_')[1]) for _value in _epochs])) 105 | 106 | _choice = -99 107 | while(_choice not in available_epochs): 108 | print('-'*_repeat_, '\n:: Please select epoch to generate features:') 109 | print('Epochs available: ', available_epochs) 110 | _choice = int(input('Choice: ')) 111 | _epoch = _epochs[0].split('_') 112 | _epoch[1] = str(_choice) 113 | _epoch = '_'.join(_epoch) 114 | else: 115 | _epoch = _epochs[0] 116 | return '{}/{}/architecture.json'.format(architecture_file, _epoch) 117 | 118 | 119 | def main(dataset, knn): 120 | 121 | import numpy as np 122 | from util.tensors import merge_dict 123 | 124 | print('-'*_repeat_, "\n:: Loading model\n") 125 | architecture_file = get_architecture(options.architecture_file[0]) 126 | model = load_model(architecture_file) 127 | 128 | new_dataset = {'train':{'X': np.array([]), 129 | 'Y': np.array([]), 130 | 'A': {'continuous': np.array([])}}, 131 | 'info': {'dataset': options.dbname, 132 | 'num_features' : str(options.num_features), 133 | 'domain' : str(options.domain), 134 | 'architecture' : str(options.architecture_file), 135 | 'architecture_file' : str(architecture_file), 136 | 'timestamp': options.timestamp}} 137 | 138 | 139 | for _domain, _num in zip(options.domain, options.num_features): 140 | domain = {'unseen':zip(knn.zsl.data, knn.zsl.ids), 141 | 'seen':zip(knn.openval.data, knn.openval.ids), 142 | 'openset':zip(knn.openset.data, knn.openset.ids)}[_domain] 143 | 144 | print('-'*_repeat_, "\n:: Generating features [{}:{}]".format(_domain, _num)) 145 | _db = get_new_features(model, domain, _num) 146 | new_dataset['train'] = merge_dict(new_dataset['train'], _db) 147 | 148 | print('-'*_repeat_, "\n:: Saving generated dataset") 149 | save_data(new_dataset) 150 | 151 | return new_dataset 152 | 153 | if __name__ == '__main__': 154 | try: 155 | __add_parent__() 156 | from options.generator import __OPTION__ as Option 157 | from util import datasets 158 | from models import * 159 | 160 | 161 | print('-'*_repeat_) 162 | print(':: Initializing: {}'.format(__file__)) 163 | print('-'*_repeat_) 164 | 165 | params = Option() 166 | options = params.parse() 167 | 168 | print('-'*_repeat_,'\n:: Loading Dataset') 169 | dataset, knn = datasets.load(options.datadir) 170 | 171 | print('-'*_repeat_,'\n:: Running main') 172 | db_answer = main(dataset, knn) 173 | print('-'*_repeat_,'\n\n:: Finish...') 174 | print(':: End of session\n\n') 175 | 176 | except Exception as e: 177 | import sys, traceback 178 | traceback.print_exc(file=sys.stdout) -------------------------------------------------------------------------------- /routines/tester.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | train.py: Training routine. 25 | Reference: 26 | Felix, R., Vijay Kumar, B. G., Reid, I., & Carneiro, G. (2018). Multi-modal Cycle-consistent Generalized Zero-Shot Learning. arXiv preprint arXiv:1808.00136. 27 | (download paper)[http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf] 28 | """ 29 | 30 | __author__ = "Rafael Felix, Vijay Kumar and Gustavo Carneiro" 31 | __copyright__ = "MIT License" 32 | __credits__ = ["Rafael Felix", "Gustavo Carneiro", "Vijay Kumar", "Ian Reid"] 33 | __license__ = "MIT License" 34 | __version__ = "1.0.2" 35 | __maintainer__ = "Rafael Felix" 36 | __email__ = "rafael.felixalves@adelaide.edu.au" 37 | __status__ = "production" 38 | 39 | 40 | 41 | _repeat_=100 42 | from .aux import __tensorboard_script__, __seed__, __git_version__, __add_parent__, update_metric 43 | 44 | def initialize(params): 45 | from util import setup, storage 46 | from os import environ 47 | 48 | params.opt.resultdir = '/'.join(params.opt.load_model.split('/')[:-4]) + '/results/' 49 | 50 | params.opt.architecture = storage.Container(storage.Json.load(options.load_model)) 51 | 52 | if params.opt.gpu_devices: 53 | environ["CUDA_VISIBLE_DEVICES"] = params.opt.gpu_devices 54 | 55 | def prediction(model, dataset, knn): 56 | from util.experiments import label2hot 57 | from util.metrics import h_mean 58 | from util.storage import Json, Dict_Average_Meter 59 | import numpy as np 60 | 61 | response = Dict_Average_Meter() 62 | 63 | data_seen = {'x': dataset.test.seen.X, 64 | 'y': label2hot(dataset.test.seen.Y-1, dataset.n_classes), 65 | 'a_dict': knn.openset.data, 66 | 'y_classes': np.zeros(dataset.n_classes), 67 | 'a': dataset.test.seen.A.continuous, 68 | 'train_type':model.namespace} 69 | 70 | data_unseen = {'x': dataset.test.unseen.X, 71 | 'y': label2hot(dataset.test.unseen.Y-1, dataset.n_classes), 72 | 'a_dict': knn.openset.data, 73 | 'y_classes': np.zeros(dataset.n_classes), 74 | 'a': dataset.test.unseen.A.continuous, 75 | 'train_type':model.namespace} 76 | 77 | test_seen, test_unseen = (model.predict(data_seen), model.predict(data_unseen)) 78 | if options.dropout: 79 | response.set_param("{}/test/seen/vanilla/y_score".format(model.namespace), test_seen) 80 | response.set_param("{}/test/unseen/vanilla/y_score".format(model.namespace), test_unseen) 81 | 82 | _data = model.predict_mcmc(data_seen) 83 | response.set_param("{}/test/seen/y_score".format(model.namespace), _data) 84 | del _data 85 | 86 | _data = model.predict_mcmc(data_unseen) 87 | response.set_param("{}/test/unseen/y_score".format(model.namespace), _data) 88 | del _data 89 | else: 90 | response.set_param("{}/test/seen/y_score".format(model.namespace), test_seen) 91 | response.set_param("{}/test/unseen/y_score".format(model.namespace), test_unseen) 92 | 93 | return response.as_dict() 94 | 95 | 96 | def benchmark(model, dataset, knn): 97 | from util.experiments import label2hot 98 | from util.metrics import h_mean 99 | from util.storage import Json, Dict_Average_Meter 100 | import numpy as np 101 | 102 | response = Dict_Average_Meter() 103 | 104 | data_seen = {'x': dataset.test.seen.X, 105 | 'y': label2hot(dataset.test.seen.Y-1, dataset.n_classes), 106 | 'a_dict': knn.openset.data, 107 | 'y_classes': np.zeros(dataset.n_classes), 108 | 'a': dataset.test.seen.A.continuous, 109 | 'train_type':model.namespace} 110 | 111 | data_unseen = {'x': dataset.test.unseen.X, 112 | 'y': label2hot(dataset.test.unseen.Y-1, dataset.n_classes), 113 | 'a_dict': knn.openset.data, 114 | 'y_classes': np.zeros(dataset.n_classes), 115 | 'a': dataset.test.unseen.A.continuous, 116 | 'train_type':model.namespace} 117 | 118 | test_seen, test_unseen = (model.evaluate(data_seen), model.evaluate(data_unseen)) 119 | _hmean = h_mean(test_seen['{}_acc'.format(model.get_name())], 120 | test_unseen['{}_acc'.format(model.get_name())]) 121 | response.update_meters("{}/test".format(model.namespace), {'h_mean': _hmean}) 122 | response.update_meters("{}/test/seen".format(model.namespace), test_seen) 123 | response.update_meters("{}/test/unseen".format(model.namespace), test_unseen) 124 | 125 | _base = "{}/test/{}/{}_acc".format(model.namespace, '{}', model.namespace) 126 | print(':: Test-Evaluation: ' 127 | 'y(U): {:.3g} | y(S): {:.3g} | H: {:.3g}'.format(response.get_meter(_base.format('unseen')).value(), 128 | response.get_meter(_base.format('seen')).value(), 129 | response.get_meter("{}/test/h_mean".format(model.namespace)).value())) 130 | return response.as_dict() 131 | 132 | 133 | 134 | def main(options, dataset, knn): 135 | 136 | from util.storage import DataH5py, Json 137 | from util.setup import mkdir 138 | from routines.aux import get_basic_model 139 | 140 | if options.load_model: 141 | from .aux import load_model 142 | model = load_model(options.load_model, options.architecture.namespace) 143 | 144 | try: 145 | print(":: Testing benchmark") 146 | results = benchmark(model=model, dataset=dataset, knn=knn) 147 | DataH5py().save_dict_to_hdf5(dic=results, filename='{}/results.h5'.format(options.output)) 148 | 149 | print(":: Saving predictions for future test...") 150 | predictions = prediction(model=model, dataset=dataset, knn=knn) 151 | DataH5py().save_dict_to_hdf5(dic=predictions, filename='{}/predictions.h5'.format(options.output)) 152 | 153 | except: 154 | import sys, traceback 155 | traceback.print_exc(file=sys.stdout) 156 | 157 | return model, None 158 | 159 | 160 | if __name__ == '__main__': 161 | print('-'*100) 162 | print(':: Training file: {}'.format(__file__)) 163 | print('-'*100) 164 | 165 | from options.test import TestOptions as Options 166 | from util import datasets 167 | from sklearn.model_selection import train_test_split 168 | import tensorflow as tf 169 | from models import * 170 | 171 | try: 172 | print(':: Seeding to guarantee reproducibility') 173 | __seed__() 174 | print(':: Parsing parameters') 175 | params = Options() 176 | options = params.parse() 177 | print('='*50,'\n:: Initializing testing') 178 | initialize(params) 179 | 180 | print('='*50,'\n:: Loading Dataset') 181 | dataset, knn = datasets.load(options.datadir) 182 | print(':: Number of classes: ', dataset.n_classes) 183 | 184 | print('='*50, '\n:: Executing main routines') 185 | model, results = main(options=options, dataset=dataset, knn=knn) 186 | 187 | except Exception as e: 188 | import sys, traceback 189 | 190 | traceback.print_exc(file=sys.stdout) -------------------------------------------------------------------------------- /models/gan.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | 26 | from .base import ModelObject 27 | from .discriminators import Discriminator 28 | from .generators import Generator 29 | from .classifier import Classifier 30 | from .regressor import Regressor 31 | from .base import BaseModel 32 | from copy import deepcopy 33 | 34 | import numpy as np 35 | 36 | class GAN(ModelObject): 37 | 38 | def __init__(self, hparams): 39 | super(GAN, self).__init__(hparams) 40 | 41 | if hparams != None: 42 | if 'namespace' not in hparams: 43 | hparams['namespace'] = 'gan/' 44 | if '__list_models__' not in hparams: 45 | self.__list_models__ = ['generator', 'discriminator'] 46 | self.__setup_models__(hparams) 47 | self.regularization = tf.Variable(-99, trainable=False) 48 | 49 | 50 | def get_models(self): 51 | return self.__list_models__ 52 | 53 | def get_basic_model(self, value): 54 | return {'classifier': Classifier, 55 | 'regressor': Regressor, 56 | 'generator': Generator, 57 | 'discriminator': Discriminator}[value] 58 | 59 | def __setup_models__(self, hparams): 60 | for _model in self.__list_models__: 61 | hparams[_model]['placeholders'] = deepcopy(hparams['placeholders']) 62 | hparams[_model]['namespace'] = '{}/{}'.format(self.namespace, _model) 63 | self.__dict__[_model] = self.get_basic_model(_model)(hparams[_model]) 64 | 65 | def set_writer(self, root): 66 | super(GAN, self).set_writer(root) 67 | for _model in self.__list_models__: 68 | self.__dict__[_model].writer = self.writer 69 | 70 | def __build_in__(self): 71 | for _model in self.__list_models__: 72 | self.__dict__[_model].build() 73 | 74 | def __build_specs__(self): 75 | # Generator step 76 | self.generator.set_loss(tf.reduce_mean(self.discriminator.forward(self.generator.output))) 77 | 78 | # Discriminator step 79 | self.d_real = tf.reduce_mean(self.discriminator.output) 80 | self.d_fake = tf.reduce_mean(self.discriminator.forward(self.generator.output)) 81 | self.discriminator.set_loss(self.wgan_loss()) 82 | 83 | def build(self, training=True): 84 | self.__build_in__() 85 | self.__build_specs__() 86 | 87 | self.generator.set_update() 88 | self.discriminator.set_update() 89 | 90 | self.sess.run(tf.local_variables_initializer()) 91 | self.sess.run(tf.global_variables_initializer()) 92 | 93 | def get_update_variables(self, gtype='generator'): 94 | if gtype == 'generator': 95 | return [self.generator._loss, self.generator._update] 96 | elif gtype == 'discriminator': 97 | return [self.discriminator._loss, self.discriminator._update] 98 | else: 99 | return [self.__dict__[gtype]._loss, self.__dict__[gtype]._update] 100 | 101 | def update_generator(self, data): 102 | return self.sess.run([self.generator._loss, 103 | self.regularization, 104 | self.generator._update], 105 | feed_dict={self.generator.a: data['a'], 106 | self.generator.z: data['z'], 107 | self.classifier.y: data['y']}) 108 | 109 | def update_discriminator(self, data): 110 | return self.sess.run([self.discriminator._loss, 111 | self.d_real, 112 | self.d_fake, 113 | self.grad_pen, 114 | self.regularization, 115 | self.discriminator._update], 116 | feed_dict={self.generator.a: data['a'], 117 | self.generator.z: data['z'], 118 | self.discriminator.a: data['a'], 119 | self.discriminator.x: data['x'], 120 | self.classifier.y: data['y']}) 121 | 122 | def wgan_loss(self): 123 | alpha = tf.random_uniform(shape=tf.shape(self.generator.output), minval=0., maxval=1.) 124 | interpolation = alpha * self.generator.output + (1. - alpha) * self.generator.output 125 | 126 | grad = tf.gradients(self.discriminator.forward(interpolation), [interpolation])[0] 127 | grad_norm = tf.norm(grad, axis=1, ord='euclidean') 128 | self.grad_pen = self.lmbda * tf.reduce_mean(tf.square(grad_norm - 1)) 129 | 130 | return self.d_real - self.d_fake + self.grad_pen 131 | 132 | def save(self, data): 133 | assert 'train_type' in data 134 | 135 | if data['train_type'] is 'gan': 136 | train_type = ['generator', 'discriminator'] 137 | elif data['train_type'] is 'all': 138 | train_type = self.__list_models__ 139 | else: 140 | train_type = list([data['train_type']]) 141 | 142 | for _model in train_type: 143 | data['dir'] = '{}/{}/epoch_{}_{}'.format(data['checkdir'], 144 | _model, 145 | data['step'], 146 | data['epochs']) 147 | self.__dict__[_model].save(data) 148 | 149 | 150 | def set_session(self, sess): 151 | self.sess = sess 152 | for _model in self.__list_models__: 153 | self.__dict__[_model].set_session(sess) 154 | 155 | 156 | 157 | def train_gan(self, data, batch_size=512): 158 | for input_ in self.placeholder_input: 159 | assert input_ in data 160 | 161 | d_loss, g_loss = np.array([]), np.array([]) 162 | d_real, d_fake, d_grad = np.array([]), np.array([]), np.array([]) 163 | for ii_, xcur, xsize in self.next_batch(data, batch_size): 164 | gdata = {'a': data['a'][ii_], 165 | 'z': self.get_noise(shape=data['a'][ii_].shape), 166 | 'y': data['y'][ii_]} 167 | 168 | ddata = {'a': data['a'][ii_], 169 | 'z': self.get_noise(shape=data['a'][ii_].shape), 170 | 'x': data['x'][ii_], 171 | 'y': data['y'][ii_]} 172 | 173 | # Generator + Discriminator update 174 | out = self.update_discriminator(ddata) 175 | d_loss = self.merge_array(d_loss, np.array(out[0])) 176 | d_real = self.merge_array(d_real, np.array(out[1])) 177 | d_fake = self.merge_array(d_fake, np.array(out[2])) 178 | d_grad = self.merge_array(d_grad, np.array(out[3])) 179 | 180 | # Generator first update 181 | out = self.update_generator(gdata) 182 | g_loss = self.merge_array(g_loss, np.array(out[0])) 183 | 184 | 185 | _msg = '{} [{}/{}] g: {:.3g} | d: {:.3g}'.format(data['info'], xcur, xsize, 186 | g_loss[-1], 187 | d_loss[-1]) 188 | self.printer(_msg) 189 | 190 | 191 | _msg = '{} [{}/{}] g: {:.3g} | d: {:.3g} [resume]\n'.format(data['info'], xcur, xsize, 192 | g_loss.mean(), 193 | d_loss.mean()) 194 | self.printer(_msg) 195 | self.generator.one_step() 196 | self.generator.params_tensorboard() 197 | self.discriminator.one_step() 198 | self.discriminator.params_tensorboard() 199 | return {'discriminator_loss': d_loss.mean(), 200 | 'discriminator_real': d_real.mean(), 201 | 'discriminator_fake': d_fake.mean(), 202 | 'discriminator_grad': d_grad.mean(), 203 | 'generator_loss': g_loss.mean()} 204 | 205 | 206 | def train(self, data, batch_size=512): 207 | self.counter += 1 208 | 209 | if data['train_type'] is 'gan': 210 | return self.train_gan(data, batch_size=batch_size) 211 | else: 212 | return self.__dict__[data['train_type']].train(data, batch_size) 213 | 214 | def evaluate(self, data): 215 | if 'train_type' in data: 216 | if data['train_type'] is not 'gan': 217 | return self.__dict__[data['train_type']].evaluate(data) 218 | 219 | return self.classifier.evaluate(data) 220 | 221 | __MODEL__ = GAN 222 | -------------------------------------------------------------------------------- /models/modelobj.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | 26 | class Print(object): 27 | def __init__(self): 28 | pass 29 | 30 | def __call__(self, x): 31 | self.print_inline(x) 32 | 33 | def print_inline(self, x): 34 | from sys import stdout 35 | stdout.write('\r{} '.format(x)) 36 | stdout.flush() 37 | 38 | def print(self, x): 39 | self.print_inline(x) 40 | 41 | def next_line(self): 42 | from sys import stdout 43 | stdout.write('\n\r') 44 | stdout.flush() 45 | 46 | class ModelObject(object): 47 | def __init__(self, hparams={}): 48 | self.counter = 0 49 | self.__set_dict__(hparams) 50 | self.printer = Print() 51 | 52 | def reset_counter(self): 53 | self.counter = 0 54 | 55 | def __init_variables__(self): 56 | self.sess.run(tf.local_variables_initializer()) 57 | self.sess.run(tf.global_variables_initializer()) 58 | 59 | def set_session(self, sess): 60 | self.sess = sess 61 | 62 | def get_session(self, sess): 63 | return self.sess 64 | 65 | def __set_dict__(self, data): 66 | for key, value in data.items(): 67 | self.__dict__[key] = value 68 | 69 | def get_subparam(self, tree, data): 70 | levels = data.split('/') 71 | if(len(levels) > 1): 72 | if levels[0] in tree: 73 | return self.get_subparam(tree[levels[0]], '/'.join(levels[1:])) 74 | else: 75 | return False 76 | else: 77 | if data in tree: 78 | return tree[data] 79 | else: 80 | return False 81 | 82 | def label2hot(self, y, dim=False): 83 | import numpy as np 84 | if not dim: 85 | dim = np.max(y) + 1 86 | return np.eye(dim)[y].astype(np.int) 87 | 88 | @property 89 | def vars(self): 90 | return [var for var in tf.global_variables() if self.namespace in var.name] 91 | 92 | def get_param(self, data): 93 | return self.get_subparam(self.__dict__, data) 94 | 95 | def contains(self, namespace): 96 | return namespace in self.__dict__ 97 | 98 | def set_param(self, namespace, data): 99 | levels = namespace.split('/') 100 | last = len(levels)-1 101 | tree = self.__dict__ 102 | for key, _level in enumerate(levels): 103 | if _level in tree: 104 | 105 | if key != last: 106 | tree = tree[_level] 107 | else: 108 | tree[_level] = data 109 | 110 | else: 111 | if key != last: 112 | tree[_level] = {} 113 | tree = tree[_level] 114 | else: 115 | tree[_level] =data 116 | 117 | 118 | def set_writer(self, root): 119 | import tensorflow as tf 120 | self.writer = tf.summary.FileWriter(root, self.sess.graph) 121 | self.writer.flush() 122 | 123 | def summary_dict(self, _base, data): 124 | for key, value in data.items(): 125 | self.set_summary('{}/{}'.format(_base, key), value) 126 | 127 | def set_summary(self, tag, value, stype='summary'): 128 | if stype == 'summary': 129 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, simple_value=value)]) 130 | self.writer.add_summary(summary, self.counter) 131 | elif stype == 'histogram': 132 | self.__summary_histogram__(tag, value) 133 | elif stype == 'text': 134 | self.__sumary__text__(tag, value) 135 | 136 | def __summary__(self, summary, merges=False): 137 | summaries = [tf.summary.scalar(name=key, tensor=value) for key, value in summary.items()] 138 | if merges is not False: 139 | summaries.append(merges) 140 | return tf.summary.merge(summaries) 141 | 142 | def __sumary__text__(self, tag, value): 143 | summary_op = tf.summary.text(tag, tf.convert_to_tensor(value, dtype=tf.string)) 144 | text = self.sess.run([summary_op])[0] 145 | self.writer.add_summary(text, self.counter) 146 | 147 | def __summary_histogram__(self, tag, values, bins=100): 148 | import numpy as np 149 | 150 | # Create a histogram using numpy 151 | counts, bin_edges = np.histogram(values, bins=bins) 152 | 153 | # Fill the fields of the histogram proto 154 | hist = tf.HistogramProto() 155 | hist.min = float(np.min(values)) 156 | hist.max = float(np.max(values)) 157 | hist.num = int(np.prod(values.shape)) 158 | hist.sum = float(np.sum(values)) 159 | hist.sum_squares = float(np.sum(values ** 2)) 160 | 161 | # Drop the start of the first bin 162 | bin_edges = bin_edges[1:] 163 | 164 | # Add bin edges and counts 165 | for edge in bin_edges: 166 | hist.bucket_limit.append(edge) 167 | for c in counts: 168 | hist.bucket.append(c) 169 | 170 | # Create and write Summary 171 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)]) 172 | self.writer.add_summary(summary, self.counter) 173 | self.writer.flush() 174 | 175 | def __summary_features__(self, tag, values, bins=100): 176 | import numpy as np 177 | 178 | # Create a histogram using numpy 179 | counts, bin_edges = np.histogram(values, bins=bins) 180 | 181 | # Fill the fields of the histogram proto 182 | hist = tf.HistogramProto() 183 | hist.min = float(np.min(values)) 184 | hist.max = float(np.max(values)) 185 | hist.num = int(np.prod(values.shape)) 186 | hist.sum = float(np.sum(values)) 187 | hist.sum_squares = float(np.sum(values ** 2)) 188 | 189 | # Drop the start of the first bin 190 | bin_edges = bin_edges[1:] 191 | 192 | # Add bin edges and counts 193 | for edge in bin_edges: 194 | hist.bucket_limit.append(edge) 195 | for c in counts: 196 | hist.bucket.append(c) 197 | 198 | # Create and write Summary 199 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)]) 200 | self.writer.add_summary(summary, self.counter) 201 | self.writer.flush() 202 | 203 | @classmethod 204 | def list_ids(self, x, shuffle=True): 205 | """ 206 | list ids: get a matrix and return a shuffle list of positions 207 | :param x: 208 | :param shuffle: 209 | :return: 210 | """ 211 | from numpy import array 212 | from numpy.random import permutation 213 | dim_x = x.shape[0] 214 | # ids_ = np.array(range(dim_x)) 215 | 216 | if shuffle: 217 | ids_ = permutation(dim_x) 218 | else: 219 | ids_ = array(range(dim_x)) 220 | 221 | return ids_, dim_x 222 | 223 | @classmethod 224 | def merge_array(self, x, y, axis=0): 225 | from numpy import size,atleast_1d, concatenate 226 | if not size(x): 227 | return atleast_1d(y) 228 | elif size(x) and size(y): 229 | return concatenate([x, atleast_1d(y)], axis) 230 | elif size(y): 231 | return atleast_1d(y) 232 | else: 233 | return atleast_1d([]) 234 | 235 | def next_batch(self, data, batch_size, shuffle=True): 236 | import numpy as np 237 | _batch_over = data['placeholder_batch'] if 'placeholder_batch' in data else self.placeholder_input[0] 238 | 239 | ids_, dim_x = self.list_ids(x=data[_batch_over], shuffle=shuffle) 240 | for batch in range(0, dim_x, batch_size): 241 | ii_ = ids_[batch: batch_size + batch] 242 | pck = (ii_, batch, dim_x) 243 | yield pck 244 | 245 | def get_noise(self, shape, mean=0.0, var=1.0): 246 | import numpy as np 247 | return np.random.normal(loc=mean, scale=var, size=shape) 248 | 249 | @classmethod 250 | def accuracy_per_class(self, predict_label, true_label, classes): 251 | ''' 252 | :param predict_label: output of model (matrix) 253 | :param true_label: labels from dataset (array of integers) 254 | :param classes: class labels list() 255 | :return: 256 | ''' 257 | from numpy import sum, float, array 258 | if isinstance(classes, int): 259 | nclass = classes 260 | classes = range(nclass) 261 | else: 262 | nclass = len(classes) 263 | 264 | acc_per_class = [] 265 | for i in range(nclass): 266 | idx = true_label == classes[i] 267 | if idx.sum() != 0: 268 | acc_per_class.append(sum(true_label[idx] == predict_label[idx]) / float(idx.sum())) 269 | if len(acc_per_class) == 0: 270 | return 0. 271 | 272 | return array(acc_per_class).mean() -------------------------------------------------------------------------------- /routines/benchmark.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | train.py: Training routine. 25 | Reference: 26 | Felix, R., Vijay Kumar, B. G., Reid, I., & Carneiro, G. (2018). Multi-modal Cycle-consistent Generalized Zero-Shot Learning. arXiv preprint arXiv:1808.00136. 27 | (download paper)[http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf] 28 | """ 29 | 30 | __author__ = "Rafael Felix, Vijay Kumar and Gustavo Carneiro" 31 | __copyright__ = "MIT License" 32 | __credits__ = ["Rafael Felix", "Gustavo Carneiro", "Vijay Kumar", "Ian Reid"] 33 | __license__ = "MIT License" 34 | __version__ = "1.0.2" 35 | __maintainer__ = "Rafael Felix" 36 | __email__ = "rafael.felixalves@adelaide.edu.au" 37 | __status__ = "production" 38 | 39 | 40 | 41 | _repeat_=100 42 | from .aux import __tensorboard_script__, __seed__, __git_version__, __add_parent__, update_metric 43 | 44 | 45 | 46 | def initialize(params): 47 | from util import setup, storage 48 | from os import environ 49 | 50 | if params.opt.setup: 51 | # Generating experimental setup folder 52 | print(':: Generating experimental setup folder') 53 | res = setup.mkexp(baseroot=params.opt.baseroot, 54 | options=params, 55 | bname='{}_{}'.format(params.opt.description, params.opt.timestamp), 56 | sideinfo=params.opt.sideinfo, 57 | subdirectories=params.opt.exp_directories) 58 | params.opt.root = res['root'] 59 | 60 | for key in params.opt.exp_directories: 61 | params.opt.__setattr__('{}dir'.format(key), '{}/{}/'.format(params.opt.root, key)) 62 | 63 | params.opt.namespace = res['namespace'] 64 | print(':: Experiment will be save in:\n:: {}'.format(params.opt.root)) 65 | options.checkpointdir = '{}/checkpoint/'.format(params.opt.root) 66 | params.save('{}/configuration_{}.json'.format(params.opt.root, params.opt.timestamp)) 67 | params.print() 68 | params.opt.architecture = storage.Container(storage.Json.load(options.architecture_file)) 69 | if params.opt.gpu_devices: 70 | environ["CUDA_VISIBLE_DEVICES"] = params.opt.gpu_devices 71 | 72 | try: 73 | version = __git_version__() 74 | storage.Json().save(version, '{}/git_version.json'.format(params.opt.root, params.opt.timestamp)) 75 | except Exception as e: 76 | print(':: Exception:: {}'.format(e)) 77 | print(Warning(':: Warning:: This project is not versioned yet')) 78 | 79 | def augment_dataset(): 80 | from util.tensors import merge_dict 81 | if options.augm_file: 82 | print(":: Augmenting original dataset") 83 | datafake = datasets.load_h5(options.augm_file) 84 | if options.augm_operation == 'merge': 85 | print(":: Merging augmented dataset to original dataset") 86 | dataset.train = Container(merge_dict(dataset.train.as_dict(), datafake.train.as_dict())) 87 | elif options.augm_operation == 'replace': 88 | print(":: Replacing original dataset by augmented dataset") 89 | dataset.train = datafake.train 90 | else: 91 | from warnings import warn 92 | warn(':: [warning] [default=merge] Augmenting operation not selected!') 93 | dataset.train = Container(merge_dict(dataset.train.as_dict(), datafake.train.as_dict())) 94 | return dataset 95 | 96 | 97 | def train(model, params, dataset, knn, options, info=''): 98 | from util.experiments import label2hot, generate_metric_list 99 | from util.storage import Json, Dict_Average_Meter 100 | from util.metrics import accuracy_per_class 101 | from sklearn.model_selection import train_test_split 102 | import numpy as np 103 | 104 | response = Dict_Average_Meter() 105 | epochs = params.epochs if 'epochs' in params.__dict__.keys() else 10 106 | batch_size = params.batch if 'batch' in params.__dict__.keys() else 512 107 | 108 | # Splitting dataset into validation and 109 | print('='*50, '\n:: [{}]Initializing training...'.format(model.namespace)) 110 | _split = train_test_split(dataset.train.X, 111 | dataset.train.Y-1, 112 | dataset.train.A.continuous, 113 | test_size=options.validation_split, 114 | random_state=42) 115 | X_train, X_val, y_train, y_val, a_train, a_val = _split 116 | 117 | if options.domain is 'openval': 118 | y_classes = np.zeros(knn.openset.data.shape[0]) 119 | y_classes = y_classes[knn.zsl.ids-1] = 1. 120 | elif options.domain is 'zsl': 121 | y_classes = np.zeros(knn.openset.data.shape[0]) 122 | y_classes[knn.openval.ids-1] = 1. 123 | else: 124 | y_classes = np.zeros(knn.openset.data.shape[0]) 125 | y_evals = np.zeros(knn.openset.data.shape[0]) 126 | 127 | # Iteration over epochs 128 | for epoch in np.arange(1, epochs+1): 129 | data = {'x': X_train, 130 | 'y': label2hot(y_train, dataset.n_classes), 131 | 'a': a_train, 132 | 'a_dict': knn.openset.data.astype(np.float32), 133 | 'y_classes': y_classes.astype(np.float32), 134 | 'info':':: || {}[{}] - Epochs {}/{} ||'.format(info, model.namespace, epoch, epochs), 135 | 'train_type':model.namespace} 136 | 137 | train_answer = model.train(data, batch_size=batch_size) 138 | train_eval = model.evaluate(data) 139 | 140 | response.update_meters("{}/train/answer".format(model.namespace), train_answer) 141 | model.summary_dict("{}/train/answer".format(model.namespace), train_answer) 142 | 143 | response.update_meters("{}/train/val".format(model.namespace), train_eval) 144 | model.summary_dict("{}/train/val".format(model.namespace), train_eval) 145 | 146 | if options.validation_split: 147 | val = {'x': X_val, 148 | 'y': label2hot(y_val, dataset.n_classes), 149 | 'a_dict': knn.openset.data, 150 | 'y_classes': y_classes.astype(np.float32), 151 | 'a': a_val, 152 | 'train_type':model.namespace} 153 | val_answer = model.evaluate(val) 154 | response.update_meters("{}/val".format(model.namespace), val_answer) 155 | model.summary_dict("{}/val".format(model.namespace), val_answer) 156 | 157 | if ((options.save_model) and (epoch >= options.save_from)) or \ 158 | (epoch in options.savepoints): 159 | model.save({'dir': '{}/epoch_{}_{}'.format(options.checkpointdir, epoch, epochs), 160 | 'step':epoch}) 161 | 162 | return response.as_dict() 163 | 164 | 165 | def main(options, dataset, knn): 166 | 167 | from util.storage import DataH5py, Json 168 | from util.setup import mkdir 169 | from routines.aux import get_basic_model 170 | import models 171 | 172 | _archfile = Json.load(options.architecture_file) 173 | if options.load_model: 174 | from .aux import load_model 175 | model = load_model(options.load_model, _archfile.namespace) 176 | else: 177 | # Setting model from json file architecture 178 | print(':: Creating new model. ') 179 | ModelClass = models.__dict__[_archfile['namespace']].__MODEL__ 180 | model = ModelClass(_archfile) 181 | print(":: Model type:", type(model)) 182 | 183 | # Setting session 184 | print(':: Setting TensorFlow session. ') 185 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=options.gpu_memory) 186 | config=tf.ConfigProto(allow_soft_placement=True, gpu_options=gpu_options) 187 | sess = tf.Session(config=config) 188 | model.set_session(sess) 189 | model.build() 190 | try: 191 | model.set_writer('{}/{}/'.format(options.logsdir, model.namespace)) 192 | response = train(model=model, 193 | params=options.architecture, 194 | dataset=dataset, knn=knn, 195 | options=options, 196 | info='{}::[{}/{}]: '.format(model.namespace, options.domain, options.dbname)) 197 | print("") 198 | DataH5py().save_dict_to_hdf5(dic=response, filename='{}/full_train.h5'.format(options.resultsdir)) 199 | 200 | except: 201 | import sys, traceback 202 | traceback.print_exc(file=sys.stdout) 203 | 204 | return model, None 205 | 206 | 207 | 208 | 209 | if __name__ == '__main__': 210 | print('-'*100) 211 | print(':: Training file: {}'.format(__file__)) 212 | print('-'*100) 213 | 214 | from options.benchmark import __OPTION__ as Options 215 | from util import datasets 216 | from util.storage import Container 217 | from sklearn.model_selection import train_test_split 218 | import tensorflow as tf 219 | from models import * 220 | 221 | try: 222 | print(':: Seeding to guarantee reproducibility') 223 | __seed__() 224 | print(':: Parsing parameters') 225 | params = Options() 226 | options = params.parse() 227 | 228 | print('-'*_repeat_,'\n:: Initializing experiment') 229 | initialize(params) 230 | print('-'*_repeat_,'\n:: Loading Dataset') 231 | dataset, knn = datasets.load(options.datadir) 232 | if options.domain == 'openset': 233 | dataset = augment_dataset() 234 | 235 | print('-'*_repeat_, '\n:: Generating tensorboard script') 236 | _tbscript_file_='tensorboard_script.sh' 237 | __tensorboard_script__(fname='/tmp/{}'.format(_tbscript_file_), 238 | logidr=options.root) 239 | 240 | __tensorboard_script__(fname='{}/{}'.format(options.root, _tbscript_file_), 241 | logidr=options.root) 242 | print(':: tensorboard script: {}'.format(_tbscript_file_)) 243 | 244 | print('-'*_repeat_, '\n:: Executing main routines\n', '-'*_repeat_) 245 | 246 | model, results = main(options=options, dataset=dataset, knn=knn) 247 | print('-'*_repeat_,"\n Ending execution...\n", '-'*_repeat_,) 248 | print(':: Logs:\n', options.root, '\n','-'*_repeat_) 249 | 250 | except Exception as e: 251 | import sys, traceback 252 | traceback.print_exc(file=sys.stdout) -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | train.py: Training routine. 25 | Reference: 26 | Felix, R., Vijay Kumar, B. G., Reid, I., & Carneiro, G. (2018). Multi-modal Cycle-consistent Generalized Zero-Shot Learning. arXiv preprint arXiv:1808.00136. 27 | (download paper)[http://openaccess.thecvf.com/content_ECCV_2018/papers/RAFAEL_FELIX_Multi-modal_Cycle-consistent_Generalized_ECCV_2018_paper.pdf] 28 | """ 29 | 30 | __author__ = "Rafael Felix, Vijay Kumar and Gustavo Carneiro" 31 | __copyright__ = "MIT License" 32 | __credits__ = ["Rafael Felix", "Gustavo Carneiro", "Vijay Kumar", "Ian Reid"] 33 | __license__ = "MIT License" 34 | __version__ = "1.0.2" 35 | __maintainer__ = "Rafael Felix" 36 | __email__ = "rafael.felixalves@adelaide.edu.au" 37 | __status__ = "production" 38 | _repeat_=100 39 | from routines.aux import __tensorboard_script__, __seed__, __git_version__, update_metric 40 | 41 | def initialize(params): 42 | from util import setup, storage 43 | from os import environ 44 | 45 | if params.opt.setup: 46 | 47 | # Generating experimental setup folder 48 | print(':: Generating experimental setup folder') 49 | res = setup.mkexp(baseroot=params.opt.baseroot, 50 | options=params, 51 | bname='{}_{}'.format(params.opt.description, params.opt.timestamp), 52 | sideinfo=params.opt.sideinfo, 53 | subdirectories=params.opt.exp_directories) 54 | params.opt.root = res['root'] 55 | 56 | for key in params.opt.exp_directories: 57 | params.opt.__setattr__('{}dir'.format(key), '{}/{}/'.format(params.opt.root, key)) 58 | 59 | params.opt.namespace = res['namespace'] 60 | print(':: Experiment will be save in:\n:: {}'.format(params.opt.root)) 61 | 62 | params.save('{}/configuration_{}.json'.format(params.opt.root, params.opt.timestamp)) 63 | params.print() 64 | 65 | params.opt.architecture = storage.Container(storage.Json.load(options.architecture_file)) 66 | if params.opt.gpu_devices: 67 | environ["CUDA_VISIBLE_DEVICES"] = params.opt.gpu_devices 68 | 69 | try: 70 | version = __git_version__() 71 | storage.Json().save(version, '{}/git_version.json'.format(params.opt.root, params.opt.timestamp)) 72 | except Exception as e: 73 | print(':: Exception:: {}'.format(e)) 74 | print(Warning(':: Warning:: This project is not versioned yet')) 75 | 76 | def train(model, params, dataset, knn, options, mflag='classifier', info=''): 77 | from util.experiments import label2hot, generate_metric_list 78 | from util.storage import Dict_Average_Meter 79 | from util.metrics import accuracy_per_class 80 | from sklearn.model_selection import train_test_split 81 | import numpy as np 82 | 83 | epochs = params.epochs if 'epochs' in params.__dict__.keys() else 10 84 | batch_size = params.batch if 'batch' in params.__dict__.keys() else 512 85 | 86 | # Splitting dataset into validation and 87 | print('='*50, '\n:: [{}]Initializing training...'.format(mflag)) 88 | _split = train_test_split(dataset.train.X, 89 | dataset.train.Y-1, 90 | dataset.train.A.continuous, 91 | test_size=options.validation_split, 92 | random_state=42) 93 | X_train, X_val, y_train, y_val, a_train, a_val = _split 94 | 95 | # OBS: this tool work as the oposite. If you want to test on ZSL you must set openval=1. 96 | # If you want to set on openval you must set zsl = 1. 97 | yclasses_train = np.zeros(knn.openset.data.shape[0]) 98 | yclasses_train[knn.zsl.ids-1] = 1. 99 | 100 | yclasses_test = np.zeros(knn.openset.data.shape[0]) 101 | yclasses_test[knn.openval.ids-1] = 1. 102 | 103 | yclasses_open = np.zeros(knn.openset.data.shape[0]) 104 | 105 | 106 | 107 | # results Container 108 | response = Dict_Average_Meter() 109 | # Iteration over epochs 110 | for epoch in np.arange(1, epochs+1): 111 | 112 | data = {'x': X_train, 113 | 'y': label2hot(y_train, dataset.n_classes), 114 | 'a': a_train, 115 | 'a_dict': knn.openset.data.astype(np.float32), 116 | 'y_classes': yclasses_train.astype(np.float32), 117 | 'info':':: || {}[{}] - Epochs {}/{} ||'.format(info, mflag, epoch, epochs), 118 | 'train_type':mflag} 119 | 120 | val = {'x': X_val, 121 | 'y': label2hot(y_val, dataset.n_classes), 122 | 'a_dict': knn.openset.data, 123 | 'y_classes': yclasses_train.astype(np.float32), 124 | 'a': a_val, 125 | 'train_type':mflag} 126 | train_answer = model.train(data, batch_size=batch_size) 127 | train_eval = model.evaluate(data) 128 | 129 | response.update_meters("{}/{}/train/answer".format(model.namespace, mflag), train_answer) 130 | model.summary_dict("{}/{}/train/answer".format(model.namespace, mflag), train_answer) 131 | 132 | response.update_meters("{}/{}/train/val".format(model.namespace, mflag), train_eval) 133 | model.summary_dict("{}/{}/train/val".format(model.namespace, mflag), train_eval) 134 | 135 | def validation(): 136 | 137 | #pre-train validation for regressor & classifier 138 | if (mflag != 'gan'): 139 | val_answer = model.evaluate(val) 140 | response.update_meters("{}/{}/val".format(model.namespace, mflag), val_answer) 141 | model.summary_dict("{}/{}/val".format(model.namespace, mflag), val_answer) 142 | 143 | # training validation for GAN 144 | if mflag is 'gan': 145 | val['z'] = model.get_noise(shape=a_val.shape) 146 | x_fake = model.generator(val) 147 | valfake = {'x': x_fake, 148 | 'y': label2hot(y_val, dataset.n_classes), 149 | 'a': a_val} 150 | 151 | fake_answer = model.evaluate(valfake) 152 | response.update_meters("{}/{}/val/fake".format(model.namespace, mflag), fake_answer) 153 | model.summary_dict("{}/{}/val/fake".format(model.namespace, mflag), fake_answer) 154 | 155 | validation() 156 | 157 | if ((options.save_model) and (epoch >= options.save_from)) or \ 158 | (epoch in options.savepoints): 159 | 160 | model.save({'checkdir': options.checkpointdir, 161 | 'step':epoch, 162 | 'epochs': epochs, 163 | 'train_type': mflag}) 164 | model.reset_counter() 165 | 166 | 167 | return response.as_dict() 168 | 169 | 170 | 171 | def main(options, dataset, knn): 172 | 173 | from util.storage import DataH5py, Json 174 | from util.setup import mkdir 175 | import models 176 | 177 | ModelClass = models.__dict__[options.architecture.namespace].__MODEL__ 178 | 179 | if options.load_model: 180 | # implement routine to load model 181 | pass 182 | else: 183 | # Setting model from json file architecture 184 | print(':: Creating new model. ') 185 | model = ModelClass(Json.load(options.architecture_file)) 186 | 187 | # Setting session 188 | print(':: Setting TensorFlow session. ') 189 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=options.gpu_memory) 190 | config=tf.ConfigProto(allow_soft_placement=True, gpu_options=gpu_options) 191 | sess = tf.Session(config=config) 192 | model.set_session(sess) 193 | model.build() 194 | 195 | try: 196 | #response is a dict that saves all training metrics 197 | response = {} 198 | if options.train_cls: 199 | model.set_writer('{}/classifier/'.format(options.logsdir)) 200 | mkdir('classifier', options.checkpointdir) 201 | response['classifier'] = train(model=model, 202 | params=options.architecture.classifier, 203 | dataset=dataset, knn=knn, 204 | options=options, 205 | info='{}::{}: '.format(model.namespace, options.dbname)) 206 | print("") 207 | DataH5py().save_dict_to_hdf5(dic=response, 208 | filename='{}/classifier_train.h5'.format(options.resultsdir)) 209 | 210 | if options.train_reg: 211 | model.set_writer('{}/regressor/'.format(options.logsdir)) 212 | mkdir('regressor', options.checkpointdir) 213 | response['regressor'] = train(model=model, 214 | params=options.architecture.regressor, 215 | dataset=dataset, knn=knn, 216 | options=options, mflag='regressor', 217 | info='{}::{}: '.format(model.namespace, options.dbname)) 218 | print("") 219 | DataH5py().save_dict_to_hdf5(dic=response, filename='{}/regressor_train.h5'.format(options.resultsdir)) 220 | 221 | 222 | if options.train_gan: 223 | mkdir('generator', options.checkpointdir) 224 | mkdir('discriminator', options.checkpointdir) 225 | model.set_writer('{}/gan/'.format(options.logsdir)) 226 | response['gan'] = train(model=model, 227 | params=options.architecture.gan, 228 | dataset=dataset, knn=knn, 229 | options=options, mflag='gan', 230 | info='{}::{}: '.format(model.namespace, options.dbname)) 231 | print("") 232 | DataH5py().save_dict_to_hdf5(dic=response, filename='{}/gan_train.h5'.format(options.resultsdir)) 233 | 234 | DataH5py().save_dict_to_hdf5(dic=response, filename='{}/full_train.h5'.format(options.resultsdir)) 235 | 236 | except: 237 | import sys, traceback 238 | traceback.print_exc(file=sys.stdout) 239 | 240 | return model, None 241 | 242 | 243 | if __name__ == '__main__': 244 | print('-'*100) 245 | print(':: Training file: {}'.format(__file__)) 246 | print('-'*100) 247 | 248 | from options.gan import GANOptions as Options 249 | from util import datasets 250 | from sklearn.model_selection import train_test_split 251 | import tensorflow as tf 252 | from models import * 253 | 254 | try: 255 | print(':: Seeding to guarantee reproducibility') 256 | __seed__() 257 | print(':: Parsing parameters') 258 | params = Options() 259 | options = params.parse() 260 | 261 | print('-'*_repeat_,'\n:: Initializing experiment') 262 | initialize(params) 263 | print('-'*_repeat_,'\n:: Loading Dataset') 264 | dataset, knn = datasets.load(options.datadir) 265 | 266 | print(':: Generating tensorboard script') 267 | _tbscript_file_='tensorboard_script.sh' 268 | __tensorboard_script__(fname='/tmp/{}'.format(_tbscript_file_), 269 | logidr=options.root) 270 | 271 | __tensorboard_script__(fname='{}/{}'.format(options.root, _tbscript_file_), 272 | logidr=options.root) 273 | print(':: tensorboard script: {}'.format(_tbscript_file_)) 274 | 275 | print('-'*_repeat_, '\n:: Executing main routines') 276 | 277 | model, results = main(options=options, dataset=dataset, knn=knn) 278 | print('-'*_repeat_,"\n Ending execution...\n", '-'*_repeat_) 279 | print(':: Logs:\n', options.root, '\n','-'*_repeat_) 280 | 281 | except Exception as e: 282 | import sys, traceback 283 | 284 | traceback.print_exc(file=sys.stdout) -------------------------------------------------------------------------------- /notebooks/dataset/processing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Processing dataset\n", 8 | "\n", 9 | "Observe that this notebook aims to illustrate the process of dataset processing to perform experiments on cycle-WGAN. Therefore, we show how to use the splits proposed by Xian et al [1], on the datasets CUB, AWA1 and SUN. We also provided the h5files for CUB, SUN, FLO and AWA1 at the end of this page (if required to perform your experiments)." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "### Requirements\n", 17 | "\n", 18 | "```\n", 19 | "jupyter 1.0.0\n", 20 | "\n", 21 | "jupyter_client 5.2.3\n", 22 | "\n", 23 | "jupyter_console 5.2.0\n", 24 | "\n", 25 | "jupyter_core 4.4.0\n", 26 | "\n", 27 | "git clone https://github.com/rfelixmg/util.git\n", 28 | "\n", 29 | "```" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 18, 35 | "metadata": {}, 36 | "outputs": [ 37 | { 38 | "name": "stdout", 39 | "output_type": "stream", 40 | "text": [ 41 | "importing dependencies...\n" 42 | ] 43 | } 44 | ], 45 | "source": [ 46 | "import os\n", 47 | "import sys\n", 48 | "sys.path.append(os.path.abspath('../../'))\n", 49 | "sys.path.append(os.path.abspath('../'))\n", 50 | "sys.path.append(os.path.abspath('./'))\n", 51 | "\n", 52 | "from scipy.io import loadmat\n", 53 | "from util.datasets import load_h5\n", 54 | "from util.storage import Container, DataH5py\n", 55 | "\n", 56 | "import numpy as np\n", 57 | "from copy import deepcopy\n", 58 | "\n", 59 | "print(\"importing dependencies...\")\n" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 2, 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "Preparing directories\n", 72 | "mkdir: cannot create directory ‘../../data/cub’: File exists\n", 73 | "mkdir: cannot create directory ‘../../data/awa1’: File exists\n", 74 | "mkdir: cannot create directory ‘../../data/sun’: File exists\n", 75 | "mkdir: cannot create directory ‘../../data/._original_’: File exists\n", 76 | "File `../../data/._original_/xian.etal.zip' already there; not retrieving.\n", 77 | "mv: cannot move '../../data/._original_/xlsa17/code' to '../../data/._original_/code': Directory not empty\n", 78 | "mv: cannot move '../../data/._original_/xlsa17/data' to '../../data/._original_/data': Directory not empty\n", 79 | "done\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "print(\"Preparing directories\")\n", 85 | "!mkdir ../../data/cub ../../data/awa1 ../../data/sun\n", 86 | "!mkdir ../../data/._original_\n", 87 | "\n", 88 | "!wget -nc http://datasets.d2.mpi-inf.mpg.de/xian/xlsa17.zip -O ../../data/._original_/xian.etal.zip\n", 89 | "!unzip -u -n -q ../../data/._original_/xian.etal.zip -d ../../data/._original_/ \n", 90 | "!mv ../../data/._original_/xlsa17/* ../../data/._original_/\n", 91 | "!wget -q -nc https://www.dropbox.com/s/7yf0b1cx900ardo/cub_attributes_reed.npy?dl=0 -O ../../data/._original_/data/CUB/reed.etal.npy\n", 92 | "#!ls ../../data/._original_/\n", 93 | "\n", 94 | "_download_folder_ = '../../data/._original_/'\n", 95 | "_outdir = '../../data/'\n", 96 | "\n", 97 | "print(\"done\")" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 3, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "def prepare_dataset(dname, basedir, has_semantic=False):\n", 107 | " from scipy.io import loadmat\n", 108 | " import numpy as np\n", 109 | " from util.storage import DictContainer, Container\n", 110 | "\n", 111 | " _dir_src = '{}/data/{}/'.format(basedir, dname)\n", 112 | " \n", 113 | " # For more information, pelase check Xian et al [1];\n", 114 | " _all_splits = loadmat('{}/{}'.format(_dir_src, 'att_splits.mat'))\n", 115 | " _odata = loadmat('{}/data/{}/res101.mat'.format(basedir, dname))\n", 116 | " \n", 117 | " # Verify whether features the \\in R^(NxD) or R^(DxN), \n", 118 | " # where N is number of samples\n", 119 | " if _odata['features'].shape[0] == 2048:\n", 120 | " _odata['features'] = _odata['features'].T\n", 121 | " # from 0 to (N-1)\n", 122 | " all_ids = np.arange(0, _odata['features'].shape[0])\n", 123 | " \n", 124 | " # Labels from 0 to (|C|-1)\n", 125 | " _odata['y'] = _odata['labels'] - 1\n", 126 | " \n", 127 | " # For more information, pelase check Xian et al [1];\n", 128 | " if has_semantic != False:\n", 129 | " # If semantic space is different then Xian et al [1];\n", 130 | " att_continuous = np.load(has_semantic)\n", 131 | " else:\n", 132 | " att_continuous = _all_splits['att'].T\n", 133 | "\n", 134 | " # Although we don't use the binary attributes, you can easily compute then following [2]\n", 135 | " binary_attributes = (att_continuous >= att_continuous.mean()).astype(np.float)\n", 136 | "\n", 137 | " # Sorting semantic features by class label\n", 138 | " all_continuous_attributes = np.array([att_continuous[_label] for _label in _odata['y']])\n", 139 | " all_binary_attributes = np.array([binary_attributes[_label] for _label in _odata['y']])\n", 140 | " \n", 141 | " # naming by Xian et al [1]\n", 142 | " _sets = ['train_loc', 'val_loc', 'trainval_loc', 'test_seen_loc', 'test_unseen_loc']\n", 143 | " # our naming for h5Files\n", 144 | " _namespaces = ['train_val', 'val', 'train', 'test/seen', 'test/unseen']\n", 145 | " \n", 146 | " ndb = DictContainer()\n", 147 | " for _set, _name in zip(_sets, _namespaces):\n", 148 | " \n", 149 | " #print(_set, _name)\n", 150 | " # as labels go from 1 to |C|, we need to subtract 1\n", 151 | " _ids = _all_splits[_set] - 1\n", 152 | " ndb.set_param('{}/A/continuous'.format(_name), all_continuous_attributes[_ids].squeeze())\n", 153 | " ndb.set_param('{}/A/binary'.format(_name), all_binary_attributes[_ids].squeeze())\n", 154 | " ndb.set_param('{}/X'.format(_name), _odata['features'][_ids].squeeze())\n", 155 | " ndb.set_param('{}/Y'.format(_name), _odata['labels'][_ids].squeeze())\n", 156 | " ndb.set_param('{}/y'.format(_name), _odata['y'][_ids].squeeze())\n", 157 | " ndb = Container(ndb.as_dict())\n", 158 | " ndb.name = dname\n", 159 | " if has_semantic != False:\n", 160 | " ndb.semantic = has_semantic\n", 161 | " return ndb" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 4, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "def prepare_knn(dname, basedir, has_semantic=False):\n", 171 | " from scipy.io import loadmat\n", 172 | " import numpy as np\n", 173 | " from util.storage import DictContainer, Container\n", 174 | "\n", 175 | " _dir_src = '{}/data/{}/'.format(basedir, dname)\n", 176 | " \n", 177 | " # For more information, pelase check Xian et al [1];\n", 178 | " _all_splits = loadmat('{}/{}'.format(_dir_src, 'att_splits.mat'))\n", 179 | " _odata = loadmat('{}/data/{}/res101.mat'.format(basedir, dname))\n", 180 | " \n", 181 | " # Labels from 0 to (|C|-1)\n", 182 | " _odata['y'] = _odata['labels'].squeeze() - 1\n", 183 | " \n", 184 | " # For more information, pelase check Xian et al [1];\n", 185 | " if has_semantic != False:\n", 186 | " # If semantic space is different then Xian et al [1];\n", 187 | " att_continuous = np.load(has_semantic)\n", 188 | " else:\n", 189 | " att_continuous = _all_splits['att'].T\n", 190 | " \n", 191 | " nknn = DictContainer()\n", 192 | " def _setting_domain_(_domain, _classes):\n", 193 | " nknn.set_param('{}/data'.format(_domain), \n", 194 | " np.array([att_continuous[i] for i in _classes]))\n", 195 | " nknn.set_param('{}/ids'.format(_domain), _classes + 1)\n", 196 | " nknn.set_param('{}/ys'.format(_domain), _classes)\n", 197 | " nknn.set_param('{}/labels'.format(_domain), \n", 198 | " np.array([_all_splits['allclasses_names'][i][0][0] for i in _classes]))\n", 199 | " \n", 200 | " nknn.set_param('{}/knn2id'.format(_domain), \n", 201 | " {key: value for key, value in enumerate(nknn.get_param('{}/ids'.format(_domain)))})\n", 202 | " nknn.set_param('{}/id2knn'.format(_domain), \n", 203 | " {value: key for key, value in enumerate(nknn.get_param('{}/ids'.format(_domain)))})\n", 204 | " nknn.set_param('{}/id2class'.format(_domain), \n", 205 | " {value: nknn.get_param('{}/labels'.format(_domain))[key] for key, value in enumerate(nknn.get_param('{}/ids'.format(_domain)))})\n", 206 | "\n", 207 | " \n", 208 | " _setting_domain_('openset', np.arange(0, att_continuous.shape[0]))\n", 209 | " \n", 210 | " _classes = np.unique(_odata['y'][_all_splits['trainval_loc'] - 1].squeeze())\n", 211 | " _setting_domain_('openval', _classes)\n", 212 | " \n", 213 | " _classes = np.unique(_odata['y'][_all_splits['test_unseen_loc'] - 1].squeeze())\n", 214 | " _setting_domain_('zsl', _classes)\n", 215 | "\n", 216 | " return nknn" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 14, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "Data prepared: awa1\n", 229 | "KNN prepared: awa1\n", 230 | "Data saved\n" 231 | ] 232 | } 233 | ], 234 | "source": [ 235 | "_dname = 'AWA1'\n", 236 | "_data = prepare_dataset(dname=_dname, basedir=_download_folder_)\n", 237 | "print(\"Data prepared: {}\".format(_dname.lower()))\n", 238 | "\n", 239 | "_knn = prepare_knn(dname=_dname, basedir=_download_folder_)\n", 240 | "print(\"KNN prepared: {}\".format(_dname.lower()))\n", 241 | "\n", 242 | "\n", 243 | "DataH5py().save(dic=_data, filename='{}/{}/data.h5'.format(_outdir, _dname.lower()))\n", 244 | "DataH5py().save(dic=_knn, filename='{}/{}/knn.h5'.format(_outdir, _dname.lower()))\n", 245 | "print(\"Data saved\")\n", 246 | "\n", 247 | "\n" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 17, 253 | "metadata": {}, 254 | "outputs": [ 255 | { 256 | "name": "stdout", 257 | "output_type": "stream", 258 | "text": [ 259 | "Data prepare: cub\n", 260 | "KNN prepared: cub\n", 261 | "Data saved\n" 262 | ] 263 | } 264 | ], 265 | "source": [ 266 | "_dname = 'CUB'\n", 267 | "\n", 268 | "# For more information, pelase check Reed et al [3];\n", 269 | "_semantic_dir = '../../data/._original_/data/CUB/reed.etal.npy'\n", 270 | "\n", 271 | "_data = prepare_dataset(dname=_dname, basedir=_download_folder_, has_semantic=_semantic_dir)\n", 272 | "print(\"Data prepare: {}\".format(_dname.lower()))\n", 273 | "\n", 274 | "_knn = prepare_knn(dname=_dname, basedir=_download_folder_, has_semantic=_semantic_dir)\n", 275 | "print(\"KNN prepared: {}\".format(_dname.lower()))\n", 276 | "\n", 277 | "\n", 278 | "DataH5py().save(dic=_data, filename='{}/{}/data.h5'.format(_outdir, _dname.lower()))\n", 279 | "DataH5py().save(dic=_knn, filename='{}/{}/knn.h5'.format(_outdir, _dname.lower()))\n", 280 | "print(\"Data saved\")\n" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 15, 286 | "metadata": {}, 287 | "outputs": [ 288 | { 289 | "name": "stdout", 290 | "output_type": "stream", 291 | "text": [ 292 | "Data prepared: sun\n", 293 | "KNN prepared: sun\n", 294 | "Data saved\n" 295 | ] 296 | } 297 | ], 298 | "source": [ 299 | "_dname = 'SUN'\n", 300 | "_data = prepare_dataset(dname=_dname, basedir=_download_folder_)\n", 301 | "print(\"Data prepared: {}\".format(_dname.lower()))\n", 302 | "\n", 303 | "_knn = prepare_knn(dname=_dname, basedir=_download_folder_)\n", 304 | "print(\"KNN prepared: {}\".format(_dname.lower()))\n", 305 | "\n", 306 | "\n", 307 | "DataH5py().save(dic=_data, filename='{}/{}/data.h5'.format(_outdir, _dname.lower()))\n", 308 | "DataH5py().save(dic=_knn, filename='{}/{}/knn.h5'.format(_outdir, _dname.lower()))\n", 309 | "print(\"Data saved\")" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "# Download H5files\n", 317 | "\n" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": {}, 323 | "source": [ 324 | "In order to guarantee reproducibility of our approach, with numbers reported on Felix et al [4]. We attached to this file the weblinks to (1) download the h5files used to performing the training of models. (2) semantic space used for CUB; and (3) the pseudo-features generated in order to train the final classifier;\n", 325 | "\n", 326 | "\n", 327 | "(1) Dataset (h5file):\n", 328 | "https://drive.google.com/open?id=1N9K-w993Cv0zgZOkF3Rpls5qrfOMpQTj\n", 329 | "\n", 330 | "(2) CUB semantic space:\n", 331 | "https://drive.google.com/open?id=1Jfltw0qbSCUvr4ekRl_3pekkCGzGolyA\n", 332 | "\n", 333 | "(3) Pseudo features generated by cycle-WGAN:\n", 334 | "https://drive.google.com/open?id=1MTGiOL3rbW6vi-WeTKbdix1hHcq6i5ki" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "# Visualization" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "We suggest to the reader to use HDFView in order to visualize the dataset matrices. HDFView is a visual tool for browsing and editing HDF4 and HDF5 files. Using HDFView, you can view a file hierarchy in a tree structure create new files, add or delete groups and datasets view and modify the content of a dataset add, delete and modify attributes.\n", 349 | "\n", 350 | "Download at: https://support.hdfgroup.org/products/java/hdfview/" 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "metadata": {}, 356 | "source": [ 357 | "# References\n", 358 | "\n", 359 | "[1] Xian, Yongqin, Bernt Schiele, and Zeynep Akata. \"Zero-shot learning-the good, the bad and the ugly.\" arXiv preprint arXiv:1703.04394 (2017).\n", 360 | "\n", 361 | "[2] Lampert, Christoph H., Hannes Nickisch, and Stefan Harmeling. \"Learning to detect unseen object classes by between-class attribute transfer.\" Computer Vision and Pattern Recognition, 2009. CVPR 2009. IEEE Conference on. IEEE, 2009.\n", 362 | "\n", 363 | "[3] Reed, Scott, et al. \"Learning deep representations of fine-grained visual descriptions.\" Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2016.\n", 364 | "\n", 365 | "[4] Felix, Rafael, et al. \"Multi-modal Cycle-consistent Generalized Zero-Shot Learning.\" Proceedings of the European Conference on Computer Vision (ECCV). 2018.\n", 366 | "\n" 367 | ] 368 | } 369 | ], 370 | "metadata": { 371 | "kernelspec": { 372 | "display_name": "Python 3", 373 | "language": "python", 374 | "name": "python3" 375 | }, 376 | "language_info": { 377 | "codemirror_mode": { 378 | "name": "ipython", 379 | "version": 3 380 | }, 381 | "file_extension": ".py", 382 | "mimetype": "text/x-python", 383 | "name": "python", 384 | "nbconvert_exporter": "python", 385 | "pygments_lexer": "ipython3", 386 | "version": "3.6.4" 387 | } 388 | }, 389 | "nbformat": 4, 390 | "nbformat_minor": 2 391 | } 392 | -------------------------------------------------------------------------------- /models/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2018 Rafael Felix Alves 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import tensorflow as tf 25 | from .modelobj import ModelObject 26 | from copy import deepcopy 27 | from .supporttypes import __OPERATORS__, __INITIALIZERS__, __OPTIMIZERS__ 28 | 29 | class BaseModel(ModelObject): 30 | 31 | def __init__(self, hparams=None): 32 | if hparams is not None: 33 | self.hyperparams = deepcopy(hparams) 34 | if 'namespace' not in hparams: 35 | self.namespace = 'base' 36 | 37 | super(BaseModel, self).__init__(hparams) 38 | self.__helpers__() 39 | self.__placeholders__() 40 | self.__architecture__() 41 | 42 | def get_name(self): 43 | if self.contains('namespace'): 44 | return self.namespace.split('/')[-1] 45 | else: 46 | return 'base' 47 | 48 | def __helpers__(self): 49 | self.layers = {} 50 | self.sess = None 51 | self.display_tensorboard = ['lr', '_weight_regularizer', 'wdecay', 'weight_penalty'] 52 | with tf.variable_scope(self.namespace, reuse=tf.AUTO_REUSE): 53 | self.global_step = tf.Variable(0, trainable=False, name='global_step') 54 | 55 | 56 | def one_step(self): 57 | self.counter+=1 58 | increment_global_step_op = tf.assign(self.current_epoch, self.current_epoch+1) 59 | self.sess.run(increment_global_step_op) 60 | 61 | def __get_operation__(self, info, net=False): 62 | if info['op'] == 'placeholder': 63 | return self.get_param(info['input']) 64 | 65 | if info['op'] == 'concat': 66 | input_ = [self.get_param(inp) for inp in info['inputs']] 67 | if isinstance(net, bool) is False: 68 | input_ = [net, *input_] 69 | return __OPERATORS__[info['op']](values=input_, axis=info['axis']) 70 | 71 | if info['op'] == 'activation': 72 | if 'params' in info: 73 | return __OPERATORS__[info['type']](net, **info['params']) 74 | else: 75 | return __OPERATORS__[info['type']](net) 76 | 77 | if info['op'] == 'layers/activation': 78 | if not self.get_param(info['input']): 79 | with tf.variable_scope(self.namespace, reuse=tf.AUTO_REUSE): 80 | if 'params' in info: 81 | _layer = __INITIALIZERS__[info['type']](**info['params']) 82 | else: 83 | _layer = __INITIALIZERS__[info['type']]() 84 | ans = _layer(net) 85 | self.set_param(info['input'], _layer) 86 | return ans 87 | else: 88 | return self.get_param(info['input'])(net) 89 | 90 | 91 | elif isinstance(net, bool) is False: 92 | return __OPERATORS__[info['op']](net, self.get_param(info['input'])) 93 | 94 | else: 95 | input_ = [self.get_param(inp) for inp in info['inputs']] 96 | return __OPERATORS__[info['op']](*input_) 97 | 98 | 99 | def __get_initializer__(self, info): 100 | if 'params' in info: 101 | return __INITIALIZERS__[info['name']](**info['params']) 102 | else: 103 | return __INITIALIZERS__[info['name']]() 104 | 105 | 106 | def __placeholders__(self): 107 | assert 'placeholders' in self.__dict__ 108 | with tf.variable_scope(self.namespace): 109 | 110 | for ph in self.placeholders: 111 | ph['dtype'] = tf.__dict__[ph['dtype']] 112 | self.__dict__[ph['name']] = tf.placeholder(**ph) 113 | 114 | def __get_optimizer__(self, name): 115 | return __OPTIMIZERS__[name] 116 | 117 | def __architecture__(self): 118 | with tf.variable_scope(self.namespace): 119 | for layer in self.architecture: 120 | layer['initializer'] = self.__get_initializer__(layer['initializer']) 121 | self.layers[layer['name']] = tf.get_variable(**layer) 122 | 123 | def get_placeholders(self): 124 | assert 'placeholders' in self.__dict__ 125 | return [self.__dict__[ph['name']] for ph in self.placeholders] 126 | 127 | def get_architecture(self): 128 | return self.layers 129 | 130 | def set_loss(self, loss): 131 | self._loss = loss 132 | self.apply_wdecay() 133 | 134 | 135 | def add2loss(self, term): 136 | self._loss += term 137 | self.set_update() 138 | 139 | def loss(self): 140 | NotImplementedError("not implemented") 141 | 142 | def get_loss(self, data): 143 | _fd = self.get_feed_dict(data) 144 | return self.sess.run([self.loss()], feed_dict=_fd)[0] 145 | 146 | def apply_wdecay(self): 147 | with tf.variable_scope(self.namespace, reuse=tf.AUTO_REUSE): 148 | if 'wdecay' in self.__dict__: 149 | self.wdecay = tf.Variable(self.wdecay, dtype=tf.float32, trainable=False, name="wdecay") 150 | else: 151 | self.wdecay = tf.Variable(0, dtype=tf.float32, trainable=False) 152 | 153 | self._weight_regularizer = tf.constant(0.0, dtype=tf.float32) 154 | for _layer in self.get_layers(): 155 | if '_fc' in _layer.name: 156 | self._weight_regularizer = tf.add(self._weight_regularizer, tf.nn.l2_loss(_layer)) 157 | 158 | self.weight_penalty = tf.multiply(self.wdecay, self._weight_regularizer) 159 | self.add2loss(self.weight_penalty) 160 | 161 | def build(self, training=True): 162 | self.__build_specs__(training) 163 | self.__init_variables__() 164 | 165 | def __build_specs__(self, training=True): 166 | self.output = self.__forward__() 167 | self.output_test = self.__forward__(training=False) 168 | 169 | if training: 170 | self.set_loss(tf.constant(0, dtype=tf.float32)) 171 | 172 | def update(self, data, tf_run=None): 173 | if tf_run is None: 174 | tf_run = [self._loss, self._update] 175 | return self.sess.run(tf_run, feed_dict=data) 176 | 177 | def get_feed_dict(self, data, idx=False): 178 | _fd = {} 179 | _placeholders = data['placeholders'] if 'placeholders' in data else self.placeholder_input 180 | for input_ in _placeholders: 181 | assert input_ in data 182 | if idx is False: 183 | _fd[self.get_param(input_)] = data[input_] 184 | else: 185 | _fd[self.get_param(input_)] = data[input_][idx] 186 | 187 | if self.contains('fix_variables'): 188 | for input_ in self.fix_variables: 189 | _fd[self.get_param(input_)] = data[input_] 190 | return _fd 191 | 192 | def train(self, data, batch_size=512): 193 | from numpy import array 194 | for input_ in self.placeholder_input: 195 | assert input_ in data 196 | 197 | _loss = array([]) 198 | for ii_, xcur, xsize in self.next_batch(data, batch_size): 199 | 200 | ddata = self.get_feed_dict(data, ii_) 201 | out = self.update(ddata) 202 | _loss = self.merge_array(_loss, out[0]) 203 | 204 | _msg = '{} [{}/{}] l: {:.3g}'.format(data['info'], xcur, xsize, 205 | _loss[-1]) 206 | self.printer.print(_msg) 207 | 208 | try: 209 | _eval = self.evaluate(data)['{}_acc'.format(self.get_name())] 210 | except: 211 | _eval = -99. 212 | 213 | _msg = '{} [{}/{}] l: {:.3g} @t1: {:.3g} [resume]\n'.format(data['info'], xcur, xsize, 214 | _loss.mean(), _eval) 215 | self.printer(_msg) 216 | self.printer.next_line() 217 | 218 | 219 | self.one_step() 220 | self.params_tensorboard() 221 | 222 | 223 | return {'{}_loss'.format(self.get_name()): _loss.mean(), 224 | '{}_eval'.format(self.get_name()): _eval} 225 | 226 | def __call__(self, data, batch_size=512): 227 | from numpy import array 228 | 229 | for input_ in self.placeholder_call: 230 | assert input_ in data 231 | 232 | out = array([]) 233 | for ii_, xcur, xsize in self.next_batch(data, batch_size, shuffle=False): 234 | fdict = {self.get_param(key): data[key][ii_] for key in self.placeholder_call} 235 | out_ = self.sess.run([self.output_test], feed_dict=fdict)[0] 236 | out = self.merge_array(out, out_) 237 | 238 | return out 239 | 240 | def run(self, data, shuffle=False, batch_size=512): 241 | from numpy import array 242 | 243 | for _key in (data['placeholders'] if data.get('placeholders') else self.placeholder_call): 244 | assert _key in data 245 | 246 | out = array([]) 247 | for ii_, xcur, xsize in self.next_batch(data, batch_size, shuffle=shuffle): 248 | fdict = self.get_feed_dict(data, ii_) 249 | out_ = self.sess.run(data['output'], feed_dict=fdict)[0] 250 | out = self.merge_array(out, out_) 251 | 252 | return out 253 | 254 | def __save_architecture__(self, data): 255 | import json 256 | assert 'dir' in data 257 | with open('{}/architecture.json'.format(data['dir']), 'w') as outfile: 258 | json.dump(self.hyperparams, outfile) 259 | 260 | 261 | @classmethod 262 | def load_architecture(self, data): 263 | """ 264 | :param: @data: {'model':obj(BaseModel), 'architecture': txt(file_path)} 265 | :return: obj(BaseModel) 266 | """ 267 | import json 268 | with open(data['architecture']) as f: 269 | hparams = json.load(f) 270 | data['model'].__init__(hparams) 271 | 272 | def load(self, data): 273 | self.__set_saver__() 274 | try: 275 | checkpoint_dir = data['dir'] 276 | import re, os 277 | print(" [*] Reading checkpoints...") 278 | ckpt = tf.train.get_checkpoint_state(checkpoint_dir) 279 | if ckpt and ckpt.model_checkpoint_path: 280 | ckpt_name = os.path.basename(ckpt.model_checkpoint_path) 281 | self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name)) 282 | counter = int(next(re.finditer("(\d+)(?!.*\d)", ckpt_name)).group(0)) 283 | print(" [*] Success to read {}".format(ckpt_name)) 284 | return True, counter 285 | else: 286 | print(" [*] Failed to find a checkpoint") 287 | return False, 0 288 | except: 289 | raise 290 | 291 | def link_checkpoint(self, save_path, namelink='last_epoch'): 292 | from os import remove, symlink 293 | _path_split_ = save_path.split('/') 294 | if _path_split_ == '': 295 | _link = '{}/{}'.format('/'.join(_path_split_[:-2]), namelink) 296 | else: 297 | _link = '{}/{}'.format('/'.join(_path_split_[:-1]), namelink) 298 | 299 | try: 300 | remove(_link) 301 | except: 302 | pass 303 | symlink(save_path, _link) 304 | 305 | def __set_saver__(self): 306 | if not self.contains('saver'): 307 | self.saver = tf.train.Saver(var_list=tf.trainable_variables(scope=self.namespace)) 308 | self.saver_full = tf.train.Saver() 309 | 310 | def save(self, data, save_all=False): 311 | assert 'dir' in data 312 | assert 'step' in data 313 | self.__set_saver__() 314 | 315 | self.saver.save(sess=self.sess, 316 | save_path='{}/{}.model'.format(data['dir'], data['step']), 317 | global_step=data['step']) 318 | if save_all: 319 | self.saver_full.save(sess=self.sess, 320 | save_path='{}/{}.model'.format(data['dir'], data['step']), 321 | global_step=data['step']) 322 | self.__save_architecture__(data) 323 | self.link_checkpoint(data['dir']) 324 | 325 | 326 | 327 | def get_layers(self): 328 | return list(self.layers.values()) 329 | 330 | def trainable_variables(self): 331 | return tf.trainable_variables(scope=self.namespace) 332 | 333 | def get_weights(self): 334 | return {key: self.sess.run([item])[0] for key, item in self.layers.items()} 335 | 336 | def __forward__(self, ret_all=False, training=True): 337 | operation = self.operators[0] 338 | net = self.__get_operation__(operation) 339 | return self.forward(net, ret_all=ret_all, training=training) 340 | 341 | def __set_operation_test__(self, operation): 342 | if operation['op'] == 'activation': 343 | if operation['type'] == 'dropout': 344 | if self.contains('test'): 345 | if 'mcmc_dropout' in self.test: 346 | return operation 347 | else: 348 | operation['params']['keep_prob'] = 1.0 349 | 350 | return operation 351 | 352 | def forward(self, data, ret_all=False, training=True): 353 | net = data 354 | 355 | ret_layers = {} 356 | for operation in self.operators[1:]: 357 | if training == False: 358 | operation = self.__set_operation_test__(deepcopy(operation)) 359 | 360 | net = self.__get_operation__(operation, net) 361 | if 'out' in operation and training: 362 | if operation['out']: 363 | ret_layers[operation['ref']] = net 364 | 365 | if len(ret_layers) > 0 and ret_all: 366 | return net, ret_layers 367 | else: 368 | return net 369 | 370 | def accuracy(self, data): 371 | return self.accuracy_per_class(data['y_pred'], data['y'], self.y_dim) 372 | 373 | def __update_lr__(self): 374 | if 'current_epoch' not in self.__dict__: 375 | with tf.variable_scope(self.namespace, reuse=tf.AUTO_REUSE): 376 | self.current_epoch = tf.Variable(0, name='current_epoch', trainable=False, dtype=tf.int32) 377 | 378 | self.lr = tf.train.inverse_time_decay(learning_rate=self.optimizer['lr'] if 'lr' in self.optimizer else 1e-2, 379 | global_step=self.current_epoch, 380 | decay_rate=self.optimizer['decay'] if 'decay' in self.optimizer else 0, 381 | decay_steps=1) 382 | 383 | 384 | 385 | def set_update(self): 386 | self._update = self.update_step(self._loss) 387 | 388 | def params_tensorboard(self): 389 | if self.counter == 1: 390 | import json 391 | _arch = json.dumps(self.hyperparams, indent=4, sort_keys=False) 392 | self.set_summary(tag='{}/architecture'.format(self.namespace), 393 | value=_arch, stype='text') 394 | 395 | for _param in self.display_tensorboard: 396 | self.set_summary(tag='{}/params/{}'.format(self.namespace, _param), 397 | value=self.sess.run([self.get_param(_param)])[0]) 398 | 399 | def update_step(self, loss, trainables=False): 400 | optimizer_func = self.__get_optimizer__(self.optimizer['name'] if 'name' in self.optimizer else 'adam') 401 | self.__update_lr__() 402 | 403 | with tf.variable_scope(self.namespace, reuse=tf.AUTO_REUSE): 404 | with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)): 405 | return optimizer_func(learning_rate=self.lr).minimize(loss, 406 | var_list=trainables if trainables else self.trainable_variables(), 407 | global_step=self.global_step, 408 | name='{}/optimizer'.format(self.namespace)) 409 | 410 | def evaluate(self, data): 411 | NotImplementedError("not implmented yet") 412 | 413 | 414 | def predict(self, data): 415 | return self(data) 416 | 417 | __MODEL__=BaseModel 418 | --------------------------------------------------------------------------------