In this blog, we will learn how to perform binary classification using Convolution Neural Networks. Here, we will be using the classic dogs vs cats dataset, where we have to classify an image as belonging to one of these two classes. So, let’s get started.
Downloading the Dataset
This dataset was made available as a part of the Kaggle competition in 2013. You can download it from here. This dataset contains 25,000 labeled images of dogs and cats in the train folder and 12,500 unlabeled images in the test folder. The size of the images in the dataset is not the same. Some samples from the dataset are shown below
Since the competition is now closed, we can’t submit the test predictions to the kaggle. Thus, to know how good we are doing, we will make the test data from 25,000 labeled images.
Preparing the Data
Here, we will split the train folder into 20,000 for training, and 2500 each for validation and testing. For this, we will create 3 folders corresponding to each train, validation and test set. In these folders, we will create 2 sub-folders as cats and dogs. You can do this manually but here we will be using the Python os module. The code for creating folders and sub-folders is shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import os, shutil # This is the path where you want to create these folders original_path = 'D:/downloads/Data' # Create 3 folders train, validation and test train_dir = os.path.join(original_path,'train1') os.mkdir(train_dir) validation_dir = os.path.join(original_path,'validation1') os.mkdir(validation_dir) test_dir = os.path.join(original_path,'test1') os.mkdir(test_dir) # Create 2 sub-folders train_cats_dir = os.path.join(train_dir,'cats') os.mkdir(train_cats_dir) train_dogs_dir = os.path.join(train_dir,'dogs') os.mkdir(train_dogs_dir) # Validation directory for cats and dogs validation_cats_dir = os.path.join(validation_dir,'cats') os.mkdir(validation_cats_dir) validation_dogs_dir = os.path.join(validation_dir,'dogs') os.mkdir(validation_dogs_dir) # test directory for cats and dogs test_cats_dir = os.path.join(test_dir,'cats') os.mkdir(test_cats_dir) test_dogs_dir = os.path.join(test_dir,'dogs') os.mkdir(test_dogs_dir) |
The above code will create folders and sub-folders in the original path specified above. Now, we will put the images in these folders. The below code places 20,000 images in train, 2500 each in the validation and test folder created above.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# This is the path where the downloaded train data is original_train = os.path.join(original_path,'train') # Put 20,000 images in train, 2500 each in the validation # and test folder created above for fname in os.listdir(original_train): category, index,_ = fname.split('.') index = int(index) src = os.path.join(original_train,fname) if category == 'cat': if index < 10000: out = os.path.join(train_cats_dir,fname) shutil.copy(src,out) elif index>=10000 and index<11250: out = os.path.join(validation_cats_dir) shutil.copy(src,out) elif index>=11250: out = os.path.join(test_cats_dir,fname) shutil.copy(src,out) else: if index < 10000: out = os.path.join(train_dogs_dir,fname) shutil.copy(src,out) elif index>=10000 and index<11250: out = os.path.join(validation_dogs_dir) shutil.copy(src,out) elif index>=11250: out = os.path.join(test_dogs_dir,fname) shutil.copy(src,out) |
Now, let’s display a sample image from say “train_cats_dir”. This is done using the following code.
1 2 3 4 5 6 7 |
import numpy as np from keras.preprocessing.image import load_img import matplotlib.pyplot as plt fname = np.random.choice(os.listdir(train_cats_dir)) img = load_img(os.path.join(train_cats_dir, fname)) plt.imshow(img) |
Data Pre-processing
The data must be processed in an appropriate form before feeding in the neural network. This includes changing the data into numpy arrays, normalizing the values between 0 and 1 or any other suitable range, etc. This can be easily done using the keras ImageDataGenerator class. This is shown in the code below
1 2 3 4 5 |
from keras.preprocessing.image import ImageDataGenerator # Normalizing the image values between 0 and 1 train_datagen = ImageDataGenerator(rescale=1./255) validation_datagen = ImageDataGenerator(rescale=1./255) |
Here, we will use the flow_from_directory method to generate batches of data.
1 2 |
train_generator = train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=100,class_mode='binary',interpolation='bilinear') validation_generator = validation_datagen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=100,class_mode='binary',interpolation='bilinear') |
Build Model
Since this is a binary classification problem, we use the sigmoid activation function in the last layer. The model architecture we will use is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from keras.layers import Dense, Flatten, Conv2D, MaxPool2D, Dropout from keras.models import Sequential model = Sequential() model.add(Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3))) model.add(MaxPool2D((2,2))) model.add(Conv2D(64,(3,3),activation='relu')) model.add(MaxPool2D((2,2))) model.add(Conv2D(128,(3,3),activation='relu')) model.add(MaxPool2D((2,2))) model.add(Conv2D(128,(3,3),activation='relu')) model.add(MaxPool2D((2,2))) model.add(Flatten()) model.add(Dense(512,activation='relu')) model.add(Dense(1,activation='sigmoid')) |
1 |
model.summary() |
For the compilation step, we will use the Adam optimizer with the binary crossentropy loss.
1 |
model.compile(loss='binary_crossentropy',optimizer = 'Adam',metrics=['accuracy']) |
Callbacks
To have some control over the training, one must use callbacks. Here, we will be using ModelCheckpoint callback which save the model weights whenever the validation accuracy improves.
1 2 3 |
from keras.callbacks import ModelCheckpoint filepath = 'D:/downloads/best_weights.hdf5' checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=0, save_best_only=True, save_weights_only=True, mode='auto',period=1) |
Fit Model
1 |
history = model.fit_generator(train_generator, steps_per_epoch=200, epochs=20, validation_data=validation_generator, validation_steps=25, callbacks=[checkpoint]) |
Visualize Training
Let’s visualize how the loss and accuracy vary during the training process. This is done using the History() object as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
train_acc = history.history['acc'] validation_acc = history.history['val_acc'] train_loss = history.history['loss'] validation_loss = history.history['val_loss'] epochs = range(1,21) plt.plot(epochs,train_acc,'bo',label='Training Accuracy') plt.plot(epochs,validation_acc,'b',label='Validation Accuracy') plt.title('Training and Validation Accuracy') plt.legend() plt.figure() plt.plot(epochs,train_loss,'bo',label='Training Loss') plt.plot(epochs,validation_loss,'b',label='Validation Loss') plt.title('Training and Validation Loss') plt.legend() |
Clearly, our model starts overfitting after 8 epoch. We know that to prevent overfitting, we can
- Perform Data Augmentation
- Use Dropout
- Reduce the capacity of the network
- Use Regularization etc.
So, let’s use Data Augmentation and Dropout and see how our model performs.
Data Augmentation
In this, we produce more examples from the existing examples by various operations such as rotating, translating, flipping, etc. Fortunately, in Keras, all these transformations can be performed using ImageDataGenerator class. Below is the code for this
1 2 3 4 5 6 7 |
train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) |
Dropout
In this, we randomly turn off some neurons (setting to zero) during training. This can be easily implemented using the Keras Dropout layer. Here, I’ve just added a single Dropout layer before the Dense layer, with a dropout rate of 50%. Rest all the model is same as above.
1 2 3 |
model.add(Flatten()) model.add(Dropout(0.5)) model.add(Dense(512,activation='relu')) |
Just change the train_datagen and add Dropout layer. Then, train the model the same as we did above. Let’s visualize the training process
Clearly, by looking at the plots, we are no longer overfitting. Just by adding a Dropout layer and augmentation, we have increased the accuracy from 87 to 95%. You can further improve the accuracy by using a pre-trained model and other regularization methods.
Hope you enjoy reading.
If you have any doubt/suggestion please feel free to ask and I will do my best to help or improve myself. Good-bye until next time.