From https://stackoverflow.com/questions/29854398/seeding-random-number-generators-in-parallel-programs
If no seed is provided explicitly, numpy.random
will seed itself using an OS-dependent source of
randomness. Usually it will use /dev/urandom
on Unix-based systems (or some Windows equivalent),
but if this is not available for some reason then it will seed itself from the wall clock. Since
self-seeding occurs at the time when a new subprocess forks, it is possible for multiple subprocesses
to inherit the same seed if they forked at the same time, leading to identical random variates being
produced by different subprocesses.
Some following texts are reprinted from [Python, NumPy, Pytorch中的多进程中每个进程的随机化种子误区] (https://blog.csdn.net/xiaojiajia007/article/details/90207113) with some modifications.
python自带的random在不同子进程中会生成不同的种子,而numpy.random不同子进程会fork相同的主进程中的种子。 pytorch中的Dataloader类的__getitem__()会在不同子进程中发生不同的torch.seed(),并且种子与多进程的worker id 有关(查看**worker_init_fn参数说明)。但是三者互不影响,必须独立地处理。因此在写自己的数据准备代码时,如果使用了 numpy中的随机化部件,一定要显示地在各个子进程中重新采样随机种子,或者使用python中的random发生随机种子。
Experiments were run on Linux-4.9.125-linuxkit-x86_64-with-Ubuntu-18.04-bionic (indeed, in a docker Virtual Machine) with Python 3.6.8, the system had 4 physical cores with 4 hyperthreads, thus 8 logical cores.
Using numpy.random
module, without seeding. Identical random sequences across subprocesses,
experiment is not reproducible:
1 |
|
Using numpy.random
module, seeding with no arguments. Different random sequences across subprocesses,
experiment is not reproducible:
1 |
|
Using numpy.random
module, seeding with None
. Different random sequences across subprocesses,
experiment is not reproducible:
1 |
|
Using numpy.random.RandomState
function, seeding with no arguments. Different random sequences across subprocesses,
experiment is not reproducible:
1 |
|
Using numpy.random.RandomState
function, seeding with None
. Different random sequences across subprocesses,
experiment is not reproducible:
1 |
|
Calling np.random.seed()
within a subprocess forces the thread-local RNG (Random Number Generator) instance to seed itself again
from /dev/urandom
or the wall clock, which will (probably) prevent you from seeing identical output
from multiple subprocesses. Best practice is to explicitly pass a different seed (or
numpy.random.RandomState
instance) to each subprocess.
Using numpy.random.RandomState
function, seeding with different seeds explicitly passed to subprocesses.
Different random sequences across subprocesses, experiment is reproducible:
1 |
|
Using Python’s default random
module, without seeding.
Different random sequences across subprocesses, experiment is not reproducible:
1 |
|
Using Python’s default random
module, seeding with no arguments.
Different random sequences across subprocesses, experiment is not reproducible:
1 |
|
Pytorch中多个进程加载随机样本Dataloader解决方法:
https://discuss.pytorch.org/t/does-getitem-of-dataloader-reset-random-seed/8097/7 除了可选择python中的random解决外,
Instead, add this line to the top of your main script (and you need to use python 3)
1 |
|
Pytorch中多个进程加载随机样本Dataloader解决方法(2022年7月16日更新):
这个bug的出现需要满足以下两个条件:
- PyTorch版本 < 1.9。PyTorch < 1.9:
torch
和random
库产生随机数没有问题,numpy
有问题。PyTorch >= 1.9: 官方修复以后,大家都没问题。 - 在Dataset的
__getitem__
方法中使用了Numpy的随机数
DataLoader
的构造函数有一个可选参数 worker_init_fn
。在加载数据之前,每个子进程都会先调用此函数。我们可以在 worker_init_fn
中设置NumPy的种子。还有一个要注意的点就是: 在默认情况下,每个子进程在epoch结束时被杀死,所有的进程资源都将丢失。在开始新的epoch时,主进程中的随机状态没有改变,用于再次初始化各个子进程,所以子进程的随机数种子和上个epoch完全相同。因此我们需要设置一个会随着epoch数目改变而改变的随机数,但是这个在实际应用中很难实现,因为在 worker_init_fn
中无法得知当前是第几个epoch。幸运的是,torch.initial_seed()
可以满足我们的需求。这个其实也是PyTorch官方的推荐作法: https://pytorch.org/docs/stable/notes/randomness.html#dataloader
为什么torch.initial_seed()
可以?
- 在子进程中运行
torch.initial_seed()
,返回的就是torch
当前的随机数种子,即base_seed + worker_id
。因为每个epoch
开始时,主进程都会重新生成一个base_seed
,所以base_seed
是随epoch
变化而变化的随机数。 此外,torch.initial_seed()
返回的是long int
类型,而Numpy只接受uint
类型([0, 2**32 - 1])
,所以需要对2**32
取模。 - 如果我们用
torch
或者random
生成随机数,而不是numpy
,就不用担心会遇到这个问题,因为PyTorch已经把torch
和random
的随机数设置为了base_seed + worker_id
。
1 |
|
知乎 可能95%的人还在犯的PyTorch错误的评论精选:
- 有一个人说: “用random numpy还会有锁死的问题”。他收到另一个网友的回复是: “卧槽,虽然还没实验,这句话解决了一个困扰我两年的问题!我应该就是numpy random导致死锁,worker设为0就没事。一直以为是pytorch的bug”
- 有一个人说: “之前遇到过,在子进程里面改用
np.random.default_rng
来获取随机数了” - 有一个人说: “不过感觉只重复augment部分的随机数,本质上还是枚举了所有可能的数据增强,所以即使对性能有影响,也比较有限吧?” 我其实也有类似的想法,不过不确定。