Este mini-tutorial es la segunda parte de mi primer tutorial sobre paralelizar R en Windows, te recomendo leerlo primero
El problema: Crear clusters es tedioso
Ya nos dimos cuenta de que el tema de crear los clusters y todas esas cosas puede ser muy tedioso, cargar los paquetes uno por uno y exportar las bases de datos es lento, olvidar un paquete crucial es pan de cada día y la idea es hacerlo más conveniente, además existe la posibilidad de que estemos usando un servidor sin acceso a Internet, lo que complica la instalación de paquetes
La solucion: Una función para clear clusters
así que cree una función, que mantengo en esta Carpeta en Github, para ser justo, la función esta inspirada en el paquete parallelsugar, pero con algunas modificaciones
La función crea un cluster de n nodos (argumento nnodes)
create_cluster esta diseñada para funcionar en servidores, ya que en general estos no tienen conexión a Internet es posible instalar los paquetes en una carpeta compartida local y usarlos dentro de nuestra función. en libroute podemos especificar la ruta a la librería de nuestra conveniencia.
create_cluster <- function(nnodes, libroute = NULL) {
# nnodes: Number of nodes
# libroute: Provided library route (different of the default library)
tryCatch({
cl <- parallel::makeCluster(nnodes) # Create an empty cluster with n nodes
loaded.package.names <- c( # Find the loaded packages in the session
sessionInfo()$basePkgs, # base packages
names(sessionInfo()$otherPkgs)
) # aditional loaded packages
# Send the packages and the complete environment to the cluster
parallel::clusterExport(cl,
"loaded.package.names", # loaded packages
envir = environment()
) # "Environment"
## Charge the packages in all nodes
if (is.null(libroute)) { # Use default library location
parallel::parLapply(cl, 1:length(cl), function(xx) {
lapply(loaded.package.names, function(yy) {
library(yy, character.only = TRUE)
})
})
} else {
# If we provide a library location use it (useful when working in remote machines)
parallel::parLapply(cl, 1:length(cl), function(xx) {
lapply(loaded.package.names, function(yy) {
library(yy, character.only = TRUE, lib.loc = libroute)
})
})
}
})
return(cl) # The result is the cluster
}
Un pequeño y reciclado ejemplo
Para ejemplificar, en este problema (sacado del tutorial anterior), teníamos que ocupar clusterCall para llamar al paquete randomForest, acá, si ya tenemos el paquete cargado en la sesión (y varios paquetes más, los exportamos a los nodos todo de una sola vez
Cargo el paquete y creo la función:
library(randomForest)
rf_boot_fun <- function(iter, data, n_sample) {
ind <- sample(1:nrow(data), size = n_sample)
data_sample <- data[ind, ]
# Ejecuto el modelo Random Forest
model1 <- randomForest(Sepal.Length ~ Petal.Length, data = data_sample)
# Extraigo el pseudo-R2
R2 <- model1$rsq[length(model1$rsq)]
# Extraigo el MSE
MSE <- model1$mse[length(model1$mse)]
results <- c("R2" = R2, "MSE" = MSE)
return(results)
}
Ahora puedo aplicar esta función con parLapply
library(parallel)
# Defino una ruta para mi libreria (opcional)
ruta_libreria <- "C:/Users/Julian/Documents/R/win-library/3.5"
# Esto puede ser simplemente nuevoclus<-create_cluster(4)
nuevoclus <- create_cluster(4, ruta_libreria)
# 10000 iteraciones de nuestra funcion con la base de datos "iris"
results <- parLapply(nuevoclus, 1:10000, rf_boot_fun, data = iris, n_sample = 100)
stopCluster(nuevoclus) # Cerramos el cluster, NO OLVIDAR
# Los primeros dos resultados
head(results, n = 2)
## [[1]]
## R2 MSE
## 0.778244 0.174310
##
## [[2]]
## R2 MSE
## 0.6912495 0.1865890
¡Listo, paralelizamos mucho mas fácil!
Espero les sirva!
Espero sus comentarios y dudas!