├── src ├── __init__.py ├── common │ ├── __init__.py │ ├── consts.py │ └── paths.py ├── models │ ├── __init__.py │ └── denseNN.py ├── analysis │ ├── __init__.py │ └── training_perf_analysis.py ├── freezing │ ├── __init__.py │ ├── inception.py │ ├── frozen_to_saved_model.py │ └── freeze.py ├── inference │ ├── __init__.py │ ├── classify.py │ └── inference.py ├── training │ ├── __init__.py │ └── train.py └── data_preparation │ ├── __init__.py │ ├── tf_record_utils.py │ ├── image_utils.py │ ├── dataset.py │ ├── stanford_ds_to_tfrecords.py │ └── kaggle_images_to_tfrecords.py ├── .DS_Store ├── images ├── preview.png ├── airedale.jpg ├── preview2.jpg ├── shih-tzu.jpg └── kaggle_leaderboard.jpg ├── requirements.txt ├── setup ├── setup.sh ├── create_dirs.sh ├── inception_download.sh └── download_stanford_dogs_dataset.sh ├── .idea └── vcs.xml ├── jupyter_notebook_config.py ├── Dockerfile ├── .gitignore ├── data └── breeds.csv └── README.md /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/freezing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/inference/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/training/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data_preparation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/.DS_Store -------------------------------------------------------------------------------- /images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/images/preview.png -------------------------------------------------------------------------------- /images/airedale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/images/airedale.jpg -------------------------------------------------------------------------------- /images/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/images/preview2.jpg -------------------------------------------------------------------------------- /images/shih-tzu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/images/shih-tzu.jpg -------------------------------------------------------------------------------- /images/kaggle_leaderboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/HEAD/images/kaggle_leaderboard.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow 2 | pandas 3 | numpy 4 | matplotlib 5 | pyprind 6 | scikit-learn 7 | scipy 8 | jupyter 9 | Pillow 10 | -------------------------------------------------------------------------------- /setup/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sh ./setup/create_dirs.sh 4 | sh ./setup/inception_download.sh 5 | sh ./setup/download_stanford_dogs_dataset.sh -------------------------------------------------------------------------------- /setup/create_dirs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p data 4 | mkdir -p checkpoints 5 | mkdir -p frozen 6 | mkdir -p graphs 7 | mkdir -p metrics 8 | mkdir -p summary 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from IPython.lib import passwd 3 | 4 | c.NotebookApp.ip = '*' 5 | c.NotebookApp.port = int(os.getenv('PORT', 8888)) 6 | c.NotebookApp.open_browser = False 7 | 8 | c.NotebookApp.password = '' 9 | c.NotebookApp.token = '' 10 | -------------------------------------------------------------------------------- /setup/inception_download.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p frozen/inception 4 | curl -o ./frozen/inception/inception.tgz http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz 5 | tar xfz ./frozen/inception/inception.tgz -C ./frozen/inception 6 | -------------------------------------------------------------------------------- /setup/download_stanford_dogs_dataset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p data/stanford_ds 4 | curl -o data/stanford_ds/images.tar http://vision.stanford.edu/aditya86/ImageNetDogs/images.tar 5 | tar xfz data/stanford_ds/images.tar -C data/stanford_ds/ 6 | curl -o data/stanford_ds/annotation.tar http://vision.stanford.edu/aditya86/ImageNetDogs/annotation.tar 7 | tar xfz data/stanford_ds/annotation.tar -C data/stanford_ds/ 8 | -------------------------------------------------------------------------------- /src/data_preparation/tf_record_utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | def int64_feature(value): 5 | return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) 6 | 7 | 8 | def float_feature(value): 9 | return tf.train.Feature(float_list=tf.train.FloatList(value=value)) 10 | 11 | 12 | def bytes_feature(value): 13 | return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7 2 | 3 | MAINTAINER Kirill Panarin 4 | 5 | EXPOSE 8888 6 | EXPOSE 6006 7 | 8 | WORKDIR /app 9 | 10 | COPY requirements.txt ./ 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | 13 | COPY jupyter_notebook_config.py /root/.jupyter/ 14 | 15 | COPY ./src /app/src 16 | COPY ./images /app/images 17 | COPY ./checkpoints /app/checkpoints 18 | COPY ./frozen /app/frozen 19 | COPY ./data/train/labels.csv /app/data/train/ 20 | COPY ./data/*.tfrecords /app/data/ 21 | COPY ./data/breeds.csv /app/data/ 22 | COPY ./*.ipynb /app/ 23 | COPY ./summary /app/summary 24 | 25 | WORKDIR /app 26 | 27 | CMD [ "jupyter", "notebook", "--allow-root"] 28 | 29 | -------------------------------------------------------------------------------- /src/common/consts.py: -------------------------------------------------------------------------------- 1 | CLASSES_COUNT = 120 2 | INCEPTION_CLASSES_COUNT = 2048 3 | INCEPTION_OUTPUT_FIELD = 'inception_output' 4 | LABEL_ONE_HOT_FIELD = 'label_one_hot' 5 | IMAGE_RAW_FIELD = 'image_raw' 6 | INCEPTION_INPUT_TENSOR = 'DecodeJpeg/contents:0' 7 | INCEPTION_OUTPUT_TENSOR = 'pool_3:0' 8 | OUTPUT_NODE_NAME = 'output_node' 9 | OUTPUT_TENSOR_NAME = OUTPUT_NODE_NAME + ':0' 10 | HEAD_INPUT_NODE_NAME = 'x' 11 | HEAD_INPUT_TENSOR_NAME = HEAD_INPUT_NODE_NAME + ':0' 12 | 13 | DEV_SET_SIZE = 3000 14 | TRAIN_SAMPLE_SIZE = 3000 15 | 16 | # name of the model being referenced by all other scripts 17 | CURRENT_MODEL_NAME = 'stanford_5_64_0001' 18 | # sets up number of layers and number of units in each layer for 19 | # the "head" dense neural network stacked on top of the Inception 20 | # pre-trained model. 21 | HEAD_MODEL_LAYERS = [INCEPTION_CLASSES_COUNT, 1024, CLASSES_COUNT] -------------------------------------------------------------------------------- /src/common/paths.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | JPEG_EXT = '.jpg' 4 | DATA_ROOT = 'data/' 5 | TRAIN_DIR = os.path.join(DATA_ROOT, 'train') 6 | TEST_DIR = os.path.join(DATA_ROOT, 'test') 7 | #TRAIN_TF_RECORDS = os.path.join(ROOT, 'dogs_train.tfrecords') 8 | TRAIN_TF_RECORDS = os.path.join(DATA_ROOT, 'stanford.tfrecords') 9 | TEST_TF_RECORDS = os.path.join(DATA_ROOT, 'dogs_test.tfrecords') 10 | LABELS = os.path.join(DATA_ROOT, 'train', 'labels.csv') 11 | BREEDS = os.path.join(DATA_ROOT, 'breeds.csv') 12 | IMAGENET_GRAPH_DEF = 'frozen/inception/classify_image_graph_def.pb' 13 | TEST_PREDICTIONS = 'predictions.csv' 14 | METRICS_DIR = 'metrics' 15 | TRAIN_CONFUSION = os.path.join(METRICS_DIR, 'training_confusion.csv') 16 | FROZEN_MODELS_DIR = 'frozen' 17 | CHECKPOINTS_DIR = 'checkpoints' 18 | GRAPHS_DIR = 'graphs' 19 | SUMMARY_DIR = 'summary' 20 | STANFORD_DS_DIR = os.path.join(DATA_ROOT, 'stanford_ds') 21 | STANFORD_DS_TF_RECORDS = os.path.join(DATA_ROOT, 'stanford.tfrecords') 22 | 23 | -------------------------------------------------------------------------------- /src/freezing/inception.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from src.common import consts 4 | import freeze 5 | from src.common import paths 6 | 7 | 8 | def inception_model(): 9 | tensors = freeze.unfreeze_into_current_graph(paths.IMAGENET_GRAPH_DEF, 10 | tensor_names=[ 11 | consts.INCEPTION_INPUT_TENSOR, 12 | consts.INCEPTION_OUTPUT_TENSOR]) 13 | 14 | def forward(sess, image_raw): 15 | out = sess.run(tensors[consts.INCEPTION_OUTPUT_TENSOR], {tensors[consts.INCEPTION_INPUT_TENSOR]: image_raw}) 16 | return out 17 | 18 | return forward 19 | 20 | 21 | if __name__ == '__main__': 22 | with tf.Session().as_default() as sess: 23 | image_raw = tf.read_file('../../images/airedale.jpg').eval() 24 | 25 | g = tf.Graph() 26 | sess = tf.Session(graph=g) 27 | 28 | with g.as_default(): 29 | model = inception_model() 30 | 31 | with g.as_default(): 32 | out = model(sess, image_raw) 33 | print(out.shape) 34 | -------------------------------------------------------------------------------- /src/data_preparation/image_utils.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import tensorflow as tf 3 | 4 | from src.common import consts 5 | from src.training import train 6 | from src.common import paths 7 | 8 | 9 | # makes sense to have k = {1, 3} 10 | def rotate_ccw(img, k = 1): 11 | return tf.image.rot90(img, k) 12 | 13 | def flip_along_width(img): 14 | return tf.image.flip_left_right(img) 15 | 16 | #delta = {-0.1, 0.1} 17 | def adjust_brightness(img, delta=-0.1): 18 | return tf.image.adjust_brightness(img, delta=delta) 19 | 20 | def adjust_contrast(img, contrast_factor=0.8): 21 | return tf.image.adjust_contrast(img, contrast_factor=contrast_factor) 22 | 23 | 24 | if __name__ == '__main__': 25 | with tf.Graph().as_default() as g, tf.Session().as_default() as sess: 26 | next_train_batch, _, _ = train.train_dev_split(sess, paths.TRAIN_TF_RECORDS, batch_size=1) 27 | 28 | batch = sess.run(next_train_batch) 29 | img_raw = batch[consts.IMAGE_RAW_FIELD] 30 | img = adjust_brightness(tf.image.decode_jpeg(img_raw[0]), 0.1).eval() 31 | 32 | print(img.shape) 33 | 34 | plt.imshow(img) 35 | plt.show() 36 | -------------------------------------------------------------------------------- /src/inference/classify.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import urllib2 4 | 5 | import numpy as np 6 | import pandas as pd 7 | import tensorflow as tf 8 | 9 | from src.common import consts 10 | from src.data_preparation import dataset 11 | from src.freezing import freeze 12 | from src.common import paths 13 | 14 | 15 | def infer(model_name, img_raw): 16 | with tf.Graph().as_default(), tf.Session().as_default() as sess: 17 | tensors = freeze.unfreeze_into_current_graph( 18 | os.path.join(paths.FROZEN_MODELS_DIR, model_name + '.pb'), 19 | tensor_names=[consts.INCEPTION_INPUT_TENSOR, consts.OUTPUT_TENSOR_NAME]) 20 | 21 | _, one_hot_decoder = dataset.one_hot_label_encoder() 22 | 23 | probs = sess.run(tensors[consts.OUTPUT_TENSOR_NAME], 24 | feed_dict={tensors[consts.INCEPTION_INPUT_TENSOR]: img_raw}) 25 | 26 | breeds = one_hot_decoder(np.identity(consts.CLASSES_COUNT)).reshape(-1) 27 | 28 | # print(breeds) 29 | 30 | df = pd.DataFrame(data={'prob': probs.reshape(-1), 'breed': breeds}) 31 | 32 | 33 | return df.sort_values(['prob'], ascending=False) 34 | 35 | 36 | def classify(resource_type, path): 37 | if resource_type == 'uri': 38 | response = urllib2.urlopen(path) 39 | img_raw = response.read() 40 | else: 41 | with open(path, 'r') as f: 42 | img_raw = f.read() 43 | 44 | return infer(consts.CURRENT_MODEL_NAME, img_raw) 45 | 46 | if __name__ == '__main__': 47 | src = sys.argv[1] 48 | path = sys.argv[2] # uri to a dog image to classify 49 | probs = classify(src, path) 50 | 51 | print(probs.sort_values(['prob'], ascending=False).take(range(5))) 52 | 53 | -------------------------------------------------------------------------------- /src/freezing/frozen_to_saved_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import urllib2 4 | 5 | import numpy as np 6 | import pandas as pd 7 | import tensorflow as tf 8 | 9 | from src.common import consts 10 | from src.data_preparation import dataset 11 | from src.freezing import freeze 12 | from src.common import paths 13 | 14 | def convert(model_name, export_dir): 15 | builder = tf.saved_model.builder.SavedModelBuilder(export_dir=export_dir) 16 | 17 | with tf.Graph().as_default(), tf.Session().as_default() as sess: 18 | tensors = freeze.unfreeze_into_current_graph( 19 | os.path.join(paths.FROZEN_MODELS_DIR, model_name + '.pb'), 20 | tensor_names=[consts.INCEPTION_INPUT_TENSOR, consts.OUTPUT_TENSOR_NAME]) 21 | 22 | raw_image_proto_info = tf.saved_model.utils.build_tensor_info(tensors[consts.INCEPTION_INPUT_TENSOR]) 23 | probs_proto_info = tf.saved_model.utils.build_tensor_info(tensors[consts.OUTPUT_TENSOR_NAME]) 24 | 25 | prediction_signature = ( 26 | tf.saved_model.signature_def_utils.build_signature_def( 27 | inputs={'image_raw': raw_image_proto_info}, 28 | outputs={'probs': probs_proto_info}, 29 | method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)) 30 | 31 | builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING], 32 | signature_def_map={ 33 | tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature 34 | }) 35 | 36 | builder.save() 37 | 38 | if __name__ == '__main__': 39 | convert(consts.CURRENT_MODEL_NAME, export_dir='/tmp/dogs_1') -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | checkpoints/ 104 | data/ 105 | frozen/ 106 | graphs/ 107 | metrics/ 108 | summary/ 109 | .idea/ 110 | dog_breeds/ 111 | idea 112 | .DS_Store/ 113 | .DS_Store 114 | -------------------------------------------------------------------------------- /src/models/denseNN.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | 4 | from src.common import consts 5 | 6 | 7 | def denseNNModel(input_node, layers, gamma=0.1): 8 | n_x = layers[0] 9 | n_y = layers[-1] 10 | L = len(layers) 11 | summaries = [] 12 | Ws = [] 13 | 14 | with tf.name_scope("placeholders"): 15 | # x = tf.placeholder(dtype=tf.float32, shape=(n_x, None), name="x") 16 | y = tf.placeholder(dtype=tf.float32, shape=(n_y, None), name="y") 17 | 18 | a = input_node 19 | 20 | with tf.name_scope("hidden_layers"): 21 | for l in range(1, len(layers) - 1): 22 | W = tf.Variable(np.random.randn(layers[l], layers[l - 1]) / tf.sqrt(layers[l - 1] * 1.0), dtype=tf.float32, 23 | name="W" + str(l)) 24 | Ws.append(W) 25 | summaries.append(tf.summary.histogram('W' + str(l), W)) 26 | b = tf.Variable(np.zeros((layers[l], 1)), dtype=tf.float32, name="b" + str(l)) 27 | summaries.append(tf.summary.histogram('b' + str(l), b)) 28 | z = tf.matmul(W, a) + b 29 | a = tf.nn.relu(z) 30 | 31 | W = tf.Variable(np.random.randn(layers[L - 1], layers[L - 2]) / tf.sqrt(layers[L - 2] * 1.0), dtype=tf.float32, 32 | name="W" + str(L - 1)) 33 | summaries.append(tf.summary.histogram('W' + str(L - 1), W)) 34 | b = tf.Variable(np.zeros((layers[L - 1], 1)), dtype=tf.float32, name="b" + str(L - 1)) 35 | summaries.append(tf.summary.histogram('b' + str(L - 1), b)) 36 | z = tf.matmul(W, a) + b 37 | 38 | with tf.name_scope("cost"): 39 | cost = tf.reduce_mean( 40 | tf.nn.softmax_cross_entropy_with_logits(labels=tf.transpose(y), logits=tf.transpose(z))) # +\ 41 | # gamma * tf.reduce_sum([tf.nn.l2_loss(w) for w in Ws]) 42 | summaries.append(tf.summary.scalar('cost', cost)) 43 | 44 | output = tf.nn.softmax(z, dim=0, name=consts.OUTPUT_NODE_NAME) 45 | 46 | return cost, output, y, summaries 47 | -------------------------------------------------------------------------------- /src/inference/inference.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import pandas as pd 5 | import tensorflow as tf 6 | 7 | from src.common import consts 8 | from src.data_preparation import dataset 9 | from src.models import denseNN 10 | from src.common import paths 11 | 12 | 13 | def infer_test(model_name, output_probs, x): 14 | BATCH_SIZE = 20000 15 | 16 | with tf.Session().as_default() as sess: 17 | ds, filename = dataset.test_features_dataset() 18 | ds_iter = ds.batch(BATCH_SIZE).make_initializable_iterator() 19 | sess.run(ds_iter.initializer, feed_dict={filename: paths.TEST_TF_RECORDS}) 20 | 21 | tf.global_variables_initializer().run() 22 | 23 | saver = tf.train.Saver() 24 | lines = open(os.path.join(paths.CHECKPOINTS_DIR, model_name + '_latest')).read().split('\n') 25 | last_checkpoint = [l.split(':')[1].replace('"', '').strip() for l in lines if 'model_checkpoint_path:' in l][0] 26 | saver.restore(sess, os.path.join(paths.CHECKPOINTS_DIR, last_checkpoint)) 27 | 28 | _, one_hot_decoder = dataset.one_hot_label_encoder() 29 | 30 | breeds = one_hot_decoder(np.identity(consts.CLASSES_COUNT)) 31 | agg_test_df = None 32 | 33 | try: 34 | while True: 35 | test_batch = sess.run(ds_iter.get_next()) 36 | 37 | inception_output = test_batch['inception_output'] 38 | ids = test_batch['id'] 39 | 40 | pred_probs = sess.run(output_probs, feed_dict={x: inception_output.T}) 41 | 42 | #print(pred_probs.shape) 43 | 44 | test_df = pd.DataFrame(data=pred_probs.T, columns=breeds) 45 | test_df.index = ids 46 | 47 | if agg_test_df is None: 48 | agg_test_df = test_df 49 | else: 50 | agg_test_df = agg_test_df.append(test_df) 51 | 52 | except tf.errors.OutOfRangeError: 53 | print('End of the dataset') 54 | 55 | agg_test_df.to_csv(paths.TEST_PREDICTIONS, index_label='id', float_format='%.17f') 56 | 57 | print('predictions saved to %s' % paths.TEST_PREDICTIONS) 58 | 59 | if __name__ == '__main__': 60 | with tf.Graph().as_default(): 61 | x = tf.placeholder(dtype=tf.float32, shape=(consts.INCEPTION_CLASSES_COUNT, None), name="x") 62 | _, output_probs, _, _ = denseNN.denseNNModel( 63 | x, consts.HEAD_MODEL_LAYERS, gamma=0.01) 64 | infer_test(consts.CURRENT_MODEL_NAME, output_probs, x) 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/analysis/training_perf_analysis.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import pandas as pd 5 | import tensorflow as tf 6 | 7 | from src.common import consts 8 | from src.data_preparation import dataset 9 | from src.models import denseNN 10 | from src.common import paths 11 | 12 | 13 | def infer_train(model_name, output_probs, x): 14 | BATCH_SIZE = 20000 15 | 16 | with tf.Session().as_default() as sess: 17 | ds, filename = dataset.features_dataset() 18 | ds_iter = ds.batch(BATCH_SIZE).make_initializable_iterator() 19 | sess.run(ds_iter.initializer, feed_dict={filename: paths.TRAIN_TF_RECORDS}) 20 | 21 | tf.global_variables_initializer().run() 22 | 23 | saver = tf.train.Saver() 24 | lines = open(os.path.join(paths.CHECKPOINTS_DIR, model_name + '_latest')).read().split('\n') 25 | last_checkpoint = [l.split(':')[1].replace('"', '').strip() for l in lines if 'model_checkpoint_path:' in l][0] 26 | saver.restore(sess, os.path.join(paths.CHECKPOINTS_DIR, last_checkpoint)) 27 | 28 | _, one_hot_decoder = dataset.one_hot_label_encoder() 29 | 30 | breeds = one_hot_decoder(np.identity(consts.CLASSES_COUNT)) 31 | agg_test_df = None 32 | 33 | try: 34 | while True: 35 | test_batch = sess.run(ds_iter.get_next()) 36 | 37 | inception_output = test_batch['inception_output'] 38 | labels = test_batch['label'] 39 | 40 | pred_probs = sess.run(output_probs, feed_dict={x: inception_output.T}) 41 | pred_probs_max = pred_probs >= np.max(pred_probs, axis=0) 42 | pred_breeds = one_hot_decoder(pred_probs_max.T) 43 | 44 | test_df = pd.DataFrame(data={'pred': pred_breeds, 'actual': labels}) 45 | 46 | if agg_test_df is None: 47 | agg_test_df = test_df 48 | else: 49 | agg_test_df = agg_test_df.append(test_df) 50 | 51 | except tf.errors.OutOfRangeError: 52 | print('End of the dataset') 53 | 54 | print(agg_test_df.take(range(0, 10))) 55 | 56 | agg_test_df.to_csv(paths.TRAIN_CONFUSION, index_label='id', float_format='%.17f') 57 | 58 | print('predictions saved to %s' % paths.TRAIN_CONFUSION) 59 | 60 | 61 | if __name__ == '__main__': 62 | with tf.Graph().as_default(): 63 | x = tf.placeholder(dtype=tf.float32, shape=(consts.INCEPTION_CLASSES_COUNT, None), name="x") 64 | _, output_probs, _, _ = denseNN.denseNNModel( 65 | x, consts.HEAD_MODEL_LAYERS, gamma=0.01) 66 | infer_train(consts.CURRENT_MODEL_NAME, output_probs, x) 67 | -------------------------------------------------------------------------------- /data/breeds.csv: -------------------------------------------------------------------------------- 1 | id,breed 2 | 0,affenpinscher 3 | 1,afghan_hound 4 | 2,african_hunting_dog 5 | 3,airedale 6 | 4,american_staffordshire_terrier 7 | 5,appenzeller 8 | 6,australian_terrier 9 | 7,basenji 10 | 8,basset 11 | 9,beagle 12 | 10,bedlington_terrier 13 | 11,bernese_mountain_dog 14 | 12,black-and-tan_coonhound 15 | 13,blenheim_spaniel 16 | 14,bloodhound 17 | 15,bluetick 18 | 16,border_collie 19 | 17,border_terrier 20 | 18,borzoi 21 | 19,boston_bull 22 | 20,bouvier_des_flandres 23 | 21,boxer 24 | 22,brabancon_griffon 25 | 23,briard 26 | 24,brittany_spaniel 27 | 25,bull_mastiff 28 | 26,cairn 29 | 27,cardigan 30 | 28,chesapeake_bay_retriever 31 | 29,chihuahua 32 | 30,chow 33 | 31,clumber 34 | 32,cocker_spaniel 35 | 33,collie 36 | 34,curly-coated_retriever 37 | 35,dandie_dinmont 38 | 36,dhole 39 | 37,dingo 40 | 38,doberman 41 | 39,english_foxhound 42 | 40,english_setter 43 | 41,english_springer 44 | 42,entlebucher 45 | 43,eskimo_dog 46 | 44,flat-coated_retriever 47 | 45,french_bulldog 48 | 46,german_shepherd 49 | 47,german_short-haired_pointer 50 | 48,giant_schnauzer 51 | 49,golden_retriever 52 | 50,gordon_setter 53 | 51,great_dane 54 | 52,great_pyrenees 55 | 53,greater_swiss_mountain_dog 56 | 54,groenendael 57 | 55,ibizan_hound 58 | 56,irish_setter 59 | 57,irish_terrier 60 | 58,irish_water_spaniel 61 | 59,irish_wolfhound 62 | 60,italian_greyhound 63 | 61,japanese_spaniel 64 | 62,keeshond 65 | 63,kelpie 66 | 64,kerry_blue_terrier 67 | 65,komondor 68 | 66,kuvasz 69 | 67,labrador_retriever 70 | 68,lakeland_terrier 71 | 69,leonberg 72 | 70,lhasa 73 | 71,malamute 74 | 72,malinois 75 | 73,maltese_dog 76 | 74,mexican_hairless 77 | 75,miniature_pinscher 78 | 76,miniature_poodle 79 | 77,miniature_schnauzer 80 | 78,newfoundland 81 | 79,norfolk_terrier 82 | 80,norwegian_elkhound 83 | 81,norwich_terrier 84 | 82,old_english_sheepdog 85 | 83,otterhound 86 | 84,papillon 87 | 85,pekinese 88 | 86,pembroke 89 | 87,pomeranian 90 | 88,pug 91 | 89,redbone 92 | 90,rhodesian_ridgeback 93 | 91,rottweiler 94 | 92,saint_bernard 95 | 93,saluki 96 | 94,samoyed 97 | 95,schipperke 98 | 96,scotch_terrier 99 | 97,scottish_deerhound 100 | 98,sealyham_terrier 101 | 99,shetland_sheepdog 102 | 100,shih-tzu 103 | 101,siberian_husky 104 | 102,silky_terrier 105 | 103,soft-coated_wheaten_terrier 106 | 104,staffordshire_bullterrier 107 | 105,standard_poodle 108 | 106,standard_schnauzer 109 | 107,sussex_spaniel 110 | 108,tibetan_mastiff 111 | 109,tibetan_terrier 112 | 110,toy_poodle 113 | 111,toy_terrier 114 | 112,vizsla 115 | 113,walker_hound 116 | 114,weimaraner 117 | 115,welsh_springer_spaniel 118 | 116,west_highland_white_terrier 119 | 117,whippet 120 | 118,wire-haired_fox_terrier 121 | 119,yorkshire_terrier 122 | -------------------------------------------------------------------------------- /src/data_preparation/dataset.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import tensorflow as tf 4 | from sklearn import preprocessing 5 | 6 | from src.common import consts 7 | from src.common import paths 8 | 9 | 10 | def get_int64_feature(example, name): 11 | return int(example.features.feature[name].int64_list.value[0]) 12 | 13 | 14 | def get_float_feature(example, name): 15 | return int(example.features.feature[name].float_list.value) 16 | 17 | 18 | def get_bytes_feature(example, name): 19 | return example.features.feature[name].bytes_list.value[0] 20 | 21 | 22 | def read_tf_record(record): 23 | features = tf.parse_single_example( 24 | record, 25 | features={ 26 | consts.IMAGE_RAW_FIELD: tf.FixedLenFeature([], tf.string), 27 | 'label': tf.FixedLenFeature([], tf.string), 28 | consts.LABEL_ONE_HOT_FIELD: tf.FixedLenFeature([consts.CLASSES_COUNT], tf.float32), 29 | consts.INCEPTION_OUTPUT_FIELD: tf.FixedLenFeature([2048], tf.float32) 30 | }) 31 | return features 32 | 33 | 34 | def read_test_tf_record(record): 35 | features = tf.parse_single_example( 36 | record, 37 | features={ 38 | 'id': tf.FixedLenFeature([], tf.string), 39 | consts.IMAGE_RAW_FIELD: tf.FixedLenFeature([], tf.string), 40 | consts.INCEPTION_OUTPUT_FIELD: tf.FixedLenFeature([2048], tf.float32) 41 | }) 42 | return features 43 | 44 | 45 | def features_dataset(): 46 | filenames = tf.placeholder(tf.string) 47 | ds = tf.contrib.data.TFRecordDataset(filenames, compression_type='') \ 48 | .map(read_tf_record) 49 | 50 | return ds, filenames 51 | 52 | 53 | def test_features_dataset(): 54 | filenames = tf.placeholder(tf.string) 55 | ds = tf.contrib.data.TFRecordDataset(filenames, compression_type='') \ 56 | .map(read_test_tf_record) 57 | 58 | return ds, filenames 59 | 60 | 61 | def one_hot_label_encoder(): 62 | train_Y_orig = pd.read_csv(paths.BREEDS, dtype={'breed': np.str}) 63 | lb = preprocessing.LabelBinarizer() 64 | lb.fit(train_Y_orig['breed']) 65 | 66 | def encode(labels): 67 | return np.asarray(lb.transform(labels), dtype=np.float32) 68 | 69 | def decode(one_hots): 70 | return np.asarray(lb.inverse_transform(one_hots), dtype=np.str) 71 | 72 | return encode, decode 73 | 74 | 75 | if __name__ == '__main__': 76 | with tf.Graph().as_default() as g, tf.Session().as_default() as sess: 77 | ds, filenames = features_dataset() 78 | ds_iter = ds.shuffle(buffer_size=1000, seed=1).batch(10).make_initializable_iterator() 79 | next_record = ds_iter.get_next() 80 | 81 | sess.run(ds_iter.initializer, feed_dict={filenames: paths.TRAIN_TF_RECORDS}) 82 | features = sess.run(next_record) 83 | 84 | _, one_hot_decoder = one_hot_label_encoder() 85 | 86 | print(one_hot_decoder(features['inception_output'])) 87 | print(features['label']) 88 | print(features['inception_output'].shape) 89 | -------------------------------------------------------------------------------- /src/data_preparation/stanford_ds_to_tfrecords.py: -------------------------------------------------------------------------------- 1 | import os 2 | import xml.etree.ElementTree 3 | 4 | import tensorflow as tf 5 | 6 | from src.common import consts 7 | import dataset 8 | from src.freezing import inception 9 | from src.common import paths 10 | from tf_record_utils import * 11 | 12 | images_root_dir = os.path.join(paths.STANFORD_DS_DIR, 'Images') 13 | annotations_root_dir = os.path.join(paths.STANFORD_DS_DIR, 'Annotation') 14 | 15 | 16 | def parse_annotation(path): 17 | xml_root = xml.etree.ElementTree.parse(path).getroot() 18 | object = xml_root.findall('object')[0] 19 | name = object.findall('name')[0].text.lower() 20 | bound_box = object.findall('bndbox')[0] 21 | 22 | return { 23 | 'breed': name, 24 | 'bndbox_xmin': bound_box.findall('xmin')[0].text, 25 | 'bndbox_ymin': bound_box.findall('ymin')[0].text, 26 | 'bndbox_xmax': bound_box.findall('xmax')[0].text, 27 | 'bndbox_ymax': bound_box.findall('ymax')[0].text 28 | } 29 | 30 | 31 | def parse_image(breed_dir, filename): 32 | path = os.path.join(images_root_dir, breed_dir, filename + '.jpg') 33 | img_raw = open(path, 'r').read() 34 | 35 | return img_raw 36 | 37 | 38 | def build_stanford_example(img_raw, inception_output, one_hot_label, annotation): 39 | example = tf.train.Example(features=tf.train.Features(feature={ 40 | 'label': bytes_feature(annotation['breed'].encode()), 41 | consts.IMAGE_RAW_FIELD: bytes_feature(img_raw), 42 | consts.LABEL_ONE_HOT_FIELD: float_feature(one_hot_label), 43 | consts.INCEPTION_OUTPUT_FIELD: float_feature(inception_output)})) 44 | 45 | return example 46 | 47 | 48 | if __name__ == '__main__': 49 | one_hot_encoder, _ = dataset.one_hot_label_encoder() 50 | 51 | with tf.Graph().as_default(), \ 52 | tf.Session().as_default() as sess, \ 53 | tf.python_io.TFRecordWriter(paths.STANFORD_DS_TF_RECORDS, 54 | tf.python_io.TFRecordCompressionType.NONE) as writer: 55 | 56 | incept_model = inception.inception_model() 57 | 58 | 59 | def get_inception_ouput(img): 60 | inception_output = incept_model(sess, img).reshape(-1).tolist() 61 | return inception_output 62 | 63 | 64 | for breed_dir in [d for d in os.listdir(annotations_root_dir)]: 65 | print(breed_dir) 66 | for annotation_file in [f for f in os.listdir(os.path.join(annotations_root_dir, breed_dir))]: 67 | print(annotation_file) 68 | annotation = parse_annotation(os.path.join(annotations_root_dir, breed_dir, annotation_file)) 69 | 70 | # print(annotation) 71 | 72 | one_hot_label = one_hot_encoder([annotation['breed']]).reshape(-1).tolist() 73 | image = parse_image(breed_dir, annotation_file) 74 | 75 | example = build_stanford_example(image, get_inception_ouput(image), one_hot_label, annotation) 76 | 77 | writer.write(example.SerializeToString()) 78 | 79 | writer.flush() 80 | writer.close() 81 | 82 | print('Finished') 83 | -------------------------------------------------------------------------------- /src/freezing/freeze.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import tensorflow as tf 4 | from tensorflow.python.tools import freeze_graph 5 | 6 | from src.common import consts 7 | from src.models import denseNN 8 | from src.common import paths 9 | 10 | 11 | def _freeze_graph(graph_path, checkpoint_path, output_node_names, output_path): 12 | restore_op_name = 'save/restore_all' 13 | filename_tensor_name = 'save/Const:0' 14 | 15 | saved_path = freeze_graph.freeze_graph( 16 | graph_path, '', True, checkpoint_path, 17 | output_node_names, restore_op_name, filename_tensor_name, 18 | output_path, False, '', '') 19 | 20 | print('Frozen model saved to ' + output_path) 21 | 22 | return saved_path 23 | 24 | 25 | def freeze_current_model(model_name, output_node_names): 26 | lines = open(os.path.join(paths.CHECKPOINTS_DIR, model_name + '_latest')).read().split('\n') 27 | last_checkpoint = [l.split(':')[1].replace('"', '').strip() for l in lines if 'model_checkpoint_path:' in l][0] 28 | 29 | checkpoint_path = os.path.join(paths.CHECKPOINTS_DIR, last_checkpoint) 30 | graph_path = os.path.join(paths.GRAPHS_DIR, model_name + '.pb') 31 | output_graph_path = os.path.join(paths.FROZEN_MODELS_DIR, model_name + '.pb') 32 | 33 | #saver = tf.train.Saver() 34 | #checkpoint_path = saver.save(sess, checkpoint_prefix, global_step=0, latest_filename=model_name) 35 | tf.train.write_graph(g, paths.GRAPHS_DIR, os.path.basename(graph_path), as_text=False) 36 | 37 | _freeze_graph(graph_path, checkpoint_path, output_node_names=output_node_names, output_path=output_graph_path) 38 | 39 | 40 | def freeze_model(model_name, checkpoint, output_node_names): 41 | checkpoint_path = os.path.join(paths.CHECKPOINTS_DIR, checkpoint) 42 | graph_path = os.path.join(paths.GRAPHS_DIR, model_name + '.pbtext') 43 | output_graph_path = os.path.join(paths.FROZEN_MODELS_DIR, model_name + '.pb') 44 | 45 | _freeze_graph(graph_path, checkpoint_path, output_node_names=output_node_names, output_path=output_graph_path) 46 | 47 | 48 | def unfreeze_into_current_graph(model_path, tensor_names): 49 | with tf.gfile.FastGFile(name=model_path, mode='rb') as f: 50 | graph_def = tf.GraphDef() 51 | graph_def.ParseFromString(f.read()) 52 | tf.import_graph_def(graph_def, name='') 53 | g = tf.get_default_graph() 54 | 55 | tensors = {t: g.get_tensor_by_name(t) for t in tensor_names} 56 | 57 | return tensors 58 | 59 | 60 | if __name__ == '__main__': 61 | with tf.Graph().as_default() as g, tf.Session().as_default() as sess: 62 | tensors = unfreeze_into_current_graph(paths.IMAGENET_GRAPH_DEF, 63 | tensor_names=[ 64 | consts.INCEPTION_INPUT_TENSOR, 65 | consts.INCEPTION_OUTPUT_TENSOR]) 66 | 67 | _, output_probs, y, _ = denseNN.denseNNModel( 68 | tf.reshape(tensors[consts.INCEPTION_OUTPUT_TENSOR], shape=(-1, 1), name=consts.HEAD_INPUT_NODE_NAME), 69 | consts.HEAD_MODEL_LAYERS,gamma=0.01) 70 | 71 | tf.global_variables_initializer().run() 72 | 73 | saver = tf.train.Saver() 74 | saver.restore(sess, os.path.join(paths.CHECKPOINTS_DIR, consts.CURRENT_MODEL_NAME)) 75 | 76 | freeze_current_model(consts.CURRENT_MODEL_NAME, output_node_names=consts.OUTPUT_NODE_NAME) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dog Breeds Classification 2 | 3 | This repo is intended to contain a set of scripts and data for reproducing dog breed classification model training, analysis, and inference. 4 | 5 | ![preview](images/preview.png) 6 | 7 | ### Prerequisites 8 | 9 | 1. Install Python 2.7 10 | 2. Install all required python dependencies: `pip install -r requirements.txt` 11 | 12 | ### Download Data 13 | 14 | 15 | 1. `cd` to this repo root directory 16 | 2. Execute setup script: `sh ./setup/setup.sh`. In its turn the script executes the following other scripts: 17 | * Creates all required directories: `sh ./create_dirs.sh` 18 | * Downloads Google Inception model: `sh ./inception_download.sh`. The link to the frozen TensorFlow model is taken from [here](https://github.com/tensorflow/models/blob/master/tutorials/image/imagenet/classify_image.py#L51) 19 | * Downloads [Stanford Dogs Dataset](http://vision.stanford.edu/aditya86/ImageNetDogs/): `sh ./download_stanford_dogs_dataset.sh` 20 | 21 | ### Prepare Data 22 | 23 | 1. Convert downloaded Stanford Dogs Dataset to TensorFlow friendly [TFRecords](https://www.tensorflow.org/programmers_guide/datasets#consuming_tfrecord_data) file: `python -m src.data_preparation.stanford_ds_to_tfrecords` 24 | 25 | ### Train 26 | 27 | This section describes how to build dog breed classification dense neural network model on top of the pre-trained by Google deep neural network (namely Inception model). 28 | 29 | 1. Give a name to the model your are going to train by assigning a name to `CURRENT_MODEL_NAME` variable in [consts.py](src/common/consts.py#L14) script 30 | 2. Configure number of layers and number of units in each layer by setting `HEAD_MODEL_LAYERS` variable in [consts.py](src/common/consts.py#L18) 31 | 3. Train the model: `python -m src.training.train`. It might take 25-35 minutes depending on the depth of your model and number of epochs (which could be configured in the train.py script itself). TensorBoard could be used to observe the training process: `tensorboard --logdir=./summary` 32 | 33 | ### Freeze Model 34 | 35 | 1. Freeze the model: `python -m src.freezing.freeze`. This will bind Inception model with the trained on the previous step "head" model and serialize it as a TensorFlow graph with variables represented as constants. This frozen model will be ready to use for classification tasks. 36 | 37 | ### Analyze 38 | 39 | 1. Produce CSV file with predicted vs actual breed. This could be used to analyze precision on the training data e.g. plot a confusion matrix (see [Confusion.ipynb](Confusion.ipynb)). Result CSV file goes to `metrics/training_confusion.csv`: `python -m src.analysis.training_perf_analysis` 40 | 41 | ### Infer 42 | 43 | 1. Use the model frozen on the previous step to classify an image either available on the filesystem or downloadable as an HTTP resource: 44 | 45 | `python -m src.inference.classify uri https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/master/images/shih-tzu.jpg` 46 | 47 | `python -m src.inference.classify file images/airedale.jpg` 48 | 49 | In my case the model was training for 25 mins (5000 epochs) and the following sample classification outputs were produced: 50 | 51 | ![airedale](images/airedale.jpg) 52 | 53 | (Image is taken from [http://dogtime.com/dog-breeds/airedale-terrier](http://dogtime.com/dog-breeds/airedale-terrier)) 54 | 55 | | breed | probability | 56 | | ------ | ------ | 57 | | airedale | 0.992064 | 58 | | otterhound | 0.005108 | 59 | | chesapeake_bay_retriever | 0.001259 | 60 | | irish_terrier | 0.000635 | 61 | | wire-haired_fox_terrier | 0.000378 | 62 | 63 | ![shi-tzu](https://raw.githubusercontent.com/stormy-ua/dog-breeds-classification/master/images/shih-tzu.jpg) 64 | 65 | (Image is taken from [http://dogtime.com/dog-breeds/shih-tzu](https://dogtime.com/dog-breeds/shih-tzu#/slide/1)) 66 | 67 | | breed | probability | 68 | | ------ | ------ | 69 | | shih-tzu | 0.752834 | 70 | | lhasa | 0.234748 | 71 | | tibetan_terrier | 0.007598 | 72 | | maltese_dog | 0.000969 | 73 | |japanese_spaniel | 0.000936 | 74 | 75 | 76 | These examples also available in the [Inference.ipynb](Inference.ipynb) Python notebook. 77 | 78 | Have a fun! 79 | 80 | ### Running in Docker 81 | 82 | The already pre-built model w/ all the scripts and Python notebooks for inference are available in the docker container. Run the container: 83 | 84 | `docker run -p 8888:8888 -p 6006:6006 kirillpanarin/dog_breed_classification` 85 | 86 | Navigate a browser to `http://localhost:8888/notebooks/Inference.ipynb` and you will be all set for doing inference against real-life dog images. 87 | 88 | ### Kagle Dog Breed Classification Competition 89 | 90 | This model ranked #3 in the [Kaggle's Dog Breed Classification Challenge](https://www.kaggle.com/c/dog-breed-identification). 91 | 92 | ![kaggle](images/kaggle_leaderboard.jpg) 93 | -------------------------------------------------------------------------------- /src/training/train.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pyprind 4 | import tensorflow as tf 5 | 6 | from src.common import consts 7 | from src.data_preparation import dataset 8 | from src.models import denseNN 9 | from src.common import paths 10 | 11 | 12 | def train_dev_split(sess, tf_records_path, dev_set_size=2000, batch_size=64, train_sample_size=2000): 13 | ds_, filename = dataset.features_dataset() 14 | 15 | ds = ds_.shuffle(buffer_size=20000) 16 | 17 | train_ds = ds.skip(dev_set_size).repeat() 18 | train_ds_iter = train_ds.shuffle(buffer_size=20000) \ 19 | .batch(batch_size) \ 20 | .make_initializable_iterator() 21 | 22 | train_sample_ds = ds.skip(dev_set_size) 23 | train_sample_ds_iter = train_sample_ds.shuffle(buffer_size=20000) \ 24 | .take(train_sample_size) \ 25 | .batch(train_sample_size) \ 26 | .make_initializable_iterator() 27 | 28 | dev_ds_iter = ds.take(dev_set_size).batch(dev_set_size).make_initializable_iterator() 29 | 30 | sess.run(train_ds_iter.initializer, feed_dict={filename: tf_records_path}) 31 | sess.run(dev_ds_iter.initializer, feed_dict={filename: tf_records_path}) 32 | sess.run(train_sample_ds_iter.initializer, feed_dict={filename: tf_records_path}) 33 | 34 | return train_ds_iter.get_next(), dev_ds_iter.get_next(), train_sample_ds_iter.get_next() 35 | 36 | 37 | def error(x, output_probs, name): 38 | expected = tf.placeholder(tf.float32, shape=(consts.CLASSES_COUNT, None), name='expected') 39 | exp_vs_output = tf.equal(tf.argmax(output_probs, axis=0), tf.argmax(expected, axis=0)) 40 | accuracy = 1. - tf.reduce_mean(tf.cast(exp_vs_output, dtype=tf.float32)) 41 | summaries = [tf.summary.scalar(name, accuracy)] 42 | 43 | merged_summaries = tf.summary.merge(summaries) 44 | 45 | def run(sess, output, expected_): 46 | acc, summary_acc = sess.run([accuracy, merged_summaries], 47 | feed_dict={x: output, expected: expected_}) 48 | 49 | return acc, summary_acc 50 | 51 | return run 52 | 53 | 54 | def make_model_name(prefix, batch_size, learning_rate): 55 | return '%s_%d_%s' % (prefix, batch_size, str(learning_rate).replace('0.', '')) 56 | 57 | 58 | if __name__ == '__main__': 59 | BATCH_SIZE = 64 60 | EPOCHS_COUNT = 5000 61 | LEARNING_RATE = 0.0001 62 | 63 | model_name = consts.CURRENT_MODEL_NAME 64 | 65 | with tf.Graph().as_default() as g, tf.Session().as_default() as sess: 66 | next_train_batch, get_dev_ds, get_train_sample_ds = \ 67 | train_dev_split(sess, paths.TRAIN_TF_RECORDS, 68 | dev_set_size=consts.DEV_SET_SIZE, 69 | batch_size=BATCH_SIZE, 70 | train_sample_size=consts.TRAIN_SAMPLE_SIZE) 71 | 72 | dev_set = sess.run(get_dev_ds) 73 | dev_set_inception_output = dev_set[consts.INCEPTION_OUTPUT_FIELD] 74 | dev_set_y_one_hot = dev_set[consts.LABEL_ONE_HOT_FIELD] 75 | 76 | train_sample = sess.run(get_train_sample_ds) 77 | train_sample_inception_output = train_sample[consts.INCEPTION_OUTPUT_FIELD] 78 | train_sample_y_one_hot = train_sample[consts.LABEL_ONE_HOT_FIELD] 79 | 80 | x = tf.placeholder(dtype=tf.float32, shape=(consts.INCEPTION_CLASSES_COUNT, None), name="x") 81 | cost, output_probs, y, nn_summaries = denseNN.denseNNModel( 82 | x, consts.HEAD_MODEL_LAYERS, gamma=0.001) 83 | optimizer = tf.train.AdamOptimizer(learning_rate=LEARNING_RATE).minimize(cost) 84 | 85 | dev_error_eval = error(x, output_probs, name='test_error') 86 | train_error_eval = error(x, output_probs, name='train_error') 87 | 88 | nn_merged_summaries = tf.summary.merge(nn_summaries) 89 | tf.global_variables_initializer().run() 90 | 91 | writer = tf.summary.FileWriter(os.path.join(paths.SUMMARY_DIR, model_name)) 92 | 93 | bar = pyprind.ProgBar(EPOCHS_COUNT, update_interval=1, width=60) 94 | 95 | saver = tf.train.Saver() 96 | 97 | for epoch in range(0, EPOCHS_COUNT): 98 | batch_features = sess.run(next_train_batch) 99 | batch_inception_output = batch_features[consts.INCEPTION_OUTPUT_FIELD] 100 | batch_y = batch_features[consts.LABEL_ONE_HOT_FIELD] 101 | 102 | _, summary = sess.run([optimizer, nn_merged_summaries], feed_dict={ 103 | x: batch_inception_output.T, 104 | y: batch_y.T 105 | }) 106 | 107 | writer.add_summary(summary, epoch) 108 | 109 | _, dev_summaries = dev_error_eval(sess, dev_set_inception_output.T, dev_set_y_one_hot.T) 110 | writer.add_summary(dev_summaries, epoch) 111 | 112 | _, train_sample_summaries = train_error_eval(sess, train_sample_inception_output.T, train_sample_y_one_hot.T) 113 | writer.add_summary(train_sample_summaries, epoch) 114 | 115 | writer.flush() 116 | 117 | if epoch % 10 == 0 or epoch == EPOCHS_COUNT: 118 | saver.save(sess, os.path.join(paths.CHECKPOINTS_DIR, model_name), latest_filename=model_name + '_latest') 119 | 120 | bar.update() 121 | -------------------------------------------------------------------------------- /src/data_preparation/kaggle_images_to_tfrecords.py: -------------------------------------------------------------------------------- 1 | import pyprind 2 | import tensorflow as tf 3 | 4 | from src.common import consts 5 | import dataset 6 | import image_utils 7 | from src.freezing import inception 8 | from src.common import paths 9 | from tf_record_utils import * 10 | 11 | def read_example(id, breed): 12 | image_str = tf.read_file(tf.string_join([paths.TRAIN_DIR, '/', id, paths.JPEG_EXT], separator='')) 13 | return id, image_str, breed 14 | 15 | 16 | def read_test_example(file_id): 17 | image_str = tf.read_file(tf.string_join([paths.TEST_DIR, '/', file_id], separator='')) 18 | parts = tf.string_split(tf.expand_dims(file_id, 0), '.').values 19 | return image_str, parts[0] 20 | 21 | 22 | def parse_row(line): 23 | vals = tf.string_split(tf.expand_dims(line, 0), ',').values 24 | return vals[0], vals[1] 25 | 26 | 27 | def build_train_example(img, one_hot_label, breed_label, inception_output): 28 | example = tf.train.Example(features=tf.train.Features(feature={ 29 | 'label': bytes_feature(breed_label.encode()), 30 | consts.IMAGE_RAW_FIELD: bytes_feature(img), 31 | consts.LABEL_ONE_HOT_FIELD: float_feature(one_hot_label), 32 | consts.INCEPTION_OUTPUT_FIELD: float_feature(inception_output)})) 33 | 34 | return example 35 | 36 | 37 | def image_augmenter(): 38 | img = tf.placeholder(tf.string) 39 | 40 | img_decoded = tf.image.decode_jpeg(img) 41 | 42 | # img1_tf = tf.image.encode_jpeg(image_utils.rotate_ccw(img_decoded, 1)) 43 | # img2_tf = tf.image.encode_jpeg(image_utils.rotate_ccw(img_decoded, 3)) 44 | img1_tf = tf.image.encode_jpeg(image_utils.adjust_contrast(img_decoded)) 45 | img2_tf = tf.image.encode_jpeg(image_utils.adjust_brightness(img_decoded, -0.15)) 46 | img3_tf = tf.image.encode_jpeg(image_utils.flip_along_width(img_decoded)) 47 | 48 | # examples.append(build_train_example( 49 | # tf.image.encode_jpeg(image_utils.adjust_brightness(img_decoded, 0.1)).eval(), 50 | # one_hot_label, breed_label, inception_output)) 51 | 52 | # examples.append(build_train_example( 53 | # tf.image.encode_jpeg(image_utils.adjust_brightness(img_decoded, -0.1)).eval(), 54 | # one_hot_label, breed_label, inception_output)) 55 | 56 | # examples.append(build_train_example( 57 | # tf.image.encode_jpeg(image_utils.adjust_contrast(img_decoded)).eval(), 58 | # one_hot_label, breed_label, inception_output)) 59 | 60 | sess = tf.get_default_session() 61 | 62 | def augment(image): 63 | img1, img2, img3 = sess.run([img1_tf, img2_tf, img3_tf], feed_dict={img: image}) 64 | 65 | return [img1, img2, img3] 66 | 67 | return augment 68 | 69 | 70 | def convert_train(tfrecords_path): 71 | one_hot_encoder, _ = dataset.one_hot_label_encoder() 72 | 73 | inception_graph = tf.Graph() 74 | inception_sess = tf.Session(graph=inception_graph) 75 | 76 | with inception_graph.as_default(), inception_sess.as_default(): 77 | incept_model = inception.inception_model() 78 | 79 | with tf.Graph().as_default(), tf.Session().as_default() as sess: 80 | 81 | labels_path = tf.placeholder(dtype=tf.string) 82 | 83 | images_ds = tf.contrib.data.TextLineDataset(labels_path) \ 84 | .skip(1) \ 85 | .map(parse_row) \ 86 | .map(read_example) 87 | 88 | labels_iter = images_ds.make_initializable_iterator() 89 | next_label = labels_iter.get_next() 90 | 91 | sess.run(labels_iter.initializer, feed_dict={labels_path: paths.LABELS}) 92 | 93 | print('Writing ', tfrecords_path) 94 | 95 | bar = pyprind.ProgBar(13000, update_interval=1, width=60) 96 | 97 | augmenter = image_augmenter() 98 | 99 | with tf.python_io.TFRecordWriter(tfrecords_path, tf.python_io.TFRecordCompressionType.NONE) as writer: 100 | try: 101 | while True: 102 | id, img, breed_label = sess.run(next_label) 103 | one_hot_label = one_hot_encoder([breed_label]).reshape(-1).tolist() 104 | 105 | def get_inception_ouput(img): 106 | with inception_graph.as_default(): 107 | inception_output = incept_model(inception_sess, img).reshape(-1).tolist() 108 | return inception_output 109 | # print(inception_output.shape) 110 | 111 | # print('writing %s - %s' % (len(img), breed_label)) 112 | 113 | images = [img] 114 | images.extend(augmenter(img)) 115 | 116 | for image in images: 117 | example = build_train_example(image, one_hot_label, breed_label, get_inception_ouput(image)) 118 | writer.write(example.SerializeToString()) 119 | 120 | bar.update() 121 | 122 | except tf.errors.OutOfRangeError: 123 | print('End of the dataset') 124 | 125 | writer.flush() 126 | writer.close() 127 | 128 | print('Finished') 129 | 130 | 131 | def convert_test(tfrecords_path): 132 | inception_graph = tf.Graph() 133 | inception_sess = tf.Session(graph=inception_graph) 134 | 135 | with inception_graph.as_default(), inception_sess.as_default(): 136 | incept_model = inception.inception_model() 137 | 138 | with tf.Graph().as_default() as g, tf.Session().as_default() as sess: 139 | 140 | labels_path = tf.placeholder(dtype=tf.string) 141 | 142 | images_ds = tf.contrib.data.Dataset.from_tensor_slices(tf.constant(tf.gfile.ListDirectory(paths.TEST_DIR))) \ 143 | .map(read_test_example) 144 | 145 | labels_iter = images_ds.make_initializable_iterator() 146 | next_label = labels_iter.get_next() 147 | 148 | sess.run(labels_iter.initializer, feed_dict={labels_path: paths.LABELS}) 149 | 150 | print('Writing ', tfrecords_path) 151 | 152 | with tf.python_io.TFRecordWriter(tfrecords_path, tf.python_io.TFRecordCompressionType.NONE) as writer: 153 | try: 154 | while True: 155 | img, id = sess.run(next_label) 156 | 157 | with inception_graph.as_default(): 158 | inception_output = incept_model(inception_sess, img).reshape(-1).tolist() 159 | # print(inception_output.shape) 160 | 161 | print('writing %s - %s' % (len(img), id)) 162 | example = tf.train.Example(features=tf.train.Features(feature={ 163 | 'id': bytes_feature(id.encode()), 164 | consts.IMAGE_RAW_FIELD: bytes_feature(img), 165 | consts.INCEPTION_OUTPUT_FIELD: float_feature(inception_output)})) 166 | 167 | writer.write(example.SerializeToString()) 168 | except tf.errors.OutOfRangeError: 169 | print('End of the dataset') 170 | 171 | writer.flush() 172 | 173 | print('Finished') 174 | 175 | 176 | #if __name__ == '__main__': 177 | #convert_train(paths.TRAIN_TF_RECORDS) 178 | #convert_test(paths.TEST_TF_RECORDS) 179 | --------------------------------------------------------------------------------