How to Get Reproducible Results with Keras

Neural network algorithms are stochastic.

This means they make use of randomness, such as initializing to random weights, and in turn the same network trained on the same data can produce different results.

This can be confusing to beginners as the algorithm appears unstable, and in fact they are by design. The random initialization allows the network to learn a good approximation for the function being learned.

Nevertheless, there are times when you need the exact same result every time the same network is trained on the same data. Such as for a tutorial, or perhaps operationally.

In this tutorial, you will discover how you can seed the random number generator so that you can get the same results from the same network on the same data, every time.

Let’s get started.

How to Get Reproducible Results from Neural Networks with Keras

How to Get Reproducible Results from Neural Networks with Keras
Photo by Samuel John, some rights reserved.

Tutorial Overview

This tutorial is broken down into 6 parts. They are:

  1. Why do I Get Different Results Every Time?
  2. Demonstration of Different Results
  3. The Solutions
  4. Seed Random Numbers with the Theano Backend
  5. Seed Random Numbers with the TensorFlow Backend
  6. What if I Am Still Getting Different Results?

Environment

This tutorial assumes you have a Python SciPy environment installed. You can use either Python 2 or 3 with this example.

This tutorial assumes you have Keras (v2.0.3+) installed with either the TensorFlow (v1.1.0+) or Theano (v0.9+) backend.

This tutorial also assumes you have scikit-learn, Pandas, NumPy, and Matplotlib installed.

If you need help setting up your Python environment, see this post:

Why do I Get Different Results Every Time?

This is a common question I see from beginners to the field of neural networks and deep learning.

This misunderstanding may also come in the for of questions like:

  • How do I get stable results?
  • How do I get repeatable results?
  • What seed should I use?

Neural networks use randomness by design to ensure they effectively learn the function being approximated for the problem. Randomness is used because this class of machine learning algorithm performs better with it than without.

The most common form of randomness used in neural networks is the random initialization of the network weights. Although randomness can be used in other areas, here is just a short list:

  • Randomness in Initialization, such as weights.
  • Randomness in Regularization, such as dropout.
  • Randomness in Layers, such as word embedding.
  • Randomness in Optimization, such as stochastic optimization.

These sources of randomness, and more, mean that when you run the exact same neural network algorithm on the exact same data, you are guaranteed to get different results.

For more on the why behind stochastic algorithms, see the post:

Demonstration of Different Results

We can demonstrate the stochastic nature of neural networks with a small example.

In this section, we will develop a Multilayer Perceptron model to learn a short sequence of numbers increasing by 0.1 from 0.0 to 0.9. Given 0.0, the model must predict 0.1; given 0.1, the model must output 0.2; and so on.

The code to prepare the data is listed below.

We will use a network with 1 input, 10 neurons in the hidden layer, and 1 output. The network will use a mean squared error loss function and will be trained using the efficient ADAM algorithm.

The network needs about 1,000 epochs to solve this problem effectively, but we will only train it for 100 epochs. This is to ensure we get a model that makes errors when making predictions.

After the network is trained, we will make predictions on the dataset and print the mean squared error.

The code for the network is listed below.

In the example, we will create the network 10 times and print 10 different network scores.

The complete code listing is provided below.

Running the example will print a different accuracy in each line.

Your specific results will differ. A sample output is provided below.

The Solutions

The are two main solutions.

Solution #1: Repeat Your Experiment

The traditional and practical way to address this problem is to run your network many times (30+) and use statistics to summarize the performance of your model, and compare your model to other models.

I strongly recommend this approach, but it is not always possible due to the very long training times of some models.

For more on this approach, see:

Solution #2: Seed the Random Number Generator

Alternately, another solution is to use a fixed seed for the random number generator.

Random numbers are generated using a pseudo-random number generator. A random number generator is a mathematical function that will generate a long sequence of numbers that are random enough for general purpose use, such as in machine learning algorithms.

Random number generators require a seed to kick off the process, and it is common to use the current time in milliseconds as the default in most implementations. This is to ensure different sequences of random numbers are generated each time the code is run, by default.

This seed can also be specified with a specific number, such as “1”, to ensure that the same sequence of random numbers is generated each time the code is run.

The specific seed value does not matter as long as it stays the same for each run of your code.

The specific way to set the random number generator differs depending on the backend, and we will look at how to do this in Theano and TensorFlow.

Seed Random Numbers with the Theano Backend

Generally, Keras gets its source of randomness from the NumPy random number generator.

For the most part, so does the Theano backend.

We can seed the NumPy random number generator by calling the seed() function from the random module, as follows:

The importing and calling of the seed function is best done at the top of your code file.

