{"cells":[{"metadata":{},"cell_type":"markdown","source":" <h1><b><center> Cassava Leaf Disease Classification </center></b></h1>\n"},{"metadata":{},"cell_type":"markdown","source":"## Key takeaways from this notebook:\n\n1. Standing in the shoes of a beginner I understand how tricky it is to understand a problem statement and solve it in your initial roadmap to become a Data Scientist. This notebook mainly focuses on the basics of understanding a problem statement and implementing it's solution.\n\n2. This notebook contains lucid explanation for every code written and it's purpose in the modelling of our Deep Learning model\n\n3. Using this as a baseline model we can provide our predictions with very good accuracy for a noob!"},{"metadata":{},"cell_type":"markdown","source":"# 1. Problem Description\n\nAs the second-largest provider of carbohydrates in Africa, cassava is a key food security crop grown by smallholder farmers because it can withstand harsh conditions. At least 80% of household farms in Sub-Saharan Africa grow this starchy root, but viral diseases are major sources of poor yields. With the help of data science, it may be possible to identify common diseases so they can be treated.\n\nExisting methods of disease detection require farmers to solicit the help of government-funded agricultural experts to visually inspect and diagnose the plants. This suffers from being labor-intensive, low-supply and costly. As an added challenge, effective solutions for farmers must perform well under significant constraints, since African farmers may only have access to mobile-quality cameras with low-bandwidth.\n\nIn this competition, we introduce a dataset of 21,397 labeled images collected during a regular survey in Uganda. Most images were crowdsourced from farmers taking photos of their gardens, and annotated by experts at the National Crops Resources Research Institute (NaCRRI) in collaboration with the AI lab at Makerere University, Kampala. This is in a format that most realistically represents what farmers would need to diagnose in real life.\n\n\n**Problem statement:**\n\nOur task is to classify each cassava image into four disease categories or a fifth category indicating a healthy leaf. With your help of this farmers may be able to quickly identify diseased plants, potentially saving their crops before they inflict irreparable damage.\n\nSource: https://www.kaggle.com/c/cassava-leaf-disease-classification"},{"metadata":{},"cell_type":"markdown","source":"# 2. Problem overview\n\n### 2.1 Data Description\n\nThere are 21397 train images consisting of 5 different class labels.\n\n\n### 2.2 ML/DL Problem\n\nIt is a multiclass classification problem where we have to classify a given leaf image into one of the 5 categories.\n\n### 2.3 Performance metric\n\nSince we have to observe how accurate our model is working the performance metric is categorical accuracy score (Accuracy score for multiclass classification), where\n\nAccuracy score = Number of correctly classified images / Total number of images"},{"metadata":{},"cell_type":"markdown","source":"# 3. Exploratory Data Analysis\n\n### 3.1 Importing data and necessary libraries"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport json\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom tensorflow import keras\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.2 Loading the data and visualizing the images"},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ndf['label'] = df['label'].astype('str')\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As you can see there are 2 columns in our dataframe, one consisting of the **image id** of our training data and other consisting the **labels** for the specific image.\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f'There are total {df.shape[0]} images in our train data.')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Initially let us first understand what our output labels mean and then we can relate them to the images."},{"metadata":{"trusted":true},"cell_type":"code","source":"with open('../input/cassava-leaf-disease-classification/label_num_to_disease_map.json', 'r') as file:\n    labels = json.load(file)\n    \nlabels","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"From the above loaded json file we can understand that labels 0, 1, 2 and 3 are some kind of disease plants and label 4 means that the leaves are healthy. Anyways the purpose of this json file is just to understand the disease name w.r.t. labels"},{"metadata":{"trusted":true},"cell_type":"code","source":"# defining some variables which will be useful later\nTRAIN_PATH = '../input/cassava-leaf-disease-classification/train_images'\nIMAGE_WIDTH = 300\nIMAGE_HEIGHT = 300\nEPOCHS = 20\nBATCH_SIZE = 32","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(16, 12))\ndf_sample = df.sample(12).reset_index(drop=True)\nfor i in range(9):\n    plt.subplot(3, 3, i+1)\n    img = cv2.imread(os.path.join(TRAIN_PATH, df_sample.image_id[i]))\n    img = cv2.resize(img, (IMAGE_HEIGHT, IMAGE_WIDTH))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(labels.get(df_sample.label[i]))\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Let us look at the types of diseases:"},{"metadata":{},"cell_type":"markdown","source":"### 1 - Cassava Bacterial Blight (CBB)\n\nXanthomonas axonopodis pv. manihotis is the pathogen that causes bacterial blight of cassava. Originally discovered in Brazil in 1912, the disease has followed cultivation of cassava across the world. Among diseases which afflict cassava worldwide, bacterial blight causes the largest losses in terms of yield.\n\n#### Symptoms:\n\nSymptoms include leaf spotting, wilting, dying, gum oozing on young shoots, and vascular coloration of mature stems and roots of susceptible varieties."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12, 7))\ndf_sample = df[df.label == '0'].sample(3).reset_index(drop=True)\nfor i in range(3):\n    plt.subplot(1, 3, i+1)\n    img = cv2.imread(os.path.join(TRAIN_PATH, df_sample.image_id[i]))\n    img = cv2.resize(img, (IMAGE_HEIGHT, IMAGE_WIDTH))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(labels.get(df_sample.label[i]))\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2 - Cassava Brown Streak Disease (CBSD)\n\nCassava brown streak virus disease (CBSD) is a damaging disease of cassava plants, and is especially troublesome in East Africa. It was first identified in 1936 in Tanzania, and has spread to other coastal areas of East Africa, from Kenya to Mozambique. Recently, it was found that two distinct viruses are responsible for the disease: cassava brown streak virus (CBSV) and Ugandan cassava brown streak virus (UCBSV).\n\n#### Symptoms:\n\nCBSD is characterized by severe chlorosis and necrosis on infected leaves, giving them a yellowish, mottled appearance.\nChlorosis may be associated with the veins, spanning from the mid vein, secondary and tertiary veins, or rather in blotches unconnected to veins.\nLeaf symptoms vary greatly depending on a variety of factors.\nThe growing conditions (i.e. altitude, rainfall quantity), plant age, and the virus species account for these differences.\nBrown streaks may appear on the stems of the cassava plant. Also, a dry brown-black necrotic rot of the cassava tuber exists, which may progress from a small lesion to the whole root.\nFinally, the roots can become constricted due to the tuber rot, stunting growth"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12, 7))\ndf_sample = df[df.label == '1'].sample(3).reset_index(drop=True)\nfor i in range(3):\n    plt.subplot(1, 3, i+1)\n    img = cv2.imread(os.path.join(TRAIN_PATH, df_sample.image_id[i]))\n    img = cv2.resize(img, (IMAGE_HEIGHT, IMAGE_WIDTH))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(labels.get(df_sample.label[i]))\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3 - Cassava Green Mottle (CGM)\n\nIt has not been confirmed to be a nepovirus; these are viruses that are transmitted by nematodes - hence the name. Narrow. Only known from Solomon Islands. It was first found on Choiseul in the 1970s; more recently (2010), similar symptoms were seen on Malaita.\n\n#### Symptoms:\n\nLook for yellow patterns on the leaves, from small dots to irregular patches of yellow and green.\nLook for leaf margins that are distorted.\nThe plants may be stunted."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12, 7))\ndf_sample = df[df.label == '2'].sample(3).reset_index(drop=True)\nfor i in range(3):\n    plt.subplot(1, 3, i+1)\n    img = cv2.imread(os.path.join(TRAIN_PATH, df_sample.image_id[i]))\n    img = cv2.resize(img, (IMAGE_HEIGHT, IMAGE_WIDTH))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(labels.get(df_sample.label[i]))\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4 - Cassava Mosaic Disease (CMD)\n\nCassava mosaic virus is the common name used to refer to any of eleven different species of plant pathogenic virus in the genus Begomovirus. African cassava mosaic virus (ACMV), East African cassava mosaic virus (EACMV), and South African cassava mosaic virus (SACMV) are distinct species of circular single-stranded DNA viruses which are transmitted by whiteflies and primarily infect cassava plants; these have thus far only been reported from Africa.\n\n#### Symptoms:\n\nInitially following infection of a cassava geminivirus in cassava, systemic symptoms develop.\nThese symptoms include chlorotic mosaic of the leaves, leaf distortion, and stunted growth.\nLeaf stalks have a characteristic S-shape.\nInfection can be overcome by the plant especially when a rapid onset of symptoms occurs. A slow onset of disease development usually correlates with death of the plant.\naffected by whiteflies\naffected by environmental factors such as temperature, wind, precipitation and plant density"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12, 7))\ndf_sample = df[df.label == '3'].sample(3).reset_index(drop=True)\nfor i in range(3):\n    plt.subplot(1, 3, i+1)\n    img = cv2.imread(os.path.join(TRAIN_PATH, df_sample.image_id[i]))\n    img = cv2.resize(img, (IMAGE_HEIGHT, IMAGE_WIDTH))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.title(labels.get(df_sample.label[i]))\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.3 Understanding distribution of train data\n\nLet us understand how our class labels are distributed"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(20, 6))\n\nplt.suptitle('Distribution of class labels', fontsize=20)\nplt.subplot(1, 2, 1)\nplt.pie(\n    df.label.value_counts(), \n    labels=[0, 1, 2, 3, 4], \n    autopct='%d%%', \n    explode=[0.01, 0.01, 0.01, 0.01, 0.01]\n)\nplt.title('Pie distribution of class labels')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nsns.countplot(df.label.values)\nplt.title('Bar distribution of class labels')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As you can see there is definitely a huge imbalance between the disease class labels. Approximately 61% data belongs to class 3 (Cassava Mosaic Disease (CMD) disease) and remaining 39% of data is divided into the remaining classes."},{"metadata":{},"cell_type":"markdown","source":"# 4. Deep learning model\n\n### 4.1 Image augmentation using Keras Image Data Generator\n\nImage augmentation is a technique of applying different transformations to original images which results in multiple transformed copies of the same image. Each copy, however, is different from the other in certain aspects depending on the augmentation techniques you apply like shifting, rotating, flipping, etc.\n\nApplying these small amounts of variations on the original image does not change its target class but only provides a new perspective of capturing the object in real life. And so, we use it is quite often for building deep learning models. Keras ImageDataGenerator is a gem! It lets you augment your images in real-time while your model is still training! You can apply any random transformations on each training image as it is passed to the model. This will not only make your model robust but will also save up on the overhead memory.\n\n#### Advantages of using Keras Image Data Generator:\n\nThe main benefit of using the Keras ImageDataGenerator class is that it is designed to provide real-time data augmentation. Meaning it is generating augmented images **on the fly** while your model is still in the training stage. But it only returns the transformed images and does not add it to the original corpus of images. If it was, in fact, the case, then the model would be seeing the original images multiple times which would definitely overfit our model.\n\nAnother advantage of ImageDataGenerator is that it requires **lower memory usage**. This is so because without using this class, we load all the images at once. But on using it, we are loading the images in batches which saves a lot of memory.\n\nTo understand keras parameters for image data generator: <a href='https://keras.io/api/preprocessing/image/'>click here</a>"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = keras.preprocessing.image.ImageDataGenerator(\n    horizontal_flip=True,\n    vertical_flip=True,\n    rotation_range=20,\n    shear_range=20,\n    zoom_range=0.2,\n    height_shift_range=0.1,\n    width_shift_range=0.1,\n    validation_split=0.2\n)\n\ntrain_imagegen = train_datagen.flow_from_dataframe(\n    df,\n    directory='../input/cassava-leaf-disease-classification/train_images',\n    x_col='image_id',\n    y_col='label',\n    subset='training',\n    target_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n    class_mode='categorical',\n    batch_size=BATCH_SIZE\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Since we have provided validation_split=0.2 as a parameter, out of 21397 images our train data generator has generated 17118 images for train data.\n\nHere class_mode='categorical' means that for eg. label=4 will be encoded as [0, 0, 0, 0, 1].\n\nFor each image it's size will be (300, 300, 3)."},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_datagen = keras.preprocessing.image.ImageDataGenerator(\n    validation_split=0.2\n)\n\nvalid_imagegen = valid_datagen.flow_from_dataframe(\n    df,\n    directory='../input/cassava-leaf-disease-classification/train_images',\n    x_col='image_id',\n    y_col='label',\n    subset='validation',\n    target_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n    class_mode='categorical',\n    batch_size=BATCH_SIZE\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4.2 Creating DL model\n\nFor creating our Deep Learning model we will be using pretrained model '**Xception**' which s a type of Transfer Learning techniques.\n\n**What is Transfer Learning?**\n\nTransfer learning is a machine learning method where a model developed for a task is reused as the starting point for a model on a second task.\n\nIt is a popular approach in deep learning where pre-trained models are used as the starting point on computer vision and natural language processing tasks given the vast compute and time resources required to develop neural network models on these problems and from the huge jumps in skill that they provide on related problems.\n\nTo check the overall list of models provided by Keras as a part of transfer learning: <a href='https://keras.io/api/applications/'>click here</a>"},{"metadata":{},"cell_type":"markdown","source":"The structure of our deep learning model is as follows:\n\n1. Xception model as a part of transfer learning application by Keras.\n2. Global Average Pooling technique to reduce the image shape and apply pooling on spatial dimensions.\n3. Dense layer to provide the probability of predictions for all 5 classes, acting as a output layer.\n\nIf you are new to Deep Learning and want to understand the functionality of Global Average Pooling layer: <a href='https://adventuresinmachinelearning.com/global-average-pooling-convolutional-neural-networks/'>click here</a>"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = keras.models.Sequential()\nmodel.add(keras.applications.Xception(input_shape=(IMAGE_HEIGHT, IMAGE_WIDTH, 3), \n                                            weights='imagenet', include_top=False))\nmodel.add(keras.layers.GlobalAveragePooling2D())\nmodel.add(keras.layers.Dense(5, activation='softmax'))\nprint(model.summary())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let us look at the structure of our Deep Learning model"},{"metadata":{"trusted":true},"cell_type":"code","source":"keras.utils.plot_model(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let us define some of the callbacks for our DL model.\n\nA callback is an object that can perform actions at various stages of training (e.g. at the start or end of an epoch, before or after a single batch, etc). \n\nTo understand more <a href='https://keras.io/api/callbacks/'>click here</a>\n\nIn this model we will be using 3 callbacks as below:"},{"metadata":{},"cell_type":"markdown","source":"1. **ModelCheckpoint** : Callback to save the Keras model or model weights at some frequency."},{"metadata":{"trusted":true},"cell_type":"code","source":"model_checkpoint = keras.callbacks.ModelCheckpoint(\n    './best_weights.h5',\n    monitor=\"val_loss\",\n    verbose=1,\n    save_best_only=True,\n    save_weights_only=True,\n    mode=\"min\"\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"2. **EarlyStopping** : Stop training when a monitored metric has stopped improving."},{"metadata":{"trusted":true},"cell_type":"code","source":"early_stopping = keras.callbacks.EarlyStopping(\n    monitor=\"val_loss\",\n    min_delta=0.001,\n    patience=5,\n    verbose=1,\n    mode=\"min\",\n    restore_best_weights=True,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3. **ReduceLROnPlateau** : Reduce learning rate when a metric has stopped improving."},{"metadata":{"trusted":true},"cell_type":"code","source":"reduce_lr = keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.1,\n    patience=2,\n    verbose=1,\n    mode=\"min\",\n    min_delta=0.001,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For this model we will be using **Adam** optimizer and since our output labels are categorical we will be using **categorical_crossentropy** as a loss function."},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have defined everything we need, it's time to train the model..."},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit_generator(\n    train_imagegen,\n    epochs=EPOCHS,\n    steps_per_epoch=(len(df)*0.8) // BATCH_SIZE,\n    validation_data=valid_imagegen,\n    validation_steps=(len(df)*0.2) // BATCH_SIZE,\n    callbacks = [model_checkpoint, early_stopping, reduce_lr]\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Since the performance metric for this competition is accuracy, let us plot the train and validation accuracy to monitor our model performance."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(15, 5))\nplt.plot(history.history['accuracy'], 'b*-', label=\"train_acc\")\nplt.plot(history.history['val_accuracy'], 'r*-', label=\"val_acc\")\nplt.grid()\nplt.title(\"train_acc vs val_acc\")\nplt.ylabel(\"Accuracy\")\nplt.xlabel(\"Epochs\")\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Being a noob myself I scratched my head for some time and later understood the requirements for notebook submission is that there should be **no internet connection** for the notebook which we will use to submit our results, but since we have used Transfer learning to model our Deep Learning model we require a internet connection.\n\nThis is because since we wanted '**imagenet**' weights for our '**Xception**' model it fetches the weights from google storage in realtime using internet connection.\n\nSo this notebook will be our baseline model where we will obtain the weights after training our model on train images and later on we will use these weights in a different notebook to predict on our test images."},{"metadata":{},"cell_type":"markdown","source":"**PS:**\n\nThe link to prediction notebook is : https://www.kaggle.com/pndeepak/cassava-predict\n\nThis prediction notebook explains how to load our model weights and use them to predict labels on test images in lucid way.\n\n<h2>Please upvote this notebook if you learnt atleast something from it and share it with others as well.</h2>"}],"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}