【问题标题】:Reproducible results in Tensorflow with tf.set_random_seed使用 tf.set_random_seed 在 Tensorflow 中重现结果
【发布时间】:2018-12-17 09:20:44
【问题描述】:

我正在尝试生成 N 组独立随机数。我有一个简单的代码,显示了 3 组 10 个随机数的问题。我注意到即使我使用tf.set_random_seed 设置种子,不同运行的结果看起来也不相似。非常感谢任何帮助或 cmets。

(py3p6) bash-3.2$ cat test.py 
import tensorflow as tf
for i in range(3):
  tf.set_random_seed(1234)
  generate = tf.random_uniform((10,), 0, 10)
  with tf.Session() as sess:
    b = sess.run(generate)
    print(b)

这是代码的输出:

# output :
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[8.559105  3.2390785 6.447526  8.316823  1.6297233 1.4103293 2.647568
 2.954973  6.5975866 7.494894 ]
[2.0277488 6.6134906 0.7579422 4.6359386 6.97507   3.3192968 2.866236
 2.2205782 6.7940736 7.2391043]

我想要类似的东西

[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]

更新 1: 实际上,我将种子初始化程序放在 for 循环中的原因是因为我想以不同的方式设置它们(例如,将其视为不同的 MCMC 运行)。这是我完成这项工作的代码,但我不确定它是否有效。基本上我会在 0 到 2^32-1 之间生成几个随机种子,并在每次运行中更改种子。非常感谢任何有助于提高内存/RAM 效率的帮助或 cmets。

import numpy as np
import tensorflow as tf
global_seed = 42
N_chains = 5
np.random.seed(global_seed)
seeds = np.random.randint(0, 4294967295, size=N_chains)

for i in range(N_chains):
    tf.set_random_seed(seeds[i])
    .... some stuff ....
    kernel_initializer = tf.random_normal_initializer(seed=seeds[i])
    .... some stuff
    with tf.Session() as sess:
         .... some stuff .....
 .
 .
 .

【问题讨论】:

  • 我也可以通过在操作中设置种子来生成相同的结果:tf.set_random_seed(1234)generate = tf.random_uniform((10,), 0, 10, seed=1234)。不知道有没有其他方法不需要在操作层设置种子。

标签: python tensorflow random-seed reproducible-research


【解决方案1】:

有一个相关的GitHub issue。 但是你的情况,请参考tf.set_random_seed的文档:

设置图级随机种子。

您可能希望使用 same 图和 same 操作来获得 不同 中的 same 随机数会议。

import tensorflow as tf

tf.set_random_seed(1234)
generate = tf.random_uniform((10,), 0, 10)
tf.get_default_graph().finalize() # something everybody tends to forget

for i in range(3):
    with tf.Session() as sess:
        b = sess.run(generate)
        print(b)

给予

[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]

在您的情况下,您在同一个图表中创建了不同的操作。

【讨论】:

  • 非常感谢您的回答!是的,这非常有效。然而,事实上,我下一步要做的是在 for 循环中不同地设置种子,以便具有不同种子的不同运行链。有意义吗?
【解决方案2】:

您在不同的运行中得到不同的结果,因为图中定义了三个 generate 变量(操作),而不是一个。这是因为你在 for 循环中有生成操作,这会导致三个操作。(Tensor("random_uniform:0"), Tensor("random_uniform_1:0"), Tensor("random_uniform_2:0"))。只需在 for 循环中执行 print(generate) 即可。您将看到上述三种不同的操作。

tf.set_random_seed 在图形级别设置种子。因此,它确定性地为图中的每个操作选择种子。因此,三个generate 操作在每次运行时都分配了相同的三个种子。这就是为什么对于每次运行,您会相应地看到所有三个变量的相同结果。 有关设置随机种子的更多信息,请查看this

因此,如果您希望每次运行会话时都获得相同的结果,您可以这样做:

tf.set_random_seed(1234)
generate = tf.random_uniform((10,), 0, 10)
for i in range(3):
    with tf.Session() as sess:
        b = sess.run(generate)
        print(b)

