Una función para paralelizar en Windows o en servidores (Tutorial paralelizar en R parte 2)

Una función para paralelizar en Windows o en servidores (Tutorial paralelizar en R parte 2)

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!

Avatar
Julián Cabezas
Environmental Data Scientist / Spatial Analyst

Related

comments powered by Disqus