Stat Tistician
Stat Tistician

Reputation: 883

Image Classification using tf.keras CNN questions about kernel and how to create test dataset?

I have a colab with which I am working. It is about using keras to build CNN. My code so far which works is as follows (epochs set to 5 due to time reasons):

import os as os
import numpy as np
import glob as gl
import matplotlib.pyplot as plt
import shutil

import tensorflow as tf

from tensorflow.keras.preprocessing.image import ImageDataGenerator

URL = 'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz'

zip_dir = tf.keras.utils.get_file('flower_photos.tgz', URL, extract=True)

base_dir = os.path.join(os.path.dirname(zip_dir), 'flower_photos')

labels = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

for label in labels:
  imagepath = os.path.join(base_dir, label)
  images = gl.glob(imagepath + '/*.jpg')
  print("{}: {} Images".format(label, len(images)))
  train, val = images[:round(len(images)*0.8)], images[round(len(images)*0.8):]

  for t in train:
    if not os.path.exists(os.path.join(base_dir, 'train', label)):
      os.makedirs(os.path.join(base_dir, 'train', label))
    shutil.move(t, os.path.join(base_dir, 'train', label))

  for v in val:
    if not os.path.exists(os.path.join(base_dir, 'val', label)):
      os.makedirs(os.path.join(base_dir, 'val', label))
    shutil.move(v, os.path.join(base_dir, 'val', label))

traindir = os.path.join(base_dir, 'train')
val_dir = os.path.join(base_dir, 'val')

batch_size = 100
img_size = 150

image_gen_train = ImageDataGenerator(rescale = 1./255,
    rotation_range=45,
    width_shift_range=0.15,
    zoom_range=0.5,
    height_shift_range=0.15,
    horizontal_flip=True)


image_gen_val = ImageDataGenerator(rescale = 1./255)


train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                               directory=traindir,
                                               shuffle=True,
                                               target_size=(img_size,img_size),
                                               class_mode='sparse')

val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                               directory=val_dir,
                                               target_size=(img_size,img_size),
                                               class_mode='sparse')

def plotImages(images_arr):
    fig, axes = plt.subplots(1, 5, figsize=(20,20))
    axes = axes.flatten()
    for img, ax in zip( images_arr, axes):
        ax.imshow(img)
    plt.tight_layout()
    plt.show()


augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, (3,3), padding='same',  activation='relu', input_shape=(img_size, img_size, 3)),
    tf.keras.layers.MaxPooling2D(2, 2),

    tf.keras.layers.Conv2D(32, (3,3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),

    tf.keras.layers.Conv2D(64, (3,3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),

    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.summary()

epochs = 5

history = model.fit(
    train_data_gen,
    epochs=epochs,
    validation_data=val_data_gen
)

Now I have two questions (the second one being more important): I use a kernel of 3,3 ( tf.keras.layers.Conv2D(16, (3,3)). In the solution however, they just use:

model.add(Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_SHAPE,IMG_SHAPE, 3)))

So just the integer 3. Why this? The task clearly states "All convolutional filters should be 3 x 3".

My second question: I would like to have not only a train and validation set, but also a test set. At the end I want to check my performance on the test dataset. So I do use the training and validation dataset in the model training, but furthermore at the end I want to compare it with the following statment:

# Evaluate against the test set.
print("\n Evaluate the new model against the test set:")
my_model.evaluate(x=x_test_normalized, y=y_test, batch_size=batch_size)

How does the code needs to be modified in order to get a training, validation and test set?

I tried to use the validation split and hoped I could use the up to know called validation set as a test set:

history = model.fit(
    train_data_gen,
    epochs=epochs,
    validation_split= 0.2
)

my_model.evaluate(x=val_data_gen, y=train_data_gen, batch_size=batch_size)

However, this does not work as my input in the model fit does not support validation split, the error message tells me.

Upvotes: 2

Views: 198

Answers (1)

Sanchit.Jain
Sanchit.Jain

Reputation: 608

Your first question: So just the integer 3. Why this? The task clearly states "All convolutional filters should be 3 x 3"

Answer: Refering to the documentation https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2D, kernel_size can be integer or tuple/list of 2 integers, specifying the height and width of the 2D convolution window. Can be a single integer to specify the same value for all spatial dimensions. So the kernel is taking shape of 3 x 3 by providing single integer 3.

Your second question: How does the code needs to be modified in order to get a training, validation and test set?

Answer: You can refer this link How to split data into 3 sets (train, validation and test)?

Edit: Second answer complete exmaple for what you have asked

import os
import glob
import matplotlib.pyplot as plt
import shutil
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

URL = 'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz'

zip_dir = tf.keras.utils.get_file('flower_photos.tgz', URL, extract=True)

base_dir = os.path.join(os.path.dirname(zip_dir), 'flower_photos')

labels = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

for label in labels:
    imagepath = os.path.join(base_dir, label)
    images = glob.glob(imagepath + '/*.jpg')
    print("{}: {} Images".format(label, len(images)))

    # here you have split dataset into 80:20 ratio for train and validation
    train, val = images[:round(len(images) * 0.8)], images[round(len(images) * 0.8):]

    # here we will split train into 75:25 ratio for train and test
    train, test = train[:round(len(train) * 0.75)], train[round(len(train) * 0.75):]
    # Note: Second one is 75:25 because overall dataset of one label will be split into 60:20:20

    for t in train:
        if not os.path.exists(os.path.join(base_dir, 'train', label)):
            os.makedirs(os.path.join(base_dir, 'train', label))
        shutil.move(t, os.path.join(base_dir, 'train', label))

    for v in val:
        if not os.path.exists(os.path.join(base_dir, 'val', label)):
            os.makedirs(os.path.join(base_dir, 'val', label))
        shutil.move(v, os.path.join(base_dir, 'val', label))

    # we will create same thing for test dataset like we did for train and val above
    for t in test:
        if not os.path.exists(os.path.join(base_dir, 'test', label)):
            os.makedirs(os.path.join(base_dir, 'test', label))
        shutil.move(t, os.path.join(base_dir, 'test', label))

train_dir = os.path.join(base_dir, 'train')
val_dir = os.path.join(base_dir, 'val')
test_dir = os.path.join(base_dir, 'test')

batch_size = 100
img_size = 150

image_gen_train = ImageDataGenerator(rescale=1. / 255,
                                     rotation_range=45,
                                     width_shift_range=0.15,
                                     zoom_range=0.5,
                                     height_shift_range=0.15,
                                     horizontal_flip=True)

image_gen_val = ImageDataGenerator(rescale=1. / 255)

image_gen_test = ImageDataGenerator(rescale=1. / 255)

train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                                     directory=train_dir,
                                                     shuffle=True,
                                                     target_size=(img_size, img_size),
                                                     class_mode='sparse')

val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                                 directory=val_dir,
                                                 target_size=(img_size, img_size),
                                                 class_mode='sparse')

# we will create a test data generator here
test_data_gen = image_gen_test.flow_from_directory(batch_size=batch_size,
                                                   directory=test_dir,
                                                   target_size=(img_size, img_size),
                                                   class_mode='sparse')


def plotImages(images_arr):
    fig, axes = plt.subplots(1, 5, figsize=(20, 20))
    axes = axes.flatten()
    for img, ax in zip(images_arr, axes):
        ax.imshow(img)
    plt.tight_layout()
    plt.show()


augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, (3, 3), padding='same', activation='relu',
                           input_shape=(img_size, img_size, 3)),
    tf.keras.layers.MaxPooling2D(2, 2),

    tf.keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(2, 2),

    tf.keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(2, 2),

    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.summary()

epochs = 5

history = model.fit(
    train_data_gen,
    epochs=epochs,
    validation_data=val_data_gen
)

# evaluate model using test data generator
model.evaluate(test_data_gen, batch_size=batch_size)

# to know what above scalar values represent
print(model.metrics_names)

Upvotes: 2

Related Questions