{"cells":[{"metadata":{"_uuid":"0c6224b2db42f8ac968015822923e692dced35db"},"cell_type":"markdown","source":"# CNN Baseline - 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 simple model (taken from the official Keras tutorial) and train it.\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."},{"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.callbacks import Callback\nfrom keras.datasets import cifar10\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Activation, Flatten\nfrom keras.layers import Conv2D, MaxPooling2D\nfrom sklearn.metrics import confusion_matrix, f1_score, precision_score, recall_score\nfrom sklearn.model_selection import train_test_split","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":"## 1. 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":{"_uuid":"a1b0ee5c2a6cf83e9a46d819cf6b83e9681a3b27"},"cell_type":"markdown","source":"## 2. 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=num_classes\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","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2bee5c9cd1102701aa9d937d52a9c06613e2a684"},"cell_type":"markdown","source":"## 3. Creating and Training the Model"},{"metadata":{"trusted":true,"_uuid":"3a4c15bf29543994a48a85f46de1234bbc22e7c8"},"cell_type":"code","source":"batch_size = 64\nnum_classes = 14\nepochs = 30\nval_split = 0.1\nsave_dir = os.path.join(os.getcwd(), 'models')\nmodel_name = 'keras_cnn_model.h5'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5ea3ca0451ab0a997111fea9ef2d1f762bc3d517"},"cell_type":"code","source":"model = Sequential()\nmodel.add(Conv2D(32, (3, 3), padding='same',\n                 input_shape=x_train.shape[1:]))\nmodel.add(Activation('relu'))\nmodel.add(Conv2D(32, (3, 3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Conv2D(64, (3, 3), padding='same'))\nmodel.add(Activation('relu'))\nmodel.add(Conv2D(64, (3, 3)))\nmodel.add(Activation('relu'))\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Flatten())\nmodel.add(Dense(512))\nmodel.add(Activation('relu'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(num_classes))\nmodel.add(Activation('softmax'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"221a5db16b6f78ce3b3ac108db36f87043a67f2b","scrolled":false},"cell_type":"code","source":"f1_metrics = Metrics()\n\nmodel.compile(\n    loss='categorical_crossentropy',\n    optimizer='adam',\n    metrics=['accuracy']\n)\n\nhist = model.fit(\n    x_train, \n    y_train,\n    batch_size=batch_size,\n    epochs=epochs,\n    callbacks=[f1_metrics],\n    validation_split=val_split,\n    shuffle=True\n)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e5544f73435319cd8ac16fbc8c0e3d6e2cbf7ede"},"cell_type":"markdown","source":"Save model and weights"},{"metadata":{"trusted":true,"_uuid":"66f66507c14a4b0b5ec40fb4989c05a3476ac2fe"},"cell_type":"code","source":"if not os.path.isdir(save_dir):\n    os.makedirs(save_dir)\nmodel_path = os.path.join(save_dir, model_name)\nmodel.save(model_path)\nprint('Saved trained model at %s ' % model_path)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1c1609efbe52b3834678586f3e527155e94861b5"},"cell_type":"markdown","source":"## 4. Evaluation"},{"metadata":{"trusted":true,"_uuid":"672356f3ada19735ba4624adf33b0b7a8d87bfe2"},"cell_type":"code","source":"history_df = pd.DataFrame(hist.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\n\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":"## 5. Submission"},{"metadata":{"trusted":true,"_uuid":"30b9113771fa2e8b109925df04f6cadc31acb00d"},"cell_type":"code","source":"y_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)\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)\nhistory_df.to_csv('history.csv', index=False)\n\nwith open('history.json', 'w') as f:\n    json.dump(hist.history, f)","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}