使用foreach进行并行计算 - 将循环输出保存到全局列表

时间:2016-01-30 16:56:57

标签: r foreach parallel-processing

我想使用foreach函数运行一个大循环。这意味着使用%dopar%运算符。

我找不到任何已完全解决此问题的问题。如果这是重复的,请指出我正确的方向,我将关闭这个问题。

我的成功喜忧参半。根据{{​​3}},它适用于我的机器上的简单示例,但是我似乎无法为自己的工作取得好成绩。 我的例子稍微复杂一点,所以魔鬼似乎一如既往的细节!我还阅读了包装创建者Revolution Analytics(help documentation)提供的“白皮书”。 我没有看到如何最好地使用.combine参数将结果应用于我的全局输出列表。 我想将一个大的列表分配给一个大的列表,而不是使用cbindc

我的例子非常复杂,但如果我进一步简化它,那么任何答案都可能无法解决我的问题。

我将执行一种移动线性模型。所以使用lm()超过50个obersvations [1:50]拟合模型,预测第51个观察[51],将结果保存到列表中。 然后我将进一步转移所有观察。所以lm超过[2:51]并预测第52次观察[52]。 我将总共使用100个观测值,因此我最多可以进行50次预测。

## ============================================ ##
##  Setup the backend for the foreach function  ##
## ============================================ ##

## doMC calls upon cores on demand, uses them and closes them
library(doMC)
registerDoMC(detectCores())     #detectCores() uses all cores

## for Windows users
#library(doParallel) --> for Windows users
#registerDoParallel(detectCores())

## ======================== ##
##  Create some dummy data  ##
## ======================== ##

## three columns, one hundred observations
my_data <- data.table(outcome = runif(100), V1 = 3*runif(100), V2 = sqrt(runif(100)))

## Have a look at the data if you like - using the DT package
library(DT)
datatable(my_data, options = list(pageLength = nrow(my_data)))

## ================================= ##
##  Perform the loop the normal way  ##
## ================================= ##

## Create container (a list of lists) for results
my_results <- sapply(c(paste0("step_", seq(1:50))), function(x) NULL)
step_results <- sapply(c("coefs", "rank", "error"), function(x) NULL)
for(i in 1:length(my_results)){my_results[[i]] <- step_results}

## Use a for loop to stpe through all the 50 'slices'
for(i in 1:50) {        #max. 50 predictions possible

    ## Fit a linear model
    my_fit <- lm("outcome ~ V1 + V2", data = my_data[i:(i+49)])

    ## Predict the next step
    my_pred <- predict(my_fit, newdata = my_data[i+50, .(V1, V2)]) 

    error <- my_data$outcome[i+50] - my_pred    #simply measure the delta to the actual value

    ## Assign some results to the container created earlier
    my_results[[i]][[1]] <- my_fit$coefficients
    my_results[[i]][[2]] <- my_fit$rank
    my_results[[i]][[3]] <- error

}
str(my_results)    ## Keep this container to compare to our next one

## ============================================ ##
##  Perform the loop using foreach and %dopar%  ##
## ============================================ ##

## Create same results object for results as previously for parallel results
par_results <- sapply(c(paste0("step_", seq(1:50))), function(x) NULL)
step_results <- sapply(c("coefs", "rank", "error"), function(x) NULL)
for(i in 1:length(par_results)){par_results[[i]] <- step_results}

my_results_par <- foreach(i = 1:50) %dopar%
    {        #max. 50 predictions possible

        my_fit <- lm("outcome ~ V1 + V2", data = my_data[i:(i+49)])     
        my_pred <- predict(my_fit, newdata = my_data[i+50, .(V1, V2)]) 
        error <- my_data$outcome[i+50] - my_pred 

        ## Assign some results to the container created earlier
        par_results[[i]][[1]] <- my_fit$coefficients
        par_results[[i]][[2]] <- my_fit$rank
        par_results[[i]][[3]] <- error

        Sys.sleep(i/20)    #Allows time to see R processes spawn on your system
        return(par_results)
    }

## We can see straight away that this didn't work as I would like it to
identical(my_results, my_results_par)   #FALSE

## This shows that the output seems good on the surface
class(my_results_par)
length(my_results_par)
## This shows that it doesn't (WARNING: very long)
str(my_results_par)

您可以在.combine函数中试用各种foreach参数,例如:

foreach(i = 1:50, .combine = "c") {computation}

foreach(i = 1:50, .combine = "cbind") {computation}

这些产品分别是一个向量和一个矩阵,但不包含我试图在每个循环中保存的所有结果。

问题

  1. 这种结构能否为您提供有关正在发生的事情的线索?
  2. 我如何使用.combine参数创建所需的输出?
  3. 我想做什么甚至可能?
  4. 我是否需要将foreach循环放在算法的不同位置?
  5. 我已经读过你可以为foreach提供自定义功能......这可能是这样做的吗?我仍然不知道如何将结果合并。

1 个答案:

答案 0 :(得分:1)

是的,这很容易做到。我们可以修改foreach的代码 - 步骤如下,我们将data.table包导出到每个工作人员。

my_results_par <- foreach(i = 1:50, .combine = append, .packages = c("data.table")) %dopar%
    {      
        my_fit <- lm("outcome ~ V1 + V2", data = my_data[i:(i+49)])     
        my_pred <- predict(my_fit, newdata = my_data[i+50, .(V1, V2)]) 
        error <- my_data$outcome[i+50] - my_pred 

        par_results <- list(
            coefs = my_fit$coefficients,
            rank = my_fit$rank,
            error = error
        )
        par_results <- list(par_results)
        names(par_results) <- paste0("step_", i)
        return(par_results)
    }
identical(my_results, my_results_par)   
[1] TRUE