与降雪库并行化的结果不可重现?
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 <- 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.apply 的 future.seed = TRUE
,那么答案是:该参数将根据当前 RNG 生成统计上合理的 RNG 子流父进程的状态(=您的主 R 会话)。有关详细信息,请参阅例如 jottr.org/2017/02/19/future-rng 和 jottr.org/2020/09/22/push-for-statical-sound-rng。以上是关于与降雪库并行化的结果不可重现?的主要内容,如果未能解决你的问题,请参考以下文章