Paralléliser le code dans R en quelques minutes

Si vous croyez aux stéréotypes, alors le langage R est quelque chose de hautement spécialisé pour les statistiques et l'apprentissage automatique. Le deuxième stéréotype est que le code R pur n'est pas très rapide: premièrement, parce qu'il est interprété, et deuxièmement, parce qu'il est exécuté séquentiellement. Bien sûr, les stéréotypes ont un lien avec la réalité, sinon ils n'existeraient pas, mais c'est pourquoi ce sont des stéréotypes, qui donnent une image extrêmement simplifiée du monde, dans laquelle beaucoup de détails sont perdus. En particulier, je souhaite aujourd'hui partager un moyen étonnamment simple d'ajouter du parallélisme à R et de multiplier pour accélérer l'exécution du code existant sans avoir à y apporter de modifications majeures. Tout cela se fait en quelques minutes seulement.



Disons que nous avons une matrice ou une table de données contenant un certain nombre de lignes et de colonnes, et que nous voulons effectuer une sorte du même type de calcul pour chacune des lignes. Par exemple, calculez la somme des carrés de ses valeurs. Il est logique de déplacer les calculs dans une fonction et de l'appeler pour chacune des lignes.



Donnée initiale:



a <- matrix(rnorm(500000, mean=0, sd=2), 100000, 50)


Fonction:



sum.of.squares <- function(n) {
  n_sq <- sapply(n, function(x) x^2)
  sum(n_sq)
}


Vous pouvez simplement faire une boucle sur les lignes et appliquer cette fonction à chacune des lignes, mais ce n'est pas la méthode la plus recommandée pour R. Les calculs pour chacune des lignes seront effectués séquentiellement, tous les calculs seront effectués sur le même noyau. Ce type de code n'est vraiment pas très efficace. Juste au cas où, notons cette option et mesurons le temps d'exécution:



b <- vector()
for(i in 1:dim(a)[1]) {
  b[i] <- sum.of.squares(a[i,])
}


Nous mesurons le temps d'exécution:



b <- vector()
start_time <- Sys.time()
for(i in 1:dim(a)[1]) {
  b[i] <- sum.of.squares(a[i,])
}
timediff <- difftime(Sys.time(), start_time)
cat(" : ", timediff, units(timediff))


On a:



 :  4.474074 secs


Nous utiliserons cette fois comme point de départ pour la comparaison avec d'autres méthodes.



. R apply(). , : 1, 2. , . – sapply(), . – . , apply() :



b <- apply(a, 1, function(x) sum.of.squares(x))


, . , , :



start_time <- Sys.time()
b <- apply(a, 1, function(x) sum.of.squares(x))
timediff <- difftime(Sys.time(),start_time)
cat(" : ", timediff, units(timediff))


:



 : 4.484046 secs


, . : , .



, , R , . : apply(), , . , , . , apply(). apply() by(), eapply(), lapply(), Map(), .mapply(), mapply(), replicate(), sapply(), tapply(), vapply(). , future_apply:



install.packages("future.apply") 


– . , :



library("future.apply")
plan(multiprocess)


. , . future::plan(). , , apply "future_". :



b <- future_apply(a, 1, function(x) sum.of.squares(x))


:



start_time <- Sys.time()
b <- future_apply(a, 1, function(x) sum.of.squares(x))
timediff <- difftime(Sys.time(),start_time)
cat(" : ", timediff, units(timediff))


:



 :  1.283569 secs


Intel Core i7-8750H 12 . 12-, .



. , , , , , . , , future_sapply, . . – . , , , (a <- data.frame(a)), , 8 . .



Eh bien voilà tout. La méthode est assez simple. Pour moi, quand j'ai découvert son existence, ce n'était qu'une aubaine. Est-il vrai que le R actuel ne prend pas en charge le calcul parallèle? Cela dépend du point de vue sur cette question, de la gravité de sa déclaration. Mais dans un sens, nous pouvons supposer que cela prend en charge.




All Articles