与降雪库并行化的结果不可重现?

Posted

技术标签:

【中文标题】与降雪库并行化的结果不可重现?【英文标题】:Results of parallelization with snowfall library not reproducible? 【发布时间】:2021-05-31 05:02:04 【问题描述】:

每次运行以下代码时,向量result_seq 中的数字保持不变,因为我在生成向量之前使用了set.seed(11)

然而,似乎即使我在生成result_par 中的数字之前再次使用set.seed(11),但每次运行代码时数字都会改变。

library(snowfall)
snowfall::sfInit(parallel = TRUE, cpus = 4)

testFun = function(i) 
  result <- rnorm(1,10,3)


nsim <- 10

set.seed(11)
result_seq <- sapply(1:nsim, testFun)
print(mean(result_seq))

set.seed(11)
result_par <- sfLapply(1:nsim, testFun)
print(mean(as.numeric(result_par)))

为什么会这样?如何确保在降雪并行化过程中生成的随机数是可重现的?

【问题讨论】:

【参考方案1】:

由于 R 是单线程的,任何并行化的代码实际上都会启动多个会话。所以在这里,您实际上在sfLapply() 中分出了4 个单独的“子”会话,并且种子设置仅在您的“父”会话中发生一次。 “子”会话不知道其他会话,因此不知道您要在每个会话中重新设置种子。

您可以将set.seed() 移动到testFun() 以解决此问题:

testFun = function(i) 
  set.seed(11)
  result <- rnorm(1,10,3)

sfExport 可能值得探索,因为它旨在将参数分配给此类上下文的“子”会话。

【讨论】:

如果您愿意切换您的并行化前端,那么 future 框架可以保证数字可重现的随机数,无论您如何并行化以及运行多少并行工作程序,或者您按顺序运行,例如result_par &lt;- future.apply::future.lapply(1:nsim, testFun, future.seed = TRUE). 但是,如果播种只发生在父会话中,然后父会话产生子会话,那么每次运行生成的数字不应该相同,因为父总是使用 set.seed(11 )。似乎子进程的行为就像 set.seed(11) 根本没有被调用一样。 新会话不会继承“父”的状态(也许这对我来说是一个不好的类比),但它不像面向对象的继承。但是您可以创建新的/子会话来复制原始/父环境。 ?future.apply::future_lapply() 的 args 让您可以精细控制将哪些内容延续到新会话中 我的经验法则:仅在脚本顶部使用set.seed(),如果有的话。如果您发现自己将其设置在其他地方,则表明您正在做一些临时性的事情,并且有可能稍后会回来咬您,例如当您忘记它并重置您在其他地方依赖的 RNG 流时 @sonicboom,如果您问的是 future.applyfuture.seed = TRUE,那么答案是:该参数将根据当前 RNG 生成统计上合理的 RNG 子流父进程的状态(=您的主 R 会话)。有关详细信息,请参阅例如 jottr.org/2017/02/19/future-rng 和 jottr.org/2020/09/22/push-for-statical-sound-rng。

以上是关于与降雪库并行化的结果不可重现?的主要内容,如果未能解决你的问题,请参考以下文章

R降雪环境问题

犰狳中的并行化

使用 CUDA 在 python 中展开一个可并行化的 for 循环

random_number()如何并行工作?

R语言并行化基础与提高

R 降雪:并行应用于表格列