Cassava Disease Detection | Python
Cassava disease affects the overall growth of the plants and their tubers depending on the level of infection. In this article, we propose a very unique deep learning method using Transfer Learning via EfficientNet resulting in an overall acc of 89.15% and 88.45% for training and testing of the detection of the Cassava Disease in Python. This network once used on plants will thus help the plants to grow strong and healthy with early diagnosis if cassava disease is there in the plant.
The article consists of:
- Downloading Dependencies
- Importing Libraries
- Data Generation
- Building Model
- Compilation and Training
- Plotting of the results
Happy Reading!
INSTALLATION
Installations (via pip command) are essential for the execution of this project
!pip install pydot !pip install pydotplus !pip install graphviz
IMPORT
Importing necessary libraries.
import os import glob import random import shutil import warnings import json import itertools import numpy as np import pandas as pd from collections import Counter from keras.models import from keras.layers import from keras.optimizers import from keras.callbacks import from tensorflow.keras.applications import import plotly.express as px import matplotlib.pyplot as plt import seaborn as sns import keras from keras.preprocessing.image import ImageDataGenerator import tensorflow as tf from PIL import Image from sklearn.model_selection import train_test_split # Defining the working directories work_dir = '../input/cassava-leaf-disease-classification/' os.listdir(work_dir) train_path = '/kaggle/input/cassava-leaf-disease-classification/train_images'
LOAD DATA
Checking the frequency of the labels. Import the JSON file with labels. Then, defining the working dataset thereby generating the training and testing sets.
data = pd.read_csv(work_dir + 'train.csv') print(data['label'].value_counts()) with open(work_dir + 'label_num_to_disease_map.json') as f: real_labels = json.load(f) real_labels = {int(k):v for k,v in real_labels.items()} data['class_name'] = data['label'].map(real_labels) real_labels train, test = train_test_split(data, test_size = 0.05, random_state = 42, stratify = data['class_name'])
3 13158 4 2577 2 2386 1 2189 0 1087 Name: label, dtype: int64 {0: 'Cassava Bacterial Blight (CBB)', 1: 'Cassava Brown Streak Disease (CBSD)', 2: 'Cassava Green Mottle (CGM)', 3: 'Cassava Mosaic Disease (CMD)', 4: 'Healthy'}
DATA GENERATION
In this section, data augmentation, pre-processing, and data generation for training. Data augmentation tackles the less dataset issue and thereby saving pre-processing time by using augmentation techniques. Pre-processing includes normalizing the dataset for efficient feature extraction while mapping. Thus, all the pre-processed and augmented images are stored for training purposes.
datagen_train = ImageDataGenerator( preprocessing_function = tf.keras.applications.efficientnet.preprocess_input, rotation_range = 40, width_shift_range = 0.2, height_shift_range = 0.2, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True, vertical_flip = True, fill_mode = 'nearest', ) datagen_val = ImageDataGenerator( preprocessing_function = tf.keras.applications.efficientnet.preprocess_input, ) train_set = datagen_train.flow_from_dataframe( train, directory=train_path, seed=42, x_col='image_id', y_col='class_name', target_size = size, class_mode='categorical', interpolation='nearest', shuffle = True, batch_size = BATCH_SIZE, ) test_set = datagen_val.flow_from_dataframe( test, directory=train_path, seed=42, x_col='image_id', y_col='class_name', target_size = size, class_mode='categorical', interpolation='nearest', shuffle=True, batch_size=BATCH_SIZE, )
Found 20327 validated image filenames belonging to 5 classes. Found 1070 validated image filenames belonging to 5 classes.
BUILD MODEL
Initialize the model with the input shape. Built using Global Average Pooling (GAP), flatten, and dense ReLU activators along with Dropout layers and concluding the network with a softmax layer.
GAP: Reduces the spatial dimension of the layers and resulting in a smoother feature map.
Flatten: Reducing the size of the vector
ReLU: Activation function which directly outputs the input if positive and zeroes if negative.
Dropout: Dropping of random neurons in layers thereby preventing overfitting error.
def create_model(): model = Sequential() # initialize the model with input shape model.add( EfficientNetB3( input_shape = (IMG_SIZE, IMG_SIZE, 3), include_top = False, weights='imagenet', drop_connect_rate=0.6, ) ) model.add(GlobalAveragePooling2D()) model.add(Flatten()) model.add(Dense( 256, activation='relu', bias_regularizer=tf.keras.regularizers.L1L2(l1=0.01, l2=0.001) )) model.add(Dropout(0.5)) model.add(Dense(n_CLASS, activation = 'softmax')) return model leaf_model = create_model()
SUMMARY + FLOWCHART
Summary of the built model in the previous section and the flowchart representing the layers of the built model for the ease of visualization.
leaf_model.summary()
Downloading data from https://storage.googleapis.com/keras-applications/efficientnetb3_notop.h5 43941888/43941136 [==============================] - 1s 0us/step Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= efficientnetb3 (Functional) (None, 15, 15, 1536) 10783535 _________________________________________________________________ global_average_pooling2d (Gl (None, 1536) 0 _________________________________________________________________ flatten (Flatten) (None, 1536) 0 _________________________________________________________________ dense (Dense) (None, 256) 393472 _________________________________________________________________ dropout (Dropout) (None, 256) 0 _________________________________________________________________ dense_1 (Dense) (None, 5) 1285 ================================================================= Total params: 11,178,292 Trainable params: 11,090,989 Non-trainable params: 87,303 _________________________________________________________________
keras.utils.plot_model(leaf_model)
BUILDING THE TRAIN FUNCTION
In this function, various built parameters are set. They are:
- Building the loss function
- Compiling of the model with the loss function built before
- Stop training when validation loss has stopped decreasing for 3 epochs using EarlyStopping()
- Then, save the model with the minimum validation loss using ModelCheckpoint()
- Reducing the learning rate once learning stagnates by ReduceLROnPlateau()
- Fit the model for training
- Then, saving the best-trained model
EPOCHS = 15 STEP_SIZE_TRAIN = train_set.n // train_set.batch_size STEP_SIZE_TEST = test_set.n // test_set.batch_size def model_fit(): leaf_model = create_model() loss = tf.keras.losses.CategoricalCrossentropy( from_logits = False, label_smoothing=0.0001, name='categorical_crossentropy' ) leaf_model.compile( optimizer = Adam(learning_rate = 1e-3), loss = loss, #'categorical_crossentropy' metrics = ['categorical_accuracy'] ) es = EarlyStopping( monitor='val_loss', mode='min', patience=3, restore_best_weights=True, verbose=1, ) checkpoint_cb = ModelCheckpoint( "Cassava_best_model.h5", save_best_only=True, monitor='val_loss', mode='min', ) reduce_lr = ReduceLROnPlateau( monitor='val_loss', factor=0.2, patience=2, min_lr=1e-6, mode='min', verbose=1, ) history = leaf_model.fit( train_set, validation_data=test_set, epochs=EPOCHS, batch_size=BATCH_SIZE, steps_per_epoch=STEP_SIZE_TRAIN, validation_steps=STEP_SIZE_TEST, callbacks=[es, checkpoint_cb, reduce_lr], ) leaf_model.save('Cassava_model'+'.h5') return history
TRAINING
Calling of the training function mentioned in the previous section. Then viewing the categorical acc of the training and validation.
try: final_model = keras.models.load_model('Cassava_model.h5') except Exception as e: with tf.device('/GPU:0'): results = model_fit() print('Train Categorical Accuracy: ', max(results.history['categorical_accuracy'])) print('Test Categorical Accuracy: ', max(results.history['val_categorical_accuracy']))
Epoch 1/15 1355/1355 [==============================] - 1598s 1s/step - loss: 0.8348 - categorical_accuracy: 0.7084 - val_loss: 0.5775 - val_categorical_accuracy: 0.8047 Epoch 2/15 1355/1355 [==============================] - 1498s 1s/step - loss: 0.5562 - categorical_accuracy: 0.8133 - val_loss: 0.4261 - val_categorical_accuracy: 0.8469 Epoch 3/15 1355/1355 [==============================] - 1497s 1s/step - loss: 0.5079 - categorical_accuracy: 0.8307 - val_loss: 0.4411 - val_categorical_accuracy: 0.8629 Epoch 4/15 1355/1355 [==============================] - 1498s 1s/step - loss: 0.4983 - categorical_accuracy: 0.8354 - val_loss: 0.4200 - val_categorical_accuracy: 0.8601 Epoch 5/15 1355/1355 [==============================] - 1503s 1s/step - loss: 0.4610 - categorical_accuracy: 0.8451 - val_loss: 0.4732 - val_categorical_accuracy: 0.8319 Epoch 6/15 1355/1355 [==============================] - 1490s 1s/step - loss: 0.4485 - categorical_accuracy: 0.8507 - val_loss: 0.4100 - val_categorical_accuracy: 0.8667 Epoch 7/15 1355/1355 [==============================] - 1481s 1s/step - loss: 0.4374 - categorical_accuracy: 0.8547 - val_loss: 0.4215 - val_categorical_accuracy: 0.8648 Epoch 8/15 1355/1355 [==============================] - 1489s 1s/step - loss: 0.4246 - categorical_accuracy: 0.8580 - val_loss: 0.4096 - val_categorical_accuracy: 0.8667 Epoch 9/15 1355/1355 [==============================] - 1492s 1s/step - loss: 0.4274 - categorical_accuracy: 0.8562 - val_loss: 0.4047 - val_categorical_accuracy: 0.8714 Epoch 10/15 1355/1355 [==============================] - 1495s 1s/step - loss: 0.4098 - categorical_accuracy: 0.8630 - val_loss: 0.3682 - val_categorical_accuracy: 0.8704 Epoch 11/15 1355/1355 [==============================] - 1494s 1s/step - loss: 0.4129 - categorical_accuracy: 0.8605 - val_loss: 0.4354 - val_categorical_accuracy: 0.8629 Epoch 12/15 1355/1355 [==============================] - 1483s 1s/step - loss: 0.3872 - categorical_accuracy: 0.8714 - val_loss: 0.4067 - val_categorical_accuracy: 0.8563 Epoch 00012: ReduceLROnPlateau reducing learning rate to 0.00020000000949949026. Epoch 13/15 1355/1355 [==============================] - 1489s 1s/step - loss: 0.3499 - categorical_accuracy: 0.8795 - val_loss: 0.3547 - val_categorical_accuracy: 0.8845 Epoch 14/15 1355/1355 [==============================] - 1487s 1s/step - loss: 0.3162 - categorical_accuracy: 0.8911 - val_loss: 0.3440 - val_categorical_accuracy: 0.8836 Epoch 15/15 1355/1355 [==============================] - 1489s 1s/step - loss: 0.3211 - categorical_accuracy: 0.8879 - val_loss: 0.3483 - val_categorical_accuracy: 0.8808 Train Categorical Accuracy: 0.8915911912918091 Test Categorical Accuracy: 0.8845070600509644
PLOT RESULTS
The plotting of the results of the trained model for visualization and analysis sake. Thus, the acc with respect to the epochs and the Loss with respect to the epochs is studied in this plot.
def trai_test_plot(acc, test_acc, loss, test_loss): fig, (ax1, ax2) = plt.subplots(1,2, figsize= (15,10)) fig.suptitle("Model's metrics comparisson", fontsize=20) ax1.plot(range(1, len(acc) + 1), acc) ax1.plot(range(1, len(test_acc) + 1), test_acc) ax1.set_title('History of Accuracy', fontsize=15) ax1.set_xlabel('Epochs', fontsize=15) ax1.set_ylabel('Accuracy', fontsize=15) ax1.legend(['training', 'validation']) ax2.plot(range(1, len(loss) + 1), loss) ax2.plot(range(1, len(test_loss) + 1), test_loss) ax2.set_title('History of Loss', fontsize=15) ax2.set_xlabel('Epochs', fontsize=15) ax2.set_ylabel('Loss', fontsize=15) ax2.legend(['training', 'validation']) plt.show() trai_test_plot( results.history['categorical_accuracy'], results.history['val_categorical_accuracy'], results.history['loss'], results.history['val_loss'] )
FINAL THOUGHTS
In this article, Cassava disease detection using EfficientNet is successfully done. And we have built GAP, dropout, flatten, and activation functions over the EfficientNet (TL). EfficientNet is a very different network that concentrates on uniform scaling of the feature maps with uniform depth, width, and resolution using a simple but efficient coefficient. Then customized loss functions for improving the quality of the network. Then, training of the network and visualization using plotting of the model. A training acc of 89.15% and test acc of 88.45 for the trained model is obtained with only 15 epochs of training.
The source code for the cassava disease detection can be found and downloaded from here.
The dataset for the net can be found in kaggle.
To learn different DL methods, you can refer to my blogs:
[1]. Image classification of Bird species (Used a VGG16 net)
[2]. Salt Identification (Used UNets fused with a Residual net)
[3]. Pneumonia X-Ray Detection (Without Transfer Learning)
References:
[1]. EfficientNet: Rethinking Model Scaling for CNN
To learn from my other ML blogs, click here.
Thank you. Hope this article was helpful!
Leave a Reply