This is a best practice because it is possible that some randomness is used when various Keras or Theano (or other) libraries are imported as part of their initialization, even before they are directly used.

We can add the two lines to the top of our example above and run it two times.

You should see the same list of mean squared error values each time you run the code (perhaps with some minor variation due to precision on different machines), as follows:

Your results should match mine (ignoring minor differences of precision).

Seed Random Numbers with the TensorFlow Backend

Keras does get its source of randomness from the NumPy random number generator, so this must be seeded regardless of whether you are using a Theano or TensorFlow backend.

It must be seeded by calling the seed() function at the top of the file before any other imports or other code.

In addition, TensorFlow has its own random number generator that must also be seeded by calling the set_random_seed() function immediately after the NumPy random number generator, as follows:

To be crystal clear, the top of your code file must have the following 4 lines before any others;

You can use the same seed for both, or different seeds. I don’t think it makes much difference as the sources of randomness feed into different processes.

Adding these 4 lines to the above example will allow the code to produce the same results every time it is run. You should see the same mean squared error values as those listed below (perhaps with some minor variation due to precision on different machines):

Your results should match mine (ignoring minor differences of precision).

What if I Am Still Getting Different Results?

To re-iterate, the most robust way to report results and compare models is to repeat your experiment many times (30+) and use summary statistics.

If this is not possible, you can get 100% repeatable results by seeding the random number generators used by your code. The solutions above should cover most situations, but not all.

What if you have followed the above instructions and still get different results from the same algorithm on the same data?

It is possible that there are other sources of randomness that you have not accounted for.

Randomness from a Third-Party Library

Perhaps your code is using an additional library that uses a different random number generator that too must be seeded.

Try cutting your code back to the minimum required (e.g. one data sample, one training epoch, etc.) and carefully read the API documentation in an effort to narrow down additional third-party libraries introducing randomness.

Randomness from Using the GPU

All of the above examples assume the code was run on a CPU.

It is possible that when using the GPU to train your models, the backend may be configured to use a sophisticated stack of GPU libraries, and that some of these may introduce their own source of randomness that you may or may not be able to account for.

For example, there is some evidence that if you are using Nvidia cuDNN in your stack, that this may introduce additional sources of randomness and prevent the exact reproducibility of your results.

Randomness from a Sophisticated Model

It is possible that because of the sophistication of your model and the parallel nature of training, that you are getting unreproducible results.

This is very likely caused by efficiencies made by the backend library and perhaps the inability to use the sequence of random numbers across cores.

I have not seen this myself, but see signs in some GitHub issues and StackOverflow questions.

You can try to reduce the complexity of your model to see if this affects the reproducibility of results, if only to narrow down the cause.

I would suggest reading up on how your backend uses randomness and see if there are any options open to you.

In Theano, see:

In TensorFlow, see:

Also, consider searching for other people with the same issue for further insight. Some great places to search include:

Summary

In this tutorial, you discovered how to get reproducible results for neural network models in Keras.

Specifically, you learned:

  • That neural networks are stochastic by design and that the source of randomness can be fixed to make results reproducible.
  • That you can seed the random number generators in NumPy and TensorFlow and this will make most Keras code 100% reproducible.
  • That there are some cases where there are additional sources of randomness and you have ideas on how to seek them out and perhaps fix them too.

Did this tutorial help?
Share your experience in the comments.

Are you still getting unreproducible results with Keras?
Share your experience; perhaps someone else here can help.

Frustrated With Your Progress In Deep Learning?

Deep Learning with Python

 What If You Could Develop A Network in Minutes

…with just a few lines of Python

Discover how in my new Ebook: Deep Learning With Python

It covers self-study tutorials and end-to-end projects on topics like:
Multilayer PerceptronsConvolutional Nets and Recurrent Neural Nets, and more…

Finally Bring Deep Learning To
Your Own Projects

Skip the Academics. Just Results.

Click to learn more.

