使用foreach循环和并行处理产生矩阵

时间:2019-05-13 10:23:57

标签: r for-loop matrix foreach doparallel

我正在尝试转换一个for循环,该循环目前用于在大型矩阵上运行进程。当前的for循环在30 x 30的部分内找到最大值,并创建一个具有最大值的新矩阵。

for循环的当前代码如下:

mat <- as.matrix(CHM) # CHM is the original raster image
maxm <- matrix(nrow=nrow(mat)/30, ncol=ncol(mat)/30) # create new matrix with new dimensions

for(i in 1:dim(maxm)[1]) {
  for(j in 1:dim(maxm)[2]) {
    row <- 30 * (i - 1) + 1
    col <- 30 * (j - 1) + 1
    maxm[i,j] <- max(CHM[row:(row + 29), col:(col + 29)])
   }
 }

我想将此转换为foreach循环以使用并行处理。我已经完成了下面的代码,但是这项工作确实很有效。我不确定如何在foreach循环中生成新矩阵:

ro<-nrow(mat)/30
co<-ncol(mat)/30
maxm <- matrix(nrow=nrow(mat)/30, ncol=ncol(mat)/30)

foreach(i=ro, .combine='cbind') %:%
  foreach(j=co, .combine='c') %dopar% {
    row <- 30 * (i - 1) + 1
    col <- 30 * (j - 1) + 1
    maxm[i,j]<-(max(CHM[row:(row + 29), col:(col + 29)]))
  }

请提出任何建议!

2 个答案:

答案 0 :(得分:2)

在并行执行任何操作之前,应尝试查看是否可以进行矢量化。一旦完成,问题“并行化是否合理?”

在此特定示例中,并行化不可能像您期望的那样快,因为在每次迭代中,您都将输出保存到一个公共对象中。 R通常不支持并行化,而是应该在所谓的“令人尴尬的可并行化”问题中寻求并行化,直到人们对并行问题的工作方式有了更好的了解。简而言之:除非您知道自己在做什么,否则不要对R中的数据执行并行更改。它不可能更快。

那对于您来说,这实际上变得非常棘手。您似乎正在执行“最大滚动窗口”,并且应将输出保存在组合矩阵中。直接将数据保存到矩阵中的另一种方法是返回具有3列xij的矩阵,其中后两个是指示哪个行/列的索引值x应该放在其中。

如Dmitriy在他的回答中所指出的那样,为了使此工作有效,需要将数据导出到每个cluster(并行会话),以便我们可以使用它。然后,以下示例显示了如何执行并行化

首先:创建一个集群并导出数据集

set.seed(1)
#Generate test example
n <- 3000
dat <- matrix(runif(n^2), ncol = n)
library(foreach)
library(doParallel)
#Create cluster
cl <- parallel::makeCluster(parallel::detectCores())
#Register it for the foreach loop
doParallel::registerDoParallel(cl)
#Export the dataset (could be done directly in the foreach, but this is more explicit)
parallel::clusterExport(cl, "dat")

接下来,我们进入foreach循环。请注意,根据文档,嵌套的foreach循环应使用%:%标记分开,如下面的示例所示:

output <- foreach(i = 1:(nrow(dat)/30), .combine = rbind, .inorder = FALSE) %:% 
    foreach(j = 1:(ncol(dat)/30), .combine = rbind, .inorder = FALSE) %dopar%{
        row <- 30 * (i - 1) + 1
        col <- 30 * (j - 1) + 1
        c(x = max(dat[row:(row + 29), col:(col + 29)]), i = i, j = j)
    }

注意 .inorder = FALSE。当我返回索引时,我不在乎顺序,仅在乎速度。 最后但并非最不重要的一点是,我们需要创建矩阵。 Matrix包函数Matrix::SparseMatrix允许指定值和索引。

output <- Matrix::sparseMatrix(output[,"i"], output[,"j"], x = output[,"x"])

这仍然相当慢。对于n = 3000,大约需要6秒钟来执行计算+导出数据的开销并不小。但这可能比使用顺序循环的相同方法要快。

答案 1 :(得分:0)

让我尝试在这里获得答案。

据我所知,R使用集群系统进行并行计算,每个节点都在自己的环境中工作。因此,对于foreach-%dopar%,首先,将所有当前的.globalEnv复制到每个群集节点,然后尝试运行在循环主体中编写的代码。代码执行后无回拷。您只会得到result = foreach(...) { }的结果。因此,每个节点中的代码maxm[i,j]<-(max(CHM[row:(row + 29), col:(col + 29)]))仅更改矩阵的本地副本,仅此而已。 因此,“正确”的代码可能会像这样:

mat <- as.matrix(CHM);
ro<-nrow(mat)/30;
co<-ncol(mat)/30;

maxm = foreach(i=1:ro, .combine='cbind') %:% 
{ 
   result = foreach(j = 1:co, .combine='c') %dopar% 
            { 
                row <- 30 * (i - 1) + 1; 
                col <- 30 * (j - 1) + 1; 
                max(CHM[row:(row + 29), col:(col + 29)]); 
            } 
   result; 
} 

也许还需要使用as.matrix来达到最大值。