如何检索 NumPy 随机数生成器的当前种子?

How can I retrieve the current seed of NumPy's random number generator?

下面导入 NumPy 并设置种子。

import numpy as np
np.random.seed(42)

不过,我对播种不感兴趣,更感兴趣的是阅读它。 random.get_state() 好像没有种子。 documentation 没有给出明显的答案。

如果我没有手动设置,如何检索 numpy.random 使用的当前种子?

我想使用当前种子来进行下一次流程迭代。

简短的回答是你根本不能(至少一般情况下不能)。

numpy 使用的 Mersenne Twister RNG 有 219937-1 种可能的内部状态,而单个 64 位整数只有 264 可能的值。因此不可能将每个 RNG 状态映射到唯一的整数种子。

可以直接使用np.random.get_state and np.random.set_state获取和设置RNG的内部状态。 get_state 的输出是一个元组,其第二个元素是一个 (624,) 32 位整数数组。这个数组有足够多的位来表示 RNG 的每个可能的内部状态 (2624 * 32 > 219937-1).

get_state 返回的元组可以像种子一样使用,以创建可重现的随机数序列。例如:

import numpy as np

# randomly initialize the RNG from some platform-dependent source of entropy
np.random.seed(None)

# get the initial state of the RNG
st0 = np.random.get_state()

# draw some random numbers
print(np.random.randint(0, 100, 10))
# [ 8 76 76 33 77 26  3  1 68 21]

# set the state back to what it was originally
np.random.set_state(st0)

# draw again
print(np.random.randint(0, 100, 10))
# [ 8 76 76 33 77 26  3  1 68 21]

检查 np.random.get_state() 返回的数组的第一个元素,对我来说似乎正是随机种子。

此贡献旨在澄清ali_m的正确答案,并作为对Dong Justin建议的重要修正。


这些是我的发现:

  1. 使用 np.random.seed(X) 设置随机种子后,您 可以 使用 np.random.get_state()[1][0].
  2. 再次找到它
  3. 不过,它对你用处不大。

以下代码部分的输出将向您展示为什么这两个陈述都是正确的。


语句 1 - 您可以使用 np.random.get_state()[1][0].

找到随机种子

如果使用 np.random.seed(123) 设置随机种子,则可以使用 state = np.random.get_state() 将随机状态检索为元组。下面是 state 的详细信息(我在 Spyder 中使用变量浏览器)。我正在使用屏幕截图,因为使用 print(state) 会淹没您的控制台,因为元组第二个元素中的数组大小。

您可以轻松地将 123 视为第二个元素中包含的数组中的第一个数字。使用 seed = np.random.get_state()[1][0] 给你 123。完美的?不完全是,因为:

声明 2 - 但是,它对您的用处不大:

起初看起来可能不是这样,因为您可以使用np.random.seed(123),使用[=22=检索相同的数字],使用[重置种子=26=],然后(貌似)用 np.random.seed(seed) 将其设置回 123 场景。但是你之前已经知道你的随机种子是什么,所以你不需要那样做。下一个代码部分还将显示您 不能 使用 np.random.get_state()[1][0] 获取任何随机状态的第一个数字并期望重新创建那个确切的场景。请注意,您很可能必须关闭并重新启动内核 完全 (或调用 np.random.seed(None))才能看到这一点。

以下代码段使用 np.random.randint() 生成 5 个介于 -10 和 10 之间的随机整数,并存储一些有关该过程的信息:

片段 1

# 1. Imports
import pandas as pd
import numpy as np

# 2. set random seed
#seedSet = None
seedSet = 123
np.random.seed(seedSet)

# 3. describe random state
state = np.random.get_state()
state5 = np.random.get_state()[1][:5]
seedState = np.random.get_state()[1][0]

# 4. generate random numbers
random = np.random.randint(-10, 10, size = 5)

# 5. organize and present findings
df = pd.DataFrame.from_dict({'seedSet':seedSet, 'seedState':seedState, 'state':state, 'random':random})
print(df)

请注意,名为 seedState 的列与 state 下的第一个数字相同。我本可以将其打印为 stand-alone 数字,但我想将其全部保存在同一个地方。另请注意,到目前为止,seedSet = 123np.random.seed(seedSet) 已被注释掉。而且因为没有设置随机种子,所以你的数字将与我的不同。但这不是这里重要的,而是结果的内部一致性:

输出 1:

   random seedSet   seedState       state
0       2    None  1558056443  1558056443
1      -1    None  1558056443  1808451632
2       4    None  1558056443   730968006
3      -4    None  1558056443  3568749506
4      -6    None  1558056443  3809593045

在这种特殊情况下 seed = np.random.get_state()[1][0] 等于 1558056443。按照 Dong Justins 回答的逻辑(以及我自己在此编辑之前的回答),您可以使用 np.random.seed(1558056443) 设置随机种子并获得相同的随机状态。下一个片段将显示您不能:

片段 2

# 1. Imports
import pandas as pd
import numpy as np

# 2. set random seed
#seedSet = None
seedSet = 1558056443
np.random.seed(seedSet)

# 3. describe random state
#state = np.random.get_state()
state = np.random.get_state()[1][:5]
seedState = np.random.get_state()[1][0]

# 4. generate random numbers
random = np.random.randint(-10, 10, size = 5)

# 5. organize and present findings
df = pd.DataFrame.from_dict({'seedSet':seedSet, 'seedState':seedState, 'state':state, 'random':random})
print(df)

输出 2:

   random     seedSet   seedState       state
0       8  1558056443  1558056443  1558056443
1       3  1558056443  1558056443  1391218083
2       7  1558056443  1558056443  2754892524
3      -8  1558056443  1558056443  1971852777
4       4  1558056443  1558056443  2881604748

看出区别了吗? np.random.get_state()[1][0] 对于输出 1 和输出 2 是相同的,但输出的其余部分不同(最重要的是随机数不相同)。因此,正如 ali_m 已经明确指出的那样:

It's therefore impossible to map every RNG state to a unique integer seed.

这个答案补充了其他人遗漏的重要细节。首先,改一下结论:

Original random seeds (set via np.random.seed) cannot be retrieved after generating numbers, but intermediates (current state) can.

参考@vestland的回答;然而,它可能会误导:生成的数字不同不是因为无法映射状态,而是因为使用了 不完整的编码get_state()[1]。完整的表示包括 pos = get_state()[2]。举例说明:

import numpy as np

state0 = np.random.get_state()
rand0  = np.random.randint(0, 10, 1)
state1 = np.random.get_state()
rand1  = np.random.randint(0, 10, 1)

assert all(s0 == s1 for s0, s1 in zip(state0[1], state1[1]))

我们生成了一个数字,但 get_state()[1] 保持不变。然而:

np.random.set_state(state0)
assert np.random.randint(0, 10, 1) == rand0

state1rand1 也是如此。因此,@vestland 的数字不同,因为当不设置种子时,pos = 623 - 而如果我们使用 np.random.seedpos = 624。为什么不方便的差异?没头绪。


总结 np.random.seed(s)

  • get_state()[1][0] 设置后立即:检索 s 完全重新创建状态
  • get_state()[1][0] 生成数字后:可能会或可能不会检索 s,但它会 不会 重新创建 current 状态(在 get_state()
  • get_state()[1][0] 生成多个数字后:将不会检索 s。这是因为 pos 耗尽了它的表示。
  • get_state() 在任何时候:将完全重新创建 那个点

最后,行为也可能因 get_state()[3:](当然还有 [0])而不同。

虽然最上面的答案说的大体上是正确的,因为一般不可能,但实际上是可能的。我会将您重定向到此人的博客:https://kamila.akagi.moe/posts/mersenne-twister/

此人开发了梅森扭曲破解算法来恢复初始种子,并提供了完整的细节和算法。我不是作者,不明白 material 的全部内容,但任何对此感兴趣的人都应该检查一下。