簡體   English   中英

使用 tf.set_random_seed 在 Tensorflow 中可重現結果

[英]Reproducible results in Tensorflow with tf.set_random_seed

我正在嘗試生成 N 組獨立的隨機數。 我有一個簡單的代碼,它顯示了 3 組 10 個隨機數的問題。 我注意到即使我使用tf.set_random_seed來設置種子,不同運行的結果看起來也不一樣。 非常感謝任何幫助或意見。

(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:

# 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 效率的幫助或評論。

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 .....
 .
 .
 .

在 tensorflow 中,隨機操作依賴於兩個不同的種子:一個全局種子,由tf.set_random_seed設置,以及一個操作種子,作為操作的參數提供。 您將 在文檔中找到有關它們如何關聯的更多詳細信息。

每個隨機操作都有不同的種子,因為每個隨機操作都為偽隨機數生成維護自己的內部狀態。 讓每個隨機生成器保持自己狀態的原因是為了能夠健壯地改變:如果它們共享相同的狀態,那么在你的圖中的某個地方添加一個新的隨機生成器會改變所有其他生成器產生的值,從而違背了使用種子。

現在,為什么我們有這種全局種子按操作種子的雙重系統? 好吧,實際上不需要全局種子。 它的存在是為了方便:它允許一次將所有隨機操作種子設置為不同的確定性(如果未知)值,而不必詳盡地遍歷所有這些值。

現在,根據文檔,當設置了全局種子而不是操作種子時,

系統確定性地選擇一個操作種子和圖級種子,以便它獲得唯一的隨機序列。

更准確地說,提供的種子是在當前圖中創建的最后一個操作的 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,你可以對齊的種子gen2gen1

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

顯然,這不應該通過代碼審查。

對於Tensorflow 2.0 tf.random.set_random_seed(seed)更改為tf.random.set_seed(seed)

請參閱 TF 文檔:

  • r2.0: https ://www.tensorflow.org/versions/r2.0/api_docs/python/tf/random/set_seed
  • r1.13: https ://www.tensorflow.org/versions/r1.13/api_docs/python/tf/random/set_random_seed

晚會晚了,但是隨機數生成器已經過大修(請參閱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)

有一個相關的GitHub 問題 但在你的情況下,請參考tf.set_random_seed的文檔:

設置圖級隨機種子。

您可能希望使用相同的圖形和相同的操作在不同的會話中獲得相同的隨機數。

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 ]

在您的情況下,您在同一個圖中創建了不同的操作。

我注意到你想要 3 個不同的包含隨機數的向量。 每次運行代碼時,都希望這三個包含隨機數的向量與第一次相同。 這種方法完全可以解釋,為什么需要四個相同的隨機向量。 您希望彼此擁有 4 個隨機向量。

定義圖表操作時可以設置兩種類型的種子:圖表級別的 Grain,由 tf.set_random_seed 設置,以及操作級別的種子,它們放置在初始化變量中,因為 Grain 在圖表上水平,每次結果都不一樣。 你必須使用 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 數字的隨機數向量。

添加此答案以供參考:可重現結果的問題可能不是直接來自 TensorFlow,而是來自底層平台。 在 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 < 2.1,添加上面和這個:

from tfdeterminism import patch
patch()

您在不同的運行中得到不同的結果,因為圖中定義了三個generate變量(操作)而不是一個。 這是因為您在 for 循環中有生成操作,這會導致三個操作。 (Tensor("random_uniform:0"), Tensor("random_uniform_1:0"), Tensor("random_uniform_2:0")) 只需在 for 循環內執行print(generate) 您將看到上述三種不同的操作。

tf.set_random_seed在圖形級別設置種子。 因此,它確定性地為圖中的每個操作挑選種子。 因此,三個generate操作在每次運行時都分配了相同的三個種子。 這就是為什么對於每次運行,您會看到所有三個變量的相應結果都相同。 有關設置隨機種子的更多信息,請查看 此內容

因此,如果您希望每次運行會話時都獲得相同的結果,您可以這樣做:

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)時。

這對我有用

tf.keras.utils.set_random_seed(1)
tf.config.experimental.enable_op_determinism()

你可以在這里找到更多

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM