为什么使用 numpy.random.seed 不是一个好习惯?

Why using numpy.random.seed is not a good practice?

我想进行使用随机数作为输入的可重现测试。我习惯在Matlab中调用rng,在Python中调用numpy.random.seed。但是,我注意到种子帮助的注释部分显示为:

This is a convenience, legacy function.

The best practice is to not reseed a BitGenerator, rather to recreate a new one. This method is here for legacy reasons. This example demonstrates best practice.

from numpy.random import MT19937
from numpy.random import RandomState, SeedSequence
rs = RandomState(MT19937(SeedSequence(123456789)))  
# Later, you want to restart the stream
rs = RandomState(MT19937(SeedSequence(987654321)))

有谁知道与文档字符串建议相比,使用种子的注意事项是什么?

来自https://numpy.org/neps/nep-0019-rng-policy.html

The preferred best practice for getting reproducible pseudorandom numbers is to instantiate a generator object with a seed and pass it around. The implicit global RandomState behind the numpy.random.* convenience functions can cause problems, especially when threads or other forms of concurrency are involved. Global state is always problematic. We categorically recommend avoiding using the convenience functions when reproducibility is involved.

That said, people do use them and use numpy.random.seed() to control the state underneath them. It can be hard to categorize and count API usages consistently and usefully, but a very common usage is in unit tests where many of the problems of global state are less likely.

This NEP does not propose removing these functions or changing them to use the less-stable Generator distribution implementations. Future NEPs might.

Specifically, the initial release of the new PRNG subsystem SHALL leave these convenience functions as aliases to the methods on a global RandomState that is initialized with a Mersenne Twister BitGenerator object. A call to numpy.random.seed() will be forwarded to that BitGenerator object. In addition, the global RandomState instance MUST be accessible in this initial release by the name numpy.random.mtrand._rand: Robert Kern long ago promised scikit-learn that this name would be stable. Whoops.

In order to allow certain workarounds, it MUST be possible to replace the BitGenerator underneath the global RandomState with any other BitGenerator object (we leave the precise API details up to the new subsystem). Calling numpy.random.seed() thereafter SHOULD just pass the given seed to the current BitGenerator object and not attempt to reset the BitGenerator to the Mersenne Twister. The set of numpy.random.* convenience functions SHALL remain the same as they currently are. They SHALL be aliases to the RandomState methods and not the new less-stable distributions class (Generator, in the examples above). Users who want to get the fastest, best distributions can follow best practices and instantiate generator objects explicitly.

This NEP does not propose that these requirements remain in perpetuity. After we have experience with the new PRNG subsystem, we can and should revisit these issues in future NEPs.