Improving Prediction Algorithm Performance with Keras Using Data Augmentation Technique

Esra Soylu
İstanbul Data Science Academy

--

One of the most important factors in achieving successful results in Deep Learning models is the number of data. The more training data there is, the more features that deep learning models can extract and therefore the higher the accuracy rate. We may not always find enough data to train images. At this stage, data augmentation is a method applied in deep learning. In this way, it is possible to obtain different data from the same data.
In the data augmentation method, diversity in the data is increased by steps such as applying filters, creating the image from different angles, and going through processes such as mirroring. This method also prevents oversampling.

In this article, we will discuss the Keras library and the ImageDataGenerator class, which performs data augmentation automatically in Keras. Let’s start by importing the necessary libraries.

pip install keras
pip install tensorflow

Before applying data augmentation, we will create a CNN model with labeled data for two classifications and observe the accuracy and loss values. Then, we will attempt to enhance the performance metrics of accuracy and loss using the data augmentation method.

To gain more detailed information about the code and layers used in the CNN model, you can refer to my previously written Medium article.

Let’s create the convolutional and pooling layers in the CNN model. Convolutional layers (Conv2D) are used to extract features from the image, while pooling layers (MaxPooling2D) are employed to reduce the feature map size. At the end of the model, we utilize the sigmoid activation function, commonly used for binary classification tasks, to classify the feature vector.

from keras import layers
from keras.models import Sequential
model=Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(150,150,3)))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(256,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Flatten())

model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(1,activation="sigmoid"))

We are determining which loss function, optimization algorithm, and metrics will be used for the model

from tensorflow.keras import optimizers
model.compile(loss="binary_crossentropy", optimizer=optimizers.RMSprop(learning_rate=1e-4),metrics=["acc"])

Let’s import the ImageDataGenerator class from TensorFlow Keras to prepare and preprocess image data. We’ll use the train_datagen for processing the training dataset and validation_datagen for processing the validation dataset. The rescale parameter is utilized to scale pixel values between 0 and 255, which helps in creating better training performance.

from tensorflow.keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)
validation_datagen = ImageDataGenerator(rescale=1./255)

Let’s specify the directories containing the images to be used for training and validation in the classification model. We’ll add the codes to ensure that train_datagen and validation_datagen objects are all of the same size, specify batch sizes during training, and indicate that binary classification will be use.

train_directory="catdog/train"
validation_directory="catdog/valid"
train_generator=train_datagen.flow_from_directory(train_directory,target_size=(150,150),batch_size=20,class_mode="binary")
validation_generator=train_datagen.flow_from_directory(validation_directory,target_size=(150,150),batch_size=20,class_mode="binary")
Before data augmentation, the number of data samples

Let’s initiate the training of the deep learning model, performing the specified number of epochs for both training and validation processes during the training.

history=model.fit(train_generator,epochs=20,
validation_data=validation_generator,validation_steps=50)
The training conducted without applying data augmentation

Let’s examine the data by visualizing it.

import matplotlib.pyplot as plt
import numpy as np

plt.style.use("seaborn-darkgrid")
plt.figure()
epochs = 20

plt.plot(np.arange(0, epochs), history.history["loss"], label="train_loss")
plt.plot(np.arange(0, epochs), history.history["val_loss"], label="val_loss")

plt.plot(np.arange(0, epochs), history.history["acc"], label="train_acc")
plt.plot(np.arange(0, epochs), history.history["val_acc"], label="val_acc")

plt.title("Training Loss and Accuracy")
plt.xlabel("Epochs")
plt.ylabel("Loss and Accuracy")
plt.legend(loc="lower left")
plt.show()
We can say that there is a discrepancy and imbalance in the distribution of the training loss and accuracy values compared to the validation loss and accuracy values, indicating that sufficient learning has not occurred.

Let’s perform the same procedure by adding data augmentation techniques to improve the performance of both the training and validation values. Changes made in the above codes will be explained.

“We are adding the dropout command during the model creation process to prevent overfitting, introducing a parameter to mitigate it.”

model1=Sequential()
model1.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(150,150,3)))
model1.add(layers.MaxPooling2D((2,2)))

model1.add(layers.Conv2D(64,(3,3),activation="relu"))
model1.add(layers.MaxPooling2D((2,2)))

model1.add(layers.Conv2D(128,(3,3),activation="relu"))
model1.add(layers.MaxPooling2D((2,2)))

model1.add(layers.Conv2D(256,(3,3),activation="relu"))
model1.add(layers.MaxPooling2D((2,2)))

model1.add(layers.Flatten())
model1.add(layers.Dropout(0.4))

model1.add(layers.Dense(512,activation="relu"))
model1.add(layers.Dense(1,activation="sigmoid"))

model1.summary

model1.compile(loss="binary_crossentropy", optimizer=optimizers.RMSprop(learning_rate=1e-4),metrics=["acc"])

We’ll add parameters to apply data augmentation techniques.

train_datagen_y= ImageDataGenerator(rescale=1./255,
rotation_range=125,
width_shift_range=0.3,
height_shift_range=0.3,
shear_range=0.3,
zoom_range=0.3,
horizontal_flip=True,fill_mode="neares")
validation_datagen_y = ImageDataGenerator(rescale=1./255)

Let’s explain the functionalities of the parameters with example images.

Rotation_Range: Randomly rotates the images by the specified degree

Width_Shift_Range: Specifies how much the image will be randomly shifted horizontally, expressed as a fraction of the total width.

Height_Shift_Range: Specifies how much the image will be randomly shifted vertically, expressed as a fraction of the total height.

Shear_Range: Shifts the image along a specified axis based on a shearing angle.

Zoom_Range: Specifies how much the image will be randomly zoomed in, expressed as a ratio.

Horizontal_flip/Vertical_flip: Randomly flips the image horizontally or vertically.

fill_mode=nearest: Specifies the fill mode to be used for filling in empty pixels generated during rotation and shifting operations. “nearest” ensures that the nearest pixel value is used

train_generator_y=train_datagen_y.flow_from_directory(train_directory,target_size=(150,150),batch_size=16,class_mode="binary")

validation_generator_y=validation_datagen_y.flow_from_directory(train_directory,target_size=(150,150),batch_size=16,class_mode="binary")
The number of data samples after applying data augmentation

Let’s proceed with training for 60 epochs after applying data augmentation methods.

history1=model1.fit(train_generator,epochs=60,steps_per_epoch=60,
validation_data=validation_generator_y,validation_steps=50)

Let’s reevaluate the accuracy and loss values after applying the data augmentation technique.

irmport matplotlib.pyplot as plt
import numpy as np

plt.style.use("seaborn-darkgrid")
plt.figure()
epochs = 60

plt.plot(np.arange(0, epochs), history1.history["loss"], label="train_loss")
plt.plot(np.arange(0, epochs), history1.history["val_loss"], label="val_loss")

plt.plot(np.arange(0, epochs), history1.history["acc"], label="train_acc")
plt.plot(np.arange(0, epochs), history1.history["val_acc"], label="val_acc")

plt.title("Training Loss and Accuracy")
plt.xlabel("Epochs")
plt.ylabel("Loss and Accuracy")
plt.legend(loc="lower left")
plt.show()
After applying data augmentation, we observe that the loss and accuracy curves indicate more successful learning.

--

--