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


问题内容

我正在使用并行运行 蒙特卡洛模拟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)

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

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]

在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]

我希望并行运行与固定种子相同。我发现如果我实施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]

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

系统信息

作业系统:Windows 10

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

脾气暴躁:1.17.2


问题答案:

numpy.random.*由于 比赛条件,
您获得的结果正在发生。numpy.random.*仅使用一个在所有线程之间共享的全局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选择的一般建议。