Open In App

Multiclass image classification using Transfer learning

Last Updated : 15 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Image classification is one of the supervised machine learning problems which aims to categorize the images of a dataset into their respective categories or labels. Classification of images of various dog breeds is a classic image classification problem. So, we have to classify more than one class that’s why the name multi-class classification, and in this article, we will be doing the same by making use of a pre-trained model InceptionResNetV2, and customizing it.

Let’s first discuss some of the terminologies.

Transfer learning: Transfer learning is a popular deep learning method that follows the approach of using the knowledge that was learned in some task and applying it to solve the problem of the related target task. So, instead of creating a neural network from scratch we “transfer” the learned features which are basically the “weights” of the network. To implement the concept of transfer learning, we make use of “pre-trained models“.

Necessities for transfer learning: Low-level features from model A (task A) should be helpful for learning model B (task B).

 Pre-trained model: Pre-trained models are the deep learning models which are trained on very large datasets, developed, and are made available by other developers who want to contribute to this machine learning community to solve similar types of problems. It contains the biases and weights of the neural network representing the features of the dataset it was trained on. The features learned are always transferrable. For example, a model trained on a large dataset of flower images will contain learned features such as corners, edges, shape, color, etc.

InceptionResNetV2: InceptionResNetV2 is a convolutional neural network that is 164 layers deep, trained on millions of images from the ImageNet database, and can classify images into more than 1000 categories such as flowers, animals, etc. The input size of the images is 299-by-299.

Dataset description:

  • The dataset used comprises of 120 breeds of dogs in total.
  • Each image has a file name which is its unique id.
  • Train dataset ( train.zip ): contains 10,222 images which are to be used for training our model
  • Test dataset (test.zip ): contains 10,357 images which we have to classify into the respective categories or labels.
  • labels.csv: contains breed names corresponding to the image id.
  • sample_submission.csv: contains correct form of sample submission to be made

All the above mentioned files can be downloaded from here.

NOTE: For better performance use GPU.

We first import all the necessary libraries.

Python3




import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
 
from sklearn.metrics import classification_report, confusion_matrix
 
# deep learning libraries
import tensorflow as tf
import keras
from keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import applications
from keras.models import Sequential, load_model
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, Flatten, Dense, Dropout
from keras.preprocessing import image
 
import cv2
 
import warnings
warnings.filterwarnings('ignore')


Loading datasets and image folders

Python3




from google.colab import drive
drive.mount("/content/drive")
 
# datasets
labels = pd.read_csv("/content/drive/My Drive/dog/labels.csv")
sample = pd.read_csv('/content/drive/My Drive/dog/sample_submission.csv')
 
# folders paths
train_path = "/content/drive/MyDrive/dog/train"
test_path = "/content/drive/MyDrive/dog/test"


Displaying the first five records of labels dataset to see its attributes.

Python3




labels.head()


Output: 

Adding ‘.jpg’ extension to each id 

This is done in order to fetch the images from the folder since the image name and id’s are the same so adding .jpg extension will help us in retrieving images easily.

Python3




def to_jpg(id):
    return id+".jpg"
 
 
labels['id'] = labels['id'].apply(to_jpg)
sample['id'] = sample['id'].apply(to_jpg)


Augmenting data:

It’s a pre-processing technique in which we augment the existing dataset with transformed versions of the existing images. We can perform scaling, rotations, increasing brightness, and other affine transformations. This is a useful technique as it helps the model to generalize the unseen data well.

ImageDataGenerator class is used for this purpose which provides a real-time augmentation of data.a 

Description of few of its parameters that are used below:

  • rescale: rescales values by the given factor
  • horizontal flip: randomly flip inputs horizontally.
  • validation_split: this is the fraction of images reserved for validation (between 0 and 1).

Python3




# Data agumentation and pre-processing using tensorflow
gen = ImageDataGenerator(
                  rescale=1./255.,
                  horizontal_flip = True,
                  validation_split=0.2 # training: 80% data, validation: 20% data
                 )
 
train_generator = gen.flow_from_dataframe(
    labels, # dataframe
    directory = train_path, # images data path / folder in which images are there
    x_col = 'id',
    y_col = 'breed',
    subset="training",
    color_mode="rgb",
    target_size = (331,331), # image height , image width
    class_mode="categorical",
    batch_size=32,
    shuffle=True,
    seed=42,
)
 
 
validation_generator = gen.flow_from_dataframe(
    labels, # dataframe
    directory = train_path, # images data path / folder in which images are there
    x_col = 'id',
    y_col = 'breed',
    subset="validation",
    color_mode="rgb",
    target_size = (331,331), # image height , image width
    class_mode="categorical",
    batch_size=32,
    shuffle=True,
    seed=42,
)


Output:  

Let’s see what a single batch of data looks like.

Python3




x,y = next(train_generator)
x.shape # input shape of one record is (331,331,3) , 32: is the batch size


Output:   

(32, 331, 331, 3)

Plotting images from the train dataset

Python3




a = train_generator.class_indices
class_names = list(a.keys())  # storing class/breed names in a list
 
 
def plot_images(img, labels):
    plt.figure(figsize=[15, 10])
    for i in range(25):
        plt.subplot(5, 5, i+1)
        plt.imshow(img[i])
        plt.title(class_names[np.argmax(labels[i])])
        plt.axis('off')
 
 plot_images(x,y)


Output:   

Building our Model

This is the main step where the neural convolution model is built.

Python3




