CNN Image Classification | Towards Data Science

In this article, I will walk through how to create an image classification model based on Convolution Neural Network (CNN) step by step. It is split into 7 parts.
Problem statementData overviewModel buildingModel compilingModel fittingModel improvementExample prediction
Problem statement
Data overview
Model building
Model compiling
Model fitting
Model improvement
Example prediction
Let’s start the journey 🏃‍♂️🏃‍♀️.
1. Problem statement
We are given a set of dog and cat images. The task is to build a model to predict the category of an animal: dog or cat?
2. Data overview
The data we collected is a subset of the Kaggle dog/cat dataset (link). In total, there are 10, 000 images, 80% for the training set, and 20% for the test set. In the training set, 4,000 images of dogs, while the test set has 1,000 images of dogs, and the rest are cats.
All images are saved in a special folder structure, making it easy for Keras to understand and differentiate the animal category of each image, as shown in Figure 1.
No need to encode the variables, as the independent variables are pixel values.
3. Model building
In general, 4 steps are required to build a CNN: Convolution, Max pooling, Flattening, and Full connection. let’s go through each in details.
3.1 Convolution
Conceptually, convolution is to apply feature detectors on the input image. To simplify the concept, take a smiling face as an input image, which is represented as an array of 0 and 1 in Figure 2.
The feature detector is also an array of numbers. For each feature detector, we slide it over the image and produce a new array of numbers, representing a feature of the image. So, the operation between an input image and a feature detector that results in a feature map is Convolution as shown below Fig.3.
If repeating the above convolution with different feature detectors, we produce as many feature maps as feature detectors, obtaining a convolution layer.
Specifically, we use Conv2D() function from Keras to build the first convolution layer.
classifier = Sequential()classifier.add(Conv2D(32, (3, 3), input_shape = (64, 64, 3), activation = 'relu'))
Note, the number of feature detectors is set to be 32, and its dimension is (3, 3). In most CNN architectures, a common practice is to start with 32 feature detectors and increase to 64 or 128 if needed.
input_shape is the shape of input images on which we apply feature detectors through convolution. The problem is that images may have different formats and image size. So, we need to convert images into the same format and fixed size. We will process the image later, and now let’s set it to be (64, 64, 3). Here, 3 is the number of channels for a colored image, (64, 64) is the image dimension for each channel, which is enough for good accuracy. Feel free to try (128, 128) if you are working on a decent GPU.
The final argument is the activation function. we use ReLU to remove any negative pixel values in feature maps. This is because depending on the parameters used in convolution, we may obtain negative pixels in feature maps. Removing negative pixels adds non-linearity for a non-linear classification problem.
3.2 Max pooling
Max pooling is to reduce the size of a feature map by sliding a table, for example (2,2), and taking the maximum value in the table. If we slide a table with a stride of 2 over 1 feature map of (5,5), we get a feature map with reduced size of (3,3) as shown in Figure 5.
Repeating max pooling on each feature map produces a pooling layer. Fundamentally, max pooling is to reduce the number of nodes in the fully connected layers without losing key features and spatial structure information in the images. Specifically, we use MaxPooling2D() function to add the pooling layer. In general, we use a 2x2 filter for pooling.
classifier.add(MaxPooling2D(pool_size = (2, 2)))
3.3 Flattening
Flattening is to take all pooled feature maps into a single vector as the input for the fully connected layers as shown in Figure 7.
Now the question: why not directly flatten the input image into a single vector 🤔? The answer is that doing that will only keep the pixel values of the image, but not the spatial structure. In another word, it will lose how each pixel is spatially connected to one around it. But with convolution, we get many feature maps, each of which represents a specific feature of the image. Thus, each node in the flattened vector will represent a specific detail of the input image.
Specifically,
classifier.add(Flatten())
3.4 Full connection
With the above, we converted an image into a one-dimensional vector. Now we will build a classifier using this vector as the input layer. First, create a hidden layer. output_dim is the number of nodes in the hidden layer. As a common practice, we choose 128 to start with and use ReLU as the activation function.
classifier.add(Dense(output_dim = 128, activation = ‘relu’))
Then add an output layer. For binary classification, output_dim is 1, and the activation function is Sigmoid.
classifier.add(Dense(output_dim =1, activation = ‘sigmoid’))
Our final model structure is shown below 🎉🎉.
4. Model compiling
With all layers added, let’s compile the CNN by choosing an SGD algorithm, a loss function, and performance metrics. We use binary_crossentropy for binary classification, and use categorical_crossentropy for multiple classification problem.
classifier.compile(optimizer = ‘adam’, loss = ‘binary_crossentropy’, metrics =’accuracy’)
5. Model fitting
Here we have 8000 images for the training set, which is not enough to avoid over-fitting. So, we perform image augmentation, such as rotating, flipping, or shearing to increase the number of images. It splits training images into batches, and each batch will be applied random image transformation on a random selection of images, to create many more diverse images.
Specifically, we will use flow_from_directory(directory) method from Keras Official website to load images and apply augmentation. This is why we structured the data folders in a specific way so that the class of each image can be identified from its folder name. The below code snippet allows us to augment images and fit and test CNN.
train_datagen = ImageDataGenerator(rescale=1./255,      shear_range=0.2, zoom_range=0.2, horizontal_flip=True)test_datagen = ImageDataGenerator(rescale=1./255)train_set = train_datagen.flow_from_directory(‘dataset/training_set’, target_size=(64, 64), batch_size=32, class_mode=’binary’)test_set = test_datagen.flow_from_directory(‘dataset/test_set’, target_size=(64, 64), batch_size=32, class_mode=’binary’)classifier.fit_generator(train_set, steps_per_epoch=8000/32, epochs=25, validation_data=test_set, validation_steps=2000/32)
Above, the target size is set to be (64, 64), the same shape as when we create the Convolution layer. Now, let’s fit and test the model.
In the end, we obtained a training accuracy of 86% and a test accuracy of 76% with a minor over-fitting ✨✨.
6. Model improvement
Obviously, there is room to improve accuracy and reduce over-fitting. Two options are either adding more convolution layers or adding more dense layers. Let’s add one more convolution layer.
classifier.add(Conv2D(32, 3, 3, activation = ‘relu’))classifier.add(MaxPooling2D(pool_size = (2, 2)))
With the above, run the model on training and test sets again. In the end, we obtained an improved test accuracy of 91% and a test accuracy of 82% 🧨🧨!
7. Example prediction
First, we create a folder ‘single_prediction’ for the images to be predicted as shown in Figure 9.
Second, we use the image module from Keras to load test images. Note, set the target_size of the image to be (64, 64).
import numpy as npfrom keras.preprocessing import imagetest_image = image.load_img(‘dataset/single_prediction/cat_or_dog_1.jpg’, target_size = (64, 64))
Remember CNN requires an input image with 3 dimensions. So we need to add a dimension for the channel, from 2D array to 3D array.
test_image = image.img_to_array(test_image)
However, this is not enough, because CNN expects another dimension for the batch. Axis is to specify the position of the dimension we are adding. So batch dimension is added at index 0.
test_image = np.expand_dims(test_image, axis = 0)
Great, time for a prediction.
result = classifier.predict(test_image)
We got a result of 1. To know the mapping between animals and their associated numerical values, we use:
training_set.class_indices
With that, we know 0 is cat, and 1 is a dog. Great. our CNN made a correct prediction!
Great! If you found this post helpful, feel free to hit those 👏 ‘s! If you need the source code, visit my Github page 🤞🤞.