Ускорение вложенного dr aws из многомерной нормали, где каждый рисунок имеет различный средний вектор в r - PullRequest
1 голос
/ 16 марта 2020

Как часть алгоритма подбора модели, мне нужно взять множитель dr aws из многомерного нормального распределения.

У dr aws есть вложенная структура, поэтому для алгоритма подбора модели мне нужно взять, например, 1000 dr aws для 5 единиц, каждая из которых содержит 500 единиц. Таким образом, всего 1000 * 5 * 500 др aws в этом примере. Для каждой из 1000 драм aws ковариационная матрица sigma для многомерного распределения будет отличаться. Для каждой из 5 * 500 единиц в пределах 1000 др aws средний вектор mu будет различным.

Я кодировал приведенный ниже пример, который сначала устанавливает пример данных интересующей меня структуры, а затем генерирует dr aws в нужном мне формате.

#set up
library(mvnfast) #for rmvn function

nPar<-1000
K<-5
n<-rep(500,K)
mu<-lapply(1:nPar,function(u){
  lapply(1:K,function(v){
    do.call(rbind,lapply(1:n[v],function(w){
      runif(1)*c(0.3,0.5)
    }))
  })
})
sigmamats<-lapply(1:nPar,function(u){
  runif(1)*matrix(c(0.5,0.1,0.1,0.5),nrow=2,ncol=2)
})

#code needing speeding up
system.time(test<-lapply(1:nPar,function(u){
  lapply(1:K,function(v){
    do.call(rbind,lapply(1:n[v],function(w){
      rmvn(n=1,
           mu=mu[[u]][[v]][w,],
           sigma=sigmamats[[u]])
    }))
  })
}))
# user     system elapsed 
# 122.01   78.49  204.06 

Я пробовал несколько различных перестановок (например, сначала использовал rbind и удалял слой вложенности w, так что в этом примере второе значение lapply работает от 1 до 2500, но это не сильно ускоряло код при все.

Мне нужно сократить это время, так как это будет оценено не менее 200 раз в моем алгоритме подбора модели. Размеры на каждом уровне (nPar, K, n) могут меняется в зависимости от оцениваемых данных.

Буду признателен за любые советы по ускорению этого процесса.

РЕДАКТИРОВАТЬ результаты профилирования:

Rprof(tmp<-tempfile())
test<-lapply(1:nPar,function(u){
   lapply(1:K,function(v){
     do.call(rbind,lapply(1:n[v],function(w){
       rmvn(n=1,
            mu=mu[[u]][[v]][w,],
            sigma=sigmamats[[u]])
     }))
   })
 })
 Rprof()
 summaryRprof(tmp)
$by.self
                 self.time self.pct total.time
".Call"             153.16    75.27     161.84
"rmvn"               21.32    10.48     190.90
"matrix"              6.20     3.05       6.46
"FUN"                 5.44     2.67     203.48
"lapply"              4.96     2.44     203.48
"getCallingDLLe"      4.84     2.38       8.68
"get0"                3.44     1.69       3.44
"<Anonymous>"         1.66     0.82       1.66
"length"              0.88     0.43       0.88
"is.matrix"           0.48     0.24       0.48
"do.call"             0.46     0.23     203.40
"is.numeric"          0.32     0.16       0.32
"is.atomic"           0.26     0.13       0.26
"match.fun"           0.06     0.03       0.06
                 total.pct
".Call"              79.54
"rmvn"               93.82
"matrix"              3.17
"FUN"               100.00
"lapply"            100.00
"getCallingDLLe"      4.27
"get0"                1.69
"<Anonymous>"         0.82
"length"              0.43
"is.matrix"           0.24
"do.call"            99.96
"is.numeric"          0.16
"is.atomic"           0.13
"match.fun"           0.03

$by.total
                       total.time total.pct
"FUN"                      203.48    100.00
"lapply"                   203.48    100.00
"do.call"                  203.40     99.96
"rmvn"                     190.90     93.82
".Call"                    161.84     79.54
"getCallingDLLe"             8.68      4.27
"matrix"                     6.46      3.17
"get0"                       3.44      1.69
"<Anonymous>"                1.66      0.82
"length"                     0.88      0.43
"is.matrix"                  0.48      0.24
"is.numeric"                 0.32      0.16
"is.atomic"                  0.26      0.13
"match.fun"                  0.06      0.03
"cmpfun"                     0.02      0.01
"compiler:::tryCmpfun"       0.02      0.01
"doTryCatch"                 0.02      0.01
"findLocalsList"             0.02      0.01
"findLocalsList1"            0.02      0.01
"funEnv"                     0.02      0.01
"make.functionContext"       0.02      0.01
"tryCatch"                   0.02      0.01
"tryCatchList"               0.02      0.01
"tryCatchOne"                0.02      0.01
                       self.time self.pct
"FUN"                       5.44     2.67
"lapply"                    4.96     2.44
"do.call"                   0.46     0.23
"rmvn"                     21.32    10.48
".Call"                   153.16    75.27
"getCallingDLLe"            4.84     2.38
"matrix"                    6.20     3.05
"get0"                      3.44     1.69
"<Anonymous>"               1.66     0.82
"length"                    0.88     0.43
"is.matrix"                 0.48     0.24
"is.numeric"                0.32     0.16
"is.atomic"                 0.26     0.13
"match.fun"                 0.06     0.03
"cmpfun"                    0.00     0.00
"compiler:::tryCmpfun"      0.00     0.00
"doTryCatch"                0.00     0.00
"findLocalsList"            0.00     0.00
"findLocalsList1"           0.00     0.00
"funEnv"                    0.00     0.00
"make.functionContext"      0.00     0.00
"tryCatch"                  0.00     0.00
"tryCatchList"              0.00     0.00
"tryCatchOne"               0.00     0.00

$sample.interval
[1] 0.02

$sampling.time
[1] 203.48

РЕДАКТИРОВАТЬ # 2 - в идеале этот код в конечном итоге go будет превращен в пакет, и поэтому мне нужен способ ускорить этот процесс, который может присутствовать в коде, вносящем вклад в пакет R

1 Ответ

1 голос
/ 16 марта 2020

Вы можете получить параллельное решение с минимальными изменениями в вашем коде, например, с помощью пакета furrr.

#code needing speeding up
library(furrr)
plan(multisession)
system.time(test<-future_map(1:nPar,function(u){
  lapply(1:K,function(v){
    do.call(rbind,lapply(1:n[v],function(w){
      rmvn(n=1,
           mu=mu[[u]][[v]][w,],
           sigma=sigmamats[[u]])
    }))
  })
}))

переходит в

   user  system elapsed 
  0.815   0.163  14.501 

в предыдущей версии на моем компьютере было

   user  system elapsed 
 30.608   1.399  32.017 

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

...