{"cells":[{"metadata":{},"cell_type":"markdown","source":"<center><h1> Introduction </h1></center>\n"},{"metadata":{},"cell_type":"markdown","source":"* **Competition** - Given a set of images of Cassava leaves we need to classify them as one of the four diseases or healthy.\n* **Data** - A collection of 21397 labelled images\n* **Evaluation** - Classification accuracy"},{"metadata":{},"cell_type":"markdown","source":"We will take a quick look at the data files and train our model in this notebook. The model can be saved and used for inference in a later notebook."},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Importing necessary libraries </h1></center> "},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\nROOT_DIR = '../input/cassava-leaf-disease-classification/'\nos.listdir(ROOT_DIR)\n\nimport json # to read in the 'label_num_to_disease_map.json' file","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport plotly.express as px\nimport seaborn as sns\nimport cv2\nimport random","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers\nfrom tensorflow.keras import models\nfrom tensorflow.keras.optimizers import Adam\n\nfrom tensorflow.keras.preprocessing.image import load_img\nfrom tensorflow.keras.preprocessing.image import img_to_array\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau\nfrom tensorflow.keras.applications import EfficientNetB3, EfficientNetB5\nfrom tensorflow.keras.utils import plot_model","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# set the training and test directory paths\nTRAIN_DIR = '../input/cassava-leaf-disease-classification/train_images/'\nTEST_DIR = '../input/cassava-leaf-disease-classification/test_images/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# set seed\nseed = 42\n\ndef seed_everything(seed):\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n    \nseed_everything(seed)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Data exploration </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(ROOT_DIR + 'train.csv')\nsample_df = pd.read_csv(ROOT_DIR + 'sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train_df.shape, sample_df.shape)\ndisplay(train_df.head())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f = open(ROOT_DIR + 'label_num_to_disease_map.json')\ndata = json.load(f)\nprint(json.dumps(data, indent = 2))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"z = train_df.sample(20)\ndisplay(z)\nimages, labels = z['image_id'].tolist(), z['label'].tolist()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Plotting 20 randomly sampled images with class </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (20,20))\nfor i in range(20):\n    plt.subplot(4,5,i+1)\n    img = cv2.imread(TRAIN_DIR + images[i])\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    plt.imshow(img)\n    plt.title(data[str(labels[i])])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Plotting a count plot and pie chart for class distribution. </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.set_style('whitegrid')\nsns.countplot(x = 'label', data = train_df, palette = 'Pastel1');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pie_df = train_df['label'].value_counts().reset_index()\npie_df.columns = ['label', 'count']\nfig = px.pie(pie_df, values = 'count', names = 'label', color_discrete_sequence = px.colors.qualitative.Pastel)\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There is a class imbalance problem here. The Cassava Mosaic Disease (CMD) samples are more compared to other classes. We can solve this by either upsampling other class samples or downsampling the CMD samples."},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Split dataset for training and validation </h1></center> \n<center> Reserving 15% of data for validation </center>"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = train_df.astype({\"label\": str})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train, test = train_test_split(train_df, test_size = 0.15, random_state = seed)\nprint(train.shape, test.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Creating ImageDataGenerator to generate data in batches and perform image augmentation. </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = 300\nsize = (IMG_SIZE,IMG_SIZE)\nbatch_size = 32","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(\n                    rotation_range = 30,\n                    width_shift_range = 0.2,\n                    height_shift_range = 0.2,\n                    shear_range = 0.2,\n                    zoom_range = 0.2,\n                    brightness_range = [0.5,1.5],\n                    horizontal_flip = True,\n                    vertical_flip = True,\n                    fill_mode = 'nearest'\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validgen = ImageDataGenerator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_generator = datagen.flow_from_dataframe(\n                    train,\n                    directory = TRAIN_DIR,\n                    x_col = \"image_id\",\n                    y_col = \"label\",\n                    target_size = size,\n                    class_mode = \"sparse\",\n                    batch_size = batch_size,\n                    shuffle = True,\n                    seed = seed,\n                    interpolation = \"nearest\"\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_generator = validgen.flow_from_dataframe(\n                    test,\n                    directory = TRAIN_DIR,\n                    x_col = \"image_id\",\n                    y_col = \"label\",\n                    target_size = size,\n                    class_mode = \"sparse\",\n                    batch_size = batch_size,\n                    shuffle = False,\n                    seed = seed,\n                    interpolation = \"nearest\"\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h2> Visualizing some of the augmented images </h2></center>"},{"metadata":{},"cell_type":"markdown","source":"### Image before Augmentation"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"img = cv2.imread(os.path.join(TRAIN_DIR,'1000201771.jpg'))\nimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Images after Augmentation"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (10,10))\n# set the title\nplt.title('Augmented Images')\n# load the image\nimg = load_img(os.path.join(TRAIN_DIR,'1000201771.jpg'))\n# convert to numpy array\ndata = img_to_array(img)\n# expand dimension to one sample\nsamples = np.expand_dims(data, 0)\n# iterator\nitr = datagen.flow(samples, batch_size = 1)\n# generate samples and plot\nfor i in range(12):\n    # define subplot\n    plt.subplot(4,3,i+1)\n    # generate batch of images\n    batch = itr.next()\n    # convert to unsigned integers for viewing\n    image = batch[0].astype('uint8')\n    # plot raw pixel data\n    plt.imshow(image)\n# show the figure\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Model creation and training </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"NUM_CLASSES = 5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_model():\n    \n    model = models.Sequential()\n    # initialize EfficientNetB3 model with input shape as (300,300,3)\n    model.add(EfficientNetB3(input_shape = (IMG_SIZE, IMG_SIZE, 3), include_top = False, weights = 'imagenet'))\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dense(256, activation = 'relu'))\n    model.add(layers.Dropout(0.5))\n    model.add(layers.Dense(NUM_CLASSES, activation = 'softmax'))\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = create_model()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(loss = 'sparse_categorical_crossentropy',\n             optimizer = Adam(learning_rate = 0.001),\n             metrics = ['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Stop training when the validation loss metric has stopped decreasing for 5 epochs.\nearly_stopping = EarlyStopping(monitor = 'val_loss',\n                               patience = 5,\n                               mode = 'min',\n                               restore_best_weights = True)\n\n# Save the model with the maximum validation accuracy \ncheckpoint = ModelCheckpoint('best_model.hdf5', \n                             monitor = 'val_accuracy',\n                             verbose = 1,\n                             mode = 'max', \n                             save_best_only = True)\n# reduce learning rate\nreduce_lr = ReduceLROnPlateau(monitor = 'val_loss',\n                              factor = 0.2,\n                              patience = 2,\n                              mode = 'min',\n                              verbose = 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 15\nSTEP_SIZE_TRAIN = train_generator.n//train_generator.batch_size\nSTEP_SIZE_VALID = valid_generator.n//valid_generator.batch_size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_generator,\n                    validation_data = valid_generator,\n                    epochs = EPOCHS,\n                    steps_per_epoch = STEP_SIZE_TRAIN,\n                    validation_steps = STEP_SIZE_VALID,\n                    callbacks = [early_stopping, checkpoint, reduce_lr]\n                   )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_model(model, show_shapes = True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<center><h1> Model evaluation </h1></center> "},{"metadata":{"trusted":true},"cell_type":"code","source":"model.evaluate_generator(generator = valid_generator, steps = STEP_SIZE_VALID)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(len(acc))\n\nplt.plot(epochs, acc, 'c-', label='Training accuracy')\nplt.plot(epochs, val_acc, 'y-', label='Validation accuracy')\nplt.title('Training and validation accuracy')\nplt.legend()\n\nplt.figure()\n\nplt.plot(epochs, loss, 'c-', label='Training Loss')\nplt.plot(epochs, val_loss, 'y-', label='Validation Loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Short version history:\n* Baseline model - 0.601\n* EfficientNetB0 - image_size = (224,224), batch_size =\t64\t- 0.841\n* EfficientNetB3 - image_size =\t(300,300), batch_size =\t32\t- 0.856\n* EfficientNetB3 with more augmentations - 0.865\n"},{"metadata":{},"cell_type":"markdown","source":"| Base model\t  |resolution |\n|-----------------|-----------|\n| EfficientNetB0  |\t224 |\n| EfficientNetB1  |\t240 |\n| EfficientNetB2  |\t260 |\n| EfficientNetB3  |\t300 |\n| EfficientNetB4  |\t380 |\n| EfficientNetB5  |\t456 |\n| EfficientNetB6  |\t528 |\n| EfficientNetB7  |\t600 |"},{"metadata":{},"cell_type":"markdown","source":"### Some useful links:\n* The prediction and submission notebook can be found here : [Inference Notebook](https://www.kaggle.com/lavanyask/cassava-leaf-disease-inference)\n* More about keras EfficientNets: [here](https://keras.io/examples/vision/image_classification_efficientnet_fine_tuning/)"},{"metadata":{},"cell_type":"markdown","source":"### Further experiments:\n* Trying out different network architecture, changing number of layers in the network\n* Hyperparameter tuning - changing epochs, batch size, number of neurons in hiddden layers, activation function ...\n* Cross Validation\n* More augmentation techniques\n* PyTorch"},{"metadata":{},"cell_type":"markdown","source":"## Do consider upvoting if you found it useful :)\n### Thank you for reading the notebook."}],"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}