Accuracy is not increasing in images classification when using TensorFlow model

Viewed 64

At my job Interview yesterday, I was asked to build a neural network using TesnorFlow in python to classify images from the flowers images dataset.

But even though it should've worked theoretically, for some reason I couldn't increase the accuracy above 20s%.

Python Version: 3.8.13 TensorFlow Versioin: 2.4.1

The data preprocessing methods from the interviewer were given as follows:

# create datase
IMG_SIZE = 160
BATCH_SIZE = 32
AUTOTUNE = tf.data.experimental.AUTOTUNE
def _parse_data(x,y):
  image = tf.io.read_file(x)  
  image = tf.image.decode_jpeg(image, channels=3) 
  image = tf.cast(image, dtype=tf.float32)
  image = tf.math.l2_normalize(image)
  
  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image,y
 
def _input_fn(x,y):
  ds = tf.data.Dataset.from_tensor_slices((x,y))
  ds = ds.map(_parse_data)
  ds = ds.shuffle(buffer_size=data_size)
  ds = ds.repeat()   
  ds = ds.batch(BATCH_SIZE)    
  ds = ds.prefetch(buffer_size=AUTOTUNE)   
  return ds

train_ds = _input_fn(x_train, y_train)
validation_ds = _input_fn(x_valid, y_valid)  

With both training and validation datasets being

<PrefetchDataset shapes: ((None, 160, 160, 3), (None,)), types: (tf.float32, tf.int32)>

With the network being as follows:

from tensorflow.keras import datasets, layers, models

model_seq = models.Sequential()
    model_seq.add(layers.experimental.preprocessing.RandomFlip("horizontal",input_shape=(IMG_SIZE,IMG_SIZE,3)))
    model_seq.add(layers.experimental.preprocessing.RandomRotation(0.2))
    model_seq.add(layers.experimental.preprocessing.Rescaling(1./255))
    model_seq.add(layers.Conv2D(16, 3, padding='same', activation='relu'))
    model_seq.add(layers.MaxPooling2D())
    model_seq.add(layers.Conv2D(32, 3, padding='same', activation='relu'))
    model_seq.add(layers.MaxPooling2D())
    model_seq.add(layers.Conv2D(64, 3, padding='same', activation='relu'))
    model_seq.add(layers.MaxPooling2D())
    model_seq.add(layers.Dropout(0.2))
    model_seq.add(layers.Flatten())
    model_seq.add(layers.Dense(128, activation='relu'))
    model_seq.add(layers.Dense(len(label_names), activation='softmax'))
    model_seq.summary()

The output layer being the only thing that isn't allowed to be change.

model_seq.add(layers.Dense(len(label_names), activation='softmax'))

(Please note I was for some reason asked to use model_seq.add(), and even though it could be triggering for some of you, please ignore it this once :) )

For compiling the model, I used the following:

model_seq.compile(optimizer="Adam",
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

And for fitting the model:

history = model_seq.fit(train_ds,epochs=20,
                        validation_data = validation_ds,
                        steps_per_epoch=100,validation_steps=100)

The things I've tried:

  1. Using different Augmentation methods (or removing the whole section from the network).

  2. Changing the Batch and Image sizes.

  3. Using Dropout layers.

  4. Using early stopping as follows:

    callback = tf.keras.callbacks.EarlyStopping(
        monitor='val_loss', 
        min_delta=0, patience=3, verbose=0, 
        mode='auto',baseline=None, 
        restore_best_weights=True)
    
    history = model_seq.fit(train_ds,epochs=20,
                            validation_data = validation_ds,
                            steps_per_epoch=100,validation_steps=100,
                            callbacks = [callback])
    

Yet despite all of the above, I couldn't get any results. Since I couldn't find out what I did wrong exactly, I'm hoping someone here could tell me, so I could learn from this experience. (Please take into consideration that I wasn't allowed to change the preprocessing functions, with the parameters IMG_SIZE and BATCH_SIZE being the only exception).

1 Answers

“TLDR: If you want to use their preprocessing part and don't change anything go to First Approach. If you want to augment images, Go to Second Approach and use ImageDataGenerator”.

