{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"# Team Deep Divers submission for\n# Brown Data Science DATA2040 Deep Learning course. \n# Authors: Haoda Song, Siyuan Li, Yuyang Li\n\n# The Kaggle notebook structure is based on the example created by Kaiwen Yang and Dr.Dan Potter\n\nimport pandas as pd\nimport numpy as np\nfrom sklearn.dummy import DummyClassifier\nimport json\nimport tensorflow as tf\nfrom functools import partial\n\nimport tensorflow.keras as keras\nimport keras\nfrom keras.layers import Dense, Dropout, Input, MaxPooling2D, ZeroPadding2D, Conv2D, Flatten\nfrom keras.models import Sequential, Model\nfrom keras.losses import categorical_crossentropy\nfrom keras.optimizers import Adam, SGD\nfrom keras.callbacks import EarlyStopping\nfrom keras.preprocessing.image import img_to_array, load_img, ImageDataGenerator\nfrom keras.utils import to_categorical\nfrom tensorflow.keras import regularizers\nfrom tensorflow.keras.layers import MaxPool2D, AveragePooling2D, GlobalAveragePooling2D\nfrom sklearn.model_selection import train_test_split\n\nimport matplotlib.pyplot as plt\nfrom mpl_toolkits.axes_grid1 import ImageGrid\n\nfrom zipfile import ZipFile\nimport time\nfrom datetime import timedelta\nfrom io import BytesIO\n\n# Image manipulation.\nimport PIL.Image\n\nimport pickle\nimport os\n\nimport random\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Exploratory Data Analysis"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels = pd.read_csv(\"../input/cassava-leaf-disease-classification/train.csv\")\ncsv_df = pd.read_csv(\"../input/cassava-leaf-disease-classification/train.csv\")\ntrain_labels.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Class0: Cassava Bacterial Blight (CBB)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"labels = pd.read_csv(os.path.join(\"../input/cassava-leaf-disease-classification/train.csv\"))\nsample0 = labels[labels.label == 0].sample(4)\nplt.figure(figsize=(15, 5))\nfor ind, (image_id, label) in enumerate(zip(sample0.image_id, sample0.label)):\n    plt.subplot(1, 4, ind + 1)\n    img = cv2.imread(os.path.join('../input/cassava-leaf-disease-classification', \"train_images\", image_id))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Class1: Cassava Brown Streak Disease (CBSD)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sample1 = labels[labels.label == 1].sample(4)\nplt.figure(figsize=(15, 5))\nfor ind, (image_id, label) in enumerate(zip(sample1.image_id, sample1.label)):\n    plt.subplot(1, 4, ind + 1)\n    img = cv2.imread(os.path.join('../input/cassava-leaf-disease-classification', \"train_images\", image_id))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Class2: Cassava Green Mottle (CGM)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sample2 = labels[labels.label == 2].sample(4)\nplt.figure(figsize=(15, 5))\nfor ind, (image_id, label) in enumerate(zip(sample2.image_id, sample2.label)):\n    plt.subplot(1, 4, ind + 1)\n    img = cv2.imread(os.path.join('../input/cassava-leaf-disease-classification', \"train_images\", image_id))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Class3: Cassava Mosaic Disease (CMD)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sample3 = labels[labels.label == 3].sample(4)\nplt.figure(figsize=(15, 5))\nfor ind, (image_id, label) in enumerate(zip(sample3.image_id, sample3.label)):\n    plt.subplot(1, 4, ind + 1)\n    img = cv2.imread(os.path.join('../input/cassava-leaf-disease-classification', \"train_images\", image_id))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Class4: Healthy"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sample4 = labels[labels.label == 4].sample(4)\nplt.figure(figsize=(15, 5))\nfor ind, (image_id, label) in enumerate(zip(sample4.image_id, sample4.label)):\n    plt.subplot(1, 4, ind + 1)\n    img = cv2.imread(os.path.join('../input/cassava-leaf-disease-classification', \"train_images\", image_id))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Citation: [Cassava Leaf Disease: Best Keras CNN](https://www.kaggle.com/maksymshkliarevskyi/cassava-leaf-disease-best-keras-cnn)"},{"metadata":{},"cell_type":"markdown","source":"### Class Distributions"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels['label']=train_labels['label'].astype(str)\nwith open('/kaggle/input/cassava-leaf-disease-classification/label_num_to_disease_map.json') as f:\n  disease_dict = json.load(f)\ndisease_df = pd.DataFrame(list(disease_dict.items()),columns = ['label','real_label'])\n\nfor i in range(len(disease_df)):\n  disease_df['label'][i] = int(i)\n\nactual_class = pd.merge(csv_df,disease_df,on='label') \n#Count the number of images in each disease\nobs_in_actual = actual_class.groupby(['label','real_label']).size()\nprint(obs_in_actual) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ax = (actual_class.value_counts(actual_class['real_label'], ascending=True)\n                 .plot(kind='barh', fontsize=\"20\", \n                       title=\"Class Distribution\", figsize=(8,5)))\n\nax.set(xlabel=\"Images per class\", ylabel=\"Classes\")\nax.xaxis.label.set_size(15)\nax.yaxis.label.set_size(15)\nax.title.set_size(15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We calculated our baseline accuracy based on the calss with the largest proportion (class 3)\nnum_class3 = 13158\nnum_total = len(train_labels.label)\nbaseline_acc = num_class3/num_total\nprint(\"Baseline Accuracy:\",baseline_acc) #0.6150","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# TFRecord Data"},{"metadata":{},"cell_type":"markdown","source":"We know the truth that Class Three have the much larger proportion than other classes so our data is imbalanced and our prediction would be biased if we only split the original data randomly. However, we cannot balance the tradeoffs between image resizing (256 x 256 and above) and handling imblanced images due to the limitations of RAM and the stratified method. Therefore, we used the Stratified data in TFRecords format generated by the Kagglor,DimitreOliveira. The details could be checked by the public notebook, [Cassava Leaf Disease-Stratified TFRecords 256x256](https://www.kaggle.com/dimitreoliveira/cassava-leaf-disease-stratified-tfrecords-256x256).\nThis dataset contains 15 files with similar proportion of each class. Each file contains around 1427 eaxamples."},{"metadata":{"trusted":true},"cell_type":"code","source":"#All files have the similar structures so we chose 12 files as training set and 3 files as validation set.\nimport glob\ntrain_dir = '../input/stratifiedtf/StratifiedTF/train_newtf/*'\ntest_dir = '../input/stratifiedtf/StratifiedTF/val_newtf/*'\ntrain_list = glob.glob(train_dir)\ntest_list = glob.glob(test_dir)\nprint(\"train: \" + str(len(train_list)) + \"\\ntest: \" + str(len(test_list)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Read TFrecords\n#the functions are adapted from the examples provided by keras reference site: \n#https://keras.io/examples/keras_recipes/tfrecord/\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example, labeled):\n    tfrecord_format = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.int64)\n    } if labeled else {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"image_name\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    if labeled:\n        label = tf.cast(example['target'], tf.int32)\n        return image, label\n    idnum = example['image_name']\n    return image, idnum\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE) \n    dataset = dataset.with_options(ignore_order) \n    dataset = dataset.map(partial(read_tfrecord, labeled=labeled), num_parallel_calls=AUTOTUNE)\n    dataset=dataset.batch(BATCH_SIZE)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTOTUNE = tf.data.experimental.AUTOTUNE\nBATCH_SIZE = 16\nIMAGE_SIZE = [256, 256]\n\ndataset_train=load_dataset(train_list,labeled=True)\ndataset_val=load_dataset(test_list,labeled=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels = train_labels\nDATASET_SIZE=len(labels)\ntrain_size = int(0.8 * DATASET_SIZE)\nval_size = int(0.2 * DATASET_SIZE)\nprint(\"Training data size:\",train_size)\nprint(\"Validation data size:\",val_size)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Models"},{"metadata":{},"cell_type":"markdown","source":"We implemented four different models, including our baseline CNN model, ResNet50, ResNet architecture, self-designed (ResNet + VGG architechture) and EfficientNet B0. Our results shows EfficientNetB0 gives the best accuracy as well as the best training performance."},{"metadata":{},"cell_type":"markdown","source":"## Baseline CNN Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_baseline():\n    inputs = Input(shape = (256,256,3))\n    \n    model = Conv2D(filters=32, kernel_size=(3, 3),activation='relu',\n                   input_shape=(256,256,3))(inputs)\n    model = AveragePooling2D(pool_size=(2, 2))(model)\n    model = Conv2D(filters=64, kernel_size=(3, 3), activation='relu')(model)\n    model = AveragePooling2D(pool_size=(2, 2))(model)\n    model = Dropout(0.5)(model)\n    model = Conv2D(filters=128, kernel_size=(3, 3), activation='relu')(model)\n    model = MaxPool2D(pool_size=(2, 2))(model)\n    model = Conv2D(filters=256, kernel_size=(3, 3), activation='relu')(model)\n    model = Dropout(0.5)(model)\n    \n    model = Flatten()(model)\n    model = Dense(512, activation = \"relu\")(model)\n    out = Dense(5, activation = 'softmax')(model)\n    model = Model(inputs=inputs, outputs=out)\n    \n    model.compile(loss='sparse_categorical_crossentropy',\n              optimizer=keras.optimizers.Adam(lr=0.001),\n              metrics=[\"sparse_categorical_accuracy\"])\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"model = build_baseline()\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nearly_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n                           patience = 5, mode = 'min', verbose = 1,\n                           restore_best_weights = True)\nreduce_lr = ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n                              patience = 5, min_delta = 0.001, \n                              mode = 'min', verbose = 1)\n\nhistory = model.fit(\n    dataset_train.repeat(),\n    steps_per_epoch = train_size//BATCH_SIZE,\n    validation_data= dataset_val.repeat(),\n    validation_steps=val_size//BATCH_SIZE,\n    epochs=20,\n    callbacks = [early_stop, reduce_lr],\n    verbose=True\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nN = 9\nplt.style.use(\"ggplot\")\nplt.figure()\nplt.plot(np.arange(0, N), history.history[\"sparse_categorical_accuracy\"], label=\"Train Accuracy\")\nplt.plot(np.arange(0, N), history.history[\"val_sparse_categorical_accuracy\"], label=\"Validation Accuracy\")\nplt.plot(np.arange(0, N), history.history[\"loss\"], label=\"Train Loss\")\nplt.plot(np.arange(0, N), history.history[\"val_loss\"], label=\"Validation Loss\")\nplt.title(\"Baseline CNN Loss and Accuracy Plot\")\nplt.xlabel(\"Epoch\")\nplt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For our baseline CNN model, overfitting occurs at epoch 5 due to the trends of training and validation losses. Therefore, the baseline CNN achieved 0.66 validation accuracy. We hope the further models would make improvements."},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import load_model\nimport os\ndef save_model(model, name):\n  model_name = '{}.h5'.format(name)\n  save_dir = os.path.join(os.getcwd(), 'saved_models')\n  \n  # Save model and weights\n  if not os.path.isdir(save_dir):\n      os.makedirs(save_dir)\n  model_path = os.path.join(save_dir, model_name)\n  model.save(model_path)\n  print('Saved trained model at %s ' % model_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"save_model(model, 'baseCNN_model') #save baseline CNN model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Self-Designed (ResNet + VGG architechture)"},{"metadata":{"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"#The functions are written by \n#https://colab.research.google.com/github/d2l-ai/d2l-tensorflow-colab/blob/master/chapter_convolutional-modern/resnet.ipynb\n!pip install d2l==0.16.1\nfrom d2l import tensorflow as d2l\nimport tensorflow as tf\n\nclass Residual(tf.keras.Model):  #@save\n    \"\"\"The Residual block of ResNet.\"\"\"\n    def __init__(self, num_channels, use_1x1conv=False, strides=1):\n        super().__init__()\n        self.conv1 = tf.keras.layers.Conv2D(\n            num_channels, padding='same', kernel_size=3, strides=strides)\n        self.conv2 = tf.keras.layers.Conv2D(\n            num_channels, kernel_size=3, padding='same')\n        self.conv3 = None\n        if use_1x1conv:\n            self.conv3 = tf.keras.layers.Conv2D(\n                num_channels, kernel_size=1, strides=strides)\n        self.bn1 = tf.keras.layers.BatchNormalization()\n        self.bn2 = tf.keras.layers.BatchNormalization()\n\n    def call(self, X):\n        Y = tf.keras.activations.relu(self.bn1(self.conv1(X)))\n        Y = self.bn2(self.conv2(Y))\n        if self.conv3 is not None:\n            X = self.conv3(X)\n        Y += X\n        return tf.keras.activations.relu(Y)\n\nclass ResnetBlock(tf.keras.layers.Layer):\n    def __init__(self, num_channels, num_residuals, first_block=False,\n                 **kwargs):\n        super(ResnetBlock, self).__init__(**kwargs)\n        self.residual_layers = []\n        for i in range(num_residuals):\n            if i == 0 and not first_block:\n                self.residual_layers.append(\n                    Residual(num_channels, use_1x1conv=True, strides=2))\n            else:\n                self.residual_layers.append(Residual(num_channels))\n\n    def call(self, X):\n        for layer in self.residual_layers.layers:\n            X = layer(X)\n        return X   ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sd_model = tf.keras.Sequential([\n        tf.keras.layers.Input(shape=(256,256,3)),\n        tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Activation('relu'),\n        tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same'),\n\n        ResnetBlock(64, 2, first_block=True),\n        ResnetBlock(128, 2),\n        ResnetBlock(256, 2),\n\n        tf.keras.layers.Conv2D(128, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.Conv2D(128, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.Conv2D(128, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.MaxPooling2D(),\n\n        tf.keras.layers.Conv2D(256, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.Conv2D(256, kernel_size=7, strides=2, padding='same'),\n        tf.keras.layers.Conv2D(256, kernel_size=7, strides=2, padding='same'),\n\n        tf.keras.layers.GlobalAvgPool2D(),\n        tf.keras.layers.Dense(units=5, activation=\"softmax\")])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"sd_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"sd_model.compile(loss='sparse_categorical_crossentropy',\n          optimizer=keras.optimizers.Adam(lr=0.0001),\n          metrics=['sparse_categorical_accuracy'])\n\nearly_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n                           patience = 5, mode = 'min', verbose = 1,\n                           restore_best_weights = True)\nreduce_lr = ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n                              patience = 5, min_delta = 0.001, \n                              mode = 'min', verbose = 1)\n\nsd_history = sd_model.fit(\n    dataset_train.repeat(), \n    steps_per_epoch = train_size//BATCH_SIZE,\n    validation_data = dataset_val.repeat(), \n    validation_steps= val_size//BATCH_SIZE,\n    callbacks=[reduce_lr, early_stop],\n    epochs = 20,\n    verbose = True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nN = 7\nplt.style.use(\"ggplot\")\nplt.figure()\nplt.plot(np.arange(0, N), sd_history.history[\"sparse_categorical_accuracy\"], label=\"Train Accuracy\")\nplt.plot(np.arange(0, N), sd_history.history[\"val_sparse_categorical_accuracy\"], label=\"Validation Accuracy\")\nplt.plot(np.arange(0, N), sd_history.history[\"loss\"], label=\"Train Loss\")\nplt.plot(np.arange(0, N), sd_history.history[\"val_loss\"], label=\"Validation Loss\")\nplt.title(\"Self-Designed Model Loss and Accuracy Plot\")\nplt.xlabel(\"Epoch\")\nplt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Our self-designed model which combines Residual blocks and VGG architectures performs better than our baseline CNN model. This model gives us 0.689 as the best validation accuracy at epoch 4 and then overfitting happens due to the trends of training and validation losses."},{"metadata":{},"cell_type":"markdown","source":"## ResNet50"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.layers import Input\n\nbase_res = ResNet50(weights=\"imagenet\", include_top=False, input_shape=(256, 256, 3))\n\nhead_res = base_res.output\nhead_res = GlobalAveragePooling2D()(head_res)\nhead_res = Dense(5, activation= \"softmax\")(head_res)\nresnet50 = Model(inputs=base_res.input, outputs=head_res)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for layer in base_res.layers:\n  layer.trainable = False","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"resnet50.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"resnet50.compile(optimizer = Adam(lr = 0.01),\n              loss = 'sparse_categorical_crossentropy',\n              metrics = ['sparse_categorical_accuracy'])\n\nreduce_lr = ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n                              patience = 5, min_delta = 0.001, \n                              mode = 'min', verbose = 1)\n\nearly_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n                           patience = 10, mode = 'min', verbose = 1,\n                           restore_best_weights = True)\n\nhistory_resnet50 = resnet50.fit(\n    dataset_train.repeat(),\n    steps_per_epoch = train_size//BATCH_SIZE,\n    validation_data= dataset_val.repeat(),\n    validation_steps=val_size//BATCH_SIZE,\n    epochs=30,\n    callbacks = [reduce_lr, early_stop],\n    verbose=True\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = 29\nplt.style.use(\"ggplot\")\nplt.figure()\nplt.plot(np.arange(0, N), history_resnet50.history[\"sparse_categorical_accuracy\"], label=\"Train Accuracy\")\nplt.plot(np.arange(0, N), history_resnet50.history[\"val_sparse_categorical_accuracy\"], label=\"Validation Accuracy\")\nplt.plot(np.arange(0, N), history_resnet50.history[\"loss\"], label=\"Train Loss\")\nplt.plot(np.arange(0, N), history_resnet50.history[\"val_loss\"], label=\"Validation Loss\")\nplt.title(\"ResNet50 Loss and Accuracy Plot\")\nplt.xlabel(\"Epoch\")\nplt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"At epoch 18 and later, the validation accuracy converges to 0.64. It means the model based on pre-trained ResNet50 performs worse than our baseline CNN model and we decided to swich on other models. "},{"metadata":{},"cell_type":"markdown","source":"## ResNet architecture"},{"metadata":{"trusted":true},"cell_type":"code","source":"resarc = tf.keras.Sequential([\n    tf.keras.layers.Input(shape=(256,256,3)),\n    tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.Activation('relu'),\n    tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same'),\n    \n    ResnetBlock(64, 2, first_block=True),\n    ResnetBlock(128, 2),\n    ResnetBlock(256, 2),\n    ResnetBlock(512, 2),\n    tf.keras.layers.GlobalAvgPool2D(),\n    tf.keras.layers.Dense(units=5, activation=\"softmax\")])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"resarc.summary()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"resarc.compile(optimizer = Adam(lr = 0.001),\n              loss = 'sparse_categorical_crossentropy',\n              metrics = ['sparse_categorical_accuracy'])\n\nhistory_resarc = resarc.fit(\n    dataset_train.repeat(),\n    steps_per_epoch = train_size//BATCH_SIZE,\n    validation_data= dataset_val.repeat(),\n    validation_steps=val_size//BATCH_SIZE,\n    epochs=20,\n    callbacks = [reduce_lr, early_stop],\n    verbose=True\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = 16\nplt.style.use(\"ggplot\")\nplt.figure()\nplt.plot(np.arange(0, N), history_resarc.history[\"sparse_categorical_accuracy\"], label=\"Train Accuracy\")\nplt.plot(np.arange(0, N), history_resarc.history[\"val_sparse_categorical_accuracy\"], label=\"Validation Accuracy\")\nplt.plot(np.arange(0, N), history_resarc.history[\"loss\"], label=\"Train Loss\")\nplt.plot(np.arange(0, N), history_resarc.history[\"val_loss\"], label=\"Validation Loss\")\nplt.title(\"ResNet(Arc) Loss and Accuracy Plot\")\nplt.xlabel(\"Epoch\")\nplt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The simple ResNet architecture achieves 0.67 accuracy which is higher than baseline accuracy (0.6150) but the improvement is not big enough to conclude the final model. At the epoch 8, the model begins to overfit the training dataset."},{"metadata":{},"cell_type":"markdown","source":"## EfficientNet B0"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.applications import EfficientNetB0\neffb0_model = tf.keras.Sequential()\neffb0_model.add(EfficientNetB0(include_top = False, weights = 'imagenet',\n                          input_shape = (256, 256, 3)))\n\neffb0_model.add(tf.keras.layers.GlobalAveragePooling2D())\neffb0_model.add(tf.keras.layers.Dense(5, activation = \"softmax\"))\n\neffb0_model.compile(optimizer = Adam(lr = 0.001),\n              loss = 'sparse_categorical_crossentropy',\n              metrics = ['sparse_categorical_accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"effb0_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\n# reduce_lr = ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n#                               patience = 5, min_delta = 0.001, \n#                               mode = 'min', verbose = 1)\n\n# early_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n#                            patience = 10, mode = 'min', verbose = 1,\n#                            restore_best_weights = True)\n\n# history_effb0 = effb0_model.fit(\n#     dataset_train.repeat(),\n#     steps_per_epoch = train_size//BATCH_SIZE,\n#     validation_data= dataset_val.repeat(),\n#     validation_steps=val_size//BATCH_SIZE,\n#     epochs=100,\n#     callbacks = [reduce_lr, early_stop],\n#     verbose=True\n# )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Import the local trained EfficientNetB0 model.\neffnetb0 = keras.models.load_model(\"../input/effnetb0/EffNetB0.h5\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"EffNetb0.png![image.png](attachment:image.png)","attachments":{"image.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAhYAAAEaCAYAAABNbR41AAAgAElEQVR4AeydB5gdR5Xv/d7b/DYACwsLLGGXsMsuS1gesEvGCxjwYuzFCRsbbMtBybbkLMlJkuUgWTnnnHPOOUvWaJJmRtJIMxLONraFw2KL875fjeu6p6fjvX1D33v6+2Zud3V1hf+tW+ffp06dc47ooQgoAoqAIqAIKAKKQEIInJNQOVqMIqAIKAKKgCKgCCgCosRCB4EioAgoAoqAIqAIJIaAEovEoNSCFAFFQBFQBBQBRUCJhY4BRUARUAQUAUVAEUgMASUWiUGpBSkCioAioAgoAoqAEgsdA4qAIqAIKAKKgCKQGAJKLHKA8q677pK/+Zu/kXPOOUcmT55sSho2bJh86EMfkv/1v/6X3HfffSb9//yf/xO5FsqJkz9ywZqxLBC4+uqr5dxzzy2LvpRaJzZv3mx+y6dOnSq1pml7FIFUIaDEwuPrYvKGLLj//u///b+Z3Hv27DH3lyxZIk899ZS89tpr8utf/1r+9//+3zJixAh58skn5cyZMyb96aefzjwXdkI5cfKHlWfvT58+3bTXXttP29fbb7/dJplPJlf6z2Qb9di+fbt55sSJE+0egWA5sfyzP/sz+dznPidTp05tl48LiNWnPvUp+aM/+iP59Kc/LTNmzOiQx53w0Y9+VPr27etOLsvrOMTi4YcfNuPxtttuK0ss4naKcWLH4R//8R+b8fXII4/I2bNnTVHZEAvGHeXqoQgoAu8goMTiHSwyZ0ze3/jGNwxhgDTYv2eeeSaTB0ENiXAeVrC2trY6k0viPIhY/Mmf/Ikw0Z48eTLT1qSJxcc+9rEMjkePHjVEgEl+69atmToXL15sMH388cflyJEj8thjj5nrVatWZfJ4nSix6IjK73//e/mHf/gH6d27t7zvfe+T//mf/+mYqcApv/vd7wpcY/vqGCd33nmnGYfNzc0yePBgo1l89NFHTUYlFu3x0itFIFsElFh4IBf2Vmjf8u3bD5/ut3LSeHP3Wto4cOCA/OAHP5C/+Iu/ELQg/+///T9BA8Lhl/973/ueyfve975XLrzwwnYkgLoRImhPeMtHI/Ctb31LmpqaTJl2wnS2lz5w8Pnd735XvvzlL8vPf/5zk8Y/L2KBJoX8tOHP//zP5T/+4z8yxIC+OsvnnDZw2PZlCn/75D3veY9AIuzx7//+73L55ZfbS/P5s5/9LFNOuxuOizBisXLlSvniF79otCAI2Ztuukl++9vfZkqora2V73//+/JXf/VXBrt//Md/lGnTpmXujx8/XkiDfL373e82pDNIXb5u3TrTZvL+5V/+pXzzm9+UvXv3ZsrjBHxGjhwpV155pcGS5bOHHnqoXZ4XXnhBLrnkEtMmltx69eolV111VaSlkPXr18v73/9+efPNN+Wf/umfZM6cOe3K5oI8X//61+VP//RPM+08duxYJh/PgBv95rs677zz5MUXXzT3+W6vvfbaTF5O3G/vjBWWbVge5DtieRCNXBR80PbdfPPN8uEPf9h8bzzfv3//TN2dOnVqVzdE6u///u/lwQcfbJfuvPAaJ//5n/8pX/3qV002+ztxfre7d+823zfk+13vepcZn/YFg9+qe8wz1vVQBCodASUWHiPAToget0zSSy+9JEOGDDG2EFabwUS4cOFCM9E88cQT5q3orbfe6kAUEGII/ssuu0z2799vhP+sWbNk165dpmw3sairqzOE4t577zVv8dXV1YKw/eQnPymvv/66eYbJjDIhK5CWqqoqIxAQGhy8rbI8wyRo20sfOGxft23bZiZ+2sThJhYIBATURRddZNqN1qFfv35m0q+vrxf6unTpUlPHvn37TD0IRg43sSDv3LlzjTbC9ps2/sEf/EGH5ZEJEyYYwcYzfoeXwLB5Dx8+bL6nW265xeCH9uPv/u7vjEC3eT772c8agQHWx48fF/IsX77c3AZPbF5YtkGjA/4QDafwseXYz0WLFpn+NTQ0CN83AhiS8fzzz9ssBifIwrhx4wRhbr+fDRs2ZPL89Kc/NYRx48aNppwrrrjCkNEoNhaMkR49epiyWBL5zne+kymXE0gFGjeEN+MFDRFY88kxadIk830gqMEFHBnzzz33nLkflVhAnukHdYAd32MYPpAEyv/4xz8uaLH4TtBsgRUHvxeILb85e4Ab3xPLkX6H1zj5r//6L/m3f/s384ibWPBbof2QXdqORpKxgjaTg98EGhDIj/1dOdvk1w5NVwTKHQElFh7fMMKWSQptgvPv/PPPz+R2EwBuuCcm0tz5eEP913/918y6bqbAt0/c+WnLpZde2i7bG2+8Yd4ymXQ5ENy099lnn83k422TN0RLPoKWQqygQgBYLYObWNAu3qp5A3Ye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"}}},{"metadata":{},"cell_type":"markdown","source":"EfficientNetB0 gives us around 0.8 validation accuracy score (Within the range: 0.79 - 0.82). Comparing to our baeline model, EfficientNetB0 improved 0.185 validation accuracy with an excellent training process. Therefore, we decided to use the EfficientNet50 as our final model to make the prediction on test set."},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.preprocessing.image import smart_resize\n\npreds = []\nsample_sub = pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/sample_submission.csv')\n\nfor image in sample_sub.image_id:\n    img = keras.preprocessing.image.load_img('/kaggle/input/cassava-leaf-disease-classification/test_images/' + image)\n    img = img_to_array(img)\n    img = smart_resize(img, (256,256))\n    img = tf.reshape(img, (-1, 256, 256, 3))\n    \n    # Now apply your model and save your prediction:\n    prediction = effnetb0.predict(img)\n    preds.append(np.argmax(prediction))\n    \n\nmy_submission = pd.DataFrame({'image_id': sample_sub.image_id, 'label': preds})\nmy_submission.to_csv('/kaggle/working/submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission=pd.read_csv(\"../input/cassava-leaf-disease-classification/sample_submission.csv\")\nsample_submission #True label","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission=pd.read_csv(\"./submission.csv\")\nsubmission #Predicted Label","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}