{"cells":[{"metadata":{"_uuid":"cc267792824dfc851c16a2b13c4ba9e6f6de1a82","_cell_guid":"781a2fcb-8b2f-4be1-abe7-fc1f5193be37"},"cell_type":"markdown","source":"# Dog Breed Identification with Keras and the InceptionV3 model\n### This notebook only uses the top 20 breeds due to memory limitations of the kernel.\n### Also included are full instructions on how to get the InceptionV3 (actually, all) pretained model/data."},{"metadata":{"_uuid":"0db5ce184df4b269259a2b6270b01226da184b72","_cell_guid":"7c2281a5-4dbb-4c86-b151-03ed3ba3094e","trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\n\nimport numpy as np \nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.preprocessing.image import img_to_array\nfrom keras.preprocessing.image import load_img\nimport cv2\n\nfrom keras.applications import inception_v3\nfrom keras.applications.inception_v3 import InceptionV3\nfrom keras.applications.inception_v3 import preprocess_input as inception_v3_preprocessor\n\nfrom keras.layers import Dense, GlobalAveragePooling2D\nfrom keras.models import Model\n\nfrom keras.optimizers import Adam\nfrom keras.metrics import categorical_crossentropy\n\nfrom sklearn.model_selection import train_test_split\n\nfrom tqdm import tqdm\n\nfrom os import makedirs\nfrom os.path import expanduser, exists, join\n","execution_count":29,"outputs":[]},{"metadata":{"_uuid":"03f0e228fcc2f0f3539a6a1ee447718c5fdd4b93"},"cell_type":"markdown","source":"# How to get the transfer learning pretrained models/data\n**When using transfer learning models you need to have access to their data/model.\nWhen using your local computer the data/model will automatically be loaded via the internet.\nSince a Kaggle kernel does not have access to the interent we need to get the pretrained model/data for the kernel by doing  the following:**\n1. **First you add the Keras Pretrained Models data by clicking on the kernel's Data tab, doing a search for it, and adding it.**\n2. **Once you have the data you need to create the directories for your kernel (which follows).**"},{"metadata":{"trusted":true,"_uuid":"3ec0b6c210b74978b37df90046eec2ab6c3d6ff5"},"cell_type":"code","source":"# Create the directories for the pretrained models\n!ls ../input/keras-pretrained-models/   # just to be sure the data is here\n\ncache_dir = expanduser(join('~', '.keras'))\nif not exists(cache_dir):\n    makedirs(cache_dir)\nmodels_dir = join(cache_dir, 'models')\nif not exists(models_dir):\n    makedirs(models_dir)\n    \n!cp ../input/keras-pretrained-models/*notop* ~/.keras/models/\n!cp ../input/keras-pretrained-models/imagenet_class_index.json ~/.keras/models/\n!cp ../input/keras-pretrained-models/resnet50* ~/.keras/models/","execution_count":24,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"108e0e118dcd014357b3f81e51870578be0b8298"},"cell_type":"code","source":"# Set the train and test folder paths.\n# NOTE: train and test are now in the 'dog-breed-identification' folder since the Keras Pretrained Models data/directory is added\ntrain_folder = '../input/dog-breed-identification/train/'\ntest_folder = '../input/dog-breed-identification/test/'","execution_count":25,"outputs":[]},{"metadata":{"_uuid":"5e37d593c908f345385cfb27bb99a3e330490870","_cell_guid":"09b158c8-d0d9-40de-9598-42a384d88700","trusted":true},"cell_type":"code","source":"# get the dog image ids and labels/breed\ntrain_dogs = pd.read_csv('../input/dog-breed-identification/labels.csv')\ntrain_dogs.head()","execution_count":26,"outputs":[]},{"metadata":{"_uuid":"2e04915f050490c15a6a7d619df551a7ea376d3f","_cell_guid":"1637b159-1b3c-45f4-888b-7b315892c3c1","trusted":true},"cell_type":"code","source":"# Take a look at the class/breed distribution\nax=pd.value_counts(train_dogs['breed'],ascending=True).plot(kind='barh',\n                                                       fontsize=\"40\",\n                                                       title=\"Class Distribution\",\n                                                       figsize=(50,100))\nax.set(xlabel=\"Images per class\", ylabel=\"Classes\")\nax.xaxis.label.set_size(40)\nax.yaxis.label.set_size(40)\nax.title.set_size(60)\nplt.show()","execution_count":13,"outputs":[]},{"metadata":{"_uuid":"cae6738d0d98d20556912d10255bdde569aa1cfb","_cell_guid":"b32d1f9f-e3c6-4b1d-8eee-72b288e4e6e2","collapsed":true,"trusted":true},"cell_type":"code","source":"# Get the top 20 breeds which is what we use in this notebook\ntop_breeds = sorted(list(train_dogs['breed'].value_counts().head(20).index))\ntrain_dogs = train_dogs[train_dogs['breed'].isin(top_breeds)]","execution_count":14,"outputs":[]},{"metadata":{"_uuid":"c08a93a1c62dd083a80d1edcbaf5fcd942c34448","_cell_guid":"5f30638e-2c2f-41bf-b002-340da8f4ba19","trusted":true},"cell_type":"code","source":"# Let's see what breeds are the top 20\nprint(top_breeds)\ntrain_dogs.shape","execution_count":15,"outputs":[]},{"metadata":{"_uuid":"9860f217cdf7a476b5e0d61576505bae8cf48717","_cell_guid":"84219bf4-4842-4000-ae16-733f02ef82ec","collapsed":true,"trusted":true},"cell_type":"code","source":"# Get the labels of the top 20\ntarget_labels = train_dogs['breed']","execution_count":16,"outputs":[]},{"metadata":{"_uuid":"afc86864d10ae8766a3ec53d68183a7b0afec300","_cell_guid":"c4292d56-49e7-4c13-8c07-58780c342623","collapsed":true,"trusted":true},"cell_type":"code","source":"# One hot code the labels - need this for the model\none_hot = pd.get_dummies(target_labels, sparse = True)\none_hot_labels = np.asarray(one_hot)","execution_count":17,"outputs":[]},{"metadata":{"_uuid":"ba2fddff7de9c94bf6035c92c477f00812ea73f4","_cell_guid":"cb927f91-febc-405e-ac8b-3539afe52532","trusted":true},"cell_type":"code","source":"# add the actual path name of the pics to the data set\ntrain_dogs['image_path'] = train_dogs.apply( lambda x: (train_folder + x[\"id\"] + \".jpg\" ), axis=1)\ntrain_dogs.head()","execution_count":18,"outputs":[]},{"metadata":{"_uuid":"34d6bdfa321367b72c50893622ab1c6c85a65282","_cell_guid":"0373cbe7-a89f-4dbe-bb9d-717ec1bca6ef","collapsed":true,"trusted":true},"cell_type":"code","source":"# Convert the images to arrays which is used for the model. Inception uses image sizes of 299 x 299\ntrain_data = np.array([img_to_array(load_img(img, target_size=(299, 299))) for img in train_dogs['image_path'].values.tolist()]).astype('float32')","execution_count":19,"outputs":[]},{"metadata":{"_uuid":"b7656f2304bd2ce220fbcdfcc23396270b1c4fec","_cell_guid":"c26e6830-8b1d-4d7f-929d-6b72be3a5b04","collapsed":true,"trusted":true},"cell_type":"code","source":"# Split the data into train and validation. The stratify parm will insure  train and validation  \n# will have the same proportions of class labels as the input dataset.\nx_train, x_validation, y_train, y_validation = train_test_split(train_data, target_labels, test_size=0.2, stratify=np.array(target_labels), random_state=100)","execution_count":21,"outputs":[]},{"metadata":{"_uuid":"ebac7b3a418fff56bbb6f8a26c207154ea88dbdb","_cell_guid":"7d1ba098-b2ba-4dce-8be3-a13c0a30975e","trusted":true},"cell_type":"code","source":"# Need to know how many rows in each of the train/test split so we can \n# calculate steps_per_epoch and validatoin_steps for the model.fit_generator\nprint ('x_train shape = ', x_train.shape)\nprint ('x_validation shape = ', x_validation.shape)","execution_count":27,"outputs":[]},{"metadata":{"_uuid":"1f391c0eed51e48bc54746a89ecab9354f507b5a","_cell_guid":"311603a2-e6c2-469d-8783-7c8b7a3b2356","trusted":true},"cell_type":"code","source":"# Calculate the value counts for train and validation data and plot to show a good stratify\n# the plot should show an equal percentage split for each class\ndata = y_train.value_counts().sort_index().to_frame()   # this creates the data frame with train numbers\ndata.columns = ['train']   # give the column a name\ndata['validation'] = y_validation.value_counts().sort_index().to_frame()   # add the validation numbers\nnew_plot = data[['train','validation']].sort_values(['train']+['validation'], ascending=False)   # sort the data\nnew_plot.plot(kind='bar', stacked=True)\nplt.show()","execution_count":28,"outputs":[]},{"metadata":{"_uuid":"d5b763c0ff358428e9cb0bb921cad1bddde0d9f3","_cell_guid":"88750c7e-16fa-434a-85d1-8006080794ff","collapsed":true,"trusted":true},"cell_type":"code","source":"# Need to convert the train and validation labels into one hot encoded format\ny_train = pd.get_dummies(y_train.reset_index(drop=True)).as_matrix()\ny_validation = pd.get_dummies(y_validation.reset_index(drop=True)).as_matrix()","execution_count":30,"outputs":[]},{"metadata":{"_uuid":"eaaa2b2a51b7369c69d258b4343fe553af06bc94","_cell_guid":"48f96e48-89eb-4c35-82be-d8c323d1be59","collapsed":true,"trusted":true},"cell_type":"code","source":"# Create train generator.\ntrain_datagen = ImageDataGenerator(rescale=1./255, \n                                   rotation_range=30, \n                                   # zoom_range = 0.3, \n                                   width_shift_range=0.2,\n                                   height_shift_range=0.2, \n                                   horizontal_flip = 'true')\ntrain_generator = train_datagen.flow(x_train, y_train, shuffle=False, batch_size=10, seed=10)","execution_count":31,"outputs":[]},{"metadata":{"_uuid":"68d62b69efc7206c67ca5a5684c34871e8db9105","_cell_guid":"e0348b2e-1809-4080-8dca-00189ef5c563","collapsed":true,"trusted":true},"cell_type":"code","source":"# Create validation generator\nval_datagen = ImageDataGenerator(rescale = 1./255)\nval_generator = train_datagen.flow(x_validation, y_validation, shuffle=False, batch_size=10, seed=10)","execution_count":32,"outputs":[]},{"metadata":{"_uuid":"93e1b800044373755b2aa0b07c1364a83e66dfb9","_cell_guid":"c0e87d6b-7110-475e-8d61-cb0760b61514","collapsed":true},"cell_type":"markdown","source":"# Following the Keras documenation example for InceptionV3\nWith one minor change - use 'Adam'"},{"metadata":{"_uuid":"adb7744ebe5266dc924401fa6e597648a43635d4","_cell_guid":"eb433527-0ea9-4692-97d0-acabb33789fd","collapsed":true,"trusted":true},"cell_type":"code","source":"# Get the InceptionV3 model so we can do transfer learning\nbase_model = InceptionV3(weights = 'imagenet', include_top = False, input_shape=(299, 299, 3))","execution_count":33,"outputs":[]},{"metadata":{"_uuid":"910cb3ecf71b921a9f52f2f5de29b5c76f6a7a49","_cell_guid":"f81eaa77-9344-4b13-9dcb-a085577fb560","collapsed":true,"trusted":true},"cell_type":"code","source":"# Add a global spatial average pooling layer\nx = base_model.output\nx = GlobalAveragePooling2D()(x)","execution_count":34,"outputs":[]},{"metadata":{"_uuid":"560b5f819e44107a9b582ebea0d1a7155b229879","_cell_guid":"3d059107-afff-473a-94d3-dd7e0978297a","collapsed":true,"trusted":true},"cell_type":"code","source":"# Add a fully-connected layer and a logistic layer with 20 classes \n#(there will be 120 classes for the final submission)\nx = Dense(512, activation='relu')(x)\npredictions = Dense(20, activation='softmax')(x)","execution_count":35,"outputs":[]},{"metadata":{"_uuid":"968dba5601962813ffe60ef6c690891ed0ece193","_cell_guid":"398add4d-18ae-4465-865d-d6701c94887b","collapsed":true,"trusted":false},"cell_type":"code","source":"# The model we will train\nmodel = Model(inputs = base_model.input, outputs = predictions)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"39fb1c54573754f7d54a622d3f8cbae782194eef","_cell_guid":"a415a4c2-ff07-4090-9d42-c8c88e8b8e15","collapsed":true,"trusted":false},"cell_type":"code","source":"# first: train only the top layers i.e. freeze all convolutional InceptionV3 layers\nfor layer in base_model.layers:\n    layer.trainable = False","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"698a5859fbcd69ab763b56c86df6ce5a07d72878","_cell_guid":"1c3058ce-debe-43f3-8ab6-3209bd3c9cc1","collapsed":true,"trusted":false},"cell_type":"code","source":"# Compile with Adam\nmodel.compile(Adam(lr=.0001), loss='categorical_crossentropy', metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7eaa32ed90e9b65d4f9f05d21f77f312e70a2d5a","_cell_guid":"447bef61-b84d-46ea-9409-9fd8376f9031","collapsed":true,"trusted":false},"cell_type":"code","source":"# Train the model\nmodel.fit_generator(train_generator,\n                      steps_per_epoch = 175,\n                      validation_data = val_generator,\n                      validation_steps = 44,\n                      epochs = 10,\n                      verbose = 2)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5b878ffeb2ed081703ee0b1b37109497e4a28c6d","_cell_guid":"e2e4b9e4-3ec3-4c00-bea0-f17d0519e6ef","collapsed":true},"cell_type":"markdown","source":"## To increase the performance you could do the following:\n 1.  **train other layers of the InceptionV3 model**\n 2. ** use other classifers to create an ensemble model loss **\n \n ---"},{"metadata":{"_uuid":"fdd4d3f2c345855972e75eef2c252f67a46e1951","_cell_guid":"3998b22d-a510-4cb8-b64e-a67bc95c366d","collapsed":true},"cell_type":"markdown","source":"## Note, the following prediction and submission are commented out due to the kernel not being able to support the x_test data size. "},{"metadata":{"_uuid":"b4f14972b4f4ff443182ea5598d1d5b2437a0040","_cell_guid":"eace05c0-ddcc-498c-948c-a5af2096f230","collapsed":true,"trusted":true},"cell_type":"code","source":"# Use the sample submission file to set up the test data - x_test\n# test_data = pd.read_csv('../input/dog-breed-identification/sample_submission.csv')","execution_count":36,"outputs":[]},{"metadata":{"_uuid":"85b9b89ba97116b46e75a38ed4a9c029f3cb5ffc","_cell_guid":"15d95877-f528-4913-9aee-858c64638cec","trusted":true},"cell_type":"code","source":"# Creae the x_test\n# x_test = []\n# for i in tqdm(test_data['id'].values):\n#     img = cv2.imread('../input/dog-breed-identification/test/{}.jpg'.format(i))\n#     x_test.append(cv2.resize(img, (299, 299)))","execution_count":41,"outputs":[]},{"metadata":{"_uuid":"ffd2d951e6c8634eec64bade5e7d102c4de262b4","_cell_guid":"43f974b9-48f9-4c71-859f-f18ace3245a5","collapsed":true,"trusted":true},"cell_type":"code","source":"# Make it an array\n# x_test = np.array(x_test, np.float32) / 255.","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5efab7f2ca3979cfa8d613df7e13046190e60f29","_cell_guid":"7c8d232c-731f-400d-a98d-02348747b0ee","collapsed":true,"trusted":false},"cell_type":"code","source":"# Predict x_test\n# predictions = model.predict(x_test, verbose=2)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"94faeb8ab0ef0e2a7739f9a21884bc5d3fe015c4","_cell_guid":"f53435ce-ba23-4684-9937-7d29246b6da4","collapsed":true,"trusted":false},"cell_type":"code","source":"# Set column names to those generated by the one-hot encoding earlier\n# col_names = one_hot.columns.values","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ca6347056925466a4e939c103c9fdeed2f4d3687","_cell_guid":"0ba27840-97ba-42c4-b9e2-2fade87f3aa3","collapsed":true,"trusted":false},"cell_type":"code","source":"# Create the submission data.\n# submission_results = pd.DataFrame(predictions, columns = col_names)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0b459c4e0f929349201bad45beeb7fa8428ec7e2","_cell_guid":"f17666d6-7f90-4cd1-8e1e-d68c0e582beb","collapsed":true,"trusted":false},"cell_type":"code","source":"# Add the id as the first column\n# submission_results.insert(0, 'id', test_data['id'])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4f89e6713bb176ec1b4958d200c6e13005b9a048","_cell_guid":"a01c9c29-e8c4-44e1-a0b2-1dbfddccff8c","collapsed":true,"trusted":false},"cell_type":"code","source":"# Save the submission\n# submission_results.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4c2d532c95d4471c8abba76de4261f065676f726","_cell_guid":"4124bd56-ac29-4847-a618-31b371ebbc8a","collapsed":true,"trusted":false},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}