0

I'm trying to convert this tflearn DCNN sample (using image preprocessing and augmemtation) to keras:

Tflearn sample:

import tflearn
from tflearn.data_utils import shuffle, to_categorical
from tflearn.layers.core import input_data, dropout, fully_connected
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.estimator import regression
from tflearn.data_preprocessing import ImagePreprocessing
from tflearn.data_augmentation import ImageAugmentation

# Data loading and preprocessing
from tflearn.datasets import cifar10
(X, Y), (X_test, Y_test) = cifar10.load_data()
X, Y = shuffle(X, Y)
Y = to_categorical(Y, 10)
Y_test = to_categorical(Y_test, 10)

# Real-time data preprocessing
img_prep = ImagePreprocessing()
img_prep.add_featurewise_zero_center()
img_prep.add_featurewise_stdnorm()

# Real-time data augmentation
img_aug = ImageAugmentation()
img_aug.add_random_flip_leftright()
img_aug.add_random_rotation(max_angle=25.)

# Convolutional network building
network = input_data(shape=[None, 32, 32, 3],
                     data_preprocessing=img_prep,
                     data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu')
network = conv_2d(network, 64, 3, activation='relu')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu')
network = dropout(network, 0.5)
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                     loss='categorical_crossentropy',
                     learning_rate=0.001)

# Train using classifier
model = tflearn.DNN(network, tensorboard_verbose=0)
model.fit(X, Y, n_epoch=50, shuffle=True, validation_set=(X_test, Y_test),
          show_metric=True, batch_size=96, run_id='cifar10_cnn')

This yielded the following results after 50 epochs:

Training Step: 26050  | total loss: 0.35260 | time: 144.306s
| Adam | epoch: 050 | loss: 0.35260 - acc: 0.8785 | val_loss: 0.64622 - val_acc: 0.8212 -- iter: 50000/50000

I then tried to convert it to Keras using the same DCNN layers, parameters and image preprocessing/augmentation:

import numpy as np
from keras.datasets import cifar10
from keras.callbacks import TensorBoard
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D, UpSampling2D, AtrousConvolution2D
from keras.layers.advanced_activations import LeakyReLU, PReLU
from keras.utils import np_utils
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K
import matplotlib
from matplotlib import pyplot as plt

np.random.seed(1337)

batch_size = 96 # how many images to process at once
nb_classes = 10 # how many types of objects we can detect in this set
nb_epoch = 50 # how long we train the system
img_rows, img_cols = 32, 32 # image dimensions
nb_filters = 32 # number of convolutional filters to use
pool_size = (2, 2) # size of pooling area for max pooling
kernel_size = (3, 3) # convolution kernel size

(X_train, Y_train), (X_test, Y_test) = cifar10.load_data()
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 3)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 3)
input_shape = (img_rows, img_cols, 3)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(Y_train, nb_classes)
Y_test = np_utils.to_categorical(Y_test, nb_classes)

datagen = ImageDataGenerator(featurewise_center=True,
                             featurewise_std_normalization=True,
                             horizontal_flip=True,
                             rotation_range=25)
datagen.fit(X_train)

model = Sequential()
model.add(Conv2D(nb_filters, kernel_size, padding='valid', input_shape=input_shape, activation='relu'))
model.add(MaxPooling2D(pool_size=pool_size))
model.add(Conv2D(nb_filters*2, kernel_size, activation='relu'))
model.add(Conv2D(nb_filters*2, kernel_size, activation='relu'))
model.add(MaxPooling2D(pool_size=pool_size))
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes, activation='softmax'))

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

# Set up TensorBoard
tb = TensorBoard(log_dir='./logs')

history = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), epochs=nb_epoch, shuffle=True, verbose=1, validation_data=(X_test, Y_test), callbacks=[tb])
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0])
print("Accuracy: %.2f%%" % (score[1]*100))

plt.plot(history.epoch,history.history['val_acc'],'-o',label='validation')
plt.plot(history.epoch,history.history['acc'],'-o',label='training')
plt.legend(loc=0)
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.grid(True)
plt.show()

This yielded far worse validation accuracy results:

Epoch 50/50
521/521 [==============================] - 84s 162ms/step - loss: 0.4723 - acc: 0.8340 - val_loss: 3.2970 - val_acc: 0.2729
Test score: 3.2969648239135743
Accuracy: 27.29%

Can anyone help me understand why? Have I misapplied/misunderstood image preprocessing/augmentation in Keras?

tokyotribe
  • 33
  • 3

1 Answers1

3

In your Keras model, you have forgotten to normalize the validation data as well. You can do this either by using datagen.mean and datagen.std computed over the training data:

# normalize test data; add a small constant to avoid division by zero,
# you can alternatively use `keras.backend.epsilon()`
X_test = (X_test - datagen.mean) / (datagen.std + 1e-8) 

or you can use the datagen.standardize() method to normalize test data:

X_test = datagen.standardize(X_test)

Look at this question on SO for more info: How does data normalization work in keras during prediction?

Don't forget that you should normalize test data by the statistics computed over the training data. NEVER EVER normalize test data by its own statistics.

Caveat: It seems that standardize alters its argument as well... yes, you can confirm this in the source code.

today
  • 32,602
  • 8
  • 95
  • 115
  • That worked, thanks! I used standardize() on X_test and validation accuracy improved significantly to 78.5%. Any idea why it's still lower than the tflearn model (82.12%)? – tokyotribe Jul 03 '18 at 21:51
  • 2
    @tokyotribe I think the difference comes from two source: 1. different initialization of layers' weights because of randomness and 2. differences in implementation of same optimizer where even the value of parameters used may be different. Though, a better comparison would be to run each model for multiple times (of course if training time is not significant), say 10 times, and then compare the average and std of accuracy of each of those 10 runs with each other and see whether they have meaningful differences or not. – today Jul 03 '18 at 22:31
  • Makes sense. Thanks a lot for the helpful explanation! – tokyotribe Jul 03 '18 at 22:47