如何使用 foreach 和 doMC 包为随机模拟设置种子?

Posted

技术标签:

【中文标题】如何使用 foreach 和 doMC 包为随机模拟设置种子?【英文标题】:How to set seed for random simulations with foreach and doMC packages? 【发布时间】:2012-01-11 14:05:08 【问题描述】:

我需要进行一些模拟,出于调试目的,我想使用set.seed 来获得相同的结果。这是我正在尝试做的示例:

library(foreach)
library(doMC)
registerDoMC(2)

set.seed(123)
a <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)
set.seed(123)
b <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)

对象ab 应该相同,即sum(abs(a-b)) 应该为零,但事实并非如此。我做错了什么,还是我偶然发现了某些功能?

我能够使用 R 2.13 和 R 2.14 在两个不同的系统上重现此问题

【问题讨论】:

【参考方案1】:

使用set.seed(123, kind = "L'Ecuyer-CMRG") 也可以解决问题,并且不需要额外的包:

set.seed(123, kind = "L'Ecuyer-CMRG")
a <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)

set.seed(123, kind = "L'Ecuyer-CMRG")
b <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)

identical(a,b)
# TRUE

【讨论】:

这个答案比 Dirk Eddulbuettel 的答案要简单得多。它有什么缺点吗? 我也被这个解决方案的简单性所吸引,但我无法重现结果(在 Windows 7 上使用 R 3.4.0、doParallel 1.0.11 和 foreach 1.4。 3) 这显然行不通。 RNG 需要在调用之间重新播种。自从it’s been misleading people 以来,我冒昧地修复了这个答案(即使我改变了它的含义)。 嗯,它显然在 2017 年曾经工作过!感谢您更新代码。【参考方案2】:

对于更复杂的循环,您可能必须在 for 循环中包含 set.seed() inside

library(foreach)
library(doMC)
registerDoMC(2)
library(doRNG)

set.seed(123)
a <- foreach(i=1:2,.combine=cbind) %dopar% 
  create_something <- c(1, 2, 3)
  rnorm(5)

set.seed(123)
b <- foreach(i=1:2,.combine=cbind) %dopar% 
  create_something  <- c(4, 5, 6)
  rnorm(5)

identical(a, b)
# FALSE

a <- foreach(i=1:2,.combine=cbind) %dopar% 
  create_something  <- c(1, 2, 3)
  set.seed(123)
  rnorm(5)

b <- foreach(i=1:2,.combine=cbind) %dopar% 
  create_something  <- c(4, 5, 6)
  set.seed(123)
  rnorm(5)

identical(a, b)
# TRUE

【讨论】:

【参考方案3】:

我的默认答案曾经是“那么不要那样做”(使用 foreach),因为 snow 包会为您做到这一点(可靠!)。

但正如@Spacedman 指出的那样,如果您想留在doFoo / foreach 家庭,Renaud 的新doRNG 正是您所寻找的。​​p>

真正的关键是一个 clusterApply 风格的调用来获取所有节点上的种子集。并且以一种跨流协调的方式。哦,我有没有提到Tierney、Rossini、Li 和Sevcikova 的snow 已经为你做了近十年了?

编辑:虽然您没有询问snow,但为了完整起见,这里是命令行中的一个示例:

edd@max:~$ r -lsnow -e'cl <- makeSOCKcluster(c("localhost","localhost"));\
         clusterSetupRNG(cl);\
         print(do.call("rbind", clusterApply(cl, 1:4, \
                                             function(x)  stats::rnorm(1)  )))'
Loading required package: utils
Loading required package: utils
Loading required package: rlecuyer
           [,1]
[1,] -1.1406340
[2,]  0.7049582
[3,] -0.4981589
[4,]  0.4821092
edd@max:~$ r -lsnow -e'cl <- makeSOCKcluster(c("localhost","localhost"));\
         clusterSetupRNG(cl);\
         print(do.call("rbind", clusterApply(cl, 1:4, \
                                             function(x)  stats::rnorm(1)  )))'
Loading required package: utils
Loading required package: utils
Loading required package: rlecuyer
           [,1]
[1,] -1.1406340
[2,]  0.7049582
[3,] -0.4981589
[4,]  0.4821092
edd@max:~$ 

编辑:为了完整起见,这里是您的示例与doRNG 文档中的内容相结合

> library(foreach)
R> library(doMC)
Loading required package: multicore

Attaching package: ‘multicore’

The following object(s) are masked from ‘package:parallel’:

    mclapply, mcparallel, pvec

R> registerDoMC(2)
R> library(doRNG)
R> set.seed(123)
R> a <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)
R> set.seed(123)
R> b <- foreach(i=1:2,.combine=cbind) %dopar% rnorm(5)
R> identical(a,b)
[1] FALSE                     ## ie standard approach not reproducible
R>
R> seed <- doRNGseed()
R> a <- foreach(i=1:2,combine=cbind) %dorng%  rnorm(5) 
R> b <- foreach(i=1:2,combine=cbind) %dorng%  rnorm(5) 
R> doRNGseed(seed)
R> a1 <- foreach(i=1:2,combine=cbind) %dorng%  rnorm(5) 
R> b1 <- foreach(i=1:2,combine=cbind) %dorng%  rnorm(5) 
R> identical(a,a1) && identical(b,b1)
[1] TRUE                      ## all is well now with doRNGseed()
R> 

【讨论】:

感谢雪。我并不精通 R 中并行编程的复杂性,所以我开始使用foreach 来实现从非并行代码到并行代码的轻松转换。我知道我错过了什么。 嗯,这就是为什么我们几年前都从雪开始,因为从标准 *apply() 函数到并行函数的过渡很容易 :)【参考方案4】:

doRNG 包对您有用吗?我怀疑您的问题是由于两个线程都在飞溅随机种子向量:

http://ftp.heanet.ie/mirrors/cran.r-project.org/web/packages/doRNG/index.html

【讨论】:

感谢您的回答,我真的很想将两者都标记为答案,但 Dirk 的回答更广泛。尽管如此,我还是赞成你的回答,因为它包含足够的信息来解决我的问题。

以上是关于如何使用 foreach 和 doMC 包为随机模拟设置种子?的主要内容,如果未能解决你的问题,请参考以下文章

R中的并行处理

在 R 的 data.table 中获取随机的内部 selfref 错误

在并行 foreach 循环中使用 source()

并行化随机森林

斗地主案例——单例集合

如何使用刀片在@foreach 中随机播放文件