【问题标题】:Need help on combine function in a Parallel Simulation study using doMC在使用 doMC 的并行模拟研究中需要组合功能的帮助
【发布时间】:2014-05-05 05:57:29
【问题描述】:

我想就为 foreach() 编写组合函数寻求帮助。考虑下面的函数:

library(mvtnorm)
library(doMC)

mySimFunc <- function(){
  myNum <- runif(1)
  myVec <- rnorm(10)
  myMat <- rmvnorm(5, rep(0, 3), diag(3))
  myListRslt <- list("myNum" = myNum, "myVec" = myVec, "myMat" = myMat)
return (myListRslt)
}

现在我想使用 foreach() %dopar% 运行上面的代码 1000 次,并且在每次迭代中我想:

  1. 按原样返回 myNum
  2. 获取 myVec 的平均值并返回
  3. 获取 myMat 的 colMeans() 并返回它。

我希望 foreach() %dopar% 返回一个最终列表,包括:

  1. 一个长度为 1000 的向量,包括 1000 个 myNum,每个对应于一个迭代
  2. 长度为 1000 的向量,包括每次迭代中 myVec 的 1000 个平均值
  3. 一个包含 1000 行的矩阵,其中每行包含该迭代中 myMat 的 colMeans

我的理想解决方案

我理想的解决方案是找到一种 foreach() 行为与 for 完全相同的方式,以便我可以简单地定义:

myNumRslt <- NULL
myVecRslt <- NULL
myMatRslt <- NULL

# and then simply aggregate result of each iteration to the variables above as:
foreach(i = 1:1000) %dopar%{
   rslt <- mySimFunc()
   myNumRslt <- c(myNumRslt, rslt$myNum)
   myVecRslt <- c(myVecRslt, mean(rslt$myVec))
   myMatRslt.tmp <- colMeans(rslt$myMat)
   myMatRslt <- rbind(myMatRslt, myMatRslt.tmp)
}

但是,不幸的是,foreach() 似乎无法做到这一点,所以我认为唯一的解决方案是编写一个类似于上述结果聚合的组合函数。

挑战

1) 我如何编写一个组合函数来返回我上面解释的内容?

2) 当我们执行 %dopar% 时(假设使用 doMC 包),doMC 是把每次迭代分配给 CPU 还是更进一步,将每次迭代分成更多部分并分配?

3) 有没有比使用 doMC 和 foreach() 更好(更有效)的方法? 想法 在这个questionBrian 中提到了一种处理包括数值在内的列表的绝妙方法。就我而言,我有数值以及向量和矩阵。在我的情况下,我不知道如何扩展 Brian 的想法。

非常感谢您的帮助。

【问题讨论】:

  • 你必须使用.combine吗?如果有,为什么?
  • 嗨@BenRollert,我对这个问题做了一些澄清。请阅读我的问题中的“理想解决方案”(刚刚添加到问题中)。正如我所解释的,我理想的解决方案是找到一种 foreach() 的行为方式与 for 完全相同的方式,这样我就可以在每次迭代中使用“c”和 rbind() 进行“结果”聚合。所以答案是否定的;我不必使用 .combine。
  • 据我了解,.combine 将对列表输出进行操作,即 .combine 中的rbind 与 rbinding 列表输出相同。完全有可能在边缘情况下有一些我不知道的性能优势,这就是为什么我很想看看其他人的反应。

标签: r foreach parallel-processing domc


【解决方案1】:

编辑

使用.combine 的清理、通用解决方案:

#modify function to include aggregation
mySimFunc2 <- function(){
myNum <- runif(1)
myVec <- mean(rnorm(10))
myMat <- colMeans(rmvnorm(5, rep(0, 3), diag(3)))
myListRslt <- list("myNum" = myNum, "myVec" = myVec, "myMat" = myMat)
return (myListRslt)
}

#.combine function
MyComb1 <- function(...) {
lst=list(...)
vec<-sapply(1:length(lst), function (i) return(lst[[i]][[1]] ))
vecavg<-sapply(1:length(lst),function (i) return(lst[[i]][[2]] ))
colmeans<-t(sapply(1:length(lst), function (i) return(lst[[i]][[3]])))
final<-list(vec,vecavg,colmeans)
names(final)<-c("vec","vecavg","colmeans")
return(final)
}

library(doParallel)
cl <- makeCluster(3) #set cores
registerDoParallel(cl)

foreach(i=1:1000,.export=c("mySimFunc2","MyComb1"),.combine=MyComb1,
.multicombine=TRUE,.maxcombine=1000, .packages=c("mvtnorm"))%dopar%{mySimFunc2()}

您现在应该有一个包含所需三个对象的列表输出,我将它们分别命名为vecvecavgcolmeans。请注意,如果迭代次数大于 100,则必须将 .maxcombine 设置为迭代次数。

附带说明一下,对这个示例任务进行并行化是没有意义的,尽管我猜真正的任务可能更复杂。

【讨论】:

  • 嗨@BenRollert,感谢您的回答。虽然您的回答绝对是我的问题 (+1) 的解决方案,但不幸的是,您的回答不是我的原始代码的实际解决方案,其中我有一个函数在每次迭代中生成 14 个不同的结果,包括矩阵。如果我运行我的 foreach () 并将所有输入存储在一个列表中,那么我可能会遇到 stackoverflow 问题。
  • Ben,现在我想得更多,我可以看到您的解决方案可以完全解决我的问题。我将等待几天,看看是否有人提出了一种更有效的方法,而且开销时间更少。如果没有,您的解决方案肯定会被接受。非常感谢您的帮助。
  • 无论哪种方式,您仍然受限于计算机上的 RAM。如果你在你的函数中聚合(如我的第二个解决方案),列表输出应该与你的最终解决方案大小相同。
  • 好的,使用 .combine 清理解决方案并将其推广到 n 次迭代
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多