用于在 MacOS 上复制随机数的 Python 3.8 多处理
Posted
技术标签:
【中文标题】用于在 MacOS 上复制随机数的 Python 3.8 多处理【英文标题】:Python 3.8 multiprocessing for copying the random on MacOS 【发布时间】:2021-06-13 05:55:36 【问题描述】:from multiprocessing import Pool, cpu_count
import numpy as np
from numpy.random import multivariate_normal
F = multivariate_normal(np.zeros(3), np.eye(3), (3, 5))
def test(k):
print(k)
res = np.zeros((5, 3))
for i in range(3):
res[:, i] = F[k, :, i]
#print(res[:, i])
return res
if __name__ == '__main__':
with Pool(cpu_count()) as pool:
result = pool.map(test, range(3))
pool.close()
pool.join()
result = np.array(results)
在python3.6中,结果等于随机矩阵F。但是在python 3.8中它们的两个矩阵是不同的。这只是一个例子。在实际代码中,我想在每个时间步中取出F的每一列,并对其进行一些操作。
【问题讨论】:
【参考方案1】:心理调试:您在 Windows(任何 Python 版本)或 macOS(Python 3.8 或更高版本)上运行,两者都默认使用 'spawn'
创建工作进程的方法,而不是 'fork'
。当这种情况发生时,__main__
模块会在子进程中导入(使用不同的名称,因此它不会再次尝试运行主代码)以模拟fork
。
这主要是可行的,但在自播 PRNG 的情况下会失败,因为它们在子进程中重新播种,并且全局是从新的 PRNG 重新生成的,而不是让子进程继承它们生成的值。
简而言之,完成这项工作的方法是:
-
使用
'fork'
作为multiprocessing
启动方法运行它(在Windows 上不可能,在macOS 上技术上允许,但可能会中断,这就是他们将默认设置更改为'spawn'
的原因)。我在 Linux 上测试了你的代码(默认是 fork
s),除了修正错字(你在一个应该是 result
的地方输入了 results
)之外,result
和 F
是那里也一样。当我在创建 Pool
之前添加 multiprocessing.set_start_method('spawn')
时,它们不匹配。
将初始化函数和参数显式传递给 Pool
,以便每个工作人员将 F
重置为在父项中看到的值
在生成 F
之前使用显式种子,因此无论过程如何,它都是一致的(缺点:每次运行都会相同,或者至少,非常可预测,具体取决于您尝试的聪明程度得到)
请注意,#2 和#3 可以结合使用以最小化数据流。在父节点中生成一个“真正的”随机种子(例如使用os.urandom
)并编写一个简单的函数来接受种子并使用它来播种PRNG并生成F
(使用global F
让它改变全局价值)。在父函数中调用该函数,并将其作为带有种子参数的初始化程序传递给每个子函数。现在,不用传递F
(可能很大)的生成值,只需要传递种子,子进程就可以在本地重现F
,而无需序列化整个事情。缺点:所有进程共享相同的随机种子;它不像硬编码的种子那样可预测,但父母和孩子会从一组相同的随机数中抽取。
【讨论】:
另一方面,不在每个工作进程中使用相同的 RNG 种子通常是个好主意。在每个过程中使用相同的 RNG 种子会导致虚假的相关性,从而破坏您的数据。 (我认为继承RNG种子的工人是multiprocessing
的一大设计缺陷。)
@user2357112supportsMonica:是的,理想情况下,每个进程的种子都不同,但F
是相同的(因为它在逻辑上是相同的值)。选项 #2 会得到这个结果。
这也适用于 3.6,因为“spawn”仅在 3.8 及更高版本的 MacOS 上更改为默认值。 3.6 默认使用“fork”
@Aaron:是的,当我回答时,OP 没有提到测试版本 3.6 或有问题的操作系统。本来可以使答案变得不那么通灵,要么被指定。 :-)以上是关于用于在 MacOS 上复制随机数的 Python 3.8 多处理的主要内容,如果未能解决你的问题,请参考以下文章
MacOS 上的 Python 3.6 和 MySQL 8.0.21:如何给表 camelCase 列名? [复制]