Files

7.2 KiB

Practical-3a (Convolutional Neural Network - Plant Diseases)

Problem Statement: Convolutional Neural Network (CNN): Use any dataset of plant disease and design a plant disease detection system using CNN.

Note

Download dataset directly from source. Haven't added it to the /Datasets directory due to its large size. tbh the dataset doesn't really matter in this case, you just need to ensure dataset directory contains train and valid sub-directories. Refer the above dataset to understand the required directory structure.


Pre-requisities

  1. Install packages using pip: pip install tensorflow keras numpy opencv-python matplotlib seaborn scikit-learn (tensorflow requires Python 3.9 - 3.12)
  2. Download and unzip the dataset in the same directory as the Jupyter notebook.
  3. Ensure your unzipped dataset has the required directory structure:
New Plant Diseases Dataset(Augmented)/
├── train
│   ├── Apple___Apple_scab
│   ├── Apple___Black_rot
│   ├── Apple___Cedar_apple_rust
├── valid
│   ├── Apple___Apple_scab
│   ├── Apple___Black_rot
│   ├── Apple___Cedar_apple_rust

Steps

  1. Import Libraries
  2. Load Dataset
  3. Exploratory Data Analysis (EDA)
  4. Split into Training and Testing Sets
  5. Build the CNN Model
  6. Compile the Model
  7. Train the Model
  8. Evaluate the Model on Test Data
  9. Plot Training vs Validation Accuracy
  10. Plot Training vs Validation Loss
  11. Confusion Matrix and Classification Report

Code

1. Import Libraries:

import os
import numpy as np
import cv2
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.utils import to_categorical

2. Load Dataset:

data = []
labels = []

# Path to dataset folder containing one subfolder per disease class
path = './New Plant Diseases Dataset(Augmented)/train/'
categories = sorted(os.listdir(path))  # sort for consistent label ordering

# Map each category name to a numeric index
label_dict = {category: idx for idx, category in enumerate(categories)}
print("Classes found:", len(categories))

max_per_class = 200  # cap images per class to avoid RAM overflow on large datasets

for category in categories:
    folder = os.path.join(path, category)
    count = 0
    for img_name in os.listdir(folder):
        if count >= max_per_class:
            break
        img_path = os.path.join(folder, img_name)
        img_array = cv2.imread(img_path)
        if img_array is not None:                          # skip unreadable files
            img_array = cv2.resize(img_array, (64, 64))   # resize to fixed 64x64 pixels
            data.append(img_array)
            labels.append(label_dict[category])
            count += 1

data = np.array(data) / 255.0   # normalize pixel values from [0,255] to [0,1]
labels = np.array(labels)
print("Dataset shape:", data.shape)
print("Labels shape:", labels.shape)

3. Exploratory Data Analysis (EDA):

print("Total images:", len(data))
print("Image shape:", data[0].shape)
print("Number of classes:", len(categories))

# Class distribution bar chart
class_counts = {cat: int((labels == idx).sum()) for cat, idx in label_dict.items()}
plt.figure(figsize=(14, 5))
plt.bar(class_counts.keys(), class_counts.values())
plt.xticks(rotation=90)
plt.title("Number of Images per Disease Class")
plt.xlabel("Class")
plt.ylabel("Count")
plt.tight_layout()
plt.show()

# Sample images from first 5 classes
plt.figure(figsize=(15, 3))
for i, category in enumerate(categories[:5]):
    idx = np.where(labels == label_dict[category])[0][0]  # index of first image in class
    plt.subplot(1, 5, i + 1)
    plt.imshow(cv2.cvtColor((data[idx] * 255).astype(np.uint8), cv2.COLOR_BGR2RGB))
    plt.title(category[:15], fontsize=8)
    plt.axis('off')
plt.suptitle("Sample Images per Class")
plt.show()

4. Split into Training and Testing Sets:

X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=42)

num_classes = len(categories)
# One-hot encode labels: e.g. class 2 of 5 → [0, 0, 1, 0, 0]
y_train = to_categorical(y_train, num_classes)
y_test  = to_categorical(y_test,  num_classes)
print("Train samples:", X_train.shape[0])
print("Test samples: ", X_test.shape[0])

5. Build the CNN Model:

model = Sequential()

model.add(Input(shape=(64, 64, 3)))               # input: 64x64 RGB image
model.add(Conv2D(32, (3, 3), activation='relu'))  # 32 filters, detect basic features
model.add(MaxPooling2D(2, 2))                      # downsample by 2x

model.add(Conv2D(64, (3, 3), activation='relu'))  # 64 filters, detect complex features
model.add(MaxPooling2D(2, 2))

model.add(Flatten())                              # convert 2D feature maps to 1D vector

model.add(Dense(128, activation='relu'))          # fully connected layer
model.add(Dropout(0.5))                           # randomly drop 50% neurons to reduce overfitting

model.add(Dense(num_classes, activation='softmax'))  # output: probability for each class

model.summary()

6. Compile the Model:

# categorical_crossentropy: standard loss for multi-class classification with one-hot labels
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

7. Train the Model:

history = model.fit(X_train, y_train, epochs=10, batch_size=32, validation_split=0.2)

8. Evaluate the Model on Test Data:

loss, accuracy = model.evaluate(X_test, y_test)
print(f"Test Loss: {loss:.4f}")
print(f"Test Accuracy: {accuracy*100:.2f}%")

9. Plot Training vs Validation Accuracy:

plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('CNN Model Accuracy Over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.grid(True)
plt.show()

10. Plot Training vs Validation Loss:

plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('CNN Model Loss Over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()

11. Confusion Matrix and Classification Report:

y_pred = np.argmax(model.predict(X_test), axis=1)  # predicted class index
y_true = np.argmax(y_test, axis=1)                  # actual class index (from one-hot)

cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(14, 12))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=categories, yticklabels=categories)
plt.title('Confusion Matrix')
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()

print("\nClassification Report:\n")
print(classification_report(y_true, y_pred, target_names=categories))

Miscellaneous