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

ove*_*mer 15 python random numpy multiprocessing

我正在研究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)
Run Code Online (Sandbox Code Playgroud)

例2.(使用numpy)

 def Foo_np(nbr_iter):
     np.random.seed()
     print np.random.uniform(0,1,nbr_iter)
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,随机数生成器都在其分叉进程中播种.

为什么我必须在numpy示例中明确地进行种子设定,而不是在Python示例中?

ali*_*i_m 20

如果未明确提供种子,numpy.random则将使用依赖于操作系统的随机源来播种自身.通常它将/dev/urandom在基于Unix的系统(或某些Windows等效系统)上使用,但如果由于某种原因这不可用,那么它将从挂钟开始播种.由于自动种子在新的子进程分叉时发生,因此如果它们同时分叉,则多个子进程可以继承相同的种子,从而导致由不同子进程生成相同的随机变量.

这通常与您运行的并发线程数相关.例如:

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]]
Run Code Online (Sandbox Code Playgroud)

你可以看到最多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))
Run Code Online (Sandbox Code Playgroud)

我不完全确定在这方面random和之间存在差异的基础numpy.random(也许它与选择自我种子的随机性来源的规则略有不同numpy.random?).我仍然建议将种子或random.Random实例显式传递给每个子进程以保证安全.您还可以使用其.jumpahead()方法random.Random设计用于改组Random多线程程序中的实例状态.