Classifying cats versus dogs with data generators#

Keras provides an API called data generator to load and transform data in batches. Data generators are also very useful for image classification. The cats and dogs image dataset comes in the form of a folder with predefined structures for the training and testing sets and for the different classes (all images that belong to a class are stored in the same folder). The data generator API will be able to understand this structure and feed the CNN model properly with the relevant images and corresponding information.

1raise SystemExit("Stop right there!");
An exception has occurred, use %tb to see the full traceback.

SystemExit: Stop right there!

Importing libraries and packages#

 1# System
 2import os
 3import pathlib
 4
 5# Mathematical operations and data manipulation
 6import numpy as np
 7
 8# Modelling
 9import tensorflow as tf
10from tensorflow.keras import layers
11from tensorflow.keras.preprocessing.image import ImageDataGenerator
1os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

Set paths#

1path = pathlib.Path(r"./datasets/cats_and_dogs_filtered")
2# Path to cats_and_dogs_filtered directory
3train_path = path / "train"
4# Path to validation directory
5validation_path = path / "validation"
1train_cats_path = train_path / "cats"
2train_dogs_path = train_path / "dogs"
3validation_cats_path = validation_path / "cats"
4validation_dogs_path = validation_path / "dogs"
1# Creating two variables called total_train and total_val that will
2# get the number of images for the training and validation sets
3total_train = len(os.listdir(train_cats_path)) + len(
4    os.listdir(train_dogs_path)
5)
6total_val = len(os.listdir(validation_cats_path)) + len(
7    os.listdir(validation_dogs_path)
8)

Data generators#

 1# Instantiating two ImageDataGenerator classes
 2data_gen_train = ImageDataGenerator(rescale=1.0 / 255)
 3data_gen_valid = ImageDataGenerator(rescale=1.0 / 255)
 4
 5batch_size = 16
 6img_height = 100
 7img_width = 100
 8
 9# Create data generators
10train_generator = data_gen_train.flow_from_directory(
11    train_path,
12    target_size=(img_height, img_width),
13    batch_size=batch_size,
14    shuffle=True,
15    class_mode="binary",
16)
17
18valid_generator = data_gen_valid.flow_from_directory(
19    validation_path,
20    target_size=(img_height, img_width),
21    batch_size=batch_size,
22    shuffle=True,
23    class_mode="binary",
24)
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.

Training of the network#

1# Setting 8 as the seed for numpy and tensorflow
2np.random.seed(8)
3tf.random.set_seed(8)
 1model = tf.keras.Sequential(
 2    [
 3        layers.Conv2D(
 4            64, 3, activation="relu", input_shape=(img_height, img_width, 3)
 5        ),
 6        layers.MaxPooling2D(),
 7        layers.Conv2D(128, 3, activation="relu"),
 8        layers.MaxPooling2D(),
 9        layers.Flatten(),
10        layers.Dense(128, activation="relu"),
11        layers.Dense(1, activation="sigmoid"),
12    ]
13)
1optimizer = tf.keras.optimizers.Adam(0.001)
1# Providing training parameters using the compile method
2model.compile(
3    loss="binary_crossentropy", optimizer=optimizer, metrics=["accuracy"]
4)
1# Inspecting the model configuration using the summary function
2model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 98, 98, 64)        1792      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 49, 49, 64)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 47, 47, 128)       73856     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 23, 23, 128)       0         
_________________________________________________________________
flatten (Flatten)            (None, 67712)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               8667264   
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 129       
=================================================================
Total params: 8,743,041
Trainable params: 8,743,041
Non-trainable params: 0
_________________________________________________________________
1# Train the model by calling the fit method
2model.fit(
3    train_generator,
4    epochs=5,
5    steps_per_epoch=total_train // batch_size,
6    validation_data=valid_generator,
7    validation_steps=total_val // batch_size,
8)
Epoch 1/5
125/125 [==============================] - 30s 234ms/step - loss: 0.7874 - accuracy: 0.5450 - val_loss: 0.6732 - val_accuracy: 0.5716
Epoch 2/5
125/125 [==============================] - 32s 255ms/step - loss: 0.6360 - accuracy: 0.6439 - val_loss: 0.6324 - val_accuracy: 0.6421
Epoch 3/5
125/125 [==============================] - 32s 259ms/step - loss: 0.5711 - accuracy: 0.6842 - val_loss: 0.6285 - val_accuracy: 0.6593
Epoch 4/5
125/125 [==============================] - 32s 259ms/step - loss: 0.4588 - accuracy: 0.7856 - val_loss: 0.6368 - val_accuracy: 0.7016
Epoch 5/5
125/125 [==============================] - 33s 262ms/step - loss: 0.3320 - accuracy: 0.8542 - val_loss: 0.6925 - val_accuracy: 0.6774
<tensorflow.python.keras.callbacks.History at 0x7f56082ba400>

The model is overfitting quite a lot.