{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"<h1><center><font size=\"6\">iWildCam 2019 EDA</font></center></h1>\n\n<center><img src=\"https://upload.wikimedia.org/wikipedia/commons/0/01/Fauna.jpg\" width=\"800\"></img></center>\n\n<br>\n\n# <a id='0'>Content</a>\n\n- <a href='#1'>Introduction</a>  \n- <a href='#2'>Prepare the data analysis</a>  \n- <a href='#3'>Data exploration</a>   \n- <a href='#4'>Model</a>   \n- <a href='#5'>References</a>"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false},"cell_type":"markdown","source":"# <a id='1'>Introduction</a>  \n\n## Competition\n\nBiologists all over the world use camera traps to monitor biodiversity and population density of animal species. They have recently been making strides towards automating the species classification challenge in camera traps, but as they try to expand the scope of these models from specific regions where they have collected training data to nearby areas we are faced with an interesting probem: how do you classify a species in a new region that you may not have seen in previous training data?\n\n## Data\n\nIn order to tackle this problem, the competition organizers have prepared a challenge where the training data and test data are from different regions, namely The American Southwest and the American Northwest. The species seen in each region overlap, but are not identical, and the challenge is to classify the test species correctly.  \n\n## Kernel\n\nIn this Kernel we perform EDA on the data and create a predictive model, using <a href='#5'>References</a> [5]-[9].  The predictive model solution, including the use of pretrained DenseNet weights and reduced image sizes, should be credited to [5].\n\n## References\n\nPlease consult the <a href='#5'>References</a> section for the datasets, Kernels and articles used in this Kernel."},{"metadata":{"_uuid":"78673a0c3481281b3a414be4c89838133d2a2655"},"cell_type":"markdown","source":"# <a id='2'>Prepare for data analysis</a>  \n\n\n## Load packages"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"1e621b6aa5dd7d26264056efe033d5c56ed544f0"},"cell_type":"code","source":"import gc\nimport os\nimport json\nimport logging\nimport datetime\nimport warnings\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom PIL import Image\nimport matplotlib.pyplot as plt\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\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8b8cda8934ba9197f0bd8863b73aa28201716614"},"cell_type":"markdown","source":"## Load data   \n\nLet's check what data files are available."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"677830f2091ad37f3e9049d48eee5fcdcfb70eef"},"cell_type":"code","source":"IS_LOCAL = False\nif(IS_LOCAL):\n    PATH=\"../input/iwildcam/\"\nelse:\n    PATH=\"../input/iwildcam-2019-fgvc6/\"\nos.listdir(PATH)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"f2b513f81c28bfd291098681e32689b83c1fdedd"},"cell_type":"code","source":"%%time\ntrain_df = pd.read_csv(os.path.join(PATH, 'train.csv'))\ntest_df = pd.read_csv(os.path.join(PATH, 'test.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"x_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')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"66943014e42809d256e43b0cd3c8aef0780e1ff6"},"cell_type":"markdown","source":"Let's check the files."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"08004ea80063eb7027d354179e6c1f7f8ed15ae8"},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8ef93992823d4f8aec4954d5e6755653f8e782e1","_kg_hide-input":true},"cell_type":"code","source":"test_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print(\"Train and test shape: {} {}\".format(train_df.shape, test_df.shape))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cf1dd97d1645fee8728091153807fd3fbfd7a318"},"cell_type":"markdown","source":"# <a id='3'>Data exploration</a>  \n\nLet's define the classes:"},{"metadata":{"trusted":true,"_uuid":"cfd87e32623a3ce3d292e7c6e6e37e04bb6ae5f1","_kg_hide-input":true},"cell_type":"code","source":"classes_wild = {0: 'empty', 1: 'deer', 2: 'moose', 3: 'squirrel', 4: 'rodent', 5: 'small_mammal', \\\n                6: 'elk', 7: 'pronghorn_antelope', 8: 'rabbit', 9: 'bighorn_sheep', 10: 'fox', 11: 'coyote', \\\n                12: 'black_bear', 13: 'raccoon', 14: 'skunk', 15: 'wolf', 16: 'bobcat', 17: 'cat',\\\n                18: 'dog', 19: 'opossum', 20: 'bison', 21: 'mountain_goat', 22: 'mountain_lion'}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bfbaa4c3f11d7ec12bc3a1722b96ad849378de8f","_kg_hide-input":true},"cell_type":"code","source":"train_df['classes_wild'] = train_df['category_id'].apply(lambda cw: classes_wild[cw])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"66f4682ec98dfa8d192d91a8b5744c12e7b9d0b8"},"cell_type":"markdown","source":"We check again train:"},{"metadata":{"trusted":true,"_uuid":"485138046c45fa26a700bbd2fd1665684b775679","_kg_hide-input":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Check images  \nLet's check how many images are in train and test images folders."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"train_image_files = list(os.listdir(os.path.join(PATH,'train_images')))\ntest_image_files = list(os.listdir(os.path.join(PATH,'test_images')))\n                         \nprint(\"Number of image files: train:{} test:{}\".format(len(train_image_files), len(test_image_files)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can observe that the number of images in train folder is smaller that the number of rows in train dataset while the number of images in test folder is equal with the number of rows in test dataset.\n\nLet's check if every row in train and test have a corresponding image in the images folders."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"%%time\ntrain_file_names = list(train_df['file_name'])\nprint(\"Matching train image names: {}\".format(len(set(train_file_names).intersection(train_image_files))))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"%%time\ntest_file_names = list(test_df['file_name'])\nprint(\"Matching test image names: {}\".format(len(set(test_file_names).intersection(test_image_files))))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"957962cf077943f6332c1baf19c65d6925593a65"},"cell_type":"markdown","source":"## Clases of images\n\nLet's check the classes of images in train_df."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"8166edb7550ac6d4c85a4d6010368277a7d71dfa"},"cell_type":"code","source":"cnt_classes_images = train_df.classes_wild.nunique()\nprint(\"There are {} classes of images\".format(cnt_classes_images))\npd.DataFrame(train_df.classes_wild.value_counts()).transpose()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"fee8debd91d0516ceae0beae0d45d867e32d3350"},"cell_type":"code","source":"def plot_classes(feature, fs=8, show_percents=True, color_palette='Set3'):\n    f, ax = plt.subplots(1,1, figsize=(2*fs,4))\n    total = float(len(train_df))\n    g = sns.countplot(train_df[feature], order = train_df[feature].value_counts().index, palette=color_palette)\n    g.set_title(\"Number and percentage of labels for each class of {}\".format(feature))\n    if(show_percents):\n        for p in ax.patches:\n            height = p.get_height()\n            ax.text(p.get_x()+p.get_width()/2.,\n                    height + 3,\n                    '{:1.2f}%'.format(100*height/total),\n                    ha=\"center\") \n    plt.show()    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"de30e3081955a0e5433f51b30f7fcf57d76be8a1","scrolled":false,"_kg_hide-input":true},"cell_type":"code","source":"plot_classes('classes_wild')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0c1fd85074913d01c727061e720a685712aeea79"},"cell_type":"markdown","source":"Most of the images are of classe `empty`, followed by `opossum`, `racoon`, `coyote` and `rabbit`."},{"metadata":{"_uuid":"c1aa3ddb4671b33da9d22dfe61a68a39e59e85a5"},"cell_type":"markdown","source":"## Number of sequences frames"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"3533f6230a6255fb06a12211ea96500a115794ff"},"cell_type":"code","source":"plot_classes('seq_num_frames', fs=3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0979ab834324e52d50adf286757a667b4ab53b25"},"cell_type":"markdown","source":"Majority of sequence number frames are 1 (61%), followed by 3 (37%), the rest (1.2%) having 5.\n\n## Locations distribution"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"25d97d219178c4fdf2b1cc90ac1a9b7bcbb12b6d"},"cell_type":"code","source":"plot_classes('location', fs=15)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false},"cell_type":"markdown","source":"Majority of images are from location 96 (18.38%) and 26 (13.95%).\n\n## Locations and classes\n\nLet's show now the locations and classes."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,26))\nt = pd.DataFrame(train_df.groupby(['classes_wild', 'location'])['seq_id'].count().reset_index())\nm = t.pivot(index='location', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed per location', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5e0ef211062b479352d884fe90873f54dfbd53be"},"cell_type":"markdown","source":"Majority of records are actually from location 96 and without specifying the wild animal class (`empty`). Also, location 26 (the next one as frequence) is as well for `empty` class. Let's see what we get if we just remove the entries with `empty`.\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,26))\ntmp = train_df[train_df['classes_wild'] != 'empty']\nt = pd.DataFrame(tmp.groupby(['classes_wild', 'location'])['seq_id'].count().reset_index())\nm = t.pivot(index='location', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed per location (except `empty`)', size=16)\nplt.show()\ndel t, tmp, m\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Rights holder\n\nThe rights holder is most probably the photo author."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"7e94f564e73afc411469f4a7f65b927ae1d8fc48"},"cell_type":"code","source":"plot_classes('rights_holder', fs=3)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2169192827237929c83f1ea13b7d99936cde475f"},"cell_type":"markdown","source":"Only two rights holder are registered in `train_df` data.  \n\nLet's check the rights holder and type of images taken by each.\n\n## Rights holder and wild animals class\n\nWe represent the relationship between the rights holder and the animal classes presented in the photo dataset.\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,4))\nt = pd.DataFrame(train_df.groupby(['classes_wild', 'rights_holder'])['seq_id'].count().reset_index())\nm = t.pivot(index='rights_holder', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed by each rights holder', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We remove the `empty` class to see better the other classes."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,4))\nt = pd.DataFrame(train_df[~(train_df.classes_wild == 'empty')].groupby(['classes_wild', 'rights_holder'])['seq_id'].count().reset_index())\nm = t.pivot(index='rights_holder', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed by each rights holder (empty class removed)', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e8678f5c119bb96d1ee75d22d16469ec5ad5ad7b"},"cell_type":"markdown","source":"## Extract date and time information\n\nWe extract date and time information from the `date_time` column."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"7ced02d0a874b2a812d0c9ff6d8320af462337d6"},"cell_type":"code","source":"try:\n    train_df['date_time'] = pd.to_datetime(train_df['date_captured'], errors='coerce')\n    train_df[\"year\"] = train_df['date_time'].dt.year\n    train_df[\"month\"] = train_df['date_time'].dt.month\n    train_df[\"day\"] = train_df['date_time'].dt.day\n    train_df[\"hour\"] = train_df['date_time'].dt.hour\n    train_df[\"minute\"] = train_df['date_time'].dt.minute\nexcept Exception as ex:\n    print(\"Exception:\".format(ex))   ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a5c931a69af37d3babaaf7d08c9a3461c8da7d67"},"cell_type":"markdown","source":"Let's check again the data."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"155fac7ea5e3c0152e95cc8677fe04c30934a893"},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"d8076851a9cc5e0caa36161494cc4dc6ff3984c1"},"cell_type":"code","source":"plot_classes('year', fs=3)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"bfdd4634c6240d4c65c90fa075cf347d88b019a2","_kg_hide-output":false},"cell_type":"code","source":"plot_classes('month', fs=5)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"462b37682e82fd5372514197e5913c956cd8fa82"},"cell_type":"code","source":"plot_classes('day', fs=10)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"4e14a90bf3e1378abe552a9f159c8a536e836e12"},"cell_type":"code","source":"plot_classes('hour', fs=8)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Classes and hours  \n\nLet's show the number of wild animals observed at different hours."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,10))\nt = pd.DataFrame(train_df.groupby(['classes_wild', 'hour'])['seq_id'].count().reset_index())\nm = t.pivot(index='hour', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed per hour', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false},"cell_type":"markdown","source":"We remove `empty` class, which is observed mostly around noon, to see better the other classes distribution on hours.\n\n\n## Classes and months\n\nWe show the number of wild animals during different months.\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"tmp = train_df[train_df['classes_wild'] != 'empty']\nfig, ax = plt.subplots(1,1,figsize=(16,12))\nt = pd.DataFrame(tmp.groupby(['classes_wild', 'hour'])['seq_id'].count().reset_index())\nm = t.pivot(index='hour', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed per hour', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can observe that majority of racoons and opossums images are captured during night.  \n\nLet's see in what month are each species images mostly captured."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(16,8))\nt = pd.DataFrame(tmp.groupby(['classes_wild', 'month'])['seq_id'].count().reset_index())\nm = t.pivot(index='month', columns='classes_wild', values='seq_id')\ns = sns.heatmap(m, linewidths=.1, linecolor='black', annot=True, cmap=\"YlGnBu\")\ns.set_title('Number of wild animals observed per month', size=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"All these visualizations suffers from one problem: the distribution of majority classes obscures the distribution of minority classes. We will try to create a heatmap for each species. Let's do this showing month and hour for each species, on a separate histogram.\n\n\n## Classes per hour and month"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"classes = train_df.classes_wild.unique()\nfig, ax = plt.subplots(7,2,figsize=(20,28))\ni = 0\nfor class_wild in classes:\n    i = i + 1\n    plt.subplot(7,2,i)\n    tmp = train_df[train_df['classes_wild'] == class_wild]\n    t = pd.DataFrame(tmp.groupby(['month', 'hour'])['seq_id'].count().reset_index())\n    m = t.pivot(index='hour', columns='month', values='seq_id')\n    s = sns.heatmap(m, linewidths=.1, linecolor='black', annot=False, cmap=\"Greens\")\n    if(i<13):\n        s.set_xlabel('')    \n    s.set_title(class_wild, size=12)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Classes per rights holder and month"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"classes = train_df.classes_wild.unique()\nfig, ax = plt.subplots(7,2,figsize=(16,24))\ni = 0\nfor class_wild in classes:\n    i = i + 1\n    plt.subplot(7,2,i)\n    tmp = train_df[train_df['classes_wild'] == class_wild]\n    t = pd.DataFrame(tmp.groupby(['rights_holder', 'month'])['seq_id'].count().reset_index())\n    m = t.pivot(index='rights_holder', columns='month', values='seq_id')\n    s = sns.heatmap(m, linewidths=.1, linecolor='black', annot=False, cmap=\"Blues\")\n    if(i<13):\n        s.set_xlabel('')    \n    s.set_title(class_wild, size=12)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"45c8ffb7687667553a6f7046ed9eda0a406153fe"},"cell_type":"markdown","source":"## Train images samples   \n\nLet's show a part of the images from train_df set."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"b6bb3eb17adc4ca76c9e36d48f9a8c6ba145fae5"},"cell_type":"code","source":"def draw_category_images(var,cols=5):\n    categories = (train_df.groupby([var])[var].nunique()).index\n    f, ax = plt.subplots(nrows=len(categories),ncols=cols, figsize=(3*cols,3*len(categories)))\n    # draw a number of images for each location\n    for i, cat in enumerate(categories):\n        sample = train_df[train_df[var]==cat].sample(cols)\n        for j in range(0,cols):\n            file=IMAGE_PATH + sample.iloc[j]['file_name']\n            im = Image.open(file)\n            ax[i, j].imshow(im, resample=True)\n            ax[i, j].set_title(cat, fontsize=9)  \n    plt.tight_layout()\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"81c32508062682263c16094a32fd03858887961f","_kg_hide-input":true},"cell_type":"code","source":"IMAGE_PATH = os.path.join(PATH,'train_images/')\ndraw_category_images('classes_wild')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Test images samples\n\nLet's also draw few test images samples."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"IMAGE_PATH = os.path.join(PATH,'test_images/')\nf, ax = plt.subplots(nrows=5,ncols=5, figsize=(15,15))\n\nfor i in range(5):\n    sample = test_df.sample(5)\n    for j in range(5):\n        file=IMAGE_PATH + sample.iloc[j]['file_name']\n        im = Image.open(file)\n        ax[i, j].imshow(im, resample=True)\n        ax[i, j].set_title('Not labeled', fontsize=9)  \nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n# <a id='4'>Model</a>  \n\nLet's convert the compact 32x32 images to float and scale to 0 to 1.  \nI used the <a href='#7'>References</a> [5] (credits should to go to this Kernel mostly), [6], [7], [8], [9].\n\n\n## Scale the images"},{"metadata":{"trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"x_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":"## Metric"},{"metadata":{"_kg_hide-input":true,"trusted":true},"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":{},"cell_type":"markdown","source":"## Define the model   \n\nI used the <a href='#7'>References</a> [5] (credits should to go to this Kernel mostly), [6], [7], [8], [9], [10] for creation of this model.\n"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"model_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":{},"cell_type":"markdown","source":"We are using the number of images classes inferred before to init the **Dense** layer."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"model = Sequential()\nmodel.add(model_densenet)\nmodel.add(layers.GlobalAveragePooling2D())\nmodel.add(layers.Dense(cnt_classes_images, activation='softmax'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"model.compile(\n    loss='categorical_crossentropy',\n    optimizer='adam',\n    metrics=['accuracy']\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A model is saved every training epoch if the validation error improved."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"checkpoint = 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)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's check the resulting model."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train the model"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"BATCH_SIZE = 64\nEPOCHS = 35\nVALID_SPLIT = 0.1\nhistory = model.fit(\n    x=x_train,\n    y=y_train,\n    batch_size=BATCH_SIZE,\n    epochs=EPOCHS,\n    callbacks=[checkpoint, f1_metrics],\n    validation_split=VALID_SPLIT\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Validation"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"with open('history.json', 'w') as f:\n    json.dump(history.history, f)\nh_df = pd.DataFrame(history.history)\nh_df['val_f1'] = f1_metrics.val_f1s\nh_df['val_precision'] = f1_metrics.val_precisions\nh_df['val_recall'] = f1_metrics.val_recalls\nepochs = range(len(h_df['val_f1']))\nplt.figure()\nfig, ax = plt.subplots(1,3,figsize=(18,4))\nax[0].plot(epochs,h_df['loss'], label='Training loss')\nax[0].plot(epochs,h_df['val_loss'], label='Validation loss')\nax[0].set_title('Training and validation loss')\nax[0].legend()\nax[1].plot(epochs,h_df['acc'],label='Training accuracy')\nax[1].plot(epochs,h_df['val_acc'], label='Validation accuracy')\nax[1].set_title('Training and validation accuracy')\nax[1].legend()\nax[2].plot(epochs,h_df['val_f1'],label='Validation f1-score')\nax[2].plot(epochs,h_df['val_precision'],label='Validation precision')\nax[2].plot(epochs,h_df['val_recall'],label='Validation recall')\nax[2].set_title('Validation f1-score, precision & recall')\nax[2].legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Submission\n\nFirst, we load the model and predict."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"model.load_weights('model.h5')\n#prepare prediction\ny_test = model.predict(x_test)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![](http://)Then, we prepare the submission."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"#submission\nsubmission_df = pd.read_csv(os.path.join(PATH,'sample_submission.csv'))\nsubmission_df['Predicted'] = y_test.argmax(axis=1)\n\nprint(submission_df.shape)\nsubmission_df.head(3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We write the submission file."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2783c3db3ada465001c7049d4612da724e64ae45"},"cell_type":"markdown","source":"# <a id='5'>References</a>\n\n[1] https://www.kaggle.com/gpreda/honey-bee-subspecies-classification   \n[2] https://www.kaggle.com/gpreda/robots-need-help  \n[3] https://www.kaggle.com/artgor/iwildcam-basic-eda  \n[4] https://www.kaggle.com/c/iwildcam-2019-fgvc6/  \n[5] https://www.kaggle.com/xhlulu/keras-cnn-starter-petfinder/  \n[6] https://www.kaggle.com/xhlulu/reducing-image-sizes-to-32x32  \n[7] https://medium.com/@thongonary/how-to-compute-f1-score-for-each-epoch-in-keras-a1acd17715a2  \n[8] https://www.kaggle.com/xhlulu/cnn-baseline-iwildcam-2019  \n[9] https://www.kaggle.com/xhlulu/densenet-transfer-learning-iwildcam-2019/  \n[10] https://www.kaggle.com/gpreda/cats-or-dogs-using-cnn-with-transfer-learning\n"}],"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}