R Language
Procesamiento en paralelo
Buscar..
Observaciones
La paralelización en máquinas remotas requiere que las bibliotecas se descarguen en cada máquina. Prefiere llamadas a package::function()
. Varios paquetes tienen paralelización incorporada de forma nativa, incluidos caret
, pls
y plyr
.
Microsoft R Open (Revolution R) también utiliza bibliotecas de subprocesos múltiples BLAS / LAPACK que paralelizan intrínsecamente muchas funciones comunes.
Procesamiento paralelo con paquete foreach
El paquete foreach
lleva la potencia del procesamiento paralelo a R. Pero antes de que quiera usar CPUs multi-core, debe asignar un clúster multi-core. El paquete doSNOW
es una posibilidad.
Un uso simple del bucle foreach es calcular la suma de la raíz cuadrada y el cuadrado de todos los números del 1 al 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 estructura de la salida de foreach
está controlada por el argumento .combine
. La estructura de salida predeterminada es una lista. En el código anterior, c
se usa para devolver un vector en su lugar. Tenga en cuenta que una función de cálculo (u operador) como "+"
también se puede usar para realizar un cálculo y devolver un objeto procesado adicional.
Es importante mencionar que el resultado de cada bucle foreach es la última llamada. Por lo tanto, en este ejemplo k
se añadirá al resultado.
Parámetro | Detalles |
---|---|
.combinar | combinar la función. Determina cómo se combinan los resultados del bucle. Los valores posibles son c , cbind , rbind , "+" , "*" ... |
.en orden | si es TRUE el resultado se ordena de acuerdo con el orden de la iteración vairable (aquí i ). Si es FALSE el resultado no es ordenado. Esto puede tener efectos positivos en el tiempo de cálculo. |
.paquetes | para las funciones proporcionadas por cualquier paquete, excepto la base , como por ejemplo mass , randomForest o si no, debe proporcionar estos paquetes con c("mass", "randomForest") |
Procesamiento paralelo con paquete paralelo
El paquete base parallel
permite el cálculo paralelo a través de forking, sockets y generación de números aleatorios.
Detectar la cantidad de núcleos presentes en el host local:
parallel::detectCores(all.tests = FALSE, logical = TRUE)
Cree un clúster de los núcleos en el host local:
parallelCluster <- parallel::makeCluster(parallel::detectCores())
Primero, se debe crear una función apropiada para la paralelización. Considere el conjunto de datos mtcars
. Una regresión en mpg
podría mejorarse creando un modelo de regresión separado para cada nivel 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])
}
Cree una función que pueda recorrer todas las iteraciones posibles de zlevels
. Esto todavía está en serie, pero es un paso importante ya que determina el proceso exacto que se realizará en paralelo.
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 esta función:
worker <- function(zlevel) {
fitmodel(zlevel,datax, datay, dataz)
}
La computación parallel
usa parallel
no puede acceder al entorno global. Afortunadamente, cada función crea un entorno local al que se puede acceder en parallel
. La creación de una función de envoltura permite la paralelización. La función a aplicar también debe colocarse dentro del entorno.
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)
}
Ahora crea un clúster y ejecuta la función de envoltura.
parallelcluster <- parallel::makeCluster(parallel::detectCores())
models <- parallel::parLapply(parallelcluster,zlevels,
wrapper(datax, datay, dataz))
Siempre detenga el grupo cuando termine.
parallel::stopCluster(parallelcluster)
El paquete parallel
incluye toda la familia apply()
, con el prefijo par
.
Generación de números aleatorios
Un problema importante con la paralelización es el uso de RNG como semillas. Los números aleatorios por el número se iteran por el número de operaciones desde el inicio de la sesión o el set.seed()
más reciente set.seed()
. Dado que los procesos paralelos surgen de la misma función, puede usar la misma semilla, ¡posiblemente causando resultados idénticos! Las llamadas se ejecutarán en serie en los diferentes núcleos, no proporcionan ninguna ventaja.
Se debe generar un conjunto de semillas y enviarlas a cada proceso paralelo. Esto se hace automáticamente en algunos paquetes ( parallel
, snow
, etc.), pero debe abordarse explícitamente en otros.
s <- seed
for (i in 1:numofcores) {
s <- nextRNGStream(s)
# send s to worker i as .Random.seed
}
Las semillas también se pueden establecer para reproducibilidad.
clusterSetRNGStream(cl = parallelcluster, iseed)
mcparallelDo
El paquete mcparallelDo
permite la evaluación del código R de forma asíncrona en sistemas operativos similares a Unix (por ejemplo, Linux y MacOSX). La filosofía subyacente del paquete está alineada con las necesidades del análisis de datos exploratorios en lugar de la codificación. Para codificar la asincronía, considere el paquete future
.
Ejemplo
Crear datos
data(ToothGrowth)
Activar mcparallelDo para realizar el análisis en una bifurcación
mcparallelDo({glm(len ~ supp * dose, data=ToothGrowth)},"interactionPredictorModel")
Hacer otras cosas, por ejemplo
binaryPredictorModel <- glm(len ~ supp, data=ToothGrowth)
gaussianPredictorModel <- glm(len ~ dose, data=ToothGrowth)
El resultado de mcparallelDo devuelve en targetEnvironment, por ejemplo, .GlobalEnv, cuando está completo con un mensaje (de forma predeterminada)
summary(interactionPredictorModel)
Otros ejemplos
# 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)
}