18

I'm training a MobileNet architecture on dummy data with Keras, on Mac OSX. I set both nump.random and tensorflow.set_random_seed, but for some reasons I can't get reproducible results: each time I rerun the code, I get different results. Why? This is not due to the GPU, because I'm running on a MacBook Pro 2017 which has a Radeon graphics card, thus Tensorflow doesn't take advantage of it. The code is run with

python Keras_test.py

so it's not a problem of state (I'm not using Jupyter or IPython: the environment should be reset each time I run the code).

EDIT: I changed my code by moving the setting of all seeds before importing Keras. The results are still not deterministic, however the variance in results is much smaller than it was before. This is very bizarre.

The current model is very small (as far as deep neural network go) without being trivial, it doesn't need a GPU to run and it trains in a few minutes on a modern laptop, so repeating my experiments is within anyone's reach. I invite you to do it: I'd be very interested in learning about the level of variation from a system to another.

import numpy as np
# random seeds must be set before importing keras & tensorflow
my_seed = 512
np.random.seed(my_seed)
import random 
random.seed(my_seed)
import tensorflow as tf
tf.set_random_seed(my_seed)

# now we can import keras
import keras.utils
from keras.applications import MobileNet
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adam
import os

height = 224
width = 224
channels = 3
epochs = 10
num_classes = 10



# Generate dummy data
batch_size = 32  
n_train = 256
n_test = 64
x_train = np.random.random((n_train, height, width, channels))
y_train = keras.utils.to_categorical(np.random.randint(num_classes, size=(n_train, 1)), num_classes=num_classes)
x_test = np.random.random((n_test, height, width, channels))
y_test = keras.utils.to_categorical(np.random.randint(num_classes, size=(n_test, 1)), num_classes=num_classes)
# Get input shape
input_shape = x_train.shape[1:]

# Instantiate model 
model = MobileNet(weights=None,
                  input_shape=input_shape,
                  classes=num_classes)

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
# Viewing Model Configuration
model.summary()

# Model file name
filepath = 'model_epoch_{epoch:02d}_loss_{loss:0.2f}_val_{val_loss:.2f}.hdf5'

# Define save_best_only checkpointer
checkpointer = ModelCheckpoint(filepath=filepath,
                             monitor='val_acc',
                             verbose=1,
                             save_best_only=True)

# Let's fit!
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          validation_data=(x_test, y_test),
          callbacks=[checkpointer])

As always, here are my Python, Keras & Tensorflow versions:

python -c 'import keras; import tensorflow; import sys; print(sys.version, 'keras.__version__', 'tensorflow.__version__')'
/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters
Using TensorFlow backend.
('2.7.15 |Anaconda, Inc.| (default, May  1 2018, 18:37:05) \n[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)]', '2.1.6', '1.8.0')

Here are some results obtained running this code multiple times: as you can see, the code saves the best model (best validation accuracy) out of 10 epochs with a descriptive filename, so comparing filenames across different runs allows to judge the variability in results.

model_epoch_01_loss_2.39_val_3.28.hdf5
model_epoch_01_loss_2.39_val_3.54.hdf5
model_epoch_01_loss_2.40_val_3.47.hdf5
model_epoch_01_loss_2.41_val_3.08.hdf5
DeltaIV
  • 4,353
  • 8
  • 33
  • 78
  • I think you have to set the random seed before importing keras. – BallpointBen Jun 02 '18 at 17:36
  • What results are you talking about that are different? Training data, loss? – nuric Jun 02 '18 at 17:37
  • I have two suggestions for you: 1. Make your training data deterministic. It will make sure that the problem is from tensorflow side. 2. I have seen people also setting python random module seed also. If Keras is using python random seed somewhere it will help the issue. ` import random random.seed(my_seed)` – Abhishek Mishra Jun 02 '18 at 18:37
  • @nuric I'm talking about loss. Would it help if I posted two different results? – DeltaIV Jun 02 '18 at 18:48
  • @BallpointBen do you mean the Tensorflow seed? I will do that. – DeltaIV Jun 02 '18 at 18:49
  • @AbhishekMishra even the python random seed? Ok, I'll do that too and report the results. – DeltaIV Jun 02 '18 at 18:49
  • 2
    I think in the end it’s all based off the numpy random seed. Set that before importing anything else – BallpointBen Jun 02 '18 at 18:59
  • @BallpointBen and all others, I set all the seeds before importing Keras but I still don't get deterministic results: please see edited code. Very weird. – DeltaIV Jun 03 '18 at 15:41

2 Answers2

38

You can find the answer at Keras docs: https://keras.io/getting-started/faq/#how-can-i-obtain-reproducible-results-using-keras-during-development.

In short, to be absolutely sure that you will get reproducible results with your python script on one computer's/laptop's CPU then you will have to do the following:

  1. Set PYTHONHASHSEED environment variable at a fixed value
  2. Set python built-in pseudo-random generator at a fixed value
  3. Set numpy pseudo-random generator at a fixed value
  4. Set tensorflow pseudo-random generator at a fixed value
  5. Configure a new global tensorflow session

Following the Keras link at the top, the source code I am using is the following:

# Seed value
# Apparently you may use different seed values at each stage
seed_value= 0

# 1. Set `PYTHONHASHSEED` environment variable at a fixed value
import os
os.environ['PYTHONHASHSEED']=str(seed_value)

# 2. Set `python` built-in pseudo-random generator at a fixed value
import random
random.seed(seed_value)

# 3. Set `numpy` pseudo-random generator at a fixed value
import numpy as np
np.random.seed(seed_value)

# 4. Set the `tensorflow` pseudo-random generator at a fixed value
import tensorflow as tf
tf.random.set_seed(seed_value)
# for later versions: 
# tf.compat.v1.set_random_seed(seed_value)

# 5. Configure a new global `tensorflow` session
from keras import backend as K
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
K.set_session(sess)
# for later versions:
# session_conf = tf.compat.v1.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
# sess = tf.compat.v1.Session(graph=tf.compat.v1.get_default_graph(), config=session_conf)
# tf.compat.v1.keras.backend.set_session(sess)

It is needless to say that you do not have to to specify any seed or random_state at the numpy, scikit-learn or tensorflow/keras functions that you are using in your python script exactly because with the source code above we set globally their pseudo-random generators at a fixed value.

