R Language
Traitement parallèle
Recherche…
Remarques
La parallélisation sur des machines distantes nécessite le téléchargement de bibliothèques sur chaque machine. Préférer les appels package::function()
. Plusieurs paquets ont une parallélisation intégrée, y compris caret
, pls
et plyr
.
Microsoft R Open (Revolution R) utilise également des bibliothèques multi-threads BLAS / LAPACK qui mettent intrinsèquement en parallèle de nombreuses fonctions communes.
Traitement parallèle avec package foreach
Le package foreach
apporte la puissance du traitement parallèle à R. Mais avant de pouvoir utiliser des processeurs multi-core, vous devez attribuer un cluster multi-core. Le package doSNOW
est une possibilité.
Une utilisation simple de la boucle foreach consiste à calculer la somme de la racine carrée et du carré de tous les nombres compris entre 1 et 100 000.
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 structure de la sortie de foreach
est contrôlée par l'argument .combine
. La structure de sortie par défaut est une liste. Dans le code ci-dessus, c
est utilisé pour retourner un vecteur à la place. Notez qu'une fonction de calcul (ou opérateur) telle que "+"
peut également être utilisée pour effectuer un calcul et renvoyer un autre objet traité.
Il est important de mentionner que le résultat de chaque boucle foreach est le dernier appel. Ainsi, dans cet exemple, k
sera ajouté au résultat.
Paramètre | Détails |
---|---|
.combiner | combine la fonction. Détermine comment les résultats de la boucle sont combinés. Les valeurs possibles sont c , cbind , rbind , "+" , "*" ... |
.en ordre | si TRUE le résultat est ordonné selon l'ordre de l'itération vairable (ici i ). Si FALSE le résultat n'est pas commandé. Cela peut avoir des effets positifs sur le temps de calcul. |
.paquets | pour les fonctions fournies par un paquetage autre que base , comme par exemple mass , randomForest ou autre, vous devez fournir ces paquets avec c("mass", "randomForest") |
Traitement parallèle avec paquet parallèle
Le package de base parallel
permet des calculs parallèles via le forking, les sockets et la génération de nombres aléatoires.
Détecte le nombre de cœurs présents sur l'hôte local:
parallel::detectCores(all.tests = FALSE, logical = TRUE)
Créez un cluster des cœurs sur l'hôte local:
parallelCluster <- parallel::makeCluster(parallel::detectCores())
Tout d'abord, une fonction appropriée pour la parallélisation doit être créée. Considérons le mtcars
données mtcars
. Une régression sur mpg
pourrait être améliorée en créant un modèle de régression distinct pour chaque niveau de 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])
}
Créez une fonction capable de parcourir toutes les itérations possibles de zlevels
. C'est toujours en série, mais c'est une étape importante car elle détermine le processus exact qui sera parallélisé.
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 cette fonction:
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
L'informatique parallel
utilisant parallel
ne peut pas accéder à l'environnement global. Heureusement, chaque fonction crée un environnement local accessible en parallel
. La création d'une fonction wrapper permet la parallélisation. La fonction à appliquer doit également être placée dans l'environnement.
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)
}
Maintenant, créez un cluster et exécutez la fonction wrapper.
parallelcluster <- parallel::makeCluster(parallel::detectCores())
models <- parallel::parLapply(parallelcluster,zlevels,
wrapper(datax, datay, dataz))
Arrêtez toujours le cluster lorsque vous avez terminé.
parallel::stopCluster(parallelcluster)
Le paquet parallel
inclut la famille apply()
complète, préfixée par le par
.
Génération de nombres aléatoires
Un problème majeur de la parallélisation est l'utilisation du RNG en tant que semences. Les nombres aléatoires par le nombre sont itérés par le nombre d'opérations du début de la session ou du dernier set.seed()
. Comme les processus parallèles proviennent de la même fonction, il peut utiliser la même graine, ce qui peut donner des résultats identiques! Les appels s'exécuteront en série sur les différents cœurs, sans avantage.
Un ensemble de graines doit être généré et envoyé à chaque processus parallèle. Cela se fait automatiquement dans certains paquets ( parallel
, snow
, etc.), mais doit être explicitement abordé dans d'autres.
s <- seed
for (i in 1:numofcores) {
s <- nextRNGStream(s)
# send s to worker i as .Random.seed
}
Des graines peuvent également être définies pour la reproductibilité.
clusterSetRNGStream(cl = parallelcluster, iseed)
mcparallelDo
Le package mcparallelDo
permet d'évaluer le code R de manière asynchrone sur les systèmes d'exploitation Unix (par exemple Linux et MacOSX). La philosophie sous-jacente du progiciel est alignée sur les besoins de l'analyse exploratoire des données plutôt que sur le codage. Pour le codage asynchrone, considérez le future
package.
Exemple
Créer des données
data(ToothGrowth)
Déclenche mcparallelDo pour effectuer une analyse sur une fourche
mcparallelDo({glm(len ~ supp * dose, data=ToothGrowth)},"interactionPredictorModel")
Faites d'autres choses, par exemple
binaryPredictorModel <- glm(len ~ supp, data=ToothGrowth)
gaussianPredictorModel <- glm(len ~ dose, data=ToothGrowth)
Le résultat de mcparallelDo retourne dans votre targetEnvironment, par exemple .GlobalEnv, quand il est complet avec un message (par défaut)
summary(interactionPredictorModel)
Autres exemples
# 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)
}