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)
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow