In recent times, the field of agriculture has been in urgent need of modernizing, since the amount of manual work people need to put in to check if plants are growing correctly is still highly extensive. Despite several advances in agricultural technology, people working in the agricultural industry still need to have the ability to sort and recognize different plants and weeds, which takes a lot of time and effort in the long term. The potential is ripe for this trillion-dollar industry to be greatly impacted by technological innovations that cut down on the requirement for manual labor, and this is where Artificial Intelligence can actually benefit the workers in this field, as the time and energy required to identify plant seedlings will be greatly shortened by the use of AI and Deep Learning. The ability to do so far more efficiently and even more effectively than experienced manual labor, could lead to better crop yields, the freeing up of human inolvement for higher-order agricultural decision making, and in the long term will result in more sustainable environmental practices in agriculture as well.
The aim of this project is to Build a Convolutional Neural Netowrk to classify plant seedlings into their respective categories.
The Aarhus University Signal Processing group, in collaboration with the University of Southern Denmark, has recently released a dataset containing images of unique plants belonging to 12 different species.
The dataset can be download from Olympus.
The data file names are:
Due to the large volume of data, the images were converted to the images.npy file and the labels are also put into Labels.csv, so that you can work on the data/project seamlessly without having to worry about the high data volume.
The goal of the project is to create a classifier capable of determining a plant's species from an image.
List of Species
# Import necessary libraries
import numpy as np # NumPy for numerical operations
import pandas as pd # Pandas for data manipulation
import matplotlib.pyplot as plt # Matplotlib for data visualization
import seaborn as sns # Seaborn for enhanced data visualization
import cv2 # OpenCV for image processing
import random # Random for generating random numbers
import tensorflow as tf # TensorFlow for deep learning
# Google Colab specific import for mounting Google Drive
from google.colab import drive
# TensorFlow and Keras modules for image data preprocessing
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# Scikit-Learn for data splitting and label binarization
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
# Keras backend for specifying the data format
from tensorflow.keras import backend
# Keras modules for building the neural network model
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization
# Optimizers for training the neural network
from tensorflow.keras.optimizers import Adam, SGD
# Scikit-Learn for evaluating model performance
from sklearn import metrics
# Keras callback for reducing learning rate on plateau
from keras.callbacks import ReduceLROnPlateau
# Pre-trained VGG16 model from Keras applications
from keras.applications.vgg16 import VGG16
# Ignore warnings for cleaner output
import warnings
warnings.filterwarnings('ignore')
drive.mount('/content/drive')
Mounted at /content/drive
images = np.load('/content/drive/MyDrive/6-ComputerVision/images.npy')
labels = pd.read_csv('/content/drive/MyDrive/6-ComputerVision/Labels.csv')
print(images.shape)
print(labels.shape)
(4750, 128, 128, 3) (4750, 1)
def plot_images(images, labels):
# Define the number of unique classes
num_classes = 10
# Get the unique categories (class labels)
categories = np.unique(labels)
# Create a dictionary to map labels to their corresponding names
keys = dict(labels['Label'])
# Define the number of rows and columns for the subplot grid
rows = 3
cols = 4
# Create a figure for plotting
fig = plt.figure(figsize=(10, 8))
# Loop through the grid of subplots
for i in range(cols):
for j in range(rows):
# Generate a random index to select an image and label
random_index = np.random.randint(0, len(labels))
# Create a subplot at the current position in the grid
ax = fig.add_subplot(rows, cols, i * rows + j + 1)
# Display the image from the 'images' array
ax.imshow(images[random_index, :])
# Set the title of the subplot to the corresponding label name
ax.set_title(keys[random_index])
# Show the plotted subplots
plt.show()
plot_images(images,labels)
sns.countplot(x=labels['Label'])
plt.xticks(rotation='vertical')
(array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]), [Text(0, 0, 'Small-flowered Cranesbill'), Text(1, 0, 'Fat Hen'), Text(2, 0, 'Shepherds Purse'), Text(3, 0, 'Common wheat'), Text(4, 0, 'Common Chickweed'), Text(5, 0, 'Charlock'), Text(6, 0, 'Cleavers'), Text(7, 0, 'Scentless Mayweed'), Text(8, 0, 'Sugar beet'), Text(9, 0, 'Maize'), Text(10, 0, 'Black-grass'), Text(11, 0, 'Loose Silky-bent')])
The data is unbalanced
for i in range(len(images)):
images[i] = cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB)
plot_images(images,labels)
Using original size
X_temp, X_test, y_temp, y_test = train_test_split(np.array(images),labels , test_size=0.1, random_state=42,stratify=labels)
X_train, X_val, y_train, y_val = train_test_split(X_temp,y_temp , test_size=0.1, random_state=42,stratify=y_temp)
print(X_train.shape,y_train.shape)
print(X_val.shape,y_val.shape)
print(X_test.shape,y_test.shape)
(3847, 128, 128, 3) (3847, 1) (428, 128, 128, 3) (428, 1) (475, 128, 128, 3) (475, 1)
# Create an instance of the LabelBinarizer class
enc = LabelBinarizer()
# Fit the LabelBinarizer to the training labels and transform them into binary form
y_train_encoded = enc.fit_transform(y_train)
# Transform the validation labels into binary form using the fitted LabelBinarizer
y_val_encoded = enc.transform(y_val)
# Transform the test labels into binary form using the same fitted LabelBinarizer
y_test_encoded = enc.transform(y_test)
print(y_train_encoded.shape)
print(y_val_encoded.shape)
print(y_test_encoded.shape)
(3847, 12) (428, 12) (475, 12)
# Perform data normalization by converting the training data to float32 and scaling it by 255.0
X_train_normalized = X_train.astype('float32') / 255.0
X_val_normalized = X_val.astype('float32')/255.0
X_test_normalized = X_test.astype('float32')/255.0
# Clear the TensorFlow session to release resources from previous runs
backend.clear_session()
# Set random seeds for reproducibility
np.random.seed(42) # Seed for NumPy random number generator
random.seed(42) # Seed for Python's built-in random module
tf.random.set_seed(42) # Seed for TensorFlow's random number generator
# Intializing a sequential model
model1 = Sequential()
# Adding first conv layer with 64 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension of images
model1.add(Conv2D(64, (3, 3), activation='relu', padding="same", input_shape=(128, 128, 3)))
# Adding max pooling to reduce the size of output of first conv layer
model1.add(MaxPooling2D((2, 2), padding = 'same'))
model1.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding = 'same'))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model1.add(Flatten())
# Adding a fully connected dense layer with 16 neurons
model1.add(Dense(16, activation='relu'))
model1.add(Dropout(0.3))
# Adding the output layer with 12 neurons and activation functions as softmax since this is a multi-class classification problem
model1.add(Dense(12, activation='softmax'))
# Adding Adam optimizer
opt=Adam()
# Compile model1
model1.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model1
model1.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_2 (Conv2D) (None, 128, 128, 64) 1792
max_pooling2d_2 (MaxPoolin (None, 64, 64, 64) 0
g2D)
conv2d_3 (Conv2D) (None, 64, 64, 32) 18464
max_pooling2d_3 (MaxPoolin (None, 32, 32, 32) 0
g2D)
flatten_1 (Flatten) (None, 32768) 0
dense_2 (Dense) (None, 16) 524304
dropout_1 (Dropout) (None, 16) 0
dense_3 (Dense) (None, 12) 204
=================================================================
Total params: 544764 (2.08 MB)
Trainable params: 544764 (2.08 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
# Train the model (model1) on the normalized training data and encoded labels
history_1 = model1.fit(
X_train_normalized, # Normalized training data
y_train_encoded, # Encoded training labels
epochs=50, # Number of training epochs
validation_data=(X_val_normalized, y_val_encoded), # Validation data
batch_size=32, # Batch size for training
verbose=2 # Verbosity level for training output
)
Epoch 1/50 121/121 - 16s - loss: 2.4274 - accuracy: 0.1508 - val_loss: 2.3271 - val_accuracy: 0.2126 - 16s/epoch - 135ms/step Epoch 2/50 121/121 - 2s - loss: 2.2814 - accuracy: 0.1804 - val_loss: 2.1556 - val_accuracy: 0.2430 - 2s/epoch - 17ms/step Epoch 3/50 121/121 - 2s - loss: 2.1708 - accuracy: 0.2212 - val_loss: 2.1322 - val_accuracy: 0.2850 - 2s/epoch - 19ms/step Epoch 4/50 121/121 - 2s - loss: 2.0989 - accuracy: 0.2399 - val_loss: 1.9977 - val_accuracy: 0.3528 - 2s/epoch - 17ms/step Epoch 5/50 121/121 - 2s - loss: 2.0075 - accuracy: 0.2719 - val_loss: 1.9149 - val_accuracy: 0.3481 - 2s/epoch - 17ms/step Epoch 6/50 121/121 - 2s - loss: 1.9376 - accuracy: 0.2966 - val_loss: 1.7253 - val_accuracy: 0.4486 - 2s/epoch - 17ms/step Epoch 7/50 121/121 - 2s - loss: 1.8492 - accuracy: 0.3197 - val_loss: 1.6004 - val_accuracy: 0.4463 - 2s/epoch - 17ms/step Epoch 8/50 121/121 - 2s - loss: 1.7568 - accuracy: 0.3618 - val_loss: 1.5472 - val_accuracy: 0.4766 - 2s/epoch - 17ms/step Epoch 9/50 121/121 - 2s - loss: 1.7192 - accuracy: 0.3715 - val_loss: 1.4602 - val_accuracy: 0.5117 - 2s/epoch - 18ms/step Epoch 10/50 121/121 - 2s - loss: 1.6814 - accuracy: 0.3865 - val_loss: 1.5373 - val_accuracy: 0.5070 - 2s/epoch - 18ms/step Epoch 11/50 121/121 - 2s - loss: 1.6599 - accuracy: 0.3995 - val_loss: 1.4627 - val_accuracy: 0.5327 - 2s/epoch - 17ms/step Epoch 12/50 121/121 - 2s - loss: 1.6472 - accuracy: 0.3995 - val_loss: 1.4317 - val_accuracy: 0.5514 - 2s/epoch - 17ms/step Epoch 13/50 121/121 - 2s - loss: 1.6108 - accuracy: 0.4094 - val_loss: 1.4844 - val_accuracy: 0.5210 - 2s/epoch - 17ms/step Epoch 14/50 121/121 - 2s - loss: 1.5799 - accuracy: 0.4289 - val_loss: 1.4495 - val_accuracy: 0.5117 - 2s/epoch - 17ms/step Epoch 15/50 121/121 - 2s - loss: 1.5525 - accuracy: 0.4182 - val_loss: 1.4290 - val_accuracy: 0.5397 - 2s/epoch - 17ms/step Epoch 16/50 121/121 - 2s - loss: 1.5402 - accuracy: 0.4193 - val_loss: 1.4209 - val_accuracy: 0.5444 - 2s/epoch - 19ms/step Epoch 17/50 121/121 - 2s - loss: 1.4938 - accuracy: 0.4398 - val_loss: 1.4355 - val_accuracy: 0.5093 - 2s/epoch - 17ms/step Epoch 18/50 121/121 - 2s - loss: 1.4716 - accuracy: 0.4484 - val_loss: 1.3679 - val_accuracy: 0.5631 - 2s/epoch - 16ms/step Epoch 19/50 121/121 - 2s - loss: 1.4543 - accuracy: 0.4544 - val_loss: 1.4210 - val_accuracy: 0.5561 - 2s/epoch - 17ms/step Epoch 20/50 121/121 - 2s - loss: 1.4052 - accuracy: 0.4682 - val_loss: 1.3766 - val_accuracy: 0.5561 - 2s/epoch - 16ms/step Epoch 21/50 121/121 - 2s - loss: 1.3982 - accuracy: 0.4752 - val_loss: 1.3758 - val_accuracy: 0.5607 - 2s/epoch - 17ms/step Epoch 22/50 121/121 - 2s - loss: 1.3745 - accuracy: 0.4835 - val_loss: 1.3792 - val_accuracy: 0.5561 - 2s/epoch - 18ms/step Epoch 23/50 121/121 - 2s - loss: 1.3492 - accuracy: 0.4991 - val_loss: 1.4329 - val_accuracy: 0.5304 - 2s/epoch - 18ms/step Epoch 24/50 121/121 - 2s - loss: 1.2864 - accuracy: 0.5212 - val_loss: 1.3322 - val_accuracy: 0.5748 - 2s/epoch - 17ms/step Epoch 25/50 121/121 - 2s - loss: 1.2789 - accuracy: 0.5274 - val_loss: 1.3433 - val_accuracy: 0.5818 - 2s/epoch - 17ms/step Epoch 26/50 121/121 - 2s - loss: 1.2490 - accuracy: 0.5279 - val_loss: 1.2841 - val_accuracy: 0.5888 - 2s/epoch - 17ms/step Epoch 27/50 121/121 - 2s - loss: 1.2248 - accuracy: 0.5342 - val_loss: 1.4532 - val_accuracy: 0.5584 - 2s/epoch - 17ms/step Epoch 28/50 121/121 - 2s - loss: 1.2338 - accuracy: 0.5389 - val_loss: 1.3571 - val_accuracy: 0.5818 - 2s/epoch - 17ms/step Epoch 29/50 121/121 - 2s - loss: 1.1742 - accuracy: 0.5565 - val_loss: 1.3772 - val_accuracy: 0.5958 - 2s/epoch - 18ms/step Epoch 30/50 121/121 - 2s - loss: 1.1552 - accuracy: 0.5719 - val_loss: 1.3836 - val_accuracy: 0.5958 - 2s/epoch - 18ms/step Epoch 31/50 121/121 - 2s - loss: 1.1467 - accuracy: 0.5859 - val_loss: 1.3866 - val_accuracy: 0.6005 - 2s/epoch - 17ms/step Epoch 32/50 121/121 - 2s - loss: 1.1329 - accuracy: 0.5807 - val_loss: 1.3193 - val_accuracy: 0.6075 - 2s/epoch - 17ms/step Epoch 33/50 121/121 - 2s - loss: 1.0973 - accuracy: 0.5940 - val_loss: 1.4119 - val_accuracy: 0.6121 - 2s/epoch - 17ms/step Epoch 34/50 121/121 - 2s - loss: 1.0972 - accuracy: 0.5898 - val_loss: 1.3717 - val_accuracy: 0.5981 - 2s/epoch - 17ms/step Epoch 35/50 121/121 - 2s - loss: 1.1272 - accuracy: 0.5838 - val_loss: 1.4113 - val_accuracy: 0.6121 - 2s/epoch - 18ms/step Epoch 36/50 121/121 - 2s - loss: 1.0516 - accuracy: 0.6093 - val_loss: 1.4827 - val_accuracy: 0.5818 - 2s/epoch - 18ms/step Epoch 37/50 121/121 - 2s - loss: 1.0062 - accuracy: 0.6252 - val_loss: 1.5935 - val_accuracy: 0.6005 - 2s/epoch - 19ms/step Epoch 38/50 121/121 - 2s - loss: 1.0131 - accuracy: 0.6265 - val_loss: 1.4703 - val_accuracy: 0.6051 - 2s/epoch - 17ms/step Epoch 39/50 121/121 - 2s - loss: 0.9791 - accuracy: 0.6296 - val_loss: 1.6125 - val_accuracy: 0.5864 - 2s/epoch - 17ms/step Epoch 40/50 121/121 - 2s - loss: 0.9576 - accuracy: 0.6483 - val_loss: 1.4929 - val_accuracy: 0.6192 - 2s/epoch - 17ms/step Epoch 41/50 121/121 - 2s - loss: 0.9698 - accuracy: 0.6454 - val_loss: 1.5237 - val_accuracy: 0.6028 - 2s/epoch - 18ms/step Epoch 42/50 121/121 - 2s - loss: 0.9484 - accuracy: 0.6551 - val_loss: 1.5303 - val_accuracy: 0.6028 - 2s/epoch - 18ms/step Epoch 43/50 121/121 - 2s - loss: 0.9471 - accuracy: 0.6473 - val_loss: 1.7645 - val_accuracy: 0.5678 - 2s/epoch - 20ms/step Epoch 44/50 121/121 - 2s - loss: 0.9554 - accuracy: 0.6486 - val_loss: 1.5442 - val_accuracy: 0.5958 - 2s/epoch - 19ms/step Epoch 45/50 121/121 - 2s - loss: 0.9414 - accuracy: 0.6532 - val_loss: 1.6463 - val_accuracy: 0.5935 - 2s/epoch - 17ms/step Epoch 46/50 121/121 - 2s - loss: 0.9262 - accuracy: 0.6535 - val_loss: 1.5798 - val_accuracy: 0.6098 - 2s/epoch - 17ms/step Epoch 47/50 121/121 - 2s - loss: 0.9149 - accuracy: 0.6574 - val_loss: 1.7731 - val_accuracy: 0.6168 - 2s/epoch - 17ms/step Epoch 48/50 121/121 - 2s - loss: 0.9387 - accuracy: 0.6548 - val_loss: 1.6198 - val_accuracy: 0.6051 - 2s/epoch - 18ms/step Epoch 49/50 121/121 - 2s - loss: 0.9099 - accuracy: 0.6751 - val_loss: 1.7563 - val_accuracy: 0.6005 - 2s/epoch - 17ms/step Epoch 50/50 121/121 - 2s - loss: 0.9175 - accuracy: 0.6603 - val_loss: 1.7941 - val_accuracy: 0.5724 - 2s/epoch - 17ms/step
# Plot the training and validation accuracy over epochs
plt.plot(history_1.history['accuracy']) # Training accuracy values
plt.plot(history_1.history['val_accuracy']) # Validation accuracy values
plt.title('Model Accuracy') # Set the plot title
plt.ylabel('Accuracy') # Label for the y-axis
plt.xlabel('Epoch') # Label for the x-axis
plt.legend(['Train', 'Validation'], loc='upper left') # Add a legend
plt.show() # Display the plot
# Evaluate the model1 on the normalized test data and encoded test labels
accuracy1 = model1.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 0s - loss: 1.7532 - accuracy: 0.5937 - 317ms/epoch - 21ms/step
y_pred1=model1.predict(X_test_normalized)
15/15 [==============================] - 0s 7ms/step
# Calculate the predicted class labels (argmax) from the model's output probabilities
y_pred1_arg = np.argmax(y_pred1, axis=1)
# Calculate the true class labels (argmax) from the encoded test labels
y_test_arg = np.argmax(y_test_encoded, axis=1)
# Calculate the confusion matrix using TensorFlow's confusion_matrix function
confusion_matrix = tf.math.confusion_matrix(y_test_arg, y_pred1_arg)
# Create a figure and axis for displaying the confusion matrix as a heatmap
f, ax = plt.subplots(figsize=(12, 12))
# Create a heatmap of the confusion matrix with annotations, formatting, and labels
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Set labels and title for the confusion matrix plot
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True labels')
ax.set_title('Confusion Matrix')
# Set the tick labels for the x and y axes to display class names
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=90)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=0)
# Display the confusion matrix plot
plt.show()
# Generate a classification report to evaluate model performance
cr1 = metrics.classification_report(y_test_arg, y_pred1_arg)
# Print the classification report
print(cr1)
precision recall f1-score support
0 0.31 0.15 0.21 26
1 0.69 0.92 0.79 39
2 0.52 0.45 0.48 29
3 0.65 0.72 0.68 61
4 0.20 0.09 0.13 22
5 0.74 0.58 0.65 48
6 0.55 0.77 0.64 65
7 0.43 0.55 0.48 22
8 0.69 0.48 0.57 52
9 0.36 0.35 0.36 23
10 0.72 0.86 0.78 50
11 0.53 0.45 0.49 38
accuracy 0.59 475
macro avg 0.53 0.53 0.52 475
weighted avg 0.58 0.59 0.58 475
Here are some insights based on the classification report for Model 1:
Overall Performance: Model 1 achieves an accuracy of 0.59, indicating that it correctly classifies 59% of the samples in the test dataset. While this is better than random guessing, there is room for improvement.
Class Imbalance: There are notable variations in precision, recall, and F1-score across different classes. Some classes, like Class 1 and Class 10, have high precision and recall, suggesting good classification performance. However, others, like Class 0 and Class 4, have lower precision and recall, indicating challenges in distinguishing these classes.
Low Recall: Classes 0, 4, and 9 have particularly low recall values (0.15, 0.09, and 0.35, respectively). This means that the model struggles to correctly identify instances of these classes, and there may be a need to explore ways to improve recall for these classes.
Class 6: Class 6 stands out with relatively high precision (0.55) and high recall (0.77), resulting in a decent F1-score of 0.64. The model performs relatively well in correctly classifying this category.
Weighted Average F1-Score: The weighted average F1-score is 0.58, indicating the overall harmonic mean of precision and recall across all classes. It reflects the model's balanced performance across classes but suggests room for improvement.
Macro Average Metrics: The macro average (average across classes) precision, recall, and F1-score are around 0.53, indicating that, on average, the model's performance is moderate across all classes.
Opportunity for Improvement: Model 1 has shown potential for improvement, particularly in classes with low recall. Strategies such as data augmentation, fine-tuning hyperparameters, or using more complex models may help enhance overall performance.
Class 10: Class 10 has a relatively high F1-score of 0.78, suggesting that the model is particularly effective at classifying this category. Understanding the features that contribute to this performance may provide insights for improving other classes.
# Clear the TensorFlow session to release resources from previous runs
backend.clear_session()
# Set random seeds for reproducibility
np.random.seed(42) # Seed for NumPy random number generator
random.seed(42) # Seed for Python's built-in random module
tf.random.set_seed(42) # Seed for TensorFlow's random number generator
# Intializing a sequential model
model2 = Sequential()
# Adding first conv layer with 128 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension of images
model2.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(128, 128, 3)))
# Adding max pooling to reduce the size of output of first conv layer
model2.add(MaxPooling2D((2, 2), padding = 'same'))
model2.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model2.add(MaxPooling2D((2, 2), padding = 'same'))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model2.add(Flatten())
# Adding a fully connected dense layer with 100 neurons
model2.add(Dense(100, activation='relu'))
model2.add(Dropout(0.3))
# Adding the output layer with 12 neurons and activation functions as softmax since this is a multi-class classification problem
model2.add(Dense(12, activation='softmax'))
# Using Adam Optimizer
opt=Adam()
# Compile model2
model2.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model2
model2.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 128, 128, 128) 3584
max_pooling2d (MaxPooling2 (None, 64, 64, 128) 0
D)
conv2d_1 (Conv2D) (None, 64, 64, 64) 73792
max_pooling2d_1 (MaxPoolin (None, 32, 32, 64) 0
g2D)
flatten (Flatten) (None, 65536) 0
dense (Dense) (None, 100) 6553700
dropout (Dropout) (None, 100) 0
dense_1 (Dense) (None, 12) 1212
=================================================================
Total params: 6632288 (25.30 MB)
Trainable params: 6632288 (25.30 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
# Train the model (model2) on the normalized training data and encoded labels
history_2 = model2.fit(
X_train_normalized, # Normalized training data
y_train_encoded, # Encoded training labels
epochs=50, # Number of training epochs
validation_data=(X_val_normalized, y_val_encoded), # Validation data
batch_size=32, # Batch size for training
verbose=2 # Verbosity level for training output
)
Epoch 1/50 121/121 - 7s - loss: 2.4091 - accuracy: 0.1612 - val_loss: 2.1446 - val_accuracy: 0.3294 - 7s/epoch - 60ms/step Epoch 2/50 121/121 - 4s - loss: 1.8369 - accuracy: 0.3977 - val_loss: 1.6292 - val_accuracy: 0.4579 - 4s/epoch - 32ms/step Epoch 3/50 121/121 - 4s - loss: 1.4049 - accuracy: 0.5199 - val_loss: 1.2059 - val_accuracy: 0.5841 - 4s/epoch - 34ms/step Epoch 4/50 121/121 - 4s - loss: 1.1613 - accuracy: 0.6020 - val_loss: 1.1277 - val_accuracy: 0.6379 - 4s/epoch - 35ms/step Epoch 5/50 121/121 - 4s - loss: 1.0037 - accuracy: 0.6491 - val_loss: 1.0513 - val_accuracy: 0.6565 - 4s/epoch - 34ms/step Epoch 6/50 121/121 - 4s - loss: 0.9165 - accuracy: 0.6714 - val_loss: 1.0462 - val_accuracy: 0.6636 - 4s/epoch - 31ms/step Epoch 7/50 121/121 - 4s - loss: 0.7617 - accuracy: 0.7315 - val_loss: 1.0630 - val_accuracy: 0.6472 - 4s/epoch - 31ms/step Epoch 8/50 121/121 - 4s - loss: 0.6677 - accuracy: 0.7694 - val_loss: 1.1051 - val_accuracy: 0.6706 - 4s/epoch - 32ms/step Epoch 9/50 121/121 - 4s - loss: 0.5584 - accuracy: 0.8089 - val_loss: 1.1125 - val_accuracy: 0.6542 - 4s/epoch - 32ms/step Epoch 10/50 121/121 - 4s - loss: 0.5176 - accuracy: 0.8232 - val_loss: 1.1514 - val_accuracy: 0.6822 - 4s/epoch - 31ms/step Epoch 11/50 121/121 - 4s - loss: 0.3979 - accuracy: 0.8695 - val_loss: 1.1779 - val_accuracy: 0.6706 - 4s/epoch - 31ms/step Epoch 12/50 121/121 - 4s - loss: 0.3415 - accuracy: 0.8796 - val_loss: 1.2011 - val_accuracy: 0.6706 - 4s/epoch - 32ms/step Epoch 13/50 121/121 - 4s - loss: 0.2958 - accuracy: 0.8955 - val_loss: 1.3397 - val_accuracy: 0.6706 - 4s/epoch - 31ms/step Epoch 14/50 121/121 - 4s - loss: 0.2503 - accuracy: 0.9140 - val_loss: 1.4257 - val_accuracy: 0.6963 - 4s/epoch - 31ms/step Epoch 15/50 121/121 - 4s - loss: 0.2378 - accuracy: 0.9212 - val_loss: 1.4574 - val_accuracy: 0.6799 - 4s/epoch - 33ms/step Epoch 16/50 121/121 - 4s - loss: 0.1894 - accuracy: 0.9358 - val_loss: 1.5125 - val_accuracy: 0.6706 - 4s/epoch - 32ms/step Epoch 17/50 121/121 - 4s - loss: 0.1874 - accuracy: 0.9340 - val_loss: 1.6013 - val_accuracy: 0.6729 - 4s/epoch - 31ms/step Epoch 18/50 121/121 - 4s - loss: 0.1607 - accuracy: 0.9462 - val_loss: 1.6804 - val_accuracy: 0.6939 - 4s/epoch - 31ms/step Epoch 19/50 121/121 - 4s - loss: 0.1710 - accuracy: 0.9452 - val_loss: 1.6851 - val_accuracy: 0.6659 - 4s/epoch - 34ms/step Epoch 20/50 121/121 - 4s - loss: 0.1595 - accuracy: 0.9465 - val_loss: 1.8106 - val_accuracy: 0.6472 - 4s/epoch - 32ms/step Epoch 21/50 121/121 - 4s - loss: 0.1383 - accuracy: 0.9496 - val_loss: 1.8633 - val_accuracy: 0.6565 - 4s/epoch - 33ms/step Epoch 22/50 121/121 - 4s - loss: 0.1357 - accuracy: 0.9581 - val_loss: 1.8609 - val_accuracy: 0.6449 - 4s/epoch - 31ms/step Epoch 23/50 121/121 - 4s - loss: 0.1296 - accuracy: 0.9548 - val_loss: 1.8822 - val_accuracy: 0.6449 - 4s/epoch - 32ms/step Epoch 24/50 121/121 - 4s - loss: 0.1196 - accuracy: 0.9605 - val_loss: 2.2240 - val_accuracy: 0.6519 - 4s/epoch - 34ms/step Epoch 25/50 121/121 - 4s - loss: 0.1086 - accuracy: 0.9636 - val_loss: 1.9999 - val_accuracy: 0.6425 - 4s/epoch - 35ms/step Epoch 26/50 121/121 - 4s - loss: 0.1192 - accuracy: 0.9610 - val_loss: 2.0435 - val_accuracy: 0.6449 - 4s/epoch - 34ms/step Epoch 27/50 121/121 - 4s - loss: 0.1354 - accuracy: 0.9532 - val_loss: 2.1546 - val_accuracy: 0.6402 - 4s/epoch - 31ms/step Epoch 28/50 121/121 - 4s - loss: 0.1031 - accuracy: 0.9659 - val_loss: 2.1499 - val_accuracy: 0.6425 - 4s/epoch - 31ms/step Epoch 29/50 121/121 - 4s - loss: 0.0984 - accuracy: 0.9670 - val_loss: 2.4585 - val_accuracy: 0.6495 - 4s/epoch - 32ms/step Epoch 30/50 121/121 - 4s - loss: 0.1037 - accuracy: 0.9683 - val_loss: 2.2342 - val_accuracy: 0.6589 - 4s/epoch - 32ms/step Epoch 31/50 121/121 - 4s - loss: 0.0830 - accuracy: 0.9709 - val_loss: 2.1878 - val_accuracy: 0.6682 - 4s/epoch - 32ms/step Epoch 32/50 121/121 - 4s - loss: 0.0871 - accuracy: 0.9719 - val_loss: 2.3209 - val_accuracy: 0.6495 - 4s/epoch - 31ms/step Epoch 33/50 121/121 - 4s - loss: 0.0936 - accuracy: 0.9657 - val_loss: 2.4916 - val_accuracy: 0.6472 - 4s/epoch - 34ms/step Epoch 34/50 121/121 - 4s - loss: 0.1150 - accuracy: 0.9631 - val_loss: 2.0547 - val_accuracy: 0.6612 - 4s/epoch - 31ms/step Epoch 35/50 121/121 - 4s - loss: 0.0826 - accuracy: 0.9685 - val_loss: 2.6856 - val_accuracy: 0.6589 - 4s/epoch - 31ms/step Epoch 36/50 121/121 - 4s - loss: 0.0894 - accuracy: 0.9724 - val_loss: 2.3628 - val_accuracy: 0.6612 - 4s/epoch - 32ms/step Epoch 37/50 121/121 - 4s - loss: 0.0723 - accuracy: 0.9756 - val_loss: 2.3740 - val_accuracy: 0.6519 - 4s/epoch - 32ms/step Epoch 38/50 121/121 - 4s - loss: 0.0842 - accuracy: 0.9735 - val_loss: 2.6775 - val_accuracy: 0.6565 - 4s/epoch - 31ms/step Epoch 39/50 121/121 - 4s - loss: 0.0819 - accuracy: 0.9750 - val_loss: 2.3979 - val_accuracy: 0.6519 - 4s/epoch - 31ms/step Epoch 40/50 121/121 - 4s - loss: 0.0511 - accuracy: 0.9831 - val_loss: 2.8271 - val_accuracy: 0.6519 - 4s/epoch - 34ms/step Epoch 41/50 121/121 - 4s - loss: 0.0689 - accuracy: 0.9800 - val_loss: 2.3517 - val_accuracy: 0.6776 - 4s/epoch - 31ms/step Epoch 42/50 121/121 - 4s - loss: 0.0691 - accuracy: 0.9784 - val_loss: 2.6714 - val_accuracy: 0.6565 - 4s/epoch - 33ms/step Epoch 43/50 121/121 - 4s - loss: 0.0847 - accuracy: 0.9678 - val_loss: 2.9020 - val_accuracy: 0.6659 - 4s/epoch - 34ms/step Epoch 44/50 121/121 - 4s - loss: 0.0670 - accuracy: 0.9776 - val_loss: 2.6398 - val_accuracy: 0.6565 - 4s/epoch - 32ms/step Epoch 45/50 121/121 - 4s - loss: 0.0971 - accuracy: 0.9672 - val_loss: 2.7063 - val_accuracy: 0.6449 - 4s/epoch - 31ms/step Epoch 46/50 121/121 - 4s - loss: 0.0959 - accuracy: 0.9670 - val_loss: 2.8654 - val_accuracy: 0.6565 - 4s/epoch - 31ms/step Epoch 47/50 121/121 - 4s - loss: 0.0641 - accuracy: 0.9782 - val_loss: 2.6422 - val_accuracy: 0.6776 - 4s/epoch - 33ms/step Epoch 48/50 121/121 - 4s - loss: 0.0640 - accuracy: 0.9763 - val_loss: 2.5796 - val_accuracy: 0.6472 - 4s/epoch - 32ms/step Epoch 49/50 121/121 - 4s - loss: 0.0951 - accuracy: 0.9709 - val_loss: 2.5174 - val_accuracy: 0.6776 - 4s/epoch - 32ms/step Epoch 50/50 121/121 - 4s - loss: 0.0577 - accuracy: 0.9802 - val_loss: 2.7181 - val_accuracy: 0.6262 - 4s/epoch - 32ms/step
# Plot the training and validation accuracy over epochs
plt.plot(history_1.history['accuracy']) # Training accuracy values
plt.plot(history_1.history['val_accuracy']) # Validation accuracy values
plt.title('Model Accuracy') # Set the plot title
plt.ylabel('Accuracy') # Label for the y-axis
plt.xlabel('Epoch') # Label for the x-axis
plt.legend(['Train', 'Validation'], loc='upper left') # Add a legend
plt.show()
# Evaluate the model1 on the normalized test data and encoded test labels
accuracy2 = model2.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 0s - loss: 2.7007 - accuracy: 0.6400 - 487ms/epoch - 32ms/step
y_pred2=model2.predict(X_test_normalized)
15/15 [==============================] - 0s 12ms/step
(475, 12)
# Calculate the predicted class labels (argmax) from the second model's output probabilities
y_pred2_arg = np.argmax(y_pred2, axis=1)
# Calculate the confusion matrix using TensorFlow's confusion_matrix function
confusion_matrix = tf.math.confusion_matrix(y_test_arg, y_pred2_arg)
# Create a figure and axis for displaying the confusion matrix as a heatmap
f, ax = plt.subplots(figsize=(12, 12))
# Create a heatmap of the confusion matrix with annotations, formatting, and labels
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Set labels and title for the confusion matrix plot
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True labels')
ax.set_title('Confusion Matrix')
# Set the tick labels for the x and y axes to display class names
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=90)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=0)
# Display the confusion matrix plot
plt.show()
# Generate a classification report to evaluate model performance
cr2=metrics.classification_report(y_test_arg,y_pred2_arg)
print(cr2)
precision recall f1-score support
0 0.26 0.35 0.30 26
1 0.78 0.79 0.78 39
2 0.62 0.72 0.67 29
3 0.86 0.79 0.82 61
4 0.00 0.00 0.00 22
5 0.62 0.60 0.61 48
6 0.60 0.60 0.60 65
7 0.52 0.68 0.59 22
8 0.71 0.67 0.69 52
9 0.60 0.52 0.56 23
10 0.89 0.82 0.85 50
11 0.52 0.63 0.57 38
accuracy 0.64 475
macro avg 0.58 0.60 0.59 475
weighted avg 0.64 0.64 0.64 475
Here are some insights based on the classification report for Model 2:
Overall Performance: Model 2 demonstrates improved overall performance compared to Model 1, with an accuracy of 0.64. This indicates that it correctly classifies 64% of the samples in the test dataset.
Class Imbalance Handling: Model 2 appears to have addressed some class imbalance issues seen in Model 1. Several classes now have higher recall values, indicating improved performance in correctly identifying instances of those classes.
Class 4 (Zero Recall): Class 4 still exhibits a challenge with zero recall, indicating that the model is unable to correctly identify any instances of this class. This is a significant limitation that needs to be addressed.
High Precision: Classes 3 and 10 stand out with relatively high precision values (0.86 and 0.89, respectively). This suggests that the model is effective in correctly classifying instances for these categories.
Balanced F1-Scores: The F1-scores for most classes show a good balance between precision and recall, with weighted average and macro average F1-scores of 0.64 and 0.59, respectively.
Class 6: Class 6 continues to have moderate performance with a precision of 0.60 and a recall of 0.60, resulting in an F1-score of 0.60.
Class 10 (High Performance): Class 10 maintains its high F1-score of 0.85, indicating that the model excels in classifying this category. Understanding the features contributing to this performance can provide insights for improving other classes.
Class 7: Class 7 has improved with a higher recall (0.68) compared to Model 1, suggesting better performance in identifying instances of this class.
Class 1: Class 1 has relatively high precision (0.78) and recall (0.79), indicating good performance in correctly classifying this category.
Class 8: Class 8 also shows improvement with a precision of 0.71 and a recall of 0.67.
Class 11: Class 11 has a balanced precision-recall trade-off with a precision of 0.52 and a recall of 0.63.
Reducing the Learning Rate:
Hint: Use ReduceLRonPlateau() function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
# Configure a learning rate reduction callback
learning_rate_reduction = ReduceLROnPlateau(
monitor='val_accuracy', # Metric to monitor for reduction
patience=3, # Number of epochs with no improvement before reducing LR
verbose=1, # Verbosity level (1: moderate, 0: minimal)
factor=0.5, # Reduction factor (new LR = old LR * factor)
min_lr=0.00001 # Minimum learning rate
)
Remember, data augmentation should not be used in the validation/test data set.
# Configure an image data augmentation generator
train_datagen = ImageDataGenerator(
rotation_range=15, # Random rotation in the range of -15 to 15 degrees
zoom_range=0.2, # Random zoom within 20%
horizontal_flip=True, # Random horizontal flipping
fill_mode='nearest' # Fill mode for handling newly created pixels
)
# Intializing a sequential model
model3 = Sequential()
# Adding first conv layer with 128 filters and kernel size 3x3 , padding 'same' provides the output size same as the input size
# Input_shape denotes input image dimension of images
model3.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(128, 128, 3)))
# Adding max pooling to reduce the size of output of first conv layer
model3.add(MaxPooling2D((2, 2), padding = 'same'))
model3.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model3.add(MaxPooling2D((2, 2), padding = 'same'))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model3.add(Flatten())
# Adding a fully connected dense layer with 100 neurons
model3.add(Dense(100, activation='relu'))
model3.add(Dropout(0.3))
# Adding the output layer with 12 neurons and activation functions as softmax since this is a multi-class classification problem
model3.add(Dense(12, activation='softmax'))
# Using Adam Optimizer
opt=Adam()
# Compile model3
model3.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model3
model3.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 128, 128, 128) 3584
max_pooling2d_4 (MaxPoolin (None, 64, 64, 128) 0
g2D)
conv2d_5 (Conv2D) (None, 64, 64, 64) 73792
max_pooling2d_5 (MaxPoolin (None, 32, 32, 64) 0
g2D)
flatten_3 (Flatten) (None, 65536) 0
dense_6 (Dense) (None, 100) 6553700
dropout_3 (Dropout) (None, 100) 0
dense_7 (Dense) (None, 12) 1212
=================================================================
Total params: 6632288 (25.30 MB)
Trainable params: 6632288 (25.30 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
# Train the model (model3) using data augmentation and a data generator
history_3 = model3.fit(
train_datagen.flow( # Data generator for augmented training data
X_train_normalized, # Normalized training data
y_train_encoded, # Encoded training labels
batch_size=32, # Batch size for training
shuffle=False # Do not shuffle the data within the generator
),
epochs=50, # Number of training epochs
validation_data=(X_val_normalized, y_val_encoded), # Validation data
verbose=2, # Verbosity level for training output
callbacks=[learning_rate_reduction] # Learning rate reduction callback
)
Epoch 1/50 121/121 - 19s - loss: 2.4326 - accuracy: 0.1482 - val_loss: 2.2579 - val_accuracy: 0.2500 - lr: 0.0010 - 19s/epoch - 157ms/step Epoch 2/50 121/121 - 19s - loss: 1.9270 - accuracy: 0.3356 - val_loss: 1.6674 - val_accuracy: 0.4696 - lr: 0.0010 - 19s/epoch - 156ms/step Epoch 3/50 121/121 - 17s - loss: 1.5866 - accuracy: 0.4476 - val_loss: 1.2732 - val_accuracy: 0.5888 - lr: 0.0010 - 17s/epoch - 142ms/step Epoch 4/50 121/121 - 17s - loss: 1.4109 - accuracy: 0.5045 - val_loss: 1.1473 - val_accuracy: 0.6145 - lr: 0.0010 - 17s/epoch - 142ms/step Epoch 5/50 121/121 - 17s - loss: 1.3374 - accuracy: 0.5404 - val_loss: 1.0762 - val_accuracy: 0.6472 - lr: 0.0010 - 17s/epoch - 141ms/step Epoch 6/50 121/121 - 19s - loss: 1.2580 - accuracy: 0.5604 - val_loss: 1.0631 - val_accuracy: 0.6332 - lr: 0.0010 - 19s/epoch - 159ms/step Epoch 7/50 121/121 - 17s - loss: 1.2001 - accuracy: 0.5755 - val_loss: 0.9927 - val_accuracy: 0.6659 - lr: 0.0010 - 17s/epoch - 142ms/step Epoch 8/50 121/121 - 17s - loss: 1.1257 - accuracy: 0.6090 - val_loss: 1.0385 - val_accuracy: 0.6449 - lr: 0.0010 - 17s/epoch - 141ms/step Epoch 9/50 121/121 - 18s - loss: 1.1373 - accuracy: 0.5934 - val_loss: 1.0461 - val_accuracy: 0.6495 - lr: 0.0010 - 18s/epoch - 151ms/step Epoch 10/50 Epoch 10: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257. 121/121 - 18s - loss: 1.1169 - accuracy: 0.6085 - val_loss: 0.9623 - val_accuracy: 0.6636 - lr: 0.0010 - 18s/epoch - 146ms/step Epoch 11/50 121/121 - 19s - loss: 1.0231 - accuracy: 0.6436 - val_loss: 0.8895 - val_accuracy: 0.7103 - lr: 5.0000e-04 - 19s/epoch - 158ms/step Epoch 12/50 121/121 - 17s - loss: 0.9654 - accuracy: 0.6548 - val_loss: 0.8633 - val_accuracy: 0.7103 - lr: 5.0000e-04 - 17s/epoch - 143ms/step Epoch 13/50 121/121 - 17s - loss: 0.9744 - accuracy: 0.6642 - val_loss: 0.8484 - val_accuracy: 0.7033 - lr: 5.0000e-04 - 17s/epoch - 140ms/step Epoch 14/50 121/121 - 17s - loss: 0.9421 - accuracy: 0.6673 - val_loss: 0.9173 - val_accuracy: 0.7407 - lr: 5.0000e-04 - 17s/epoch - 142ms/step Epoch 15/50 121/121 - 17s - loss: 0.9237 - accuracy: 0.6785 - val_loss: 0.7868 - val_accuracy: 0.7360 - lr: 5.0000e-04 - 17s/epoch - 142ms/step Epoch 16/50 121/121 - 17s - loss: 0.8990 - accuracy: 0.6875 - val_loss: 0.9149 - val_accuracy: 0.7103 - lr: 5.0000e-04 - 17s/epoch - 141ms/step Epoch 17/50 121/121 - 19s - loss: 0.8916 - accuracy: 0.6966 - val_loss: 0.7840 - val_accuracy: 0.7453 - lr: 5.0000e-04 - 19s/epoch - 156ms/step Epoch 18/50 121/121 - 17s - loss: 0.8743 - accuracy: 0.7003 - val_loss: 0.7876 - val_accuracy: 0.7523 - lr: 5.0000e-04 - 17s/epoch - 142ms/step Epoch 19/50 121/121 - 17s - loss: 0.8630 - accuracy: 0.7099 - val_loss: 0.7501 - val_accuracy: 0.7617 - lr: 5.0000e-04 - 17s/epoch - 142ms/step Epoch 20/50 121/121 - 17s - loss: 0.8232 - accuracy: 0.7091 - val_loss: 0.7185 - val_accuracy: 0.7664 - lr: 5.0000e-04 - 17s/epoch - 140ms/step Epoch 21/50 121/121 - 17s - loss: 0.8262 - accuracy: 0.7216 - val_loss: 0.7174 - val_accuracy: 0.7897 - lr: 5.0000e-04 - 17s/epoch - 140ms/step Epoch 22/50 121/121 - 19s - loss: 0.8122 - accuracy: 0.7172 - val_loss: 0.7149 - val_accuracy: 0.7850 - lr: 5.0000e-04 - 19s/epoch - 158ms/step Epoch 23/50 121/121 - 18s - loss: 0.7885 - accuracy: 0.7349 - val_loss: 0.7016 - val_accuracy: 0.7897 - lr: 5.0000e-04 - 18s/epoch - 153ms/step Epoch 24/50 Epoch 24: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628. 121/121 - 17s - loss: 0.7672 - accuracy: 0.7317 - val_loss: 0.6871 - val_accuracy: 0.7687 - lr: 5.0000e-04 - 17s/epoch - 141ms/step Epoch 25/50 121/121 - 18s - loss: 0.7552 - accuracy: 0.7398 - val_loss: 0.6563 - val_accuracy: 0.8014 - lr: 2.5000e-04 - 18s/epoch - 145ms/step Epoch 26/50 121/121 - 17s - loss: 0.7331 - accuracy: 0.7520 - val_loss: 0.6708 - val_accuracy: 0.7967 - lr: 2.5000e-04 - 17s/epoch - 142ms/step Epoch 27/50 121/121 - 17s - loss: 0.7101 - accuracy: 0.7541 - val_loss: 0.6563 - val_accuracy: 0.8061 - lr: 2.5000e-04 - 17s/epoch - 142ms/step Epoch 28/50 121/121 - 17s - loss: 0.7209 - accuracy: 0.7494 - val_loss: 0.6701 - val_accuracy: 0.7850 - lr: 2.5000e-04 - 17s/epoch - 143ms/step Epoch 29/50 121/121 - 17s - loss: 0.7038 - accuracy: 0.7531 - val_loss: 0.6503 - val_accuracy: 0.7944 - lr: 2.5000e-04 - 17s/epoch - 142ms/step Epoch 30/50 Epoch 30: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814. 121/121 - 17s - loss: 0.6847 - accuracy: 0.7609 - val_loss: 0.6512 - val_accuracy: 0.7897 - lr: 2.5000e-04 - 17s/epoch - 140ms/step Epoch 31/50 121/121 - 19s - loss: 0.6535 - accuracy: 0.7824 - val_loss: 0.6386 - val_accuracy: 0.8014 - lr: 1.2500e-04 - 19s/epoch - 156ms/step Epoch 32/50 121/121 - 17s - loss: 0.6765 - accuracy: 0.7681 - val_loss: 0.6149 - val_accuracy: 0.8178 - lr: 1.2500e-04 - 17s/epoch - 143ms/step Epoch 33/50 121/121 - 19s - loss: 0.6690 - accuracy: 0.7751 - val_loss: 0.6223 - val_accuracy: 0.8014 - lr: 1.2500e-04 - 19s/epoch - 159ms/step Epoch 34/50 121/121 - 19s - loss: 0.6758 - accuracy: 0.7694 - val_loss: 0.6176 - val_accuracy: 0.8294 - lr: 1.2500e-04 - 19s/epoch - 156ms/step Epoch 35/50 121/121 - 17s - loss: 0.6347 - accuracy: 0.7827 - val_loss: 0.6257 - val_accuracy: 0.8248 - lr: 1.2500e-04 - 17s/epoch - 142ms/step Epoch 36/50 121/121 - 17s - loss: 0.6473 - accuracy: 0.7796 - val_loss: 0.6193 - val_accuracy: 0.8107 - lr: 1.2500e-04 - 17s/epoch - 140ms/step Epoch 37/50 Epoch 37: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05. 121/121 - 17s - loss: 0.6452 - accuracy: 0.7772 - val_loss: 0.6073 - val_accuracy: 0.8224 - lr: 1.2500e-04 - 17s/epoch - 140ms/step Epoch 38/50 121/121 - 18s - loss: 0.6344 - accuracy: 0.7803 - val_loss: 0.6109 - val_accuracy: 0.8178 - lr: 6.2500e-05 - 18s/epoch - 148ms/step Epoch 39/50 121/121 - 17s - loss: 0.6351 - accuracy: 0.7806 - val_loss: 0.6069 - val_accuracy: 0.8248 - lr: 6.2500e-05 - 17s/epoch - 140ms/step Epoch 40/50 Epoch 40: ReduceLROnPlateau reducing learning rate to 3.125000148429535e-05. 121/121 - 17s - loss: 0.6510 - accuracy: 0.7668 - val_loss: 0.6209 - val_accuracy: 0.8154 - lr: 6.2500e-05 - 17s/epoch - 140ms/step Epoch 41/50 121/121 - 17s - loss: 0.6042 - accuracy: 0.7954 - val_loss: 0.6044 - val_accuracy: 0.8154 - lr: 3.1250e-05 - 17s/epoch - 142ms/step Epoch 42/50 121/121 - 19s - loss: 0.6302 - accuracy: 0.7855 - val_loss: 0.5998 - val_accuracy: 0.8154 - lr: 3.1250e-05 - 19s/epoch - 157ms/step Epoch 43/50 Epoch 43: ReduceLROnPlateau reducing learning rate to 1.5625000742147677e-05. 121/121 - 17s - loss: 0.6338 - accuracy: 0.7845 - val_loss: 0.6000 - val_accuracy: 0.8178 - lr: 3.1250e-05 - 17s/epoch - 139ms/step Epoch 44/50 121/121 - 19s - loss: 0.6193 - accuracy: 0.7863 - val_loss: 0.5939 - val_accuracy: 0.8248 - lr: 1.5625e-05 - 19s/epoch - 157ms/step Epoch 45/50 121/121 - 17s - loss: 0.6128 - accuracy: 0.7915 - val_loss: 0.6011 - val_accuracy: 0.8107 - lr: 1.5625e-05 - 17s/epoch - 142ms/step Epoch 46/50 Epoch 46: ReduceLROnPlateau reducing learning rate to 1e-05. 121/121 - 17s - loss: 0.6301 - accuracy: 0.7855 - val_loss: 0.5950 - val_accuracy: 0.8201 - lr: 1.5625e-05 - 17s/epoch - 142ms/step Epoch 47/50 121/121 - 17s - loss: 0.6276 - accuracy: 0.7811 - val_loss: 0.5938 - val_accuracy: 0.8248 - lr: 1.0000e-05 - 17s/epoch - 140ms/step Epoch 48/50 121/121 - 17s - loss: 0.6194 - accuracy: 0.7835 - val_loss: 0.5941 - val_accuracy: 0.8248 - lr: 1.0000e-05 - 17s/epoch - 139ms/step Epoch 49/50 121/121 - 18s - loss: 0.6252 - accuracy: 0.7835 - val_loss: 0.5969 - val_accuracy: 0.8318 - lr: 1.0000e-05 - 18s/epoch - 146ms/step Epoch 50/50 121/121 - 18s - loss: 0.6237 - accuracy: 0.7775 - val_loss: 0.5993 - val_accuracy: 0.8271 - lr: 1.0000e-05 - 18s/epoch - 152ms/step
plt.plot(history_3.history['accuracy'])
plt.plot(history_3.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
# Evaluate the model3 on the normalized test data and encoded test labels
accuracy3 = model3.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 0s - loss: 0.6493 - accuracy: 0.7937 - 204ms/epoch - 14ms/step
# Generate predictions using model3 on the normalized test data
y_pred3 = model3.predict(X_test_normalized)
15/15 [==============================] - 0s 12ms/step
y_pred3_arg=np.argmax(y_pred3,axis=1)
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred3_arg)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
ax.set_xlabel('Predicted labels');ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_),rotation=90)
ax.yaxis.set_ticklabels(list(enc.classes_),rotation=0)
plt.show()
# Generate a classification report to evaluate the performance of model3
cr3 = metrics.classification_report(y_test_arg, y_pred3_arg)
# Print the classification report
print(cr3)
precision recall f1-score support
0 0.27 0.15 0.20 26
1 0.90 0.95 0.92 39
2 0.88 0.79 0.84 29
3 0.83 0.90 0.87 61
4 0.88 0.68 0.77 22
5 0.88 0.92 0.90 48
6 0.69 0.82 0.75 65
7 0.72 0.82 0.77 22
8 0.73 0.90 0.81 52
9 0.89 0.35 0.50 23
10 0.81 0.86 0.83 50
11 0.94 0.79 0.86 38
accuracy 0.79 475
macro avg 0.79 0.74 0.75 475
weighted avg 0.79 0.79 0.78 475
Here are some insights based on the classification report for Model 3:
Overall Outstanding Performance: Model 3 exhibits impressive overall performance, with an accuracy of 0.79. This indicates that it correctly classifies 79% of the samples in the test dataset.
High Precision and Recall: Most classes show high precision and recall values, resulting in excellent F1-scores. This indicates that the model effectively classifies a wide range of classes with a good balance between precision and recall.
Class 1 and Class 5: Classes 1 and 5 stand out with exceptionally high precision, recall, and F1-scores, indicating that the model excels in classifying these categories.
Class 9 (Low Recall): Class 9 continues to have relatively low recall (0.35), suggesting that the model struggles to correctly identify instances of this class. Investigating potential reasons and considering strategies for improvement is advisable.
Weighted Average F1-Score: The weighted average F1-score is 0.78, which is significantly higher than previous models. This demonstrates that Model 3 achieves a strong balance between precision and recall across all classes.
Macro Average Metrics: The macro average (average across classes) precision, recall, and F1-score are around 0.79, indicating consistently strong performance across classes.
Class 11 (High F1-Score): Class 11 has a notably high F1-score of 0.86, indicating excellent performance in classifying this category.
Class 4: Class 4 has improved with higher precision (0.88) and recall (0.68) compared to previous models.
Class 2 and Class 7: Classes 2 and 7 have high precision (0.88 and 0.72) and recall (0.79 and 0.82), showcasing strong classification performance.
Class 10: Class 10 continues to perform well with an F1-score of 0.83.
Class 8: Class 8 has a high F1-score of 0.81, indicating good performance.
Class 6: Class 6 maintains a balanced F1-score of 0.75.
Class 0: Class 0 has lower precision (0.27) and recall (0.15), indicating room for improvement in identifying instances of this class.
Class 3: Class 3 has high precision (0.83) and recall (0.90), showcasing strong performance.
# Load the VGG16 model with pre-trained weights from ImageNet
# - 'weights' specifies to use pre-trained weights
# - 'include_top' specifies not to include the fully connected layers at the top
# - 'input_shape' specifies the input shape of the images (128x128 pixels with 3 color channels)
vgg_model = VGG16(weights='imagenet', include_top=False, input_shape=(128, 128, 3))
# Display a summary of the loaded VGG16 model architecture
vgg_model.summary()
Model: "vgg16"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 128, 128, 3)] 0
block1_conv1 (Conv2D) (None, 128, 128, 64) 1792
block1_conv2 (Conv2D) (None, 128, 128, 64) 36928
block1_pool (MaxPooling2D) (None, 64, 64, 64) 0
block2_conv1 (Conv2D) (None, 64, 64, 128) 73856
block2_conv2 (Conv2D) (None, 64, 64, 128) 147584
block2_pool (MaxPooling2D) (None, 32, 32, 128) 0
block3_conv1 (Conv2D) (None, 32, 32, 256) 295168
block3_conv2 (Conv2D) (None, 32, 32, 256) 590080
block3_conv3 (Conv2D) (None, 32, 32, 256) 590080
block3_pool (MaxPooling2D) (None, 16, 16, 256) 0
block4_conv1 (Conv2D) (None, 16, 16, 512) 1180160
block4_conv2 (Conv2D) (None, 16, 16, 512) 2359808
block4_conv3 (Conv2D) (None, 16, 16, 512) 2359808
block4_pool (MaxPooling2D) (None, 8, 8, 512) 0
block5_conv1 (Conv2D) (None, 8, 8, 512) 2359808
block5_conv2 (Conv2D) (None, 8, 8, 512) 2359808
block5_conv3 (Conv2D) (None, 8, 8, 512) 2359808
block5_pool (MaxPooling2D) (None, 4, 4, 512) 0
=================================================================
Total params: 14714688 (56.13 MB)
Trainable params: 14714688 (56.13 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
for layer in vgg_model.layers:
layer.trainable = False
# Create a Sequential model (model4)
model4 = Sequential()
# Adding the convolutional part of the VGG16 model (vgg_model) from above
model4.add(vgg_model)
# Flattening the output of the VGG16 model because it is from a convolutional layer
model4.add(Flatten())
# Adding a dense hidden layer with 100 units and ReLU activation
model4.add(Dense(100, activation='relu'))
# Adding dropout to prevent overfitting (30% dropout rate)
model4.add(Dropout(0.3))
# Adding the output layer with 12 neurons and softmax activation for multi-class classification
model4.add(Dense(12, activation='softmax'))
# Using the Adam optimizer for training
opt = Adam()
# Compile the model (model4) with categorical cross-entropy loss and accuracy metric
model4.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Display a summary of the model4 architecture
model4.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
vgg16 (Functional) (None, 4, 4, 512) 14714688
flatten_4 (Flatten) (None, 8192) 0
dense_8 (Dense) (None, 100) 819300
dropout_4 (Dropout) (None, 100) 0
dense_9 (Dense) (None, 12) 1212
=================================================================
Total params: 15535200 (59.26 MB)
Trainable params: 820512 (3.13 MB)
Non-trainable params: 14714688 (56.13 MB)
_________________________________________________________________
# Train the model (model4) using data augmentation and a data generator
history_4 = model4.fit(
train_datagen.flow( # Data generator for augmented training data
X_train_normalized, # Normalized training data
y_train_encoded, # Encoded training labels
batch_size=32, # Batch size for training
shuffle=False # Do not shuffle the data within the generator
),
epochs=50, # Number of training epochs
validation_data=(X_val_normalized, y_val_encoded), # Validation data
verbose=2, # Verbosity level for training output
callbacks=[learning_rate_reduction] # Learning rate reduction callback
)
Epoch 1/50 121/121 - 19s - loss: 2.1400 - accuracy: 0.2542 - val_loss: 1.7945 - val_accuracy: 0.4182 - lr: 0.0010 - 19s/epoch - 158ms/step Epoch 2/50 121/121 - 20s - loss: 1.7595 - accuracy: 0.3912 - val_loss: 1.5877 - val_accuracy: 0.4603 - lr: 0.0010 - 20s/epoch - 169ms/step Epoch 3/50 121/121 - 17s - loss: 1.5555 - accuracy: 0.4466 - val_loss: 1.3824 - val_accuracy: 0.5467 - lr: 0.0010 - 17s/epoch - 144ms/step Epoch 4/50 121/121 - 17s - loss: 1.4341 - accuracy: 0.4965 - val_loss: 1.2985 - val_accuracy: 0.5444 - lr: 0.0010 - 17s/epoch - 144ms/step Epoch 5/50 121/121 - 19s - loss: 1.3638 - accuracy: 0.5183 - val_loss: 1.1809 - val_accuracy: 0.6005 - lr: 0.0010 - 19s/epoch - 158ms/step Epoch 6/50 121/121 - 18s - loss: 1.2956 - accuracy: 0.5441 - val_loss: 1.1316 - val_accuracy: 0.6215 - lr: 0.0010 - 18s/epoch - 147ms/step Epoch 7/50 121/121 - 17s - loss: 1.2465 - accuracy: 0.5620 - val_loss: 1.0711 - val_accuracy: 0.6238 - lr: 0.0010 - 17s/epoch - 145ms/step Epoch 8/50 121/121 - 18s - loss: 1.2196 - accuracy: 0.5659 - val_loss: 1.1279 - val_accuracy: 0.6449 - lr: 0.0010 - 18s/epoch - 148ms/step Epoch 9/50 121/121 - 18s - loss: 1.1657 - accuracy: 0.5857 - val_loss: 1.0610 - val_accuracy: 0.6636 - lr: 0.0010 - 18s/epoch - 145ms/step Epoch 10/50 121/121 - 18s - loss: 1.1375 - accuracy: 0.6002 - val_loss: 1.0019 - val_accuracy: 0.6916 - lr: 0.0010 - 18s/epoch - 146ms/step Epoch 11/50 121/121 - 18s - loss: 1.1154 - accuracy: 0.6033 - val_loss: 1.0260 - val_accuracy: 0.6565 - lr: 0.0010 - 18s/epoch - 148ms/step Epoch 12/50 121/121 - 17s - loss: 1.1005 - accuracy: 0.6103 - val_loss: 1.0327 - val_accuracy: 0.6846 - lr: 0.0010 - 17s/epoch - 144ms/step Epoch 13/50 Epoch 13: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257. 121/121 - 19s - loss: 1.0771 - accuracy: 0.6153 - val_loss: 0.9818 - val_accuracy: 0.6869 - lr: 0.0010 - 19s/epoch - 160ms/step Epoch 14/50 121/121 - 19s - loss: 1.0235 - accuracy: 0.6366 - val_loss: 0.9263 - val_accuracy: 0.7173 - lr: 5.0000e-04 - 19s/epoch - 157ms/step Epoch 15/50 121/121 - 17s - loss: 1.0076 - accuracy: 0.6470 - val_loss: 0.9111 - val_accuracy: 0.7079 - lr: 5.0000e-04 - 17s/epoch - 142ms/step Epoch 16/50 121/121 - 17s - loss: 0.9827 - accuracy: 0.6501 - val_loss: 0.9084 - val_accuracy: 0.6986 - lr: 5.0000e-04 - 17s/epoch - 143ms/step Epoch 17/50 Epoch 17: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628. 121/121 - 17s - loss: 0.9812 - accuracy: 0.6551 - val_loss: 0.9076 - val_accuracy: 0.7173 - lr: 5.0000e-04 - 17s/epoch - 143ms/step Epoch 18/50 121/121 - 17s - loss: 0.9378 - accuracy: 0.6751 - val_loss: 0.8815 - val_accuracy: 0.7009 - lr: 2.5000e-04 - 17s/epoch - 143ms/step Epoch 19/50 121/121 - 18s - loss: 0.9292 - accuracy: 0.6714 - val_loss: 0.8692 - val_accuracy: 0.7056 - lr: 2.5000e-04 - 18s/epoch - 145ms/step Epoch 20/50 Epoch 20: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814. 121/121 - 19s - loss: 0.9170 - accuracy: 0.6782 - val_loss: 0.8608 - val_accuracy: 0.7173 - lr: 2.5000e-04 - 19s/epoch - 153ms/step Epoch 21/50 121/121 - 17s - loss: 0.9298 - accuracy: 0.6748 - val_loss: 0.8679 - val_accuracy: 0.7150 - lr: 1.2500e-04 - 17s/epoch - 142ms/step Epoch 22/50 121/121 - 24s - loss: 0.8806 - accuracy: 0.6881 - val_loss: 0.8634 - val_accuracy: 0.7290 - lr: 1.2500e-04 - 24s/epoch - 200ms/step Epoch 23/50 121/121 - 20s - loss: 0.9246 - accuracy: 0.6675 - val_loss: 0.8656 - val_accuracy: 0.7220 - lr: 1.2500e-04 - 20s/epoch - 165ms/step Epoch 24/50 121/121 - 18s - loss: 0.8933 - accuracy: 0.6834 - val_loss: 0.8547 - val_accuracy: 0.7290 - lr: 1.2500e-04 - 18s/epoch - 147ms/step Epoch 25/50 Epoch 25: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05. 121/121 - 18s - loss: 0.9097 - accuracy: 0.6753 - val_loss: 0.8513 - val_accuracy: 0.7243 - lr: 1.2500e-04 - 18s/epoch - 145ms/step Epoch 26/50 121/121 - 19s - loss: 0.9113 - accuracy: 0.6772 - val_loss: 0.8639 - val_accuracy: 0.7173 - lr: 6.2500e-05 - 19s/epoch - 154ms/step Epoch 27/50 121/121 - 18s - loss: 0.8868 - accuracy: 0.6961 - val_loss: 0.8525 - val_accuracy: 0.7196 - lr: 6.2500e-05 - 18s/epoch - 152ms/step Epoch 28/50 Epoch 28: ReduceLROnPlateau reducing learning rate to 3.125000148429535e-05. 121/121 - 18s - loss: 0.8895 - accuracy: 0.6875 - val_loss: 0.8601 - val_accuracy: 0.7196 - lr: 6.2500e-05 - 18s/epoch - 146ms/step Epoch 29/50 121/121 - 19s - loss: 0.8865 - accuracy: 0.6907 - val_loss: 0.8491 - val_accuracy: 0.7220 - lr: 3.1250e-05 - 19s/epoch - 157ms/step Epoch 30/50 121/121 - 17s - loss: 0.8986 - accuracy: 0.6751 - val_loss: 0.8559 - val_accuracy: 0.7243 - lr: 3.1250e-05 - 17s/epoch - 144ms/step Epoch 31/50 Epoch 31: ReduceLROnPlateau reducing learning rate to 1.5625000742147677e-05. 121/121 - 18s - loss: 0.8736 - accuracy: 0.6972 - val_loss: 0.8481 - val_accuracy: 0.7266 - lr: 3.1250e-05 - 18s/epoch - 151ms/step Epoch 32/50 121/121 - 19s - loss: 0.8839 - accuracy: 0.6894 - val_loss: 0.8511 - val_accuracy: 0.7220 - lr: 1.5625e-05 - 19s/epoch - 154ms/step Epoch 33/50 121/121 - 18s - loss: 0.8632 - accuracy: 0.6966 - val_loss: 0.8477 - val_accuracy: 0.7243 - lr: 1.5625e-05 - 18s/epoch - 151ms/step Epoch 34/50 Epoch 34: ReduceLROnPlateau reducing learning rate to 1e-05. 121/121 - 18s - loss: 0.8839 - accuracy: 0.6883 - val_loss: 0.8493 - val_accuracy: 0.7266 - lr: 1.5625e-05 - 18s/epoch - 147ms/step Epoch 35/50 121/121 - 18s - loss: 0.8833 - accuracy: 0.6891 - val_loss: 0.8477 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 18s/epoch - 149ms/step Epoch 36/50 121/121 - 17s - loss: 0.8798 - accuracy: 0.6883 - val_loss: 0.8486 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 17s/epoch - 144ms/step Epoch 37/50 121/121 - 25s - loss: 0.8657 - accuracy: 0.6922 - val_loss: 0.8487 - val_accuracy: 0.7243 - lr: 1.0000e-05 - 25s/epoch - 208ms/step Epoch 38/50 121/121 - 22s - loss: 0.8712 - accuracy: 0.6956 - val_loss: 0.8488 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 22s/epoch - 186ms/step Epoch 39/50 121/121 - 18s - loss: 0.8700 - accuracy: 0.6966 - val_loss: 0.8481 - val_accuracy: 0.7336 - lr: 1.0000e-05 - 18s/epoch - 150ms/step Epoch 40/50 121/121 - 19s - loss: 0.8805 - accuracy: 0.6894 - val_loss: 0.8469 - val_accuracy: 0.7313 - lr: 1.0000e-05 - 19s/epoch - 153ms/step Epoch 41/50 121/121 - 25s - loss: 0.8865 - accuracy: 0.6839 - val_loss: 0.8480 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 25s/epoch - 205ms/step Epoch 42/50 121/121 - 17s - loss: 0.8733 - accuracy: 0.6888 - val_loss: 0.8488 - val_accuracy: 0.7290 - lr: 1.0000e-05 - 17s/epoch - 143ms/step Epoch 43/50 121/121 - 23s - loss: 0.8696 - accuracy: 0.6966 - val_loss: 0.8482 - val_accuracy: 0.7220 - lr: 1.0000e-05 - 23s/epoch - 188ms/step Epoch 44/50 121/121 - 20s - loss: 0.8820 - accuracy: 0.6909 - val_loss: 0.8477 - val_accuracy: 0.7243 - lr: 1.0000e-05 - 20s/epoch - 164ms/step Epoch 45/50 121/121 - 19s - loss: 0.8777 - accuracy: 0.6860 - val_loss: 0.8460 - val_accuracy: 0.7196 - lr: 1.0000e-05 - 19s/epoch - 154ms/step Epoch 46/50 121/121 - 18s - loss: 0.8485 - accuracy: 0.7055 - val_loss: 0.8464 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 18s/epoch - 145ms/step Epoch 47/50 121/121 - 17s - loss: 0.8788 - accuracy: 0.6878 - val_loss: 0.8475 - val_accuracy: 0.7243 - lr: 1.0000e-05 - 17s/epoch - 143ms/step Epoch 48/50 121/121 - 18s - loss: 0.8833 - accuracy: 0.6920 - val_loss: 0.8465 - val_accuracy: 0.7266 - lr: 1.0000e-05 - 18s/epoch - 148ms/step Epoch 49/50 121/121 - 18s - loss: 0.8670 - accuracy: 0.6977 - val_loss: 0.8473 - val_accuracy: 0.7243 - lr: 1.0000e-05 - 18s/epoch - 150ms/step Epoch 50/50 121/121 - 17s - loss: 0.8605 - accuracy: 0.6943 - val_loss: 0.8483 - val_accuracy: 0.7243 - lr: 1.0000e-05 - 17s/epoch - 142ms/step
plt.plot(history_4.history['accuracy'])
plt.plot(history_4.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
# Evaluate the model4 on the normalized test data and encoded test labels
accuracy4 = model4.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 1s - loss: 0.8099 - accuracy: 0.7263 - 661ms/epoch - 44ms/step
# Generate predictions using model4 on the normalized test data
y_pred4 = model4.predict(X_test_normalized)
15/15 [==============================] - 1s 38ms/step
(475, 12)
y_pred4_arg=np.argmax(y_pred4,axis=1)
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred4_arg)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
ax.set_xlabel('Predicted labels');ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_),rotation=90)
ax.yaxis.set_ticklabels(list(enc.classes_),rotation=0)
plt.show()
# Generate a classification report to evaluate the performance of model4
cr4 = metrics.classification_report(y_test_arg, y_pred4_arg)
# Print the classification report
print(cr4)
precision recall f1-score support
0 0.56 0.35 0.43 26
1 0.72 0.87 0.79 39
2 0.70 0.72 0.71 29
3 0.81 0.79 0.80 61
4 0.82 0.41 0.55 22
5 0.74 0.67 0.70 48
6 0.64 0.92 0.75 65
7 0.74 0.77 0.76 22
8 0.68 0.75 0.72 52
9 0.55 0.26 0.35 23
10 0.86 0.84 0.85 50
11 0.80 0.74 0.77 38
accuracy 0.73 475
macro avg 0.72 0.67 0.68 475
weighted avg 0.73 0.73 0.72 475
Here are some insights based on the classification report for Model 4:
Overall Good Performance: Model 4 demonstrates good overall performance, with an accuracy of 0.73. This indicates that it correctly classifies 73% of the samples in the test dataset.
Precision and Recall: Model 4 maintains a balanced trade-off between precision and recall for most classes, resulting in moderate to high F1-scores.
Class 6 (High Recall): Class 6 stands out with high recall (0.92), indicating strong performance in identifying instances of this class. This is an improvement compared to earlier models.
Class 0 (Low Recall): Class 0 still has a relatively low recall (0.35), suggesting challenges in correctly identifying instances of this class.
Class 1 and Class 10: Classes 1 and 10 continue to perform well, with high F1-scores of 0.79 and 0.85, respectively.
Class 4: Class 4 shows significant improvement compared to previous models, with an F1-score of 0.55.
Weighted Average F1-Score: The weighted average F1-score is 0.72, indicating a good overall balance between precision and recall across all classes.
Macro Average Metrics: The macro average (average across classes) precision, recall, and F1-score are around 0.72, suggesting a relatively consistent performance across classes.
Class 11: Class 11 maintains a good F1-score of 0.77.
Class 9: Class 9 has the lowest F1-score among the classes (0.35), indicating room for improvement in correctly classifying instances of this class.
Class 3 and Class 8: Classes 3 and 8 have balanced precision-recall trade-offs, resulting in F1-scores of 0.80 and 0.72, respectively.
Class 7: Class 7 maintains a high F1-score of 0.76.
Class 5: Class 5 shows good performance with an F1-score of 0.70.
Class 2: Class 2 maintains a moderate F1-score of 0.71.
Comment on the final model you have selected and use the same in the below code to visualize the image.
# List of models and their classification reports
models = [model1, model2, model3, model4]
classification_reports = [cr1, cr2, cr3, cr4]
# Iterate through the models and their classification reports
for i, (model, classification_report) in enumerate(zip(models, classification_reports), start=1):
print(f"Classification Report for Model {i}:")
print(classification_report)
print("-" * 50) # Add a separator for better readability
Classification Report for Model 1:
precision recall f1-score support
0 0.31 0.15 0.21 26
1 0.69 0.92 0.79 39
2 0.52 0.45 0.48 29
3 0.65 0.72 0.68 61
4 0.20 0.09 0.13 22
5 0.74 0.58 0.65 48
6 0.55 0.77 0.64 65
7 0.43 0.55 0.48 22
8 0.69 0.48 0.57 52
9 0.36 0.35 0.36 23
10 0.72 0.86 0.78 50
11 0.53 0.45 0.49 38
accuracy 0.59 475
macro avg 0.53 0.53 0.52 475
weighted avg 0.58 0.59 0.58 475
--------------------------------------------------
Classification Report for Model 2:
precision recall f1-score support
0 0.26 0.35 0.30 26
1 0.78 0.79 0.78 39
2 0.62 0.72 0.67 29
3 0.86 0.79 0.82 61
4 0.00 0.00 0.00 22
5 0.62 0.60 0.61 48
6 0.60 0.60 0.60 65
7 0.52 0.68 0.59 22
8 0.71 0.67 0.69 52
9 0.60 0.52 0.56 23
10 0.89 0.82 0.85 50
11 0.52 0.63 0.57 38
accuracy 0.64 475
macro avg 0.58 0.60 0.59 475
weighted avg 0.64 0.64 0.64 475
--------------------------------------------------
Classification Report for Model 3:
precision recall f1-score support
0 0.27 0.15 0.20 26
1 0.90 0.95 0.92 39
2 0.88 0.79 0.84 29
3 0.83 0.90 0.87 61
4 0.88 0.68 0.77 22
5 0.88 0.92 0.90 48
6 0.69 0.82 0.75 65
7 0.72 0.82 0.77 22
8 0.73 0.90 0.81 52
9 0.89 0.35 0.50 23
10 0.81 0.86 0.83 50
11 0.94 0.79 0.86 38
accuracy 0.79 475
macro avg 0.79 0.74 0.75 475
weighted avg 0.79 0.79 0.78 475
--------------------------------------------------
Classification Report for Model 4:
precision recall f1-score support
0 0.56 0.35 0.43 26
1 0.72 0.87 0.79 39
2 0.70 0.72 0.71 29
3 0.81 0.79 0.80 61
4 0.82 0.41 0.55 22
5 0.74 0.67 0.70 48
6 0.64 0.92 0.75 65
7 0.74 0.77 0.76 22
8 0.68 0.75 0.72 52
9 0.55 0.26 0.35 23
10 0.86 0.84 0.85 50
11 0.80 0.74 0.77 38
accuracy 0.73 475
macro avg 0.72 0.67 0.68 475
weighted avg 0.73 0.73 0.72 475
--------------------------------------------------
Based on the provided classification reports for the four models, let's analyze the performance and select the best model.
Model 1:
Model 2:
Model 3:
Model 4:
Based on the accuracy and F1-score, Model 3 appears to be the best-performing model among the four options. It has the highest accuracy and F1-score (weighted average), indicating better overall classification performance on the test dataset. Model 2 also shows good performance but is slightly behind Model 3.
# Visualizing the predicted and correct label of images from test data
# Display the first image from the test data
plt.figure(figsize=(2, 2))
plt.imshow(X_test[2])
plt.show()
# Predict and print the label using Model 3
print('Predicted Label:', enc.inverse_transform(model3.predict((X_test_normalized[2].reshape(1, 128, 128, 3)))))
# Get and print the true label using inverse_transform()
print('True Label:', enc.inverse_transform(y_test_encoded)[2])
# Display the second image from the test data
plt.figure(figsize=(2, 2))
plt.imshow(X_test[33])
plt.show()
# Predict and print the label using Model 3
print('Predicted Label:', enc.inverse_transform(model3.predict((X_test_normalized[33].reshape(1, 128, 128, 3)))))
# Get and print the true label using inverse_transform()
print('True Label:', enc.inverse_transform(y_test_encoded)[33])
# Display the third image from the test data
plt.figure(figsize=(2, 2))
plt.imshow(X_test[36])
plt.show()
# Predict and print the label using Model 3
print('Predicted Label:', enc.inverse_transform(model3.predict((X_test_normalized[36].reshape(1, 128, 128, 3)))))
# Get and print the true label using inverse_transform()
print('True Label:', enc.inverse_transform(y_test_encoded)[36])
1/1 [==============================] - 0s 129ms/step Predicted Label ['Small-flowered Cranesbill'] True Label Small-flowered Cranesbill
1/1 [==============================] - 0s 21ms/step Predicted Label ['Cleavers'] True Label Cleavers
1/1 [==============================] - 0s 20ms/step Predicted Label ['Shepherds Purse'] True Label Shepherds Purse