Skip to content

Computer Vision Applications

Deep Learning for Plant Disease Classification using Images

Author: Alina Cherkas

  • This notebook is used to develop a Convolutional Neural Network for plant disease classification using Plant Leaf Diseases Dataset from [1]. The dataset contains over 55k images of 14 different plants affected by various diseases, resulting in 39 classes in total.
  • Using a simple 6-layer CNN, I achieve 85% accuracy and 82% macro-average F1-score on the test set for this multi-class classification problem. I find that the model does an excellent job at identifying the plant type and distinguishing between healthy and infected leaves, but it sometimes confuses various types of diseases for a given plant, e.g. confusing Corn___Cercospora_leaf_spot Gray_leaf_spot with Corn___Northern_Leaf_Blight.
  • This notebook also uses popular explainable AI methods like LIME [2], Integrated Gradients [3], Grad-CAM++ [4], and to interpret model predictions.
  • Tested with Python 3.9.6

References

[1] G. G. and A. P. J., ‘Identification of plant leaf diseases using a nine-layer deep convolutional neural network’, Computers & Electrical Engineering, vol. 76, pp. 323–338, Jun. 2019, doi: 10.1016/j.compeleceng.2019.04.011.

[2] M. T. Ribeiro, S. Singh, and C. Guestrin, ‘“Why Should I Trust You?”: Explaining the Predictions of Any Classifier’. arXiv, Aug. 09, 2016. doi: 10.48550/arXiv.1602.04938.

[3] M. Sundararajan, A. Taly, and Q. Yan, ‘Axiomatic Attribution for Deep Networks’. arXiv, Jun. 12, 2017. doi: 10.48550/arXiv.1703.01365.

[4] A. Chattopadhyay, A. Sarkar, P. Howlader, and V. N. Balasubramanian, ‘Grad-CAM++: Improved Visual Explanations for Deep Convolutional Networks’, in 2018 IEEE Winter Conference on Applications of Computer Vision (WACV), Mar. 2018, pp. 839–847. doi: 10.1109/WACV.2018.00097.

Libraries

# install specific package versions
# !pip install -q -r requirements.txt
# !pip show tensorflow
# standard packages
import os

# wrangling
import numpy as np
import pandas as pd

# visualisation
import plotly.express as px
import plotly.io as pio

# deep learning and explainability
import tensorflow as tf
from tensorflow.keras.utils import image_dataset_from_directory
from tensorflow.keras import layers
from tensorflow.keras.optimizers.legacy import Adam
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.utils.class_weight import compute_class_weight
from omnixai.data.image import Image
from omnixai.explainers.vision import VisionExplainer

# local utils.py
import utils

# plotting settings
pio.templates.default = 'plotly_white'
pio.renderers.default = 'plotly_mimetype'

1. Data Preparation

The code in this section downloads the data and creates dataset for training, validation and testing using TensorFlow image_dataset_from_directory utility.

The next code cell downloads the original dataset (without augmentations) from Mendeley.

%%bash
export dataset=Plant_leaf_diseases_dataset_without_augmentation.zip
curl https://data.mendeley.com/public-files/datasets/tywbtsjrjv/files/d5652a28-c1d8-4b76-97f3-72fb80f94efc/file_downloaded -L --output $dataset
mkdir data
unzip -q $dataset -d data
rm $dataset
# target image size is smaller to downsize the images
image_size = (64, 64)
# create training and validation data loades using 80/20 split
dataset_train, dataset_valid = image_dataset_from_directory(
    directory=str(os.path.join('data', 'Plant_leave_diseases_dataset_without_augmentation')),
    labels='inferred',
    label_mode='int',
    # class_names=LABELS,
    color_mode='rgb',
    batch_size=32,
    image_size=image_size,
    shuffle=True,
    seed=42,
    validation_split=.2,
    subset='both',
    interpolation='bilinear',
    follow_links=False,
    crop_to_aspect_ratio=False,
)

I split the validation set (which contains 20% of the data) equally into test and validation sets, each containing approximately 10% of the original dataset.

# split the validation set into validation and test sets
num_batches = tf.data.experimental.cardinality(dataset_valid).numpy()
dataset_test = dataset_valid.take(num_batches // 2)
dataset_valid = dataset_valid.skip(num_batches // 2)
# sanity check for tensor dimensions
images, labels = next(iter(dataset_train))
images.shape, labels.shape

Examples in the dataset are images of leaves, both healthy and infected, taken indoors. There are also some images of background.

# display a sample of images from the first batch
title = 'Figure 1. Actual Labels for Images in the Train Set'
labels = [dataset_train.class_names[label] for label in labels.numpy()]
images, labels = utils.sample_images_and_labels(images=images.numpy(), labels=labels, k=12)
fig = utils.display_images_with_labels(images=images, labels=labels, per_row=4, vertical_spacing=0.06)
fig.update_layout(title=title, height=900, width=1200)
fig.show()

The next cells are used to explore the distribution of labels, which, as is common in many real-world cases, are unevenely distributed. There are many more examples for some classes than for others. Admittedly, there are several plants which only appear as either healthy (Soybean___healthy) or infected (Orange___Haunglongbing_(Citrus_greening)).