在并行程序中播种随机数生成器
Seeding random number generators in parallel programs
我正在研究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)
例 2.(使用 numpy)
def Foo_np(nbr_iter):
np.random.seed()
print np.random.uniform(0,1,nbr_iter)
在这两种情况下,随机数生成器都在其分叉进程中播种。
为什么我必须在 numpy 示例中明确地进行播种,而不是在 Python 示例中?
如果没有明确提供种子,numpy.random
将使用依赖于 OS 的随机源为自己播种。通常它会在基于 Unix 的系统上使用 /dev/urandom
(或某些 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]]
您可以看到最多 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))
我不完全确定 random
和 numpy.random
在这方面的区别是什么(也许与numpy.random
?)。为了安全起见,我仍然建议明确地将种子或 random.Random
实例传递给每个子进程。您还可以使用 random.Random
的 .jumpahead()
方法,该方法专为在多线程程序中混洗 Random
实例的状态而设计。
这是一个不错的 blog post,它将解释 numpy.random
的工作方式。
如果您使用 np.random.rand()
,它将采用您导入 np.random
模块时创建的种子。所以你需要在每个线程手动创建一个新的种子(参见博客中的示例 post 例如)。
python 随机模块没有这个问题,并自动为每个线程生成不同的种子。
numpy 1.17 刚刚介绍 [quoting]“..实现的三种策略可用于跨多个进程生成可重复的伪随机数(本地或分布式) .."
第一个策略是使用 SeedSequence 对象。那里有很多父/子选项,但对于我们的情况,如果你想要生成相同的随机数,但每个 运行:
(python3, 从 4 个进程打印 3 个随机数)
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]]
如果您想要相同的结果用于复制目的,您可以简单地使用相同的种子 (17) 重新播种 numpy:
import numpy as np
from multiprocessing import Pool
def rng_mp(seed):
np.random.seed(seed)
return [ np.random.rand() for i in range(3) ]
n_proc = 4
pool = Pool(processes=n_proc)
pool.map(rng_mp, [17] * n_proc)
# same results each run:
[[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486]]
我正在研究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)
例 2.(使用 numpy)
def Foo_np(nbr_iter):
np.random.seed()
print np.random.uniform(0,1,nbr_iter)
在这两种情况下,随机数生成器都在其分叉进程中播种。
为什么我必须在 numpy 示例中明确地进行播种,而不是在 Python 示例中?
如果没有明确提供种子,numpy.random
将使用依赖于 OS 的随机源为自己播种。通常它会在基于 Unix 的系统上使用 /dev/urandom
(或某些 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]]
您可以看到最多 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))
我不完全确定 random
和 numpy.random
在这方面的区别是什么(也许与numpy.random
?)。为了安全起见,我仍然建议明确地将种子或 random.Random
实例传递给每个子进程。您还可以使用 random.Random
的 .jumpahead()
方法,该方法专为在多线程程序中混洗 Random
实例的状态而设计。
这是一个不错的 blog post,它将解释 numpy.random
的工作方式。
如果您使用 np.random.rand()
,它将采用您导入 np.random
模块时创建的种子。所以你需要在每个线程手动创建一个新的种子(参见博客中的示例 post 例如)。
python 随机模块没有这个问题,并自动为每个线程生成不同的种子。
numpy 1.17 刚刚介绍 [quoting]“..实现的三种策略可用于跨多个进程生成可重复的伪随机数(本地或分布式) .."
第一个策略是使用 SeedSequence 对象。那里有很多父/子选项,但对于我们的情况,如果你想要生成相同的随机数,但每个 运行:
(python3, 从 4 个进程打印 3 个随机数)
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]]
如果您想要相同的结果用于复制目的,您可以简单地使用相同的种子 (17) 重新播种 numpy:
import numpy as np
from multiprocessing import Pool
def rng_mp(seed):
np.random.seed(seed)
return [ np.random.rand() for i in range(3) ]
n_proc = 4
pool = Pool(processes=n_proc)
pool.map(rng_mp, [17] * n_proc)
# same results each run:
[[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486],
[0.2946650026871097, 0.5305867556052941, 0.19152078694749486]]