MNIST Dataset - 3 Layers + CNN (Using Convolutions) + Using ImageDataGenerator

In [1]:
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import os

Load and explore the data

In [2]:
from tensorflow.keras.preprocessing.image import load_img

base_dir = "./data/"
happy_dir = os.path.join(base_dir, "happy/")
sad_dir = os.path.join(base_dir, "sad/")

print("Sample happy image:")
plt.imshow(load_img(f"{os.path.join(happy_dir, os.listdir(happy_dir)[0])}"))
plt.show()

print("\nSample sad image:")
plt.imshow(load_img(f"{os.path.join(sad_dir, os.listdir(sad_dir)[0])}"))
plt.show()
Sample happy image:
Sample sad image:

Let's check the images shapes and pixel values:

In [3]:
from tensorflow.keras.preprocessing.image import img_to_array

# Load the first example of a happy face
sample_image  = load_img(f"{os.path.join(happy_dir, os.listdir(happy_dir)[0])}")

# Convert the image into its numpy array representation
sample_array = img_to_array(sample_image)

print(f"Each image has shape: {sample_array.shape}")

print(f"The maximum pixel value used is: {np.max(sample_array)}")
Each image has shape: (150, 150, 3)
The maximum pixel value used is: 255.0

Looks like the images have a resolution of 150x150. This is very important because this will be the input size of the first layer in our network.

The last dimension refers to each one of the 3 RGB channels that are used to represent colored images.

Defining the callback

In [4]:
class myCallback(tf.keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs={}):
        if logs.get('accuracy') is not None and logs.get('accuracy') > 0.999:
            print("\nReached 99.9% accuracy so cancelling training!")
            self.model.stop_training = True

Pre-processing the data

In [6]:
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Instantiate the ImageDataGenerator class.
# Remember to set the rescale argument.
train_datagen = ImageDataGenerator(rescale=1/255)

# Specify the method to load images from a directory and pass in the appropriate arguments:
# - directory: should be a relative path to the directory containing the data
# - targe_size: set this equal to the resolution of each image (excluding the color dimension)
# - batch_size: number of images the generator yields when asked for a next batch. Set this to 10.
# - class_mode: How the labels are represented. Should be one of "binary", "categorical" or "sparse".
#               Pick the one that better suits here given that the labels are going to be 1D binary labels.
train_generator = train_datagen.flow_from_directory("./data/",
                                                    target_size=(150, 150),
                                                    batch_size=10,
                                                    class_mode='binary')
Found 80 images belonging to 2 classes.

Creating and training our model

In [7]:
from tensorflow.keras import optimizers, losses
from tensorflow.keras.optimizers import RMSprop

# Instantiate the callback
callbacks = myCallback()

# Define the model
model = tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
        tf.keras.layers.MaxPooling2D(2, 2),
        tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),
        tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(1, activation='sigmoid')
])

# Compile the model
model.compile(loss='binary_crossentropy',
              optimizer=RMSprop(learning_rate=0.001),
              metrics=['accuracy']) 



# Train the model
history = model.fit(train_generator,
                    epochs=20,
                    callbacks=[myCallback()]
                   ) 
Epoch 1/20
8/8 [==============================] - 2s 189ms/step - loss: 4.7697 - accuracy: 0.5250
Epoch 2/20
8/8 [==============================] - 2s 199ms/step - loss: 0.6035 - accuracy: 0.6750
Epoch 3/20
8/8 [==============================] - 2s 184ms/step - loss: 0.5458 - accuracy: 0.7125
Epoch 4/20
8/8 [==============================] - 2s 195ms/step - loss: 0.2371 - accuracy: 0.9000
Epoch 5/20
8/8 [==============================] - 2s 200ms/step - loss: 0.1369 - accuracy: 0.9750
Epoch 6/20
8/8 [==============================] - 2s 197ms/step - loss: 0.1991 - accuracy: 0.9000
Epoch 7/20
8/8 [==============================] - 2s 191ms/step - loss: 0.1608 - accuracy: 0.9625
Epoch 8/20
8/8 [==============================] - 2s 197ms/step - loss: 0.0857 - accuracy: 0.9750
Epoch 9/20
8/8 [==============================] - 2s 197ms/step - loss: 0.0683 - accuracy: 0.9750
Epoch 10/20
8/8 [==============================] - 2s 212ms/step - loss: 0.0316 - accuracy: 0.9875
Epoch 11/20
8/8 [==============================] - ETA: 0s - loss: 0.0080 - accuracy: 1.0000
Reached 99.9% accuracy so cancelling training!
8/8 [==============================] - 2s 195ms/step - loss: 0.0080 - accuracy: 1.0000
In [9]:
print(f"Model reached the desired accuracy after {len(history.epoch)} epochs")
Model reached the desired accuracy after 11 epochs