Создайте матрицу, используя цикл foreach и параллельную обработку - PullRequest
1 голос
/ 13 мая 2019

Я пытаюсь преобразовать цикл 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 ]

2 голосов
/ 13 мая 2019

Прежде чем выполнять какое-либо действие параллельно, следует попытаться выяснить, возможна ли какая-либо векторизация. И как только это будет сделано, вопрос «разумна ли параллелизация?»

В этом конкретном примере распараллеливание вряд ли будет таким быстрым, как вы ожидаете, так как на каждой итерации вы сохраняете свои выходные данные в общий объект. R обычно не поддерживает это в распараллеливании, и вместо этого нужно искать распараллеливание в так называемых «смущающе параллельных» задачах, пока не получат лучшее понимание того, как работают параллельные проблемы. Вкратце: не выполняйте параллельные изменения данных в R, если вы не знаете, что делаете. Это вряд ли будет быстрее.

Тем не менее, в вашем случае это становится довольно сложно. Похоже, вы выполняете «Rolling-Max Window», и вывод должен быть сохранен в комбинированной матрице. Альтернативный способ сохранения данных непосредственно в матрице - это вернуть матрицу с 3 столбцами x, i, j, где два последних являются индексами, которые указывают, какой строке / столбцу соответствует значение x должны быть помещены в.

Чтобы это работало, как отметил Дмитрий в своем ответе, данные необходимо экспортировать в каждый 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 секунд + немаловажные накладные расходы на экспорт данных. Но это, вероятно, быстрее, чем тот же метод с использованием последовательных циклов.

0 голосов
/ 13 мая 2019

Позвольте мне попытаться получить ответ здесь.

Как я знаю, 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 для maxm.

...