为什么 numpy 随机种子没有保持固定但 RandomState 在并行运行时是?

RK1*_*RK1 8 python parallel-processing numpy random-seed

我运行蒙特卡罗模拟并行使用joblib。然而,我注意到虽然我的种子是固定的,但我的结果一直在变化。但是,当我连续运行该过程时,它如我所料保持不变。

下面我实现了一个小例子,模拟具有较高方差的正态分布的均值。

加载库并定义函数

import numpy as np
from joblib import Parallel, delayed

def _estimate_mean():
    np.random.seed(0)
    x = np.random.normal(0, 2, size=100)
    return np.mean(x)
Run Code Online (Sandbox Code Playgroud)

串联实现的第一个示例- 结果都与预期相同。

tst = [_estimate_mean() for i in range(8)]
In [28]: tst
Out[28]:
[0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897]
Run Code Online (Sandbox Code Playgroud)

在 Parallel 中实现的第二个例子:(注意有时手段是一样的,其他时候不一样)

tst = Parallel(n_jobs=-1, backend="threading")(delayed(_estimate_mean)() for i in range(8))

In [26]: tst
Out[26]:
[0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.1640259414956747,
 -0.11846452111932627,
 -0.3935934130918206]
Run Code Online (Sandbox Code Playgroud)

我希望并行运行与固定种子相同。我发现如果我实施RandomState修复种子似乎可以解决问题:

def _estimate_mean():
    local_state = np.random.RandomState(0)
    x = local_state.normal(0, 2, size=100)
    return np.mean(x)
tst = Parallel(n_jobs=-1, backend="threading")(delayed(_estimate_mean)() for i in range(8))

In [28]: tst
Out[28]:
[0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897,
 0.11961603106897]
Run Code Online (Sandbox Code Playgroud)

使用RandomState和仅在使用seed固定种子时有什么区别numpy.random,为什么后者在并行运行时不能可靠地工作?

系统信息

操作系统:Windows 10

Python:3.7.3(默认,2019 年 4 月 24 日,15:29:51)[MSC v.1915 64 位 (AMD64)]

麻木:1.17.2

Pet*_* O. 5

您得到的结果numpy.random.*是由于竞争条件而发生的。numpy.random.*仅使用一个全局 PRNG,该全局 PRNG 在所有线程之间共享,无需同步。由于线程同时并行运行,并且它们对全局 PRNG 的访问在它们之间不同步,因此它们都竞相访问 PRNG 状态(因此 PRNG 的状态可能会在其他线程背后发生变化)。为每个线程提供自己的 PRNG ( RandomState) 可以解决此问题,因为不再存在由多个线程在不同步的情况下共享的任何状态。


由于您使用的是 NumPy 1.17,您应该知道有一个更好的替代方案:NumPy 1.17 引入了新的随机数生成系统;它使用所谓的位生成器(例如 PCG)和随机生成器(例如 new numpy.random.Generator.

这是更改 RNG 政策提案的结果,该政策规定numpy.random.*一般不应再使用函数。这尤其是因为numpy.random.*在全局状态上运行。

NumPy 文档现在包含以下详细信息:

在新的RNG体系中。另请参阅“非加密 PRNG 的种子生成”,摘自我的一篇文章,其中包含有关 RNG 选择的一般建议。