Outcast
  • 4,588
  • 3
  • 36
  • 85
  • This code snippet is not working for me. I'm using Keras API (2.2.4-tf) from TensorFlow (version 1.14.0) for deep learning in Google Co-lab. Please suggest. – Dr Nisha Arora Sep 15 '19 at 02:47
  • @DrNishaArora, I cannot really tell without knowing more about your application and what it is happening. It may be that you use another package along with Numpy, Tensorflow and you have not set the seed at it or that you use a version of Tensorflow/Keras where the code above does not work. Not sure but probably it is not the latter since nobody else thus far has reported me sth like that. – Outcast Sep 16 '19 at 09:33
  • I tried your code [& many other codes which I found working for others] for the simplest ANN with just one input layer and 6 sets of data points to make it simple. I made sure to reset run-time, everytime, I tried different code. I'm not from IT background, so might be I'm doing some silly mistake. Please suggest as I want reproducibility in my other colab files (DNN, CNN, etc). Here's the link of my colab file: https://colab.research.google.com/drive/1AFYfPEC0k6WoDEYY4ZZi6nPkZLivHlIA – Dr Nisha Arora Sep 17 '19 at 18:27
  • More importantly, I want to understand how it works instead of just copy-pasting someone's code to help me. Please suggest some resource from where I can learn about it. To be honest I don't understand much about hardware accelerators such as GPU, CPU & TPU. The courses of deep learning don't talk about them – Dr Nisha Arora Sep 17 '19 at 18:40
  • @DrNishaArora, I do not think that you do sth silly since the answer of mine but also of others here cover the basic aspects of reproducibility with Tensorflow/Keras. To start with, as I underline at my post what I wrote is *on one computer's/laptop's CPU* and I emphasise this exactly because running the same thing on different enviroment's or with GPUs etc may require some additional things to ensure reproducibility. Therefore, to start with, you use GoogleLab instead of your own pc/laptop why by definition may mean that a different configuration is needed to ensure reproducibility. – Outcast Sep 18 '19 at 15:10
  • Secondly, if you GPUs/TPUs then (as I mentioned above) my post does not necessarily apply to you as such. – Outcast Sep 18 '19 at 15:10
  • Does that mean, I need to connect to local runtime in co-lab to get reproducible results? Another possibility is, I guess, to run the code on IDEs like Spyder/Pycharm or Jupyter Lab. Please correct, if I'm wrong. – Dr Nisha Arora Sep 19 '19 at 14:16
  • I did all the step 1-4 and couldn't get deterministic results. However, after applying step 5, it worked! Does this mean I have to run a single CPU to get reproducible results? It really surprised me. – whyisyoung Oct 05 '19 at 19:10
  • Hi @whyisyoung, thank you for your question. Step 5 is mandatory for this purpose because it initialises anew the tensorflow variables (by running a new session) instead of keeping any old values which will be used at the (initial) values of the next running of your script etc. I hope that this makes sense :) – Outcast Oct 07 '19 at 17:51
  • @PoeteMaudit Thanks for the reply. So in deep learning fields, all the people just ignore the randomness of GPU and save the best weights they encountered? In that way, people can never reproduce the result if they try to start training from the very beginning. – whyisyoung Oct 08 '19 at 20:18
  • @whyisyoung, to be honest I am not sure about what happens when using GPUs :) – Outcast Oct 09 '19 at 09:42
  • @PoeteMaudit Okay. Anyway, thank you for your answer! – whyisyoung Oct 09 '19 at 16:28
  • @whyisyoung, you are welcome! Best of luck with your ML application :) – Outcast Oct 10 '19 at 09:10
  • @PoeteMaudit if i am using `tf.keras` instead of `keras` the 5th step still remains the same ? Thanks – Siddhant Tandon Oct 29 '19 at 09:54
  • I think so @SiddhantTandon :) . I do not see why it should be different but I may be missing something. – Outcast Oct 31 '19 at 11:30
  • @PoeteMaudit I have written subclassed models inherited from `tf.keras` but i am unable to reproduce the results. However now i changed all my models to inherit from `keras` and I can get reproducible results now, dont know why this was happening. – Siddhant Tandon Oct 31 '19 at 11:52
  • @SiddhantTandon, so in the case where you used 'tf.keras' I think that perhaps you may have to use `tf.keras.clear_session` (https://www.tensorflow.org/api_docs/python/tf/keras/backend/clear_session) somewhere at step (5) of my code? You may experiment a bit where exactly at (5) and how exactly. For example, as a first line at (5) and then doing the same as I do? Or in a different way? If you do test it then please let me know what happened :) – Outcast Oct 31 '19 at 13:13
  • @PoeteMaudit thanks for the reply, I did try that too, but nothing really changed. I also think there could probably be some problems due to the version of `tf.keras` because of default eager execution but im not really sure. Will post an update if I test something further. – Siddhant Tandon Oct 31 '19 at 13:49
  • I have followed all the 5 steps and still not able to reproduce results. I am even setting seeds on every iteration. I am using a Sequential model so in the dense layers also the kernel initializers are being seeded. still get different results – Eswar Chitirala Jul 13 '20 at 06:26
  • @EswarChitirala, I see :). Keep in mind that as I am saying above the code applies for one's computer (one) CPU - if you are using GPUs or multi-processing then probably it won't work and having absolutely reproducible results in these cases is quite a lot hard to configure (unless you use a specific software which provides you this capability). – Outcast Jul 16 '20 at 08:48
  • @Outcast I am doing this on a single hardware. To give much clear of what exactly I am doing. I have a model.fit in 1 cell of Jupyter notebook. I run them twice and get different results. Nothing has been modified between the 2 runs. I am not running this on GPU or using any multi-processing – Eswar Chitirala Jul 17 '20 at 10:02
  • @EswarChitirala, ok thank you for the extra details. I cannot necessarily think of something for now but the first thing which comes in my mind is about the tensorflow and keras version and that something has changed in relation to this. For example I see that the latest versions of them use `tf.random.set_seed` which is slightly different from what I have written above but I do not know if this makes a difference etc. – Outcast Jul 17 '20 at 13:09
2

The key point of making result reproducible is to disable GPU. See my answer at another question (link https://stackoverflow.com/a/57121117/9501391) which has been accepted.

guorui
  • 786
  • 2
  • 6
  • 19