3

I've just noticed that I get different results everytime I run a Keras model. I've tried the solutions from this issue on GitHub, basically:

  • Setting up the seeds before importing anything else
  • Setting shuffle=False on fit_generator()

Even though I did this, I still can't seem to reproduce the same results.

I've posted this same question on the issue I've just linked, but I've decided to post here as well because of the visibility, hoping anyone can help me figure out what's wrong.

import numpy as np
import tensorflow as tf
import random as rn
import os
os.environ['PYTHONHASHSEED'] = '0'
np.random.seed(42)
rn.seed(12345)
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
from keras import backend as K
tf.set_random_seed(1234)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
K.set_session(sess)

from keras.layers import Input, Dropout, Flatten, Conv2D, MaxPooling2D, Dense, Activation, Lambda,GlobalAveragePooling2D
from keras.optimizers import RMSprop , SGD, Adam,Nadam
from keras.callbacks import ModelCheckpoint, Callback, EarlyStopping, History
from keras.preprocessing.image import ImageDataGenerator
from keras.applications import VGG16, VGG19, ResNet50, Xception
from keras.models import Model

batch_size = 32
num_channels = 3
img_size = 512
img_full_size = (img_size, img_size, num_channels)
num_classes = 2
seed = 1 # for image transformations
train_path = 'keras_folders/train/'
validation_path = 'keras_folders/val/'
test_path = 'keras_folders/test/'

train_datagen = ImageDataGenerator(
    rescale=1./255,
    horizontal_flip=True)

validation_datagen = ImageDataGenerator(
    rescale=1./255)

test_datagen = ImageDataGenerator(
    rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    train_path,
    target_size=(img_size, img_size),
    batch_size=batch_size,
    class_mode='categorical', 
    seed=seed)

validation_generator = validation_datagen.flow_from_directory(
    validation_path,
    target_size=(img_size, img_size),
    batch_size=batch_size,
    shuffle=False,
    class_mode='categorical',
    seed=seed)

from collections import Counter
counter = Counter(train_generator.classes)
max_val = float(max(counter.values()))
class_weights = {class_id : max_val/num_images for class_id, num_images in counter.items()}  

conv_base = VGG16(weights='imagenet', include_top=False, input_shape=img_full_size)
conv_base.trainable=True
for layer in conv_base.layers[:4]:
    layer.trainable = False
x = Flatten()(conv_base.output)
x = Dense(256, activation='relu')(x)
x = Dropout(0.218)(x)
predictions = Dense(num_classes, activation='softmax')(x)
model = Model(inputs = conv_base.input , outputs=predictions)

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

train_samples = train_generator.samples
validation_samples = validation_generator.samples
model.fit_generator(
    train_generator,
    class_weight=class_weights,
    steps_per_epoch= train_samples // batch_size,
    epochs=1,
    validation_data= validation_generator,
    validation_steps= validation_samples // batch_size,
    shuffle=False)
jruivo
  • 447
  • 1
  • 8
  • 22
  • may you specify what does it mean "reproduce the same results". It can be understood that the results may not be identical for any run since there is a random initialization and you are using Adam (gradient descent optimization, which most like finds local optimal and do not guarantee the global one) – Jirka Aug 23 '18 at 06:05
  • Since OP is using random seeds, even with re-runs, the same initializations should be used (pseudo-randomness). Therefore, we should expect the same outcome every run. As a sidenode from my point: What system are you running on? Windows/Linux? GPU training? CPU? – dennlinger Aug 23 '18 at 06:32
  • @dennlinger I'm using Linux and GPU training – jruivo Aug 23 '18 at 10:04
  • 1
    Then you have to make sure to also set the random seed for the GPU, as this is different from the (CPU) TensorFlow seed you are currently setting. See [here](https://github.com/tensorflow/tensorflow/issues/12871) and [here](https://www.twosigma.com/insights/article/a-workaround-for-non-determinism-in-tensorflow/) – dennlinger Aug 23 '18 at 10:25
  • 1
    DataGenerators aren't safe for multiprocessing and may cause the batches to be processed in different order on different runs. Please take a look at "Sequences" as an alternative: https://keras.io/utils/#sequence – Mark Loyman Aug 23 '18 at 20:46
  • Thank you @MarkLoyman, I'm gonna take a look at that. – jruivo Aug 23 '18 at 21:19

1 Answers1

0

I think you have to do the opposite. The fit function has shuffle switched on by default, while the fit_generator function takes shuffling from your generator. Your train_generator has set the seed parameter, but not the shuffle parameter. Is it possible that your ImageDataGenerator has shuffle set to False as default?

This discussion suggests you turn on shuffle in your training iterator: https://github.com/keras-team/keras/issues/2389. I had the same problem and this resolved it.

Setting a seed is only necessary if you want to want to reproduce your results exactly for a given piece of code. I doubt that setting a seed will yield the exact same results between fit and fit_generator.

Cerno
  • 751
  • 4
  • 14