在并行程序中播种随机数发生器

问题描述 投票:16回答:3

我正在研究Python的多处理模块。我有两种情况:

例如1

def Foo(nbr_iter):
    for step in xrange(int(nbr_iter)) :
        print random.uniform(0,1)
...

from multiprocessing import Pool

if __name__ == "__main__":
    ...
    pool = Pool(processes=nmr_parallel_block)
    pool.map(Foo, nbr_trial_per_process)

Ex2。(使用numpy)

 def Foo_np(nbr_iter):
     np.random.seed()
     print np.random.uniform(0,1,nbr_iter)

在两种情况下,随机数生成器都被植入其分叉过程中。

为什么我必须在numpy示例中显式地进行播种,而不是在Python示例中?

python numpy random multiprocessing
3个回答
22
投票

如果未明确提供任何种子,则numpy.random将使用依赖于OS的随机性作为种子本身。通常,它会在基于Unix的系统(或某些Windows等效系统)上使用/dev/urandom,但是如果由于某种原因无法使用它,它将从挂钟中获得种子。由于在新的子流程派生时发生了自种,因此如果多个子流程同时分叉,则多个子流程有可能继承相同的种子,从而导致不同子流程产生相同的随机变量。

通常,这与您正在运行的并发线程数相关。例如:

import numpy as np
import random
from multiprocessing import Pool

def Foo_np(seed=None):
    # np.random.seed(seed)
    return np.random.uniform(0, 1, 5)

pool = Pool(processes=8)
print np.array(pool.map(Foo_np, xrange(20)))

# [[ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.11283279  0.28180632  0.28365286  0.51190168  0.62864241]
#  [ 0.11283279  0.28180632  0.28365286  0.51190168  0.62864241]
#  [ 0.28917586  0.40997875  0.06308188  0.71512199  0.47386047]
#  [ 0.11283279  0.28180632  0.28365286  0.51190168  0.62864241]
#  [ 0.64672339  0.99851749  0.8873984   0.42734339  0.67158796]
#  [ 0.11283279  0.28180632  0.28365286  0.51190168  0.62864241]
#  [ 0.14463001  0.80273208  0.5559258   0.55629762  0.78814652] <-
#  [ 0.11283279  0.28180632  0.28365286  0.51190168  0.62864241]]

[您可以看到多达8个线程的组同时用同一种子分叉,给了我相同的随机序列(我已经用箭头标记了第一组)。

在子流程中调用np.random.seed()会强制线程本地RNG实例再次从/dev/urandom或挂钟中进行播种,这(可能)将阻止您从多个子流程中看到相同的输出。最佳做法是将不同的种子(或numpy.random.RandomState实例)显式传递给每个子流程,例如:

def Foo_np(seed=None):
    local_state = np.random.RandomState(seed)
    print local_state.uniform(0, 1, 5)

pool.map(Foo_np, range(20))

我不完全确定randomnumpy.random在这方面的区别是什么(也许与numpy.random相比,选择自种随机源的规则略有不同?)。我仍然建议显式地将种子或random.Random实例传递给每个子流程,以确保安全。您还可以使用.jumpahead().jumpahead()方法,该方法旨在将多线程程序中的random.Random实例的状态改组。


1
投票

这里是一个不错的Random,它将解释blog post的工作方式。

如果使用numpy.random,它将获取导入np.random.rand()模块时创建的种子。因此,您需要在每个线程上手动创建一个新种子(例如,参见博客文章中的示例)。

python random模块没有这个问题,并自动为每个线程生成不同的种子。


1
投票

np.random刚刚介绍了[引用]“ ..已实施的三种策略可用于跨多个进程产生可重复的伪随机数(本地或分布式)。

第一种策略正在使用numpy 1.17对象。那里有很多父/子选项,但是对于我们来说,如果您想要相同的生成随机数,但每次运行都不同

((python3,从4个进程中打印3个随机数)

SeedSequence

如果要用于复制的相同结果

,则可以使用相同的种子(17)简单地重新设置numpy的种子:
from numpy.random import SeedSequence, default_rng
from multiprocessing import Pool

def rng_mp(rng):
    return [ rng.random() for i in range(3) ]

seed_sequence = SeedSequence()
n_proc = 4
pool = Pool(processes=n_proc)
pool.map(rng_mp, [ default_rng(seed_sequence) for i in range(n_proc) ])

# 2 different runs
[[0.2825724770857644, 0.6465318335272593, 0.4620869345284885],
 [0.2825724770857644, 0.6465318335272593, 0.4620869345284885],
 [0.2825724770857644, 0.6465318335272593, 0.4620869345284885],
 [0.2825724770857644, 0.6465318335272593, 0.4620869345284885]]

[[0.04503760429109904, 0.2137916986051025, 0.8947678672387492],
 [0.04503760429109904, 0.2137916986051025, 0.8947678672387492],
 [0.04503760429109904, 0.2137916986051025, 0.8947678672387492],
 [0.04503760429109904, 0.2137916986051025, 0.8947678672387492]]
© www.soinside.com 2019 - 2024. All rights reserved.