但是你为什么要创建n 会话。理想情况下,您应该创建一个会话,然后运行会话 n 次。不需要为每次运行创建一个新会话,并且每次它尝试将图中的变量和操作放置到设备(GPU 或 CPU)。

【讨论】:

  • 感谢您的回答。对此,我真的非常感激。我试图在 for 循环中设置随机种子的原因是因为我想为每个循环更改种子(对不起!我的最终目标是在种子设置不同的情况下执行 N 次任务,我想要一切可重现)。到目前为止,我可以通过在循环中使用设置全局种子和操作种子来使其可重现,但我不确定这是否是有效的方法。
【解决方案3】:

在 tensorflow 中,随机操作依赖于两个不同的种子:一个由tf.set_random_seed 设置的全局种子,以及一个作为操作参数提供的操作种子。您将找到更多关于它们之间关系的详细信息in the docs

每个随机操作都有不同的种子,因为每个随机操作都维护自己的内部状态以生成伪随机数。让每个随机生成器保持其自己的状态的原因是对变化具有鲁棒性:如果它们共享相同的状态,那么在图中某处添加一个新的随机生成器会改变所有其他生成器产生的值,从而违背了使用种子。

现在,为什么我们有这种全局 per-op 种子的双重系统?好吧,实际上不需要全局种子。它的存在是为了方便:它允许一次将所有随机操作种子设置为不同的确定性(如果未知)值,而无需详尽地遍历所有这些值。

现在,根据文档,当设置了全局种子但没有设置操作种子时,

系统确定性地选择一个操作种子和图级种子,以便获得一个唯一的随机序列。

更准确地说,提供的种子是在当前图中创建的最后一个操作的 id。因此,全局种子随机操作对图中的变化极为敏感,尤其是对那些在其自身之前创建的变化。

例如,

import tensorflow as tf
tf.set_random_seed(1234)
generate = tf.random_uniform(())
with tf.Session() as sess:
  print(generate.eval())
  # 0.96046877

现在如果我们之前创建一个节点,结果会发生变化:

import tensorflow as tf
tf.set_random_seed(1234)
tf.zeros(()) # new op added before 
generate = tf.random_uniform(())
with tf.Session() as sess:
  print(generate.eval())
  # 0.29252338

如果一个节点是在之后创建的,它不会影响操作种子:

import tensorflow as tf
tf.set_random_seed(1234)
generate = tf.random_uniform(())
tf.zeros(()) # new op added after
with tf.Session() as sess:
  print(generate.eval())
  # 0.96046877

显然,就像您的情况一样,如果您生成多个操作,它们将有不同的种子:

import tensorflow as tf
tf.set_random_seed(1234)
gen1 = tf.random_uniform(())
gen2 = tf.random_uniform(())
with tf.Session() as sess:
  print(gen1.eval())
  print(gen2.eval())
  # 0.96046877
  # 0.85591054

出于好奇,为了验证种子只是图中最后使用的 id 这一事实,您可以将 gen2 的种子与 gen1 对齐

import tensorflow as tf
tf.set_random_seed(1234)
gen1 = tf.random_uniform(())
# 4 operations seems to be created after seed has been picked
seed = tf.get_default_graph()._last_id - 4
gen2 = tf.random_uniform((), seed=seed)
with tf.Session() as sess:
  print(gen1.eval())
  print(gen2.eval())
  # 0.96046877
  # 0.96046877

但显然,这不应该通过代码审查。

【讨论】:

  • 非常感谢您的详尽回答。对此,我真的非常感激。但是在我的工作中,我需要使用不同的随机种子运行多个链(尽管在问题中我提到了相同的种子。这只是为了让我的问题看起来更简单)。我也希望它是可重现的,这就是为什么我也想设置全球种子。是否清楚我为什么要在 for 循环中设置种子?
  • tf.random.set_seed(seed) 对我来说工作得很好,不需要对会话做任何事情
【解决方案4】:

对于 Tensorflow 2.0tf.random.set_random_seed(seed) 更改为 tf.random.set_seed(seed)

参见 TF 文档:

【讨论】:

  • 这对我来说非常好用。也不需要np.random.seed(1),正如其他文章中所建议的那样
【解决方案5】:

派对迟到了,但是随机数生成器已经过大修(请参阅https://github.com/tensorflow/community/pull/38 以总结该过程)并且tf.random.experimental.Generator 类现在提供了所需的功能。

从 TF 1.14(包括 TF 2.0)开始,您可以播种生成器并获得完全相同的随机数,而不管会话、平台甚至架构如何。

import tensorflow as tf

rng = tf.random.experimental.Generator.from_seed(1234)
rng.uniform((), 5, 10, tf.int64)  # draw a random scalar (0-D tensor) between 5 and 10

详见文档:

解决您的特定问题(我使用的是 TF 2.0):

for i in range(3):
  b = tf.random.uniform((10,), 0, 10, seed=1234)
  print(b)

给予

tf.Tensor(
[2.7339518  9.339194   5.2865124  8.912003   8.402512   0.53086996
 4.385383   4.8005686  2.2077608  2.1795273 ], shape=(10,), dtype=float32)
tf.Tensor(
[9.668942   3.4503186  7.4577675  2.9200733  1.8064988  6.1576104
 3.9958012  1.889689   3.8289428  0.36031008], shape=(10,), dtype=float32)
tf.Tensor(
[8.019657  4.895439  5.90925   2.418766  4.524292  7.901089  9.702316
 5.1606855 9.744821  2.4418736], shape=(10,), dtype=float32)

此时

for i in range(3):
  rng = tf.random.experimental.Generator.from_seed(1234)
  b = rng.uniform((10,), 0, 10)
  print(b)

给你想要的:

tf.Tensor(
[3.581475  1.132276  5.6670904 6.712369  3.2565057 1.7095459 8.468903
 6.2697005 1.0973608 2.7732193], shape=(10,), dtype=float32)
tf.Tensor(
[3.581475  1.132276  5.6670904 6.712369  3.2565057 1.7095459 8.468903
 6.2697005 1.0973608 2.7732193], shape=(10,), dtype=float32)
tf.Tensor(
[3.581475  1.132276  5.6670904 6.712369  3.2565057 1.7095459 8.468903
 6.2697005 1.0973608 2.7732193], shape=(10,), dtype=float32)

【讨论】:

    【解决方案6】:

    我注意到您希望拥有 3 个包含随机数的不同向量。每次要运行代码时,都希望这三个包含随机数的向量与第一次相同。 这种方法完全可以解释,为什么需要四个相同的随机向量。您希望彼此有 4 个随机向量。

    定义图表操作时可以设置两种类型的种子: 图表级别的谷物,由 tf.set_random_seed 设置, 和 操作级别的种子,放置在初始化变量中 由于粒度处于图表级别,因此每次结果都不同。你必须使用 tf.InteractiveSession()

    tf.set_random_seed(1234)
    
    sess = tf.InteractiveSession()
    print(sess.run(tf.random_uniform((10,), 0, 10, seed=1)))
    print(sess.run(tf.random_uniform((10,), 0, 10, seed=2)))
    print(sess.run(tf.random_uniform((10,), 0, 10, seed=3)))
    print(sess.run(tf.random_uniform((10,), 0, 10, seed=4)))
    

    你会得到 4 个随机数向量,其中包含从 0 到 10 的数字。

    【讨论】:

      【解决方案7】:

      添加此答案以供参考: 可重现结果的问题可能不是直接来自 TensorFlow,而是来自底层平台。见this issue on Keras

      如果在 Nvidia GPU 上运行,Nvidia 有一个库可以帮助获得确定性结果:tensorflow-determinism

      pip install tensorflow-determinism
      

      然后你像这样使用它:

      import tensorflow as tf
      import os
      os.environ['TF_DETERMINISTIC_OPS'] = '1'
      

      仍然建议添加这些字段:

      SEED = 123
      os.environ['PYTHONHASHSEED']=str(SEED)
      random.seed(SEED)
      np.random.seed(SEED)
      tf.random.set_seed(SEED)
      

      对于Tensorflow

      from tfdeterminism import patch
      patch()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-31
        • 2018-02-02
        • 2018-02-03
        • 2016-11-12
        • 1970-01-01
        • 2020-11-22
        • 2018-04-03
        • 2019-08-23
        相关资源
        最近更新 更多