Diego Alzate

🤖 Proyectos de IA

  • Natural Language Processing
  • Computer Vision
  • Deep Learning
  • Supervised Learning
  • Unsupervised Learning
  • Ensemble Techniques

📍 Navegación

  • Inicio
  • Proyectos

Introduction to Computer Vision: Plant Seedlings Classification¶

Problem Statement¶

Context¶

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.

Objective¶

The aim of this project is to Build a Convolutional Neural Netowrk to classify plant seedlings into their respective categories.

Data Dictionary¶

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:

    • images.npy
    • Label.csv
  • 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

  • Black-grass
  • Charlock
  • Cleavers
  • Common Chickweed
  • Common Wheat
  • Fat Hen
  • Loose Silky-bent
  • Maize
  • Scentless Mayweed
  • Shepherds Purse
  • Small-flowered Cranesbill
  • Sugar beet

Note: Please use GPU runtime to execute the code efficiently¶

Importing necessary libraries¶

In [ ]:
# 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')

Loading the dataset¶

In [ ]:
drive.mount('/content/drive')
Mounted at /content/drive
In [ ]:
images = np.load('/content/drive/MyDrive/6-ComputerVision/images.npy')
labels = pd.read_csv('/content/drive/MyDrive/6-ComputerVision/Labels.csv')

Data Overview¶

Understand the shape of the dataset¶

In [ ]:
print(images.shape)
print(labels.shape)
(4750, 128, 128, 3)
(4750, 1)

Exploratory Data Analysis¶

  • EDA is an important part of any project involving data.
  • It is important to investigate and understand the data better before building a model with it.
  • A few questions have been mentioned below which will help you understand the data better.
  • A thorough analysis of the data, in addition to the questions mentioned below, should be done.
  1. How are these different category plant images different from each other?
  2. Is the dataset provided an imbalance? (Check with using bar plots)
In [ ]:
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()
In [ ]:
plot_images(images,labels)
In [ ]:
sns.countplot(x=labels['Label'])
plt.xticks(rotation='vertical')
Out[ ]:
(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')])

Observations¶

The data is unbalanced

Data Pre-Processing¶

Convert the BGR images to RGB images.¶

In [ ]:
for i in range(len(images)):
    images[i] = cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB)

RGB Images example¶

In [ ]:
plot_images(images,labels)

Data Preparation for Modeling¶

  • Before you proceed to build a model, you need to split the data into train, test, and validation to be able to evaluate the model that you build on the train data
  • You'll have to encode categorical features and scale the pixel values.
  • You will build a model using the train data and then check its performance

Split the dataset¶

Using original size

In [ ]:
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)
In [ ]:
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)

Encode the target labels¶

In [ ]:
# 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)
In [ ]:
print(y_train_encoded.shape)
print(y_val_encoded.shape)
print(y_test_encoded.shape)
(3847, 12)
(428, 12)
(475, 12)

Model 1¶

Data Normalization¶

In [ ]:
# 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

Model Building¶

In [ ]:
# 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
In [ ]:
# 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)
_________________________________________________________________
In [ ]:
# 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

Model evaluation¶

In [ ]:
# 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

Evaluating the model on test data¶

In [ ]:
# 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
In [ ]:
y_pred1=model1.predict(X_test_normalized)
15/15 [==============================] - 0s 7ms/step
In [ ]:
# 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()
In [ ]:
# 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

Observations¶

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.

Model 2¶

In [ ]:
# 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
In [ ]:
# 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)
_________________________________________________________________
In [ ]:
# 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
In [ ]:
# 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()
In [ ]:
# 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
In [ ]:
y_pred2=model2.predict(X_test_normalized)
15/15 [==============================] - 0s 12ms/step
Out[ ]:
(475, 12)
In [ ]:
# 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()
In [ ]:
# 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

Observations¶

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.

Model 3¶

Model Performance Improvement¶

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.

In [ ]:
# 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
)

Data Augmentation¶

Remember, data augmentation should not be used in the validation/test data set.

In [ ]:
# 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
)
In [ ]:
# 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)
_________________________________________________________________
In [ ]:
# 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
In [ ]:
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()
In [ ]:
# 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
In [ ]:
# Generate predictions using model3 on the normalized test data
y_pred3 = model3.predict(X_test_normalized)
15/15 [==============================] - 0s 12ms/step
In [ ]:
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()
In [ ]:
# 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

Observations¶

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.

Model 4¶

In [ ]:
# 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)
_________________________________________________________________
In [ ]:
for layer in vgg_model.layers:
    layer.trainable = False
In [ ]:
# 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)
_________________________________________________________________
In [ ]:
# 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
In [ ]:
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()
In [ ]:
# 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
In [ ]:
# Generate predictions using model4 on the normalized test data
y_pred4 = model4.predict(X_test_normalized)
15/15 [==============================] - 1s 38ms/step
Out[ ]:
(475, 12)
In [ ]:
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()
In [ ]:
# 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

Observations¶

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.

Final Model¶

Comment on the final model you have selected and use the same in the below code to visualize the image.

In [ ]:
# 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

--------------------------------------------------

Observations¶

Based on the provided classification reports for the four models, let's analyze the performance and select the best model.

  • Model 1:

    • Accuracy: 0.59
    • F1-score (weighted avg): 0.58
  • Model 2:

    • Accuracy: 0.64
    • F1-score (weighted avg): 0.64
  • Model 3:

    • Accuracy: 0.79
    • F1-score (weighted avg): 0.78
  • Model 4:

    • Accuracy: 0.73
    • F1-score (weighted avg): 0.72

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 prediction¶

In [ ]:
# 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

Actionable Insights and Business Recommendations¶

Model Selection:¶

  • Model 3, with an accuracy of 0.79 and a weighted F1-score of 0.78, is the best-performing model among the options considered. Therefore, it should be selected as the preferred model for this classification task.

Data Quality:¶

  • High-quality data is crucial for improving model performance. Collect more diverse and representative data if possible.

Data Augmentation:¶

  • Data augmentation techniques, as used in the training of Model 3, have proven effective in improving model generalization. Continue to explore and apply data augmentation strategies to enhance model performance further.

Business Impact:¶

  • Assess the impact of the model's predictions on your business processes. Measure how it contributes to efficiency, decision-making, and customer satisfaction. Continuously refine and adapt your processes based on the model's outputs.

Alternative Models:¶

  • While Model 3 is the current choice, periodically evaluate and benchmark alternative models and machine learning techniques to stay competitive and ensure the best possible performance.

Training Schedule:¶

  • Depending on the nature of the data and the problem, consider retraining the model at regular intervals (e.g., monthly or quarterly) to keep it up-to-date with changing patterns in the data.