In this tutorial, we will guide you through the process of creating a dog breed classifier using Tensorflow, powered by MobileNetV2. Tensorflow is a versatile deep learning library that allows us to leverage pre-trained models like MobileNetV2, which is known for its efficiency and accuracy in image classification tasks.
By the end of this tutorial, you will have a fully functional classifier that can accurately predict dog breeds from a given image. The steps involved include loading the pre-trained model, preparing the dataset, training the classifier, and evaluating its performance.
Before we begin, make sure you have the following:
We start by importing all the essential libraries that will be used throughout the project. Here's a breakdown of each import:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Lambda, GlobalAveragePooling2D, Dropout, Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint
cv2 (OpenCV): This library is used for image processing tasks. In this project, it can help us read, modify, or visualize images.
numpy: A fundamental package for scientific computing in Python. It allows us to efficiently handle arrays, which is crucial for processing images.
matplotlib.pyplot: A library for creating visualizations. We will use it to display sample images and monitor model performance through plots.
os: The os module provides functions to interact with the operating system, like navigating file directories, which is helpful when dealing with datasets.
tensorflow: TensorFlow is the deep learning framework used for training our classifier.
ImageDataGenerator: A tool for augmenting and preprocessing image datasets before feeding them into the model.
MobileNetV2: A pre-trained model architecture that is lightweight and efficient for mobile and embedded applications. We'll fine-tune it for our dog breed classification task.
preprocess_input: This function prepares images for the MobileNetV2 model by scaling pixel values in a way that matches the training of the original model.
Sequential: A Keras model that is a linear stack of layers. We'll use it to build our model.
Input, Lambda, GlobalAveragePooling2D, Dropout, Dense: These are Keras layers that will help in defining our neural network architecture. We'll go through their specific usage as we build the model.
Adam: A popular optimizer that adapts the learning rate during training for faster convergence.
ModelCheckpoint: This callback helps in saving the best model during training by monitoring its performance.
Before we train the model, we need to load and explore the dataset to ensure that it's structured correctly. In this case, we are working with a dataset of dog images that is divided into different folders, where each folder corresponds to a dog breed.
= ['../input/70-dog-breedsimage-data-set/test/'+x for x in os.listdir('../input/70-dog-breedsimage-data-set/test/')]
class_folder_paths for class_folder_path in class_folder_paths:
print('{0}:'.format(class_folder_path), ' ', len(os.listdir(class_folder_path)))
In this step, we define the paths to our training, validation, and test datasets. These directories contain the images that will be used to train and evaluate the dog breed classifier.
= '../input/70-dog-breedsimage-data-set/train/'
TRAIN_DIR = '../input/70-dog-breedsimage-data-set/valid/'
VAL_DIR = '../input/70-dog-breedsimage-data-set/test/' TEST_DIR
To get a better understanding of our dataset, we can visualize some sample images from the training set. This step helps us verify that the images are correctly labeled and gives us insight into the diversity of breeds in our dataset.
import matplotlib.pyplot as plt
from PIL import Image
=os.listdir(TRAIN_DIR)
train_dogs= plt.subplots(nrows=2, ncols=5, figsize=(15, 7))
fig, axes for i, ax in enumerate(axes.flat):
dir = TRAIN_DIR + "/" + train_dogs[i] + "/"
open(dir + os.listdir(dir)[0]))
ax.imshow(Image.
ax.set_title(train_dogs[i])
plt.tight_layout() plt.show()
In this step, we set up data generators for our training, validation, and test datasets. Data augmentation helps improve the robustness of our model by artificially increasing the size of the training dataset and introducing variability in the training data.
= ImageDataGenerator(horizontal_flip = True,
train_data_gen =20,
rotation_range=0.1,
width_shift_range=0.1,
height_shift_range=0.2)
zoom_range
= train_data_gen.flow_from_directory(TRAIN_DIR,
train_generator = (224, 224),
target_size = 'rgb',
color_mode = 32,
batch_size ='categorical',
class_mode = True)
shuffle
= ImageDataGenerator()
val_data_gen
= val_data_gen.flow_from_directory(VAL_DIR,
val_generator = (224, 224),
target_size = 'rgb',
color_mode = 32,
batch_size = 'categorical',
class_mode = False)
shuffle
= val_data_gen.flow_from_directory(TEST_DIR,
test_generator = (224, 224),
target_size = 'rgb',
color_mode = 32,
batch_size = 'categorical',
class_mode = False) shuffle
Found 7946 images belonging to 70 classes.
Found 700 images belonging to 70 classes.
Found 700 images belonging to 70 classes.
train_data_gen = ImageDataGenerator(...)
initializes
the ImageDataGenerator for training images with several augmentation
parameters:horizontal_flip=True
: Randomly flip images
horizontally. rotation_range=20: Randomly rotate images in the range of
20 degrees. width_shift_range=0.1: Randomly shift images horizontally by
up to 10% of the image width.height_shift_range=0.1
: Randomly shift images
vertically by up to 10% of the image height.train_generator = train_data_gen.flow_from_directory(...)
creates
the training data generator that will read images from the
TRAIN_DIR.target_size=(224, 224)
: Resizes all images to 224x224
pixels, which is the input size expected by MobileNetV2.color_mode='rgb'
: Specifies that the images should be
read in RGB color mode.batch_size=32
: Specifies the number of images to be
yielded from the generator per batch.class_mode='categorical'
: Indicates that the labels
will be one-hot encoded (suitable for multi-class classification).val_data_gen = ImageDataGenerator()
initializes a
generator for the validation set without any augmentation (to maintain
the integrity of validation data).val_generator = val_data_gen.flow_from_directory(...)
sets up the validation data generator similarly to the training
generator.In this step, we extract the class indices from the training generator and create a mapping from the indices to the corresponding dog breed names. This mapping will help us understand which index corresponds to which dog breed when making predictions.
= train_generator.class_indices
labels = dict((v,k) for k,v in labels.items())
class_mapping class_mapping
{0: 'Afghan',
1: 'African Wild Dog',
2: 'Airedale',
3: 'American Hairless',
4: 'American Spaniel',
5: 'Basenji',
64 more...
In this step, we define the architecture of our dog breed classifier using the MobileNetV2 model. This model is efficient for image classification tasks, especially with limited computational resources.
= Sequential([Input((224,224,3)),
before_mobilenet
Lambda(preprocess_input)])
= MobileNetV2(input_shape = (224,224,3), include_top = False)
mobilenet
= Sequential([GlobalAveragePooling2D(),
after_mobilenet 0.2),
Dropout(70, activation = 'softmax')])
Dense(
= Sequential([before_mobilenet, mobilenet, after_mobilenet]) model
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224_no_top.h5
9412608/9406464 [==============================] - 0s 0us/step
9420800/9406464 [==============================] - 0s 0us/step
before_mobilenet
: This sequential model includes an
input layer that specifies the shape of the input images (224x224 pixels
with 3 color channels for RGB).Lambda(preprocess_input)
: This layer applies the
preprocess_input function from MobileNetV2, which normalizes the image
data to be compatible with the MobileNetV2 model's expectations.mobilenet = MobileNetV2(input_shape=(224, 224, 3), include_top=False)
:
This line initializes the MobileNetV2 model without the top
classification layer (hence include_top=False). This allows us to use
the pre-trained model as a feature extractor for our specific
classification task.after_mobilenet
: This sequential model adds layers
after the MobileNetV2 base:GlobalAveragePooling2D()
: This layer reduces the
spatial dimensions of the output from the MobileNetV2 model by averaging
the feature maps, resulting in a 1D tensor that can be easily
processed.Dropout(0.2)
: This layer randomly drops 20% of the
neurons during training to help prevent overfitting.Dense(70, activation='softmax')
: This fully connected
layer outputs the final predictions, where 70 corresponds to the number
of dog breeds (classes), and softmax activation ensures that the output
values represent probabilities that sum to 1.model = Sequential([before_mobilenet, mobilenet, after_mobilenet])
:
This line combines all the layers into a single sequential model that
can be trained.In this step, we compile the MobileNetV2 model by specifying the optimizer, loss function, and evaluation metrics. This step prepares the model for training.
= Adam(learning_rate=0.00001)
opt compile(optimizer = opt, loss = 'categorical_crossentropy', metrics = ['accuracy'])
model.
None, 224, 224, 3)))
model.build(((
before_mobilenet.summary()
mobilenet.summary() after_mobilenet.summary()
opt = Adam(learning_rate=0.00001)
: This line
initializes the Adam optimizer with a very small learning rate of
0.00001. Adam is a popular optimization algorithm that adapts the
learning rate for each parameter, making it effective for a wide range
of problems.model.compile(...)
: This method configures the model
for training with the following parameters:optimizer=opt
: Uses the Adam optimizer we defined
earlier.loss='categorical_crossentropy'
: Specifies the loss
function to be used during training. Categorical crossentropy is
suitable for multi-class classification tasks, where each training
sample belongs to one of multiple classes (in our case, dog
breeds).metrics=['accuracy']
: Specifies that we want to track
the accuracy of the model during training and evaluation. Accuracy is a
common metric for classification tasks, indicating the proportion of
correct predictions.model.build(((None, 224, 224, 3)))
: This line
explicitly builds the model by specifying the input shape of the images.
The None indicates that the batch size can vary. The shape (224, 224, 3)
corresponds to the input images, which are 224x224 pixels in size with 3
color channels (RGB).before_mobilenet.summary()
: Displays a summary of the
before_mobilenet model, showing the layers, output shapes, and the
number of parameters in each layer.mobilenet.summary()
: Displays a summary of the
MobileNetV2 base model, providing similar details for its
architecture.after_mobilenet.summary()
: Displays a summary of the
after_mobilenet model, which includes the pooling, dropout, and dense
layers.In this step, we train the MobileNetV2 model using the training data while validating its performance on a separate validation dataset. We also utilize a callback to save the best model during training.
= ModelCheckpoint('./model/', save_best_only = True)
train_cb
= val_generator, callbacks = [train_cb], epochs = 20) model.fit(train_generator, validation_data
Epoch 1/20
249/249 [==============================] - 150s 556ms/step - loss: 4.1956 - accuracy: 0.0540 - val_loss: 3.5333 - val_accuracy: 0.1671
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 2/20
249/249 [==============================] - 119s 478ms/step - loss: 3.2224 - accuracy: 0.2662 - val_loss: 2.4435 - val_accuracy: 0.5057
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 3/20
249/249 [==============================] - 119s 476ms/step - loss: 2.3437 - accuracy: 0.4873 - val_loss: 1.5793 - val_accuracy: 0.6800
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 4/20
249/249 [==============================] - 119s 477ms/step - loss: 1.7103 - accuracy: 0.6311 - val_loss: 1.0961 - val_accuracy: 0.7986
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 5/20
249/249 [==============================] - 118s 472ms/step - loss: 1.2946 - accuracy: 0.7192 - val_loss: 0.8493 - val_accuracy: 0.8500
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 6/20
249/249 [==============================] - 118s 474ms/step - loss: 1.0649 - accuracy: 0.7570 - val_loss: 0.7184 - val_accuracy: 0.8686
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 7/20
249/249 [==============================] - 119s 476ms/step - loss: 0.8976 - accuracy: 0.7892 - val_loss: 0.6450 - val_accuracy: 0.8814
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 8/20
249/249 [==============================] - 118s 475ms/step - loss: 0.7812 - accuracy: 0.8131 - val_loss: 0.5898 - val_accuracy: 0.8971
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 9/20
249/249 [==============================] - 118s 473ms/step - loss: 0.6945 - accuracy: 0.8276 - val_loss: 0.5611 - val_accuracy: 0.9014
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 10/20
249/249 [==============================] - 118s 475ms/step - loss: 0.6254 - accuracy: 0.8370 - val_loss: 0.5310 - val_accuracy: 0.9086
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 11/20
249/249 [==============================] - 118s 473ms/step - loss: 0.5733 - accuracy: 0.8528 - val_loss: 0.5126 - val_accuracy: 0.9100
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 12/20
249/249 [==============================] - 119s 477ms/step - loss: 0.5330 - accuracy: 0.8621 - val_loss: 0.5036 - val_accuracy: 0.9157
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 13/20
249/249 [==============================] - 118s 475ms/step - loss: 0.5022 - accuracy: 0.8681 - val_loss: 0.4853 - val_accuracy: 0.9200
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 14/20
249/249 [==============================] - 120s 480ms/step - loss: 0.4662 - accuracy: 0.8755 - val_loss: 0.4751 - val_accuracy: 0.9214
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 15/20
249/249 [==============================] - 118s 474ms/step - loss: 0.4325 - accuracy: 0.8803 - val_loss: 0.4653 - val_accuracy: 0.9257
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 16/20
249/249 [==============================] - 119s 476ms/step - loss: 0.4106 - accuracy: 0.8898 - val_loss: 0.4611 - val_accuracy: 0.9214
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 17/20
249/249 [==============================] - 119s 475ms/step - loss: 0.3779 - accuracy: 0.9028 - val_loss: 0.4560 - val_accuracy: 0.9271
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 18/20
249/249 [==============================] - 119s 476ms/step - loss: 0.3522 - accuracy: 0.9056 - val_loss: 0.4498 - val_accuracy: 0.9314
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 19/20
249/249 [==============================] - 118s 474ms/step - loss: 0.3476 - accuracy: 0.9057 - val_loss: 0.4458 - val_accuracy: 0.9257
/opt/conda/lib/python3.7/site-packages/keras/utils/generic_utils.py:497: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.
category=CustomMaskWarning)
Epoch 20/20
249/249 [==============================] - 118s 474ms/step - loss: 0.3208 - accuracy: 0.9134 - val_loss: 0.4465 - val_accuracy: 0.9257
<keras.callbacks.History at 0x7fc74bd914d0>
train_cb = ModelCheckpoint('./model/', save_best_only=True)
:
This line creates a callback that saves the model to the specified
directory (./model/). The save_best_only=True argument ensures that only
the model with the best validation performance (lowest validation loss)
is saved, preventing the storage of inferior models during
training.model.fit(...)
: This method trains the model on the
training data (train_generator) and evaluates it on the validation data
(val_generator). The training process involves the following
parameters:train_generator
: Provides the training data in
batches.validation_data=val_generator
: Evaluates the model on
the validation dataset after each epoch, helping to monitor
performance.callbacks=[train_cb]
: Includes the train_cb to save the
best model during training based on validation loss.epochs=20
: Specifies that the model will be trained for
20 epochs. An epoch is one complete pass through the training
dataset.In this final step, we save the trained MobileNetV2 model to a file and evaluate its performance on the test dataset.
'MobileNetV2_model.h5')
model.save( model.evaluate(test_generator)
22/22 [==============================] - 4s 161ms/step - loss: 0.1654 - accuracy: 0.9629
[0.16537855565547943, 0.9628571271896362]
model.save('MobileNetV2_model.h5')
: This line saves the
entire trained model to a file named MobileNetV2_model.h5. This file
format (HDF5) allows us to store the model architecture, weights, and
training configuration in a single file, making it easy to load and use
later without the need for retraining.model.evaluate(test_generator)
: This method evaluates
the saved model on the test dataset provided by test_generator. The
evaluation returns the loss value (0.16) and any metrics specified
during the model compilation (in our case, accuracy i.e 0.96 ). This is
crucial for assessing how well the model performs on completely unseen
data.In this tutorial, we successfully built a dog breed classifier using the MobileNetV2 model, leveraging transfer learning techniques to achieve effective image classification. Here’s a summary of what we covered:
Data Preparation: We started by preparing the dataset, including training, validation, and test sets, and visualized some sample images to understand the data better.
Data Augmentation: We implemented data augmentation techniques to enhance the training dataset, helping the model generalize better and reduce the risk of overfitting.
Model Architecture: We constructed the model using MobileNetV2, combining it with additional layers to adapt it for our specific classification task.
Model Compilation: We compiled the model with the Adam optimizer and categorical crossentropy loss function, preparing it for the training phase.
Training the Model: The model was trained on the training data, with validation data used to monitor its performance and save the best version of the model.
Model Evaluation: Finally, we saved the trained model and evaluated its performance on the test dataset, giving us a final accuracy score.
This tutorial demonstrates the complete workflow of building an image classification model using deep learning. You can now take this knowledge and apply it to other classification tasks or further refine the model with more advanced techniques.
Feel free to explore and experiment with the model, adjust hyperparameters, and try different architectures to improve performance. Happy coding and best of luck with your machine learning journey!