{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"1.0 Exploratory Data Analysis","metadata":{}},{"cell_type":"code","source":"import os\n\ndata_path = \"../input/cassava-leaf-disease-classification/\"\n\n#train set\nsrc_train = os.path.join(data_path, 'train_images')\ntrain_csv = os.path.join(data_path, 'train.csv')\ndisease_labels = os.path.join(data_path, 'label_num_to_disease_map.json')\n\n#test images\ntest_images = os.path.join(data_path, 'test_images')\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"First, the train csv and json label file are read into pandas data frame. This is done for easy data manupulation.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\n#read train csv to data frame\ndf = pd.read_csv(train_csv)\n\n#convert label to int\ndf['label'] = df['label'].astype('string')\ndf","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import json\n\nwith open(disease_labels, 'r') as j:\n     contents = json.loads(j.read())\n\ncontents    \n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The training data set has 5 class label.","metadata":{}},{"cell_type":"code","source":"#create a dict of abbreviated label names\nlabel_classes = {}\n\nlabel_classes['0'] = 'CBB'\nlabel_classes['1']  = 'CBSD'\nlabel_classes['2']  = 'CGM'\nlabel_classes['3']  = 'CMD'\nlabel_classes['4']  = 'Healthy'\nlabel_classes","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code merges the disease labels to the data frame with file names","metadata":{}},{"cell_type":"code","source":"#left join df to ds_label\ndf0 = df\n\n#add column with file name and label\ndf0['image_id_label_sht_name'] = df0['image_id'] +  ' / ' +   df0['label']\ndf0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The training data set 5 class label.","metadata":{}},{"cell_type":"code","source":"#help function to display images in a grid\nimport os\nimport matplotlib.pyplot as plt\nfrom keras.preprocessing import image\n\n\ndef display_images_grid(images, img_folder, has_class_label=False, row_col_ind=(4, 4, 0)):\n    rows, cols, i = row_col_ind\n    \n    fig = plt.figure(figsize=(15, 15))\n    label = \"\"\n\n    for fname in images [: rows * cols]:\n      if has_class_label:\n        fname, lbl = fname.split('/')\n        label = fname +'/ '+ label_classes.get(lbl.strip(), \"\")\n\n      plt.subplot(rows, cols, i+1)\n      plt.title(label)\n      plt.xticks([]), plt.yticks([])\n      plt.tight_layout()\n        \n        \n            \n      img = image.load_img(os.path.join(img_folder,  fname.strip()), target_size=(150, 150))\n                    \n      plt.imshow(img)\n            \n      i += 1\n      label = \"\"\n        \n    return plt","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code displays 3 images from each group.","metadata":{}},{"cell_type":"code","source":"import random\n\n\nrandom.seed(1234)\n\n#get 3 image from each group\nsample_imgs = df0[['label','image_id_label_sht_name']].groupby('label').sample(n=3)['image_id_label_sht_name'] \n\n#diplay images\nplt = display_images_grid(images=sample_imgs, img_folder=src_train, has_class_label=True, row_col_ind=(5, 5, 0))\n\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code plots the distribution of the images in each group.","metadata":{}},{"cell_type":"code","source":"#plot the count \nimport seaborn as sns\n\n\nsns.countplot(x = df0['label'])\n\nprint(contents)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**2.0 Preprocess the images**","metadata":{}},{"cell_type":"markdown","source":"Transfer learning will be used to create a model for predicting the classes. A pre-trained model will be used therefore only a subset of the training images will be used to train the model. Also, data augmentation will be applied to the images. This will help to effective increase the training set size and hopefully imporove the model performance.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nfrom keras.preprocessing.image import ImageDataGenerator\n\ntrain_gen = ImageDataGenerator(\n                                rotation_range=180,\n                                width_shift_range=0.1,\n                                height_shift_range=0.1,\n                                brightness_range=[0.1,0.9],\n                                shear_range=25,\n                                zoom_range=0.3,\n                                channel_shift_range=0.1,\n                                horizontal_flip=True,\n                                vertical_flip=True,\n                                rescale=1/255,\n                                validation_split=0.10\n                               )\n                                    \n    \nvalid_gen = ImageDataGenerator(rescale=1/255,\n                               validation_split = 0.10\n                              )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target_size = (224, 224)\nbatch_size = 20\n\n\ntrain_generator = train_gen.flow_from_dataframe(dataframe=df,\n                                                 directory = src_train,\n                                                 x_col = \"image_id\",\n                                                 y_col = \"label\",\n                                                 target_size = target_size,\n                                                 class_mode = \"categorical\",\n                                                 batch_size = batch_size,\n                                                 seed=1234,\n                                                 shuffle = True,\n                                                 subset = \"training\")\n\nvalidation_generator = valid_gen.flow_from_dataframe(dataframe=df, \n                                                 directory = src_train,\n                                                 x_col = \"image_id\",\n                                                 y_col = \"label\",\n                                                 target_size = target_size,\n                                                 class_mode = \"categorical\",\n                                                 batch_size = batch_size,\n                                                 seed=1234,\n                                                 shuffle = False,\n                                                 subset = \"validation\"\n                                                 )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**3.0 Train model**\n\nA pre-trained model will be used to build the model. Code below downloads the pre-trained model.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import optimizers\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping\n\n#helper function to train model\n#https://machinelearningmastery.com/understand-the-dynamics-of-learning-rate-on-deep-learning-neural-networks/\ndef train_model(model, model_name='Unknown', optimizer=optimizers.RMSprop):\n  learning_rates = [1E-4, 1E-5]# [1E-1, 1E-2, 1E-3, 1E-4, 1E-5]\n\n  fig = plt.figure(figsize=(15, 15))\n  \n  #loop over the learningrates\n  for i in range(len(learning_rates)):\n    print(model_name + \" - Learning Rate : {} \".format(learning_rates[i]))\n    \n    #compile the model - optimizers.RMSprop(lr=2e-5)\n    model.compile(loss='categorical_crossentropy',\n                optimizer=optimizer(lr=learning_rates[i]),\n                metrics=['acc'])\n    \n    #early stopping\n    es = EarlyStopping(monitor='val_acc', mode='max', verbose=1, patience=30)\n    \n    #train the model\n    history = model.fit(train_generator, \n                    steps_per_epoch=32,\n                    epochs=300,\n                    validation_data=validation_generator,\n                    validation_steps=10,\n                   # callbacks=[es],\n                    verbose=0)\n    \n        \n    plt.subplot(2, 2, i + 1)\n\n\n    #plt.subplot(len(learning_rates) % 3, 3, i + 1)\n    \n    #plot accurarancy of model\n    acc = history.history['acc']\n    val_acc = history.history['val_acc']\n\n    epochs = range(1, len(acc) + 1)\n\n    plt.plot(epochs, acc, 'b', label='train')\n    plt.plot(epochs, val_acc, 'bo', label='validation')\n    plt.title(model_name + ': lrate='+ str(learning_rates[i]), pad=-50)\n\n    #print(model_name + ' - Validation acc {}'.format(history.history['val_acc'][-1]))\n\n    print(model_name + ' - Validation acc (last pt): % 1.2f' %(history.history['val_acc'][-1] * 100))\n    print(\"==============================\")\n\n    \n    \n    #add legend to last chart\n    if (i + 1 == len(learning_rates)):\n      plt.legend()\n\n  return plt","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code downloads a pre-trained model.","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.applications import ResNet152V2\n\n#model shape\ninput_shape = target_size + (3,)\n\n#pretrained model weights path\nweights_path = '../input/resnet152v2/resnet152v2_weights_tf_dim_ordering_tf_kernels_notop.h5'\n\n#download pre-trained model\nconv_res_base = ResNet152V2(weights = weights_path,\n                        include_top=False,\n                        input_shape=input_shape)\n\n#freeze the conv_base - so that weights are not changed during training\nconv_res_base.trainable = False","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below code adds the classifier.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.regularizers import l2\nfrom tensorflow.keras.layers import GaussianNoise\n\nmodel = keras.Sequential()\n\nmodel.add(conv_res_base)\n\n#add classifier layers\nmodel.add(layers.GaussianNoise(0.1))\nmodel.add(layers.Dense(4096, kernel_regularizer=l2(0.01), activation='relu'))\nmodel.add(layers.Dense(4096, kernel_regularizer=l2(0.01), activation='relu'))\nmodel.add(layers.BatchNormalization(axis=-1))\nmodel.add(layers.GlobalAveragePooling2D())\nmodel.add(layers.Flatten())\nmodel.add(layers.Dense(1024, kernel_regularizer=l2(0.01), activation='relu'))\nmodel.add(layers.Dense(5, activation='softmax'))\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#compile the model - optimizers.RMSprop(lr=2e-5)\nmodel.compile(loss='categorical_crossentropy',\n              optimizer=optimizers.Adamax(lr=1E-5),\n              metrics=['acc'])\n    \n    \n#train the model\nhistory = model.fit(train_generator, \n                    steps_per_epoch=32,\n                    epochs=300,\n                    validation_data=validation_generator,\n                    validation_steps=10,\n                    verbose=0)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plot accurarancy of model\nacc = history.history['acc']\nval_acc = history.history['val_acc']\n\nepochs = range(1, len(acc) + 1)\n\nplt.plot(epochs, acc, 'b', label='train')\nplt.plot(epochs, val_acc, 'bo', label='validation')\n\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**4.0 Prepare Kaggle submission file**\n\nBelow code runs the model on test set and prepares submission file.","metadata":{}},{"cell_type":"code","source":"from keras.preprocessing import image\nimport numpy as np\n\n#function to convert images in folder to tensors\ndef convert_imgs_to_tensors(img_folder, target_size=target_size):\n  # dimensions of images\n  img_width, img_height = target_size\n\n  # load all images into a list\n  images = []\n\n  for img in os.listdir(img_folder):\n    img = os.path.join(img_folder, img)\n    img = image.load_img(img, target_size=(img_width, img_height))\n    img = image.img_to_array(img)\n    img = np.expand_dims(img, axis=0)\n    img /= 255.\n    images.append(img)\n\n  # stack up images list to pass for model\n  images = np.vstack(images)\n\n  return images","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images = convert_imgs_to_tensors(test_images)\n\n#make predictions on hold out images\npredictions = model.predict(images, batch_size=20)\n\n#store predictions in pandas dataframe\nimport pandas as pd\n\ndf_preds = pd.DataFrame({'image_id': os.listdir(test_images),\n                         'label': [np.argmax(pred) for pred in predictions]})\n\ndf_preds.to_csv('submission.csv', index=False) \ndf_preds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Display test image prediction","metadata":{}},{"cell_type":"code","source":"#display test image prediction\nimgs = (df_preds.image_id.head().astype('str') +'/ '+ df_preds.label.head().astype('str')).to_list()\n\nplt = display_images_grid(images=imgs, img_folder=test_images, has_class_label=True, row_col_ind=(5, 2, 0))\n\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}}]}