First Approach As you say: I had to use the preprocessing functions and don't change this and because I don't access your data, I use cifar10 dataset and use your preprocessing part. (only line of reading from file changed).

  1. Because you shouldn't change IMG_SIZE=160 in the preprocessing part, I add this layer : tf.keras.layers.Lambda(lambda image: tf.image.resize(image, (32, 32)))) to the network because working with large images causes a crash.
  2. You don't need a very large network, first check with a small network then step by step add parameters then add layers.

We can get a better result like the below: (On cifar10 with your network I get 10% accuracy like you.)

import tensorflow as tf
(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()

# create datase
IMG_SIZE = 160
BATCH_SIZE = 32
data_size = 32
AUTOTUNE = tf.data.experimental.AUTOTUNE
def _parse_data(x,y):
#   image = tf.io.read_file(x) <- because don't read from path 
  image = x
#   image = tf.image.decode_jpeg(image, channels=3) <- because don't read from path and don't have jpeg
  image = tf.cast(image, dtype=tf.float32)
  image = tf.math.l2_normalize(image)

  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image,y

def _input_fn(x,y):
  ds = tf.data.Dataset.from_tensor_slices((x,y))
  ds = ds.map(_parse_data)
  ds = ds.shuffle(buffer_size=data_size)
  ds = ds.repeat()   
  ds = ds.batch(BATCH_SIZE)    
  ds = ds.prefetch(buffer_size=AUTOTUNE)   
  return ds

train_ds = _input_fn(X_train, y_train)
validation_ds = _input_fn(X_test, y_test) 

model = tf.keras.Sequential([
    tf.keras.Input(shape=(160, 160, 3)),
    tf.keras.layers.Lambda(lambda image: tf.image.resize(image, (32, 32))),
    tf.keras.layers.Conv2D(filters=32,kernel_size=(3,3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(50,activation='relu'),
    tf.keras.layers.Dense(10,activation='softmax')

])

model.compile(optimizer="Adam",
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['accuracy'])

history = model.fit(train_ds,epochs=4,
                        validation_data = validation_ds,
                        steps_per_epoch=100,validation_steps=100)

Output:

Epoch 1/4
100/100 [==============================] - 8s 38ms/step - loss: 2.2445 - accuracy: 0.1375 - val_loss: 2.1406 - val_accuracy: 0.2138
Epoch 2/4
100/100 [==============================] - 3s 33ms/step - loss: 2.0552 - accuracy: 0.2688 - val_loss: 1.9764 - val_accuracy: 0.3250
Epoch 3/4
100/100 [==============================] - 4s 38ms/step - loss: 1.9468 - accuracy: 0.3022 - val_loss: 1.9014 - val_accuracy: 0.3200
Epoch 4/4
100/100 [==============================] - 4s 36ms/step - loss: 1.8936 - accuracy: 0.3341 - val_loss: 1.8883 - val_accuracy: 0.3419

Second Approach: Augment images with ImageDataGenerator:

import tensorflow as tf

flowers = tf.keras.utils.get_file(
    'flower_photos',
    'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
    untar=True)


data_gen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1./255, 
    rotation_range = 10,    # Degree range for random rotations.
    horizontal_flip = True, # Randomly flip inputs horizontally.
    vertical_flip = True,   # Randomly flip inputs vertically.
    )
    
imgs_dataset = data_gen.flow_from_directory(flowers, class_mode='categorical', 
                                            target_size=(160, 160), batch_size=32,
                                            shuffle=True)


model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(filters=32,kernel_size=(3,3),input_shape=(160, 160, 3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(50,activation='relu'),
    tf.keras.layers.Dense(5,activation='softmax')

])
model.compile(optimizer='adam', loss='categorical_crossentropy',metrics=['accuracy'])

model.fit(imgs_dataset,epochs=5)

Output:

Found 3670 images belonging to 5 classes.
Epoch 1/5
115/115 [==============================] - 39s 311ms/step - loss: 1.7904 - accuracy: 0.4161
Epoch 2/5
115/115 [==============================] - 27s 236ms/step - loss: 1.0878 - accuracy: 0.5605
Epoch 3/5
115/115 [==============================] - 28s 244ms/step - loss: 1.0252 - accuracy: 0.6005
Epoch 4/5
115/115 [==============================] - 27s 233ms/step - loss: 0.9735 - accuracy: 0.6196
Epoch 5/5
115/115 [==============================] - 29s 248ms/step - loss: 0.9313 - accuracy: 0.6455
Related