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

Why is numpy random seed not remaining fixed but RandomState is when run in parallel?

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

我在并行中实现的第二个例子:(注意有时手段完全相同,其他时候则不同)

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]

What is the difference between using RandomState and just seed when fixing the seeds using numpy.random and why would the latter not reliably work when running in parallel ?

系统信息

OS: 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 的状态可能会在其他线程之后改变' 背)。为每个线程提供自己的 PRNG (RandomState) 解决了这个问题,因为不再有任何状态在没有同步的情况下由多个线程共享。


由于您使用的是 NumPy 1.17,您应该知道有一个更好的选择:NumPy 1.17 引入了 new random number generation system;它使用所谓的位生成器,例如PCG,以及随机生成器,例如新的numpy.random.Generator.

这是 proposal to change the RNG policy 的结果,其中指出通常不应再使用 numpy.random.* 函数。这尤其是因为 numpy.random.* 对全局状态进行操作。

NumPy 文档现在有关于—

的详细信息

在新的RNG系统中。另请参阅我的一篇文章中的“Seed Generation for Noncryptographic PRNGs”,其中包含有关 RNG 选择的一般建议。