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

时间:2015-04-24 17:59:05

标签: python numpy random multiprocessing

我正在研究Python的多处理模块。 我有两个案例:

实施例。 1

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

Ex 2.(使用numpy)

{{1}}

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

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

3 个答案:

答案 0 :(得分:15)

如果没有明确提供种子,numpy.random将使用依赖于操作系统的随机源来播种自身。通常它将在基于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实例传递给每个子进程以保证安全。您还可以使用random.Random Random方法,该方法旨在改变多线程程序中for (i in 1:n){ for (k in 1:c){ lat1=radians(Geocode[i,1]) lon1=radians(Geocode[i,2]) lat2=radians(Center_lat[k,2]) lon2=radians(Center_lon[k,2]) R <- 3958.756 # Earth mean radius [miles] dist_mat[i,k] <- acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2) * cos(lon2-lon1)) * R } } 个实例的状态。

答案 1 :(得分:1)

这是一个不错的blog post,它将说明numpy.random的工作方式。

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

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

答案 2 :(得分:0)

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

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

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

In [321]: p = np.random.rand(1000, 120)

In [322]: p /= p.sum(axis=1, keepdims=True)

In [323]: %timeit vectorized_choice(p, 20, items=np.arange(1, p.shape[1]+1))
6.41 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [324]: %timeit random_choice_prob_vectorized(p, 20, choice=np.arange(1, p.shape[1]+1))
6.29 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

如果您希望相同的结果用于复制目的,则可以使用相同的种子(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]]