# load the InceptionResNetV2 architecture with imagenet weights as base
base_model = tf.keras.applications.InceptionResNetV2(
                     include_top=False,
                     weights='imagenet',
                     input_shape=(331,331,3)
                     )
 
base_model.trainable=False
# For freezing the layer we make use of layer.trainable = False
# means that its internal state will not change during training.
# model's trainable weights will not be updated during fit(),
# and also its state updates will not run.
 
model = tf.keras.Sequential([
        base_model,  
        tf.keras.layers.BatchNormalization(renorm=True),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(256, activation='relu'),
        tf.keras.layers.Dropout(0.5),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dense(120, activation='softmax')
    ])


BatchNormalization :

  • It is a normalization technique which is done along mini-batches instead of the full data set.
  • It is used to speed up training and use higher learning rates.
  • It maintains the mean output close to 0 and the output standard deviation close to 1.

GlobalAveragePooling2D :

  • It takes a tensor of size (input width) x (input height) x (input channels) and computes the average value of all values across the entire (input width) x (input height) matrix for each of the (input channels).
  • The dimensionality of the images is reduced by reducing the number of pixels in the output from the previous neural network layer.
  • By using this we get a 1-dimensional tensor of size (input channels) as our output.
  • 2D Global average pooling operation. Here ‘Depth’ = ‘Filters’

Dense Layers: These layers are regular fully connected neural network layers connected after the convolutional layers.

Drop out layer: is also used whose function is to randomly drop some neurons from the input unit so as to prevent overfitting. The value of 0.5 indicates that 0.5 fractions of neurons have to be dropped.

Compile the model:

Before training our model we first need to configure it and that is done by model.compile() which defines the loss function, optimizers, and metrics for prediction.

Python3




model.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])
# categorical cross entropy is taken since its used as a loss function for
# multi-class classification problems where there are two or more output labels.
# using Adam optimizer for better performance
# other optimizers such as sgd can also be used depending upon the model


Displaying a summary report of the model

By displaying the summary we can check our model to confirm that everything is as expected.

Python3




model.summary()


Output:  

Defining callbacks to preserve the best results:

Callback: It is an object that can perform actions at various stages of training (for example, at the start or end of an epoch, before or after a single batch, etc).

Python3




early = tf.keras.callbacks.EarlyStopping( patience=10,
                                          min_delta=0.001,
                                          restore_best_weights=True)
# early stopping call back


Training the model: It means that we are finding a set of values for weights and biases that have a low loss on average across all the records.

Python3




batch_size=32
STEP_SIZE_TRAIN = train_generator.n//train_generator.batch_size
STEP_SIZE_VALID = validation_generator.n//validation_generator.batch_size
 
# fit model
history = model.fit(train_generator,
                    steps_per_epoch=STEP_SIZE_TRAIN,
                    validation_data=validation_generator,
                    validation_steps=STEP_SIZE_VALID,
                    epochs=25,
                    callbacks=[early]


Output: 

Save the model

We can save the model for further use.

Python3




model.save("Model.h5")


Visualizing the model’s performance

Python3




# store results
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
 
 
# plot results
# accuracy
plt.figure(figsize=(10, 16))
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['font.size'] = 14
plt.rcParams['axes.grid'] = True
plt.rcParams['figure.facecolor'] = 'white'
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.title(f'\nTraining and Validation Accuracy. \nTrain Accuracy:
          {str(acc[-1])}\nValidation Accuracy: {str(val_acc[-1])}')


Output:  Text(0.5, 1.0, ‘\nTraining and Validation Accuracy. \nTrain Accuracy: 0.9448809027671814\nValidation Accuracy: 0.9022817611694336’)

Python3




# loss
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.title(f'Training and Validation Loss. \nTrain Loss:
          {str(loss[-1])}\nValidation Loss: {str(val_loss[-1])}')
plt.xlabel('epoch')
plt.tight_layout(pad=3.0)
plt.show()


Output:   

A line graph of training vs validation accuracy and loss was also plotted. The graph indicates that the accuracies of validation and training were almost consistent with each other and above 90%. The loss of the CNN model is a negative lagging graph which indicates that the model is behaving as expected with a reducing loss after each epoch.

Evaluating the accuracy of the model

Python3




accuracy_score = model.evaluate(validation_generator)
print(accuracy_score)
print("Accuracy: {:.4f}%".format(accuracy_score[1] * 100))
 
print("Loss: ",accuracy_score[0])


Output: 

Viewing the Test Image

Python3




test_img_path = test_path+"/000621fb3cbb32d8935728e48679680e.jpg"
 
img = cv2.imread(test_img_path)
resized_img = cv2.resize(img, (331, 331)).reshape(-1, 331, 331, 3)/255
 
plt.figure(figsize=(6,6))
plt.title("TEST IMAGE")
plt.imshow(resized_img[0])


Output: 

Making predictions on the test data

Python3




predictions = []
 
for image in sample.id:
    img = tf.keras.preprocessing.image.load_img(test_path +'/'+ image)
    img = tf.keras.preprocessing.image.img_to_array(img)
    img = tf.keras.preprocessing.image.smart_resize(img, (331, 331))
    img = tf.reshape(img, (-1, 331, 331, 3))
    prediction = model.predict(img/255)
    predictions.append(np.argmax(prediction))
 
my_submission = pd.DataFrame({'image_id': sample.id, 'label':  predictions})
my_submission.to_csv('submission.csv', index=False)
 
# Submission file output
print("Submission File: \n---------------\n")
print(my_submission.head()) # Displaying first five predicted output


Output: 



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads