Параллельное моделирование Монте-Карло в R с использованием снегопада

Я пытаюсь сравнить до тысяч предполагаемых бета-дистрибутивов. Каждое бета-распределение характеризуется двумя параметрами формы альфа и бета. Сейчас я рисую по 100 000 образцов каждого дистрибутива. В качестве окончательного результата я хочу получить порядок распределений с наивысшей вероятностью в каждом розыгрыше выборки. Мой первый подход заключался в использовании lapply для создания матрицы числовых значений N * NDRAWS, которая потребляла слишком много памяти, поскольку N превышала 10 000. (10,000 * 100,000 * 8 байтов)

Поэтому я решил использовать последовательный подход к упорядочиванию каждого розыгрыша, затем суммировать порядок всех розыгрышей и получить окончательный порядок, как показано в примере ниже:

set.seed(12345)
N=100
NDRAWS=100000
df <- data.frame(alpha=sample(1:20, N, replace=T), beta=sample(1:200, N, replace=T))  

vec    <- vector(mode = "integer", length = N )

for(i in 1:NDRAWS){
  # order probabilities after a single draw for every theta
  pos <- order(rbeta(N, shape1=df$alpha, shape2=df$beta) )

  # sum up winning positions for every theta
  vec[pos] <- vec[pos] + 1:N
}

# order thetas
ord <- order(-vec)

df[ord,]

Это потребляет только N * 4 байта памяти, так как нет гигантской матрицы, а есть единственный вектор длины N. Мой вопрос теперь в том, как ускорить эту операцию с помощью снегопада (или любого другого многоядерного пакета), воспользовавшись моим преимуществом. 4 ядра процессора вместо одного ядра ???

# parallelize using snowfall pckg
library(snowfall)
sfInit( parallel=TRUE, cpus=4, type="SOCK")
sfLapply( 1:NDRAWS, function(x) ?????? )
sfStop()

Любая помощь приветствуется!


person Christian Borck    schedule 29.10.2013    source источник
comment
В чем проблема? Используете реальный пакет?   -  person PascalVKooten    schedule 30.10.2013
comment
Вопрос в том, как преобразовать цикл for в sfLapply, чтобы окончательный упорядоченный data.frame был идентичен тому, что в моем примере?   -  person Christian Borck    schedule 30.10.2013
comment
Это меня смущает: vec[pos] <- vec[pos] + 1:N. Вы пытаетесь поместить вектор в одно значение?   -  person PascalVKooten    schedule 30.10.2013
comment
Я знаю, что это немного сбивает с толку, может быть, есть более прямой подход. Что я делаю, так это добавляю слоты позиций одного упорядоченного розыгрыша для каждого розыгрыша 1: N.   -  person Christian Borck    schedule 30.10.2013
comment
Ах хорошо. Как я теперь вижу, это не является последовательным, если вы собираетесь складывать результаты в один элемент на каждой итерации (который не распределяется по памяти)?   -  person PascalVKooten    schedule 30.10.2013
comment
Может попробовать показать несколько строк ввода и вывода?   -  person PascalVKooten    schedule 30.10.2013
comment
Обновленный ответ, я запутался.   -  person PascalVKooten    schedule 30.10.2013
comment
Спасибо за ваши усилия и за указание на foreach и doParallel. А также для ваших подсказок для новичков в StackOverflow;) Я стараюсь иметь это в виду в будущих обсуждениях.   -  person Christian Borck    schedule 01.11.2013


Ответы (2)


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

Вот ваш полный пример, преобразованный для использования пакета foreach с серверной частью doParallel:

set.seed(12345)
N=100
NDRAWS=100000
df <- data.frame(alpha=sample(1:20, N, replace=T),
                 beta=sample(1:200, N, replace=T))
library(doParallel)
nworkers <- detectCores()
cl <- makePSOCKcluster(nworkers)
clusterSetRNGStream(cl, c(1,2,3,4,5,6,7))
registerDoParallel(cl)

vec <- foreach(ndraws=rep(ceiling(NDRAWS/nworkers), nworkers),
               .combine='+') %dopar% {
  v <- integer(N)
  for(i in 1:ndraws) {
    pos <- order(rbeta(N, shape1=df$alpha, shape2=df$beta) )
    v[pos] <- v[pos] + 1:N
  }
  v
}
ord <- order(-vec)
df[ord,]

Обратите внимание, что это дает другие результаты, чем последовательная версия, потому что рабочие генерируют разные случайные числа. Я использовал поддержку параллельных случайных чисел, предоставляемую параллельным пакетом, поскольку это хорошая практика.

person Steve Weston    schedule 30.10.2013
comment
Спасибо, Стив, это именно то, что я искал. Результаты немного отличаются из-за ГСЧ, как вы упомянули, но это хороший компромисс, учитывая улучшение скорости. Вы случайно не знаете, работает ли это и в системе Linux? Ядра определяются автоматически? - person Christian Borck; 31.10.2013
comment
Да, я написал и протестировал его в Linux, но он также должен работать в Windows и Mac OS X. И я использовал функцию detectCores, чтобы определить количество ядер. - person Steve Weston; 01.11.2013

Что ж, функциональность есть. Я не уверен, что вы будете возвращать с каждой итерацией.

Может, попробовать это?

myFunc <- function(xx, N) {
  pos <- order(rbeta(N, shape1=df$alpha, shape2=df$beta) )
  vec[pos] + 1:N
}

Использование doParallel позволит вам добавлять результаты:

require(doParallel)
registerDoParallel(cores=4)
foreach(i=1:NDRAWS, .combine='+') %dopar% myFunc(i, N)
person PascalVKooten    schedule 29.10.2013
comment
Привет, Dualinity, спасибо за быстрый ответ. Я случайно опубликовал образец суммы функций (rnorm (x)). Я действительно хочу преобразовать мой пример кода цикла for во что-то, что можно обрабатывать параллельно без создания гигантских матриц. - person Christian Borck; 30.10.2013
comment
Да, мне очень жаль. Он просто взорвался мне в лицо. - person PascalVKooten; 30.10.2013
comment
Существует также параллельный for цикл, разве это не похоже на то, что вы ищете? - person PascalVKooten; 30.10.2013
comment
@ChristianBorck Как правило, если вы хотите добавить что-то в сообщение, вы можете опубликовать свой собственный ответ, в котором вы бы сослались на мою помощь и предоставили полное решение. Или, если вам все еще нужна помощь, вы можете задать дополнительные вопросы. Если этот ответ помог, вы можете проголосовать за него или ответить, когда у вас будет достаточно репутации. Добро пожаловать в StackOverflow. - person PascalVKooten; 30.10.2013