【问题标题】:Dealing with multidimensional output in parallel programming在并行编程中处理多维输出
【发布时间】:2017-10-28 22:06:53
【问题描述】:

我目前正在开发一个程序,以评估多个预测模型在模拟数据上的样本外性能。对于那些熟悉金融的人来说,它就像回测交易策略一样,只是我会评估预测而不是交易。

我目前使用 for 循环处理此类任务的一些对象是 7 维数组(维代表蒙特卡罗复制、数据生成过程、预测范围、模型参数选择的 3 维以及所有样本外分析所涵盖的时期)。显然,它的速度非常慢,所以并行计算对我来说是必须的。

我的问题是:如何在 R 中跟踪超过 2 个维度?让我们只向您展示使用“for 循环”和我的意思只有 3 个维度:

x <- array(dim=c(2,2,2))
     for (i in 1:2){
       for (j in 1:2){
         for (k in 1:2){
           x[i,j,k] <- i+j+k
         }
       }
     }

如果我使用“foreach”之类的东西,我会非常恼火的是,据我所知,可用的组合功能将返回列表、矩阵或向量——但不会返回任意大的多维数组。例如:

library(doParallel)
library(foreach)

# Get the number of cores to use
no_cores <- max(1, detectCores()-1)

# Make cluster object using no_cores
cl <- makeCluster(no_cores)

# Initialize cluster for parallel computing
registerDoParallel(cl)

x <- foreach(i=1:2, .combine=rbind)%:%
       foreach(j=1:2, .combine=cbind)%:%
         foreach(k=1:2, .combine=c)%dopar%{
           i+j+k
     }

在这里,我基本上将结果组合成向量,然后是矩阵,最后是逐行堆积矩阵。另一种选择是使用列表,或者通过列堆积矩阵,但是当您有 7 个维度和数百万次迭代要跟踪时,您可以想象会出现混乱。

我想我也可以编写自己的“组合”函数并获得我想要的那种输出,但我怀疑我不是第一个遇到这个问题的人。要么有一种方法可以完全按照我的意愿去做,要么这里有人可以指出一种不同的方式来存储我的结果。我在解决这个问题上选择了一条效率低得荒谬的道路,这并不奇怪——毕竟我是经济学家,而不是数据科学家!

任何帮助将不胜感激。提前致谢。

【问题讨论】:

    标签: r multidimensional-array parallel-processing


    【解决方案1】:

    今晚我终于偶然发现了一个可用的解决方案。我可以使用 'abind' 包的 'abind' 函数沿我选择的维度创建一个适当的组合函数:

    library(abind)
    
    # Get the number of cores to use
    no_cores <- max(1, detectCores()-1)
    
    # Make cluster object using no_cores
    cl <- makeCluster(no_cores)
    
    # Initialize cluster for parallel computing
    registerDoParallel(cl)
    
    mbind <- function(...) abind(..., along=3)
    
    x <- foreach(i=1:2, .combine=mbind)%:%
       foreach(j=1:2, .combine=cbind)%:%
         foreach(k=1:2, .combine=c)%dopar%{
           i+j+k
     }
    

    但是,我仍然想看看是否有人可以通过其他方式做我想做的事情。可能有很多方法可以做到这一点,而且我是 R 新手,但这种解决方案是一种明显的可能性。

    【讨论】:

    • 这是我使用的包和方法,但您应该使用.multicombine=TRUE 选项和mbind,否则mbind 将永远不会使用两个以上的参数调用,这可能会显着伤害你的表现。请参阅 stackoverflow.com/a/17572065/2109128 了解我对类似问题的回答。
    【解决方案2】:

    我会做什么并且我已经在我的一个包 bigstatsr 中使用了。

    只取一个维度并将其切割成no_cores块。它应该有足够的迭代次数(例如 4 核 20 次)。对于每次迭代,构造您想要的数组的一部分并将其存储在一个临时文件中。 ,使用这些文件的内容来填充整个数组。通过这样做,您只填充预分配的对象,这应该更快更容易。

    例子:

    x.all <- array(dim=c(20,2,2))
    no_cores <- 3    
    tmpfile <- tempfile()    
    range.parts <- bigstatsr:::CutBySize(nrow(x.all), nb = no_cores)
    
    library(foreach)
    cl <- parallel::makeCluster(no_cores)
    doParallel::registerDoParallel(cl)
    
    foreach(ic = 1:no_cores) %dopar% {
    
      ind <- bigstatsr:::seq2(range.parts[ic, ])
      x <- array(dim = c(length(ind), 2, 2))
    
      for (i in seq_along(ind)){
        for (j in 1:2){
          for (k in 1:2){
            x[i,j,k] <- ind[i]+j+k
          }
        }
      }
    
      saveRDS(x, file = paste0(tmpfile, "_", ic, ".rds"))
    }
    parallel::stopCluster(cl)
    
    for (ic in 1:no_cores) {
      ind <- bigstatsr:::seq2(range.parts[ic, ])
      x.all[ind, , ] <- readRDS(paste0(tmpfile, "_", ic, ".rds"))
    }
    
    print(x.all)
    

    除了写文件,你还可以直接返回foreach中数组的no_cores部分,并与右边的abind组合。

    【讨论】:

    • 其实是一个很好的解决方案。我确实花了一些时间与 R 一起“了解”您在这里尝试做的事情,但我会将此建议放在次要位置,直到我有一些新的花哨的工作要做。
    猜你喜欢
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 2016-07-14
    • 2016-01-08
    • 1970-01-01
    相关资源
    最近更新 更多