如何在 keras 中获得可重现的结果

Posted

技术标签:

【中文标题】如何在 keras 中获得可重现的结果【英文标题】:How to get reproducible results in keras 【发布时间】:2015-12-01 21:05:09 【问题描述】:

每次我从 Keras 框架 (https://github.com/fchollet/keras/blob/master/examples/imdb_lstm.py) 运行 imdb_lstm.py 示例时都会得到不同的结果(测试准确度) 代码在顶部包含np.random.seed(1337),在任何 keras 导入之前。它应该防止它为每次运行生成不同的数字。我错过了什么?

更新:如何复制:

    安装 Keras (http://keras.io/) 执行几次https://github.com/fchollet/keras/blob/master/examples/imdb_lstm.py。它将训练模型并输出测试精度。 预期结果:每次运行的测试准确度都相同。 实际结果:每次运行的测试准确度都不同。

UPDATE2:我在带有 MinGW/msys 的 Windows 8.1 上运行它,模块版本: theano 0.7.0 numpy 1.8.1 scipy 0.14.0c1

UPDATE3:我把问题缩小了一点。如果我使用 GPU 运行示例(设置 theano 标志 device=gpu0),那么我每次都会得到不同的测试精度,但是如果我在 CPU 上运行它,那么一切都会按预期工作。我的显卡:NVIDIA GeForce GT 635)

【问题讨论】:

我无法在 ubuntu 14.04 上复制运行代码 theano -> 0.6.0, numpy -> '1.9.2', scipy -> '0.15.1' 可能问题在于我使用的是 Windows。 numpy.random.uniform 工作正常,总是产生相同的结果。 GPU 代码必须大量使用SIMD-like 指令。这可能会导致以随机顺序调用随机生成器。 GPU 也是一个自治实体,它可以使用自己的随机生成器。毕竟,在 GPU 上运行任何你想要的代码都不是一件容易的事。 您使用的是哪个 CUDA 版本?你安装了 cuDNN 吗?我相信后者会为速度做出一些牺牲,从而导致 gpu 上的非确定性行为。 (应该是轻微的,我认为这与在 backrprop 上计算的原子操作有关,但你不会每次都得到相同的值。) 【参考方案1】:

您可以在 Keras 文档中找到答案:https://keras.io/getting-started/faq/#how-can-i-obtain-reproducible-results-using-keras-during-development。

简而言之,要绝对确保您的 Python 脚本在一台计算机/笔记本电脑的 CPU 上能够获得可重现的结果,那么您必须执行以下操作:

    PYTHONHASHSEED 环境变量设置为固定值 将python内置伪随机生成器设置为固定值 将numpy 伪随机生成器设置为固定值 将tensorflow 伪随机生成器设置为固定值 配置新的全局tensorflow 会话

按照顶部的Keras链接,我使用的源代码如下:

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

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

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

# 3. Set the `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)

不用说,您不必在您的 Python 脚本中使用的 numpyscikit-learntensorflow/keras 函数中指定任何 seedrandom_state正是因为上面的源代码我们将它们的伪随机生成器全局设置为一个固定值。

【讨论】:

对于更高版本的tensorflow,如果遇到错误,请使用tf.random.set_random_seed(seed_value) 谢谢,这对我有用!只是为了确定:运行脚本后我需要做些什么来“将随机性恢复正常”吗?还是设置seed_values只有“一次性效果”? 嘿@Frank,我认为如果你设置这样的种子值,它不会恢复正常,除非你重新启动内核等(或自己设置不同的种子值等)。跨度> 【参考方案2】:

Theano 的documentation 谈到了播种随机变量的困难以及为什么它们使用自己的随机数生成器为每个图实例播种。

在不同的 RandomOp 之间共享一个随机数生成器 实例使生成相同的流变得困难,无论 图中的其他操作,并保持 RandomOps 隔离。 因此,图中的每个 RandomOp 实例都会有其非常 自己的随机数发生器。该随机数生成器是输入 到功能。在典型用法中,我们将使用 函数输入 (value, update) 来传递和更新 rng 对于每个 RandomOp。通过将 RNG 作为输入传递,可以 使用访问函数输入的正常方法来访问每个 RandomOp 的 rng。在这种方法中,没有预先存在的 处理整体的组合随机数状态的机制 图形。所以建议是提供缺失的功能( 最后三个要求)通过辅助函数:seed, getstate, 设置状态。

他们还提供了examples 以了解如何为所有随机数生成器播种。

您还可以播种由 a 分配的所有随机变量 RandomStreams 对象的种子方法。这颗种子将 用于播种临时随机数生成器,这将反过来 为每个随机变量生成种子。

>>> srng.seed(902340)  # seeds rv_u and rv_n with different seeds each

【讨论】:

但是为了播种它们,我们需要访问 keras 将使用的 theano 的随机对象。是否可以通过 keras API 进行操作?【参考方案3】:

我的代码终于得到了可重现的结果。这是我在网上看到的答案的组合。第一件事就是按照@alex 所说的去做:

    设置numpy.random.seed; 对 Python 3 使用 PYTHONHASHSEED=0

然后您必须通过使用以下附加 THEANO_FLAGS 调用您的 Keras 代码来解决 @user2805751 指出的关于 cuDNN 的问题:

    dnn.conv.algo_bwd_filter=deterministic,dnn.conv.algo_bwd_data=deterministic

最后,您必须按照this comment 修补您的 Theano 安装,这基本上包括:

    将所有对*_dev20 运算符的调用替换为theano/sandbox/cuda/opt.py 中的常规版本。

这应该可以为相同的种子获得相同的结果。

请注意,可能会放缓。我看到运行时间增加了大约 10%。

【讨论】:

【参考方案4】:

问题现在在 Tensorflow 2.0 中得到解决!我在使用 TF 1.x 时遇到了同样的问题(请参阅 If Keras results are not reproducible, what's the best practice for comparing models and choosing hyper parameters? ),但是

import os
####*IMPORANT*: Have to do this line *before* importing tensorflow
os.environ['PYTHONHASHSEED']=str(1)

import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers 
import random
import pandas as pd
import numpy as np

def reset_random_seeds():
   os.environ['PYTHONHASHSEED']=str(1)
   tf.random.set_seed(1)
   np.random.seed(1)
   random.seed(1)

#make some random data
reset_random_seeds()
NUM_ROWS = 1000
NUM_FEATURES = 10
random_data = np.random.normal(size=(NUM_ROWS, NUM_FEATURES))
df = pd.DataFrame(data=random_data, columns=['x_' + str(ii) for ii in range(NUM_FEATURES)])
y = df.sum(axis=1) + np.random.normal(size=(NUM_ROWS))

def run(x, y):
    reset_random_seeds()

    model = keras.Sequential([
            keras.layers.Dense(40, input_dim=df.shape[1], activation='relu'),
            keras.layers.Dense(20, activation='relu'),
            keras.layers.Dense(10, activation='relu'),
            keras.layers.Dense(1, activation='linear')
        ])
    NUM_EPOCHS = 500
    model.compile(optimizer='adam', loss='mean_squared_error')
    model.fit(x, y, epochs=NUM_EPOCHS, verbose=0)
    predictions = model.predict(x).flatten()
    loss = model.evaluate(x,  y) #This prints out the loss by side-effect

#With Tensorflow 2.0 this is now reproducible! 
run(df, y)
run(df, y)
run(df, y)

【讨论】:

【参考方案5】:

在 Tensorflow 2.0 中,您可以像这样设置随机种子:

import tensorflow as tf
tf.random.set_seed(221)


from tensorflow import keras
from tensorflow.keras import layers


model = keras.Sequential( [ 
layers.Dense(2,name = 'one'),
layers.Dense(3,activation = 'sigmoid', name = 'two'),
layers.Dense(2,name = 'three')])

x = tf.random.uniform((12,12))
model(x)

【讨论】:

【参考方案6】:

这对我有用:

SEED = 123456
import os
import random as rn
import numpy as np
from tensorflow import set_random_seed

os.environ['PYTHONHASHSEED']=str(SEED)
np.random.seed(SEED)
set_random_seed(SEED)
rn.seed(SEED)

【讨论】:

【参考方案7】:

我想在之前的答案中添加一些内容。如果您使用 python 3 并且希望每次运行都获得可重现的结果,则必须

    在代码开头设置 numpy.random.seed 将 PYTHONHASHSEED=0 作为参数提供给 python 解释器

【讨论】:

【参考方案8】:

我已经使用 Keras 训练和测试了 Sequential() 类型的神经网络。我对嘈杂的语音数据进行了非线性回归。我使用以下代码生成随机种子:

import numpy as np
seed = 7
np.random.seed(seed)

每次我对相同的数据进行训练和测试时,我都会得到与 val_loss 完全相同的结果。

【讨论】:

你用过GPU吗?什么后端:Theano 或 TensorFlow? 我使用 CPU 和 Theano 后端。 知道了。 CPU对我来说也很好用。我只有在 GPU 上运行时才会遇到问题。【参考方案9】:

看起来更容易。仅此而已,它可以工作:

import numpy as np
import tensorflow as tf
import random as python_random

def reset_seeds():
   np.random.seed(123) 
   python_random.seed(123)
   tf.random.set_seed(1234)

reset_seeds() 

问题的关键,非常重要,是在每次运行模型之前调用函数reset_seeds()。当我在 Google Collab 中检查时,这样做您将获得可重现的结果。

【讨论】:

这种方法几乎对我有用。我必须将os.environ["PYTHONHASHSEED"] = str(seed_value) 添加到函数体的开头,然后它才起作用。【参考方案10】:

我同意前面的评论,但可重现的结果有时需要相同的环境(例如已安装的包、机器特性等)。因此,我建议将您的环境复制到其他地方,以防得到可重现的结果。尝试使用以下技术之一:

    Docker。如果你有一个 Linux 这很容易将你的环境移动到其他地方。您也可以尝试使用DockerHub。 Binder。这是一个复制科学实验的云平台。 Everware。这是另一个“可重复使用科学”的云平台。请参阅 Github 上的 project repository。

【讨论】:

我的问题是,当我运行两次训练时,即使在相同的环境下,我也无法获得可重现的结果。【参考方案11】:

会议论文:用于可重复确定性的深度学习网络中的非随机权重初始化,出版日期 2019 年 6 月 5 日在利兹贝克特大学 (LBU) 举行的第 10 届 IEEE 国际会议可靠系统、服务和技术 (DESSERT-19) 上发表、英国、英国、爱尔兰和 IEEE 乌克兰分部 2019 年 6 月 5-7 日

https://ieeexplore.ieee.org/document/8770007

展示了如何通过强制执行代码的关键区域来获得可重复的结果。

它已扩展为期刊论文:Repeatable Determinism using Non-Random Weight Initialisations in Smart City Applications of Deep Learning 发表在智能城市特别版的可靠智能环境杂志上,并使用 glorot xavier 限制并使用感知器层达到相同的精度,但将权重增加到线性顺序,这可能对感知器层中的规则提取具有优势。

【讨论】:

【参考方案12】:

与之前所说的不同,只有 Tensorflow 种子对随机生成权重有影响(最新版本的 Tensorflow 2.6.0 和 Keras 2.6.0)

这是一个小测试,您可以运行它来检查每个种子的影响(np 是 numpy,tf 是 tensorflow,random 是 Python 随机库):

# Testing how seeds influence results
# -----------------------------------

print("Seed specification")

my_seed = 36
# To vary python hash, numpy random, python random and tensorflow random seeds
a, b, c, d = 0, 0, 0, 0

os.environ['PYTHONHASHSEED'] = str(my_seed+a) # Has no effect
np.random.seed(my_seed+b) # Has no effect
random.seed(my_seed+c) # Has no effect
tf.random.set_seed(my_seed+d) # Has an effect

print("Making ML model")

keras.mixed_precision.set_global_policy('float64')

model = keras.Sequential([
    layers.Dense(2, input_shape=input_shape),#, activation='relu'),
    layers.Dense(output_nb, activation=None),
    ])
#
weights_save = model.get_weights()

print("Some weights:", weights_save[0].flatten())

我们注意到变量abc 对结果没有影响。 只有d 对结果有影响。

因此,在最新版本的 Tensorflow 中,只有 tensorflow 随机种子对权重的随机选择有影响

【讨论】:

以上是关于如何在 keras 中获得可重现的结果的主要内容,如果未能解决你的问题,请参考以下文章

只有在一个线程上运行时才可能实现 Keras 模型的重现性?

为啥我的结果仍然无法重现?

如何获得可重现但不同的 GroupKFold 实例

如何在 Keras 中更改 softmax 输出的温度

如何制作良好的可重现 pandas 示例

如果 Keras 结果不可重现,那么比较模型和选择超参数的最佳做法是啥?