R Language
Elaborazione parallela
Ricerca…
Osservazioni
La parallelizzazione su macchine remote richiede il download di librerie su ogni macchina. Preferisco chiamate package::function()
. Diversi pacchetti hanno la parallelizzazione nativamente integrata, tra cui caret
, pls
e plyr
.
Microsoft R Open (Revolution R) utilizza anche librerie BLAS / LAPACK multi-threaded che parallelizza intrinsecamente molte funzioni comuni.
Elaborazione parallela con pacchetto foreach
Il pacchetto foreach
porta la potenza dell'elaborazione parallela su R. Ma prima di utilizzare CPU multi core è necessario assegnare un cluster multi core. Il pacchetto doSNOW
è una possibilità.
Un semplice utilizzo del ciclo foreach consiste nel calcolare la somma della radice quadrata e il quadrato di tutti i numeri da 1 a 100000.
library(foreach)
library(doSNOW)
cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)
f <- foreach(i = 1:100000, .combine = c, .inorder = F) %dopar% {
k <- i ** 2 + sqrt(i)
k
}
La struttura dell'output di foreach
è controllata dall'argomento .combine
. La struttura di output predefinita è una lista. Nel codice sopra, c
viene utilizzato per restituire un vettore. Si noti che una funzione di calcolo (o operatore) come "+"
può anche essere utilizzata per eseguire un calcolo e restituire un ulteriore oggetto elaborato.
È importante ricordare che il risultato di ogni ciclo foreach è l'ultima chiamata. Quindi, in questo esempio k
sarà aggiunto al risultato.
Parametro | Dettagli |
---|---|
.combine | combinare la funzione. Determina come vengono combinati i risultati del ciclo. I valori possibili sono c , cbind , rbind , "+" , "*" ... |
.In ordine | se TRUE il risultato è ordinato in base all'ordine dell'iterazione vairable (qui i ). Se FALSE il risultato non è ordinato. Questo può avere effetti postivi sul tempo di calcolo. |
.pacchi | per le funzioni fornite da qualsiasi pacchetto tranne base , come ad esempio mass , randomForest o altro, devi fornire questi pacchetti con c("mass", "randomForest") |
Elaborazione parallela con pacchetto parallelo
Il pacchetto parallel
base consente il calcolo parallelo tramite biforcazione, prese e generazione di numeri casuali.
Rileva il numero di core presenti sul localhost:
parallel::detectCores(all.tests = FALSE, logical = TRUE)
Creare un cluster dei nuclei sul localhost:
parallelCluster <- parallel::makeCluster(parallel::detectCores())
Innanzitutto, è necessario creare una funzione appropriata per la parallelizzazione. Considera il set di dati mtcars
. Una regressione su mpg
potrebbe essere migliorata creando un modello di regressione separato per ogni livello di cyl
.
data <- mtcars
yfactor <- 'cyl'
zlevels <- sort(unique(data[[yfactor]]))
datay <- data[,1]
dataz <- data[,2]
datax <- data[,3:11]
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
Crea una funzione in grado di scorrere tutte le possibili iterazioni degli zlevels
. Questo è ancora in serie, ma è un passo importante in quanto determina il processo esatto che verrà parallelizzato.
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
for (zlevel in zlevels) {
print("*****")
print(zlevel)
print(fitmodel(zlevel, datax, datay, dataz))
}
Curry questa funzione:
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
Il calcolo parallel
tramite parallel
non può accedere all'ambiente globale. Fortunatamente, ogni funzione crea un ambiente locale che può accedere in parallel
. La creazione di una funzione wrapper consente la parallelizzazione. Anche la funzione da applicare deve essere collocata all'interno dell'ambiente.
wrapper <- function(datax, datay, dataz) {
# force evaluation of all paramters not supplied by parallelization apply
force(datax)
force(datay)
force(dataz)
# these variables are now in an enviroment accessible by parallel function
# function to be applied also in the environment
fitmodel <- function(zlevel, datax, datay, dataz) {
glm.fit(x = datax[dataz == zlevel,], y = datay[dataz == zlevel])
}
# calling in this environment iterating over single parameter zlevel
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
return(worker)
}
Ora crea un cluster ed esegui la funzione wrapper.
parallelcluster <- parallel::makeCluster(parallel::detectCores())
models <- parallel::parLapply(parallelcluster,zlevels,
wrapper(datax, datay, dataz))
Arresta sempre il cluster al termine.
parallel::stopCluster(parallelcluster)
Il pacchetto parallel
include l'intera famiglia apply()
, preceduta dal par
.
Generazione di numeri casuali
Un grosso problema con la parallelizzazione è l'uso di RNG come semi. I numeri casuali per il numero vengono iterati dal numero di operazioni dall'inizio della sessione o set.seed()
. Poiché i processi paralleli derivano dalla stessa funzione, possono utilizzare lo stesso seme, causando probabilmente risultati identici! Le chiamate verranno eseguite in serie sui diversi core, non forniscono alcun vantaggio.
Un set di semi deve essere generato e inviato a ciascun processo parallelo. Questo viene fatto automaticamente in alcuni pacchetti ( parallel
, snow
, ecc.), Ma deve essere esplicitamente indirizzato in altri.
s <- seed
for (i in 1:numofcores) {
s <- nextRNGStream(s)
# send s to worker i as .Random.seed
}
I semi possono anche essere impostati per la riproducibilità.
clusterSetRNGStream(cl = parallelcluster, iseed)
mcparallelDo
Il pacchetto mcparallelDo
consente la valutazione del codice R in modo asincrono su sistemi operativi Unix (ad esempio Linux e MacOSX). La filosofia di fondo del pacchetto è allineata con le esigenze dell'analisi dei dati esplorativi piuttosto che con la codifica. Per codificare l'asincronia, considera il pacchetto future
.
Esempio
Crea dati
data(ToothGrowth)
Trigger mcparallelPer eseguire analisi su una forcella
mcparallelDo({glm(len ~ supp * dose, data=ToothGrowth)},"interactionPredictorModel")
Fai altre cose, ad es
binaryPredictorModel <- glm(len ~ supp, data=ToothGrowth)
gaussianPredictorModel <- glm(len ~ dose, data=ToothGrowth)
Il risultato di mcparallelDo ritorna nel tuo targetEnvironment, ad esempio .GlobalEnv, quando è completo di un messaggio (per impostazione predefinita)
summary(interactionPredictorModel)
Altri esempi
# Example of not returning a value until we return to the top level
for (i in 1:10) {
if (i == 1) {
mcparallelDo({2+2}, targetValue = "output")
}
if (exists("output")) print(i)
}
# Example of getting a value without returning to the top level
for (i in 1:10) {
if (i == 1) {
mcparallelDo({2+2}, targetValue = "output")
}
mcparallelDoCheck()
if (exists("output")) print(i)
}