{"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":"## Cassava Leaf Disease Classification","metadata":{}},{"cell_type":"markdown","source":"I built an ensemble model using Keras's ResNet152 and EfficientNetB3.  \nThe result was 0.8777, which was not very high, but I will publish the notebook for information sharing.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport os\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom fastai.vision.all import *","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preprocessing","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create some folders for ImageDataGenerator\nImageDataGenerator needs to allocate images to each class folder in advance, so we need to create a folder for each class.","metadata":{}},{"cell_type":"code","source":"images_folder = ['CBB', 'CBSD', 'CGM', 'CMD', 'Healthy']\n\ntrain_folder_path = './train_images'\nval_folder_path = './val_images'\n\n#Create some folders\nfor img_f in images_folder:\n    os.makedirs(train_folder_path + '/'+ img_f, exist_ok=True)\n    os.makedirs(val_folder_path + '/'+ img_f, exist_ok=True)\n    \nprint(\"Train folder:\", os.listdir(train_folder_path))\nprint(\"Validation folder:\", os.listdir(val_folder_path))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check \"train.csv\"","metadata":{}},{"cell_type":"code","source":"dataset_path = Path('../input/cassava-leaf-disease-classification')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(dataset_path/'train.csv')\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the number of pictures for each class","metadata":{}},{"cell_type":"code","source":"n_picture = []\nfor l in range(5):    \n    n_picture.append(len(train_df[train_df['label']==l]))\nprint(\"Number of pictures:\", n_picture)\nprint(\"Total:\", sum(n_picture))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.bar(images_folder, n_picture)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Copy image files to each folder\nDivide the image files into the created folders at a ratio of 8:2 for use with ImageDataGenerator.  \nIf you have some images you don't want to use, add the filename to ‘avoid_list’.  \nExample: avoid_list = ['1000015157.jpg', '1000201771.jpg']","metadata":{}},{"cell_type":"code","source":"# Put the images you want to exclude in the list\navoid_list = []\n\n# train_test_split\ntotal_size = sum(n_picture)\nratio = 0.8\ntotal_train = int(total_size*ratio)\ntotal_val = int(total_size-total_train)\n\nfor l in range(5):\n    file_count = 0\n    i = 0                    \n    file_count = 0\n    \n    # validation data\n    while i < int(n_picture[l]*ratio):\n        file_name = train_df[train_df['label']==l].loc[train_df[train_df['label']==l].index[i]]['image_id']\n        in_path = dataset_path/'train_images'/file_name\n        out_path = val_folder_path + '/' + images_folder[l] + '/' + file_name\n        shutil.copyfile(in_path, out_path)\n        _, _, files = next(os.walk(val_folder_path + '/' + images_folder[l]))\n        file_count = len(files)\n        i += 1\n        \n    # train data\n    while i < n_picture[l]:\n        file_name = train_df[train_df['label']==l].loc[train_df[train_df['label']==l].index[i]]['image_id']\n        #Use avoid_list only for train data\n        if not file_name in avoid_list:\n            in_path = dataset_path/'train_images'/file_name\n            out_path = train_folder_path + '/' + images_folder[l] + '/' + file_name\n            shutil.copyfile(in_path, out_path)\n            _, _, files = next(os.walk(train_folder_path + '/' + images_folder[l]))\n            file_count = len(files)\n        i += 1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data augmentation\n","metadata":{}},{"cell_type":"code","source":"train_image_generator = ImageDataGenerator(rescale=1./255,\n                                        rotation_range=360,\n                                        width_shift_range=0.1,\n                                        height_shift_range=0.1,\n                                        horizontal_flip=True,\n                                        vertical_flip=True,\n                                        zoom_range=0.5,\n                                        shear_range=0.2,\n                                        brightness_range=[0.5,1.0],\n                                        channel_shift_range=100,\n                                        fill_mode = 'nearest')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"validation_image_generator = ImageDataGenerator(rescale=1./255) # 検証データのジェネレータ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 8\nepochs = 20\n\nIMG_HEIGHT = 512\nIMG_WIDTH = 512","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data_gen = train_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\n                                                           directory=train_folder_path,\n                                                           shuffle=True,\n                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),\n                                                           class_mode='categorical',\n                                                           classes = images_folder)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_data_gen = validation_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\n                                                              directory=val_folder_path,\n                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),\n                                                              class_mode='categorical',\n                                                              classes = images_folder)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Train generator's label:\\n\", train_data_gen.class_indices)\nprint(\"Validation generator's label:\\n\", val_data_gen.class_indices)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Check the picture","metadata":{}},{"cell_type":"code","source":"# https://www.tensorflow.org/tutorials/images/classification?hl=ja\ndef plotImages(images_arr):\n    fig, axes = plt.subplots(1, 7, figsize=(20,20))\n    axes = axes.flatten()\n    for img, ax in zip( images_arr, axes):\n        ax.imshow(img)\n        ax.axis('off')\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"augmented_images = [train_data_gen[0][0][0] for i in range(7)]\nplotImages(augmented_images)\naugmented_images = [train_data_gen[1][0][0] for i in range(7)]\nplotImages(augmented_images)\naugmented_images = [train_data_gen[2][0][0] for i in range(7)]\nplotImages(augmented_images)\naugmented_images = [train_data_gen[3][0][0] for i in range(7)]\nplotImages(augmented_images)\naugmented_images = [train_data_gen[4][0][0] for i in range(7)]\nplotImages(augmented_images)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nimport keras\nfrom tensorflow.keras.applications import ResNet152, EfficientNetB3\nfrom tensorflow.keras import models, layers\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom keras.optimizers import Adamax\nfrom keras.losses import CategoricalCrossentropy\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Models\nreference  \nhttps://www.kaggle.com/bununtadiresmenmor/starter-keras-efficientnet","metadata":{}},{"cell_type":"code","source":"def modelResNet152():\n    \n    model = models.Sequential()\n    model.add(ResNet152(include_top = False, \n                      weights='imagenet',\n                      #weights = \"../input/resnet152traind/resnet152_weights_tf_dim_ordering_tf_kernels_notop.h5\",\n                      input_shape=(IMG_HEIGHT,IMG_WIDTH, 3)))\n    \n    model.add(Dropout(0.5))\n    model.add(layers.GlobalAveragePooling2D())\n    \n    #additional\n    model.add(layers.Dense(1024, activation = 'relu'))\n    model.add(Dropout(0.25))\n    \n    model.add(layers.Dense(5, activation = \"softmax\"))\n    \n    return model \n    ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def modelEfficientNetB3():\n\n    model = models.Sequential()\n    model.add(EfficientNetB3(include_top = False, \n                            weights = 'imagenet',\n                            #weights = \"../input/effib3trained/efficientnetb3_notop.h5\",\n                            input_shape=(IMG_HEIGHT,IMG_WIDTH, 3)))\n    \n    model.add(Dropout(0.8))\n    model.add(layers.GlobalAveragePooling2D())\n    \n    #additional\n    model.add(layers.Dense(512, activation = 'relu'))\n    model.add(Dropout(0.4))\n    model.add(layers.Dense(256, activation = 'relu'))\n    model.add(Dropout(0.5))\n \n    model.add(layers.Dense(5, activation = \"softmax\"))\n    \n    return model ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_res152 = modelResNet152()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_effiB3 = modelEfficientNetB3()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_res152.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_effiB3.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Callbacks\nI used the following three items for the callback.\n- ModelCheckpoint  \n    If the minimum value of val_loss is updated, it will save the model for each epoch.\n- EarlyStopping  \n     If val_loss cannot be updated for 7 consecutive epochs, training will end.\n- ReduceLROnPlateau  \n     If val_loss cannot be updated for 2 consecutive epochs, the learning rate will be multiplied by 0.1.","metadata":{}},{"cell_type":"code","source":"model_checkpoint_resnet = ModelCheckpoint(\n                            \"./checkpoint_resnet.h5\",\n                            monitor = \"val_loss\",\n                            verbose = 1,\n                            save_best_only = True,\n                            save_weights_only = False,\n                            mode = \"min\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_checkpoint_effinet = ModelCheckpoint(\n                            \"./checkpoint_effinet.h5\",\n                            monitor = \"val_loss\",\n                            verbose = 1,\n                            save_best_only = True,\n                            save_weights_only = False,\n                            mode = \"min\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"early_stop = EarlyStopping(\n                            monitor = \"val_loss\",\n                            min_delta=0.001,\n                            patience=7,\n                            verbose=1,\n                            mode=\"min\",\n                            restore_best_weights=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"reduce_lr = ReduceLROnPlateau(\n                            monitor=\"val_loss\",\n                            factor=0.1,\n                            patience=2,\n                            verbose=1,\n                            mode=\"min\",\n                            min_delta=0.0001)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Compile","metadata":{}},{"cell_type":"code","source":"model_res152.compile(\n            optimizer=tf.keras.optimizers.Adamax(learning_rate=0.001),\n            loss = CategoricalCrossentropy(label_smoothing=0.3,reduction=\"auto\",name=\"categorical_crossentropy\"),\n            metrics = [\"accuracy\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_effiB3.compile(\n            optimizer=tf.keras.optimizers.Adamax(learning_rate=0.001),\n            loss = CategoricalCrossentropy(label_smoothing=0.3,reduction=\"auto\",name=\"categorical_crossentropy\"),\n            metrics = [\"accuracy\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Training\nIt takes 6 hours on the GPU.","metadata":{}},{"cell_type":"code","source":"epochs = 15","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history_res152 = model_res152.fit_generator(\n                    train_data_gen,\n                    steps_per_epoch=None,\n                    epochs=epochs,\n                    validation_data=val_data_gen,\n                    validation_steps=None,\n                    callbacks = [model_checkpoint_resnet,early_stop,reduce_lr]\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 10","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history_effiB3 = model_effiB3.fit_generator(\n                    train_data_gen,\n                    steps_per_epoch=None,\n                    epochs=epochs,\n                    validation_data=val_data_gen,\n                    validation_steps=None,\n                    callbacks = [model_checkpoint_effinet,early_stop,reduce_lr]\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#saving the models\nmodel_res152.save('saved_model_resnet.h5')\nmodel_effiB3.save('saved_model_effinet.h5')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = history_res152.history['accuracy']\nval_acc = history_res152.history['val_accuracy']\n\nloss = history_res152.history['loss']\nval_loss = history_res152.history['val_loss']\n\nepochs_range = range(len(acc))\n\nplt.plot(epochs_range, acc, label='Training Accuracy')\nplt.plot(epochs_range, val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\nplt.show()\n\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = history_effiB3.history['accuracy']\nval_acc = history_effiB3.history['val_accuracy']\n\nloss = history_effiB3.history['loss']\nval_loss = history_effiB3.history['val_loss']\n\nepochs_range = range(len(acc))\n\nplt.plot(epochs_range, acc, label='Training Accuracy')\nplt.plot(epochs_range, val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\nplt.show()\n\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predict","metadata":{}},{"cell_type":"markdown","source":"I submitted the following content as a Predict Notebook separately from the Training Notebook.  \nIf you want to use the model saved in the training notebook, first upload the saved model from \"+ Add Data\".  \nThen, specify the path of the file in the argument of load_model.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport os\nimport pandas as pd\nfrom fastai.vision.all import *","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\nfrom keras.models import load_model\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Load the saved model","metadata":{"trusted":true}},{"cell_type":"code","source":"#Choose either\nmodel_res_pred = tf.keras.models.load_model('./saved_model_resnet.h5')\n#model_res_pred = tf.keras.models.load_model('./checkpoint_resnet.h5')\n\n#Check the architecture\nmodel_res_pred.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Choose either\nmodel_effi_pred = tf.keras.models.load_model('./saved_model_effinet.h5')\n#model_effi_pred = tf.keras.models.load_model('./checkpoint_effinet.h5')\n\n#Check the architecture\nmodel_effi_pred.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Creating a submit file","metadata":{}},{"cell_type":"code","source":"dataset_path = Path('../input/cassava-leaf-disease-classification')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Check the sample submission file","metadata":{}},{"cell_type":"code","source":"sample_df = pd.read_csv(dataset_path/'sample_submission.csv')\nsample_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Creat a folder for test files","metadata":{}},{"cell_type":"code","source":"test_folder_path = './test_images'\n\nif not os.path.exists(test_folder_path):\n    os.mkdir(test_folder_path) ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Copy the test file together with the folder ","metadata":{}},{"cell_type":"code","source":"test_ds_path = '../input/cassava-leaf-disease-classification/test_images'\ntest_dir_path = './test_images/all_classes'\n\nif not os.path.exists(test_dir_path):\n    shutil.copytree(test_ds_path, test_dir_path)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_, _, files = next(os.walk(test_dir_path))\nfile_count = len(files)\n\nprint(\"Number of pictures: \", file_count)\nprint(\"Picture name: \", files)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image_generator = ImageDataGenerator(rescale=1./255)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_folder_path = './test_images'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#IMG_HEIGHT = 512\n#IMG_WIDTH = 512","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_generator = test_image_generator.flow_from_directory(\n                                        directory=test_folder_path,\n                                        target_size=(IMG_HEIGHT, IMG_WIDTH),\n                                        class_mode=None,\n                                        shuffle=False\n                                        )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_res = model_res_pred.predict_generator(test_generator, verbose=1)\nprint(pred_res)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_effi = model_effi_pred.predict_generator(test_generator, verbose=1)\nprint(pred_effi)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Ensemble\nCombine the output results of ResNet and EfficientNet.","metadata":{}},{"cell_type":"code","source":"total_pred = pred_res*0.5 + pred_effi*0.5\nprint(\"Ensemble predict:\", total_pred)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predicted_class_indices = np.argmax(total_pred, axis=1)\nprint(\"Predicted class indices:\", predicted_class_indices)\n\nlabels_dict = ({'CBB': 0, 'CBSD': 1, 'CGM': 2, 'CMD': 3, 'Healthy': 4})\nlabels = dict((v,k) for k,v in labels_dict.items())\npredictions = [labels[k] for k in predicted_class_indices]\nprint(\"Predicted class:\", predictions)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"# Submission dataframe\nsubmit_ID = sample_df.loc[:]['image_id']\nsubmit_TARGET = pd.DataFrame(predicted_class_indices)\n\nsubmission_df = pd.concat([submit_ID, submit_TARGET], axis=1)\nsubmission_df.columns = ['image_id','label']\n\nsubmission_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.to_csv('submission.csv',index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Thank you.","metadata":{}}]}