19 Responses to How to Get Reproducible Results with Keras

  1. Marcel June 30, 2017 at 6:51 pm #

    Hey,

    even though I’m setting a random seed for both numpy and tensorflow as described by your post, I’m being unable to reproduce the training of a model consisting of LSTM layers(+ 1 Dense at the end).

    Can you tell me if this is simply by the nature of LSTMs or if there is something else I can look into?

    Thanks.

    • Jason Brownlee July 1, 2017 at 6:32 am #

      No, I believe LSTM results are reproducible if the seed is tied down.

      Are you using a tensorflow backend?
      Is Keras, TF and your scipy stack up to date?

  2. Marcel July 4, 2017 at 11:56 pm #

    I’m using the tensorflow backend and yes, everything is up to date. Freshly installed on Arch Linux at home. I stumpled upon the problem at work and want this to be fixed.

    I tried the imdb_lstm example of keras with fixed random seeds for numpy and tensorflow just as you described, using one model only which was saved after compiling but before training.
    I’m loading this model and training it again with, sadly, different results.
    I can see though that through the start of both trainings the accuracies and losses are the same until sample 1440 of 25000 where they begin to differ slightly and stay so until the end, e.g. 0.7468 vs. 0.7482 which I wouldn’t see critical. At the end then the validation accuracy though differs by 0.05 which should not be normal, in this case 0.8056 vs. 0.7496.
    I’m only training 1 epoch for this example .

    I also got pointed at LSTMs being deterministic as their equations don’t have any random part and thus results should be reproducible.

    I guess I will ask the guys of keras about this as it seems to be a deeper issue to me.

    If you have another idea, let me know. Great site btw, I often stumpled upon your blog already when I began learning machine learning 🙂

    Thanks for you help 🙂

    • Jason Brownlee July 6, 2017 at 10:17 am #

      Try an MLP in the same setup and see if it is the problem or data handling (e.g. embedding) introducing the random vars.

  3. kotb July 25, 2017 at 9:26 pm #

    Hi DR jason,

    I encountered the same problem as Marcel, and after doing research i found out that the problem is in Tensorflow Backend.I switch to theano backend, and i get reproducible results

  4. Sharod Roy Choudhury August 21, 2017 at 7:57 pm #

    I have read your responses and there seems to be a problem with the Tensorflow backend. Is there a solution for that because I need to use the tensorflow backend only and not the theano backend. I also have a single LSTM layer network and running on a server with 56 CPU cores and CentOS. Pls help !!!

    • Jason Brownlee August 22, 2017 at 6:38 am #

      Did you try the methods for tensorflow listed in this post? Did they work for you?

      • Sharod Roy Choudhury August 22, 2017 at 2:08 pm #

        According to the solutions you gave:

        I am running the program on the same machine, so environment is being repeated.

        I am also seeding the random number generator for numpy and tensorflow as you have shown in your post.

        I am running my program on a server but using CPU only, no GPU.

        I am using only Keras and numpy so explicitly no third party software.

        My model isn’t that sophisticated either. Just a single layer LSTM and a softmax layer binary classification.

        Pls help !! I am stuck !!

        • Jason Brownlee August 23, 2017 at 6:40 am #

          All of my best ideas are in the post above.

          Double check your experiments.

  5. Jim Goodwin September 20, 2017 at 2:46 pm #

    Repeat of prior posts; I can’t edit it, and HTML trashed it:

    Some experiences getting Theano to reproduce, which may
    be of value to others.

    My setup was Win7 64 bit, Keras 2.0.8,
    Theano 0.10.0beta2.dev-c3c477df9439fa466eb50335601d5c854491def8,

    Most of the effort was using my GPU, a GEForce 1060,
    but I checked at the end that everything worked with
    the CPU as well. What I was trying to do was make the
    “Nietzsche” LSTM example reproduce exactly. The source
    for that is at

    https://raw.githubusercontent.com/fchollet/keras/master/examples/lstm_text_generation.py

    General remark: It is harder than it looks to get reproducibility,
    but it does work. It’s harder because there are so many little
    ways that some nondeterminism can sneak in on you, and because
    there is no decent documentation for anything, and the info on
    the web is copious, but disorganized, and frequently out of
    date or just plain wrong. In the end there was no way to find
    the last bug except the laborious process of repeatedly modifying
    the code and adding print statements of critical state data to
    find the place the divergence began. Crude, but after you’ve
    tried to be smart about it and failed enough times, the only way.

    Lessons learned:

    (1)
    It is indeed necessary to create a .theanorc (if it isn’t already
    there) and add certain lines to it. That file on windows is found at
    C:\Users\yourloginname\.theanorc

    If you try to create it in Windows Explorer, windows will block
    you because it doesn’t think “.theano” is a complete file name
    (it’s only a suffix). Create it as .theano.txt and then rename it
    using a command shell.

    It is said you need to add these lines to your .theanorc file:

    [dnn.conv]
    algo_bwd_data=deterministic
    algo_bwd_filter=deterministic
    optimizer_excluding=conv_dnn

    It turned out I didn’t need the last one and I commented it out.
    I imagine it is needed if you are using conv nets; I wasn’t.

    (2)
    You need to seed the random number generator FIRST THING:

    import numpy as np
    np.random.seed(any-constant-number)

    You need to do this absolutely as early as possible.
    You can’t do it before a “from future…” import but
    do it right after that, in your __main__ i.e. the .py
    file you run from the IDE or command line.

    Don’t get clever about this and put it in your favorite
    utility file and then import that early. I tried and it
    wasn’t worth it; there are so many ways something can
    get imported and mess with the RNG.

    (3)
    This was my last bug, and naturally therefore the dumbest:

    There are two random number generators (at least) floating
    around the Python world, numpy.random and Python’s native
    random.py. The code posted at the URL above uses BOTH of
    them: now one, now the other. Francois, I love you man, but…

    I seeded one, but never noticed that there was another.
    The unseeded one naturally caused the program to diverge.

    Make sure you’re using one of them only, throughout.
    If you are unsure what all your libraries might be doing,
    I suppose you could just seed them both (haven’t tried).

    (4)
    Prepare for the siege: cut your program down before you begin.
    Use a tiny dataset, fewer iterations, do whatever you can do
    to reduce the runtime. You don’t need to run it for 10 minutes
    to see that it’s going to diverge. When it works you can run
    it once with the full original settings. Well, okay, twice,
    to prove the point.

    (5)
    GPU and CPU give different results. This is natural.
    Each is reproducible, but they are different.
    The .theanorc settings and code changes (pinning RNG’s)
    to get reproducibility are the same.

    (6)
    During training, Theano produces lines like these on the console:

    76288/200287 [==========……………….] – ETA: 312s – loss: 2.2663

    These will not reproduce exactly. This is not a problem; it
    is just a real-time progress report, and the point at which
    it decides to report can vary slightly if the run goes a little
    faster or slower, but the run is not diverging. Often it does
    report at the same number (here: 76288) and when that happens,
    the reported loss will be the same. The ETA values (estimated time
    remaining to finish the epoch) will always vary a little.

    (7)
    How exact is exact? If you’ve got it right, it’s exact
    to the last bloody digit. Close may be good enough for
    your purposes, but true reproducibility is exact. If the
    final loss value looks pretty close, but doesn’t match exactly,
    you have not reproduced the run. If you go back and look at
    loss values in the middle of the run, you are apt to find
    they were all over the place, and you just got lucky that
    the final values ended up close.

    • Jason Brownlee September 20, 2017 at 3:04 pm #

      Really nice write-up Jim, thank you so much for sharing!

    • Jim Goodwin September 21, 2017 at 3:04 am #

      I forgot one crucial trick. The best thing to monitor, to see
      if it is diverging, is the sequence of loss values during training.
      Using the console output that model.train() normally produces has
      the issues I mentioned above under point 6. Using LossHistory
      callback is way better. See “Example: recording loss history”
      at https://keras.io/callbacks/#example-recording-loss-history

      Jason, thanks for this blog page, I don’t know how much I have
      added to it, but without it I wouldn’t have succeeded.
      /jim

      • Jason Brownlee September 21, 2017 at 5:55 am #

        Thanks Jim, I really love to hear about your experiments and their results.

        Maybe I should start a little community forum for us “boots on the ground practitioners” 🙂

    • Jim Goodwin September 22, 2017 at 10:01 pm #

      I’ve seen it written that requiring deterministic execution will slow down execution by as much as two times.

      When I timed the LSTM setup described above, on GPU, the difference was negligible: 0.07% — 5 seconds on 6,756.

      It may depend on what kind of net you are running, but my example above was unaffected.

      That makes it acceptable to use deterministic execution by default. (Just remember to re-fiddle the random number generator seed if you actually want a number of different runs, eg to average metrics.)
      /jim

      • Jason Brownlee September 23, 2017 at 5:40 am #

        Interesting.

        Generally, I think the only place that fixing the random seed has is in debugging code. We should not be fixing the random seed when developing predictive models.

  6. Jim Goodwin September 20, 2017 at 2:49 pm #

    … and I meant to say I was using Python 2.7.13

  7. Hazem Al Saied October 13, 2017 at 2:50 am #

    Hi Jason,
    Thank you for this helpful tutorial, but i still have a question!

    If we are building a model and we want to test the effect of some changes, changing the input vector, some activation function, the optimiser etc … and we want to know if they are really enhancing the model, do you think that it makes sense if we mixed the two mentioned ways.
    In other words, we can repeat the execution for n times +30, every time we generate a random integer as a seed and we save its value.
    At the end, we calculate the average accuracy and recover the seed value generating the most close score of this average, and we use this seed value for our next experiences.

    Do you agree that in this scenario we get the “most representative” random values which could be usable and reliable in the tuning phase?

    • Jason Brownlee October 13, 2017 at 5:50 am #

      No, I would recommend multiple runs with different random numbers. The idea is to control for the stochastic nature of the algorithm, you need different randomness for this.

Leave a Reply