7.2 KiB
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
/Datasetsdirectory due to its large size. tbh the dataset doesn't really matter in this case, you just need to ensure dataset directory containstrainandvalidsub-directories. Refer the above dataset to understand the required directory structure.
Pre-requisities
- Install packages using
pip:pip install tensorflow keras numpy opencv-python matplotlib seaborn scikit-learn(tensorflowrequires Python 3.9 - 3.12) - Download and unzip the dataset in the same directory as the Jupyter notebook.
- 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
- Import Libraries
- Load Dataset
- Exploratory Data Analysis (EDA)
- Split into Training and Testing Sets
- Build the CNN Model
- Compile the Model
- Train the Model
- Evaluate the Model on Test Data
- Plot Training vs Validation Accuracy
- Plot Training vs Validation Loss
- 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))