{"cells":[{"metadata":{"_uuid":"0c6224b2db42f8ac968015822923e692dced35db"},"cell_type":"markdown","source":"# DenseNet Transfer Learning - iWildCam 2019\n\nBreakdown of this notebook:\n1. **Loading the 32x32 dataset**: Load the data generated in *Reducing Image Sizes to 32x32*.\n2. **Create Callback for F1 Score**: F1-macro score is the official metric of the competition. We create a callback to keep track of that value as we train the model.\n3. **Creating and Training the Model**: Create a DenseNet model, and load weights pretrained on ImageNet. Train it on the entire dataset.\n4. **Evaluation**: Display the plots from the training history.\n5. **Submission**: Run predictions with `model.predict`, and create submission csv file.\n\n### References\n* [cifar10_cnn_keras.py](https://github.com/keras-team/keras/blob/master/examples/cifar10_cnn.py): Heavily inspired from this tutorial created by the Keras team. The architecture and training process is directly taken from them.\n* [Keras CNN Starter - PetFinder](https://www.kaggle.com/xhlulu/keras-cnn-starter-petfinder/): History plot and submission are inspired by this kernel\n* [Reducing Image Sizes to 32x32](https://www.kaggle.com/xhlulu/reducing-image-sizes-to-32x32): Image data (`X_train`, `X_test`) come from the output of this kernel.\n* [How to compute f1 score for each epoch in Keras](https://medium.com/@thongonary/how-to-compute-f1-score-for-each-epoch-in-keras-a1acd17715a2): Needed to compute the F1 Score after each epoch.\n* [CNN Baseline - iWildCam 2019](https://www.kaggle.com/xhlulu/cnn-baseline-iwildcam-2019): This is a fork of this notebook."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport json\n\nimport numpy as np\nimport pandas as pd\nimport keras\nfrom keras import layers\nfrom keras.applications import DenseNet121\nfrom keras.callbacks import Callback, ModelCheckpoint\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.layers import Dense, Dropout, Activation, Flatten\nfrom keras.layers import Conv2D, MaxPooling2D\nfrom keras.models import Sequential\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, f1_score, precision_score, recall_score","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8595f283fb90558f10bb93489016e826dc4a6544"},"cell_type":"code","source":"os.listdir('../input')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2302da2f218e50185e00059bfac60f1fca7d15e8"},"cell_type":"markdown","source":"## Loading the 32x32 dataset"},{"metadata":{"trusted":true,"_uuid":"09ba8de2a6a103f86f51b9df4641013b335fdc78"},"cell_type":"code","source":"# The data, split between train and test sets:\nx_train = np.load('../input/reducing-image-sizes-to-32x32/X_train.npy')\nx_test = np.load('../input/reducing-image-sizes-to-32x32/X_test.npy')\ny_train = np.load('../input/reducing-image-sizes-to-32x32/y_train.npy')\n\nprint('x_train shape:', x_train.shape)\nprint(x_train.shape[0], 'train samples')\nprint(x_test.shape[0], 'test samples')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"98bcca14dbabfc710c45993990bbd6881c89923f"},"cell_type":"code","source":"# Convert the images to float and scale it to a range of 0 to 1\nx_train = x_train.astype('float32')\nx_test = x_test.astype('float32')\nx_train /= 255.\nx_test /= 255.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create Callback for F1 score"},{"metadata":{"trusted":true,"_uuid":"53289edcced35cb632b33c92431e0c69bfa70c53"},"cell_type":"code","source":"class Metrics(Callback):\n    def on_train_begin(self, logs={}):\n        self.val_f1s = []\n        self.val_recalls = []\n        self.val_precisions = []\n\n    def on_epoch_end(self, epoch, logs={}):\n        X_val, y_val = self.validation_data[:2]\n        y_pred = self.model.predict(X_val)\n\n        y_pred_cat = keras.utils.to_categorical(\n            y_pred.argmax(axis=1),\n            num_classes=14\n        )\n\n        _val_f1 = f1_score(y_val, y_pred_cat, average='macro')\n        _val_recall = recall_score(y_val, y_pred_cat, average='macro')\n        _val_precision = precision_score(y_val, y_pred_cat, average='macro')\n\n        self.val_f1s.append(_val_f1)\n        self.val_recalls.append(_val_recall)\n        self.val_precisions.append(_val_precision)\n\n        print((f\"val_f1: {_val_f1:.4f}\"\n               f\" — val_precision: {_val_precision:.4f}\"\n               f\" — val_recall: {_val_recall:.4f}\"))\n\n        return\n\nf1_metrics = Metrics()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2bee5c9cd1102701aa9d937d52a9c06613e2a684"},"cell_type":"markdown","source":"## Creating and Training the Model"},{"metadata":{"trusted":true,"_uuid":"3d672303ef8958936449b1a4fd9eb30530fb0484"},"cell_type":"code","source":"densenet = DenseNet121(\n    weights='../input/densenet-keras/DenseNet-BC-121-32-no-top.h5',\n    include_top=False,\n    input_shape=(32,32,3)\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3d672303ef8958936449b1a4fd9eb30530fb0484"},"cell_type":"code","source":"model = Sequential()\nmodel.add(densenet)\nmodel.add(layers.GlobalAveragePooling2D())\nmodel.add(layers.Dense(14, activation='softmax'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3d672303ef8958936449b1a4fd9eb30530fb0484"},"cell_type":"code","source":"model.compile(loss='categorical_crossentropy',\n              optimizer='adam',\n              metrics=['accuracy'])\n\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"324c6cab174953fb8dcebd29ff0ad038c3c719cb"},"cell_type":"code","source":"model.compile(\n    loss='categorical_crossentropy',\n    optimizer='adam',\n    metrics=['accuracy']\n)\n\ncheckpoint = ModelCheckpoint(\n    'model.h5', \n    monitor='val_acc', \n    verbose=1, \n    save_best_only=True, \n    save_weights_only=False,\n    mode='auto'\n)\n\nhistory = model.fit(\n    x=x_train,\n    y=y_train,\n    batch_size=64,\n    epochs=7,\n    callbacks=[checkpoint, f1_metrics],\n    validation_split=0.1\n)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1c1609efbe52b3834678586f3e527155e94861b5"},"cell_type":"markdown","source":"## Evaluation"},{"metadata":{"trusted":true,"_uuid":"672356f3ada19735ba4624adf33b0b7a8d87bfe2"},"cell_type":"code","source":"with open('history.json', 'w') as f:\n    json.dump(history.history, f)\n\nhistory_df = pd.DataFrame(history.history)\nhistory_df['val_f1'] = f1_metrics.val_f1s\nhistory_df['val_precision'] = f1_metrics.val_precisions\nhistory_df['val_recall'] = f1_metrics.val_recalls\nhistory_df[['loss', 'val_loss']].plot()\nhistory_df[['acc', 'val_acc']].plot()\nhistory_df[['val_f1', 'val_precision', 'val_recall']].plot()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"81ef9f687914e8c851fc990bc5672b56bb907998"},"cell_type":"markdown","source":"## Submission"},{"metadata":{"trusted":true,"_uuid":"30b9113771fa2e8b109925df04f6cadc31acb00d"},"cell_type":"code","source":"model.load_weights('model.h5')\ny_test = model.predict(x_test)\n\nsubmission_df = pd.read_csv('../input/iwildcam-2019-fgvc6/sample_submission.csv')\nsubmission_df['Predicted'] = y_test.argmax(axis=1)\n\nprint(submission_df.shape)\nsubmission_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"40d47171db3c3628643982074335e12ab1be87b8"},"cell_type":"code","source":"submission_df.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}