Keras LSTM - 为啥“相同”模型和相同权重的结果不同?

Posted

技术标签:

【中文标题】Keras LSTM - 为啥“相同”模型和相同权重的结果不同?【英文标题】:Keras LSTM - why different results with "same" model & same weights?Keras LSTM - 为什么“相同”模型和相同权重的结果不同? 【发布时间】:2018-02-17 13:18:46 【问题描述】:

(注意:在每个模型创建之前正确修复 RNG 状态,如评论中的评论中所述,实际上解决了我的问题,因为 3 位小数内的结果是一致的,但是不是完全如此,所以有一个隐藏的随机性来源没有通过播种RNG来解决......可能有些lib使用时间毫秒或smth......如果有人对此有任何想法,知道会很酷,所以我会等待而不是结束问题:))

我创建了一个 Keras LSTM 模型(用于预测一些时间序列数据,并不重要),并且每次我尝试重新创建一个相同的模型(从 json 加载相同的模式配置,从文件加载相同的权重,编译函数的参数相同),我在相同的火车和测试数据上得到截然不同的结果。为什么?

代码大致是这样的:

# fix random
import random
random.seed(42)

# make model & compile
model = Sequential([
    LSTM(50, input_shape=(None, 1), return_sequences=True),
    LSTM(100, return_sequences=False),
    Dense(1),
    Activation("linear")
])
model.compile(loss="mse", optimizer="rmsprop")

# save it and its initial random weights
model_json = model.to_json()
model.save_weights("model.h5")

# fit and predict
model.fit(x_train, y_train, epochs=3)
r = model.predict(x_test)

# create new "identical" model
model2 = model_from_json(model_json)
model2.load_weights("model.h5")
model2.compile(loss="mse", optimizer="rmsprop")

# fit and predict "identical" model
model2.fit(x_train, y_train, epochs=3)
r2 = model2.predict(x_test)

# ...different results :(

我知道模型具有初始随机权重,因此我将它们保存起来并重新加载它们。我也很偏执,假设有一些我可能不知道的“隐藏”参数,所以我将模型序列化为 json 并重新加载,而不是手动重新创建一个相同的参数(尝试过,顺便说一句)。而且我还修复了随机数生成器。

这是我第一次接触 Keras,总的来说我也是神经网络的初学者。但这让我发疯...... wtf可以变化?!


关于修复随机数生成器:我使用 TensorFlow 后端运行 Keras,并且在开始时我有这些代码行来尝试修复 RNG 以用于实验目的:

import random
random.seed(42)
import numpy
numpy.random.seed(42)
from tensorflow import set_random_seed
set_random_seed(42)

...但他们仍然没有修复随机性。

而且我知道目标是让我的模型表现得非随机尽管具有 NN 固有的随机性。但出于实验目的,我需要临时修复此问题(我什至可以在一台机器上重现它!)。

【问题讨论】:

我不确定这会如何影响结果,但您还没有“修复”第二个模型的随机数生成器。您需要从相同的状态 (seed=42) 再次启动它,并且您需要第二次对生成器运行完全相同的一组调用。此外,您不知道 Keras 是如何获得它的随机数的!事实上,它很可能不是从random 模块中获取它们的。正如下面的答案所假设的那样,它甚至可能无法从numpy 获得它们。 如果你想获得一致的结果,你应该指定不同的种子。根据 keras 后端(theano 或 tensorflow),有两种方法可以指定随机种子。见这里:***.com/questions/45970112/… @senderle 这个。我没有意识到当然RNG状态在运行时会发生变化,所以我不仅需要在开始时修复它,还要在制作model2之前重新修复它......猜猜它是“星期五炸脑” :) 这几乎解决了我的问题,从某种意义上说仍然存在随机性,但是前 3 位小数是可重现的(我想某些库依赖项有它自己隐藏的随机事物) .这已经足够好了,所以我可以将“实际方差”(仅基于稍微不同的训练数据的不同预测)与“模型随机性”区分开来,并且可以开始着手解决第一个问题!谢谢! 你是在 GPU 上训练吗? 谢谢!我发现 TensorFlow 2.2 附带一个操作系统环境变量 TF_DETERMINISTIC_OPS,如果设置为“1”,将确保仅使用确定性 GPU 操作。设置为 1 修复了我的大部分 GPU 不确定性,除了一些我最终留在 CPU 上的 TensorFlow 操作。 【参考方案1】:

机器学习算法通常是非确定性的。这意味着每次运行它们时,结果都会有所不同。这与权重的随机初始化有关。如果要使结果可重现,则必须消除表中的随机性。一种简单的方法是使用随机种子

import numpy as np
import tensorflow as tf

np.random.seed(1234)
tf.random.set_seed(1234)

# rest of your code

如果您想要随机性因子但输出的方差不那么高,我建议您降低学习率或更改优化器(我建议使用学习率相对较低的 SGD 优化器)。 here!


关于 TensorFlow 的随机生成器的注意事项是,除了全局种子(即tf.random.set_seed())之外,它们还使用内部计数器,所以如果你运行

tf.random.set_seed(1234)
print(tf.random.uniform([1]).numpy())
print(tf.random.uniform([1]).numpy())

您将分别获得0.53803930.3253647。但是,如果您重新运行相同的 sn-p,您将再次获得相同的两个数字。

有关随机种子如何在 TensorFlow 中工作的详细说明,请参阅 here。


对于较新的 TF 版本,也要注意这一点: TensorFlow 2.2 附带一个操作系统环境变量 TF_DETERMINISTIC_OPS,如果设置为 '1',将确保仅使用确定性 GPU 操作。

【讨论】:

如果 keras 使用 theano 后端,这将起作用。对于 tensorflow 后端,您还需要使用 set_random_seed 指定种子。见这里:***.com/questions/45970112/… 好的,首先感谢您提醒numpy.random.seed,我只设置了random.seed,但是......它不会改变任何东西:|是的,减少输出的整体差异是实际目标,但是现在我只想了解为什么会发生这种情况?!就像我修复了一切 i>,我应该在相同的数据上得到相同的结果!在那之后,是的,还有很多需要优化/调整...但首先要做的事情是:理智和可重复性是我想要的。 @NeuronQ 你的后端是theano吗?如果是,在我看来,这应该与您保存和加载体重的方式或序列化方式有关。在使用数据 x_train=np.reshape(np.arange(10),(10,1,1)) x_test=x_train[:] y_train=2* 添加 np.random.seed(42) 后,我尝试运行您的模型np.arange(10) 并且每次运行时都得到完全相同的结果。这意味着当我们忽略所有“加载数据\权重”部分时,模型本身是可重现的。 @MiriamFarber 也尝试过,仍然是随机的。我可能会继续尝试调整优化器和其他参数以减少可变性......但我仍然非常恼火,因为我在某个地方有一个我不知道的额外随机性来源! i> ...我了解 NN 本质上是随机的,但我想知道每个随机源是什么,并且能够在运行实验时至少在一台机器上修复它。跨度> @NeuronQ 在这种情况下,将以下 4 行添加到代码顶部: from numpy.random import seed seed(1) from tensorflow import set_random_seed set_random_seed(2)【参考方案2】:

此代码适用于使用 tensorflow 后端的 keras

这是因为权重是使用随机数初始化的,因此每次都会得到不同的结果。这是预期的行为。要获得可重现的结果,您需要将随机种子设置为。下面的示例设置操作级和图形级种子以获取更多信息,请参阅here

import tensorflow as tf
import random as rn

os.environ['PYTHONHASHSEED'] = '0'

# Setting the seed for numpy-generated random numbers
np.random.seed(37)

# Setting the seed for python random numbers
rn.seed(1254)

# Setting the graph-level random seed.
tf.set_random_seed(89)

from keras import backend as K

session_conf = tf.ConfigProto(
      intra_op_parallelism_threads=1,
      inter_op_parallelism_threads=1)

#Force Tensorflow to use a single thread
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)

K.set_session(sess)

# Rest of the code follows from here on ...

【讨论】:

我很好奇你能否补充一下为什么设置 intra_op_paraellism 是必要的,我没有在其他地方看到尽可能多的随机化建议。 TF 中是否存在基于无法预测的依赖于线程的行为产生随机结果的操作,或者这只是为了绝对确定?其他人建议删除 GPU 支持(我认为这对很多人来说不是一个选项),但听起来从 TF 2.2 开始,环境变量 "TF_DETERMINISTIC_OPS" = '1' 应该可以处理。 AttributeError: 模块 'tensorflow' 没有属性 'set_random_seed'【参考方案3】:

我通过添加os.environ['TF_DETERMINISTIC_OPS'] = '1'解决了这个问题

这里是一个例子:

import os
os.environ['TF_DETERMINISTIC_OPS'] = '1'
#rest of the code
# tf version 2.3.1

【讨论】:

以上是关于Keras LSTM - 为啥“相同”模型和相同权重的结果不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用相同的 Keras 模型和输入进行预测时会得到不同的结果?

为啥我的 Keras TimeDistributed CNN + LSTM 模型期望形状不完整

Keras LSTM 错误 - Logits 和标签应该具有相同的形状

如何使用 LSTM Keras 预测未来库存

用于 Keras 中句子相似性的具有 LSTM 的连体网络定期给出相同的结果

为啥我在使用 Scikit-Learn Keras 模型函数时,对于相同的网络配置会得到不同的准确度结果?