1 Introducción

Se emplearán la siguiente paquetería con el énfasis en cómo implementar bosques aleatorios con ranger (Wright and Ziegler 2017). Otra implementación de bosques se lleva cabo con h2o (LeDell et al. 2021). El resto de este material se basa fuertemente en el cap 11 de Boehmke and Greenwell (2020).

# data
library(mlbench)
library(tidyverse)
library(tidymodels)
library(rpart.plot)
library(vip)
data("BreastCancer")
BCrec <- recipe(Class ~ ., data = BreastCancer) %>%
  step_naomit(all_predictors()) %>%
  prep(BreastCancer, verbose = FALSE) %>%
  bake(new_data = NULL)
BCrec2 <- BCrec %>% select(-Class,-Id) %>% mutate_all(as.numeric)
BCrec4 <- tibble(BCrec2,BCrec %>% select(Class))
set.seed(1234)
BCrec_split <- initial_split(BCrec4)

BCrec_train <- training(BCrec_split)
BCrec_test <- testing(BCrec_split)

# librerías auxiliares
library(dplyr)    # manejo de datos
library(ggplot2)  # mejores gráficas

# librerías de modelación
library(ranger)   # implementación en c++ de random forest 
library(h2o)      # implementación en java-based de random forest

2 Extensión del bagging

El ensacado o bagging de árboles introduce un componente aleatoria en el proceso de construcción de árboles al construir muchos árboles en copias de arranque de los datos de entrenamiento. El bagging luego agrega las predicciones en todos los árboles.

Los bosques aleatorios ayudan a reducir la correlación de árboles al inyectar más aleatoriedad en el proceso de crecimiento de los árboles. Más específicamente, mientras crece un árbol de decisión durante el proceso de baggging, los bosques aleatorios realizan una aleatorización de variables divididas donde cada vez que se realiza una división, la búsqueda de la variable dividida se limita a un subconjunto aleatorio de de las características . Los valores predeterminados típicos son \(m_{try}=\frac{p}{3}\) (regresión) y \(m_{try}=\sqrt{p}\) (clasificación), pero esto debe considerarse un parámetro de ajuste.

El algoritmo básico para un bosque aleatorio de regresión o clasificación se puede generalizar de la siguiente manera:

  1. Given a training data set
  2. Select number of trees to build (n_trees)
  3. for i = 1 to n_trees do
  4.  Generate a bootstrap sample of the original data
  5.  Grow a regression/classification tree to the bootstrapped data
  6.  for each split do
  7.  | Select m_try variables at random from all p variables
  8.  | Pick the best variable/split-point among the m_try
  9.  | Split the node into two child nodes
  10.  end
  11. Use typical tree model stopping criteria to determine when a
    tree is complete (but do not prune)
  12. end
  13. Output ensemble of trees

Dado que el algoritmo selecciona aleatoriamente una muestra de arranque para entrenar y una muestra aleatoria de características para usar en cada división, se produce un conjunto más diverso de árboles que tiende a disminuir la correlación de árboles más allá de los árboles en bolsas y, a menudo, aumenta drásticamente el poder predictivo.

3 Rendimiento OOB

Los bosques aleatorios se han vuelto populares porque tienden a proporcionar un rendimiento muy bueno desde el primer momento. Aunque tienen varios hiperparámetros que se pueden ajustar, los valores predeterminados tienden a producir buenos resultados.

Por ejemplo, al entrenar un modelo de bosque aleatorio con todos los hiperparámetros configurados en sus valores predeterminados, obtenemos un OOB rmse que es mejor que cualquier modelo hasta ahora.

# number of features
n_features <- length(setdiff(names(BCrec4), "Cell.size"))

# train a default random forest model
BC_rf1 <- ranger(
  Cell.size ~ ., 
  data = BCrec_train,
  mtry = floor(n_features / 3),
  respect.unordered.factors = "order",
  seed = 123
)

# get OOB RMSE
(default_rmse <- sqrt(BC_rf1$prediction.error))
## [1] 1.240171

4 Hiperparámetros

Existen varios hiperparámetros ajustables a considerar al entrenar un modelo. Los principales incluyen:

  1. La cantidad de árboles en el bosque
  2. El número de funciones a considerar en cualquier división determinada: mtry
  3. La complejidad de cada árbol
  4. El esquema de muestreo
  5. La regla de división que se debe usar durante la construcción del árbol.

4.1 Número de árboles

La primera consideración es la cantidad de árboles dentro del bosque aleatorio. Aunque técnicamente no es un hiperparámetro, el número de árboles debe ser lo suficientemente grande para estabilizar la tasa de error. Una buena regla general es comenzar con \(10\) veces el número de características, sin embargo, a medida que se ajustan otros hiperparámetros como \(m_{try}\) y el tamaño del nodo, es posible que se requieran más o menos árboles. Más árboles proporcionan estimaciones de error más robustas y estables y medidas de importancia variable; sin embargo, el impacto en el tiempo de cálculo aumenta linealmente con el número de árboles.

4.2 Hiperparámetro de aleatorización de variables divididas

El hiperparámetro que controla la función de aleatorización de variables divididas de los bosques aleatorios a menudo se denomina \(m_{try}\) y ayuda a equilibrar la correlación de árboles baja con una fuerza predictiva razonable. Con problemas de regresión, el valor predeterminado suele ser \(m_{try}=\frac{p}{3}\) y para la clasificación \(\sqrt{p}\). Sin embargo, cuando hay menos predictores relevantes (por ejemplo, datos ruidosos), un valor más alto de \(m_{try}\) tiende a funcionar mejor porque hace que sea más probable seleccionar aquellas características con la señal más fuerte. Cuando hay muchos predictores relevantes, un \(m_{try}\) bajo podría funcionar mejor.

4.3 Complejidad del árbol

Los bosques aleatorios se construyen sobre árboles de decisiones individuales; en consecuencia, la mayoría de las implementaciones de bosques aleatorias tienen uno o más hiperparámetros como el tamaño del nodo, la profundidad máxima, el número máximo de nodos terminales o el tamaño de nodo requerido para permitir divisiones adicionales. El tamaño del nodo es probablemente el hiperparámetro más común para controlar la complejidad del árbol y la mayoría de las implementaciones utilizan los valores predeterminados de uno para la clasificación y cinco para la regresión, ya que estos valores tienden a producir buenos resultados. Sin embargo, si los datos tienen muchos predictores ruidosos y valores altos tienen un mejor rendimiento, entonces el rendimiento puede mejorar al aumentar el tamaño del nodo (es decir, disminuir la profundidad y complejidad del árbol). Además, si el tiempo de cálculo es una preocupación, a menudo se puede disminuir el tiempo de ejecución sustancialmente aumentando el tamaño del nodo y tener solo impactos marginales en la estimación de error.

4.4 Esquema de muestreo

El esquema de muestreo predeterminado para bosques aleatorios es el bootstrap donde se muestrea el \(100%\) de las observaciones con reemplazo (en otras palabras, cada copia de bootstrap tiene el mismo tamaño que los datos de entrenamiento originales); sin embargo, se ajusta tanto el tamaño de la muestra como si tomar la muestra con o sin reemplazo. El parámetro de tamaño de la muestra determina cuántas observaciones se extraen para el entrenamiento de cada árbol. Disminuir el tamaño de la muestra conduce a árboles más diversos y, por lo tanto, a una menor correlación entre árboles, lo que puede tener un efecto positivo en la precisión de la predicción. En consecuencia, si hay algunas características dominantes de datos, reducir el tamaño de la muestra también puede ayudar a minimizar la correlación entre árboles.

Además, con muchas características categóricas de un número variable de niveles, el muestreo con reemplazo puede conducir a una selección dividida de variables sesgada. En consecuencia, categorías que no están equilibradas, el muestreo sin reemplazo proporciona un uso menos sesgado de todos los niveles en los árboles del bosque aleatorio.

4.5 Regla de división

La regla de división predeterminada durante la construcción de árboles de bosques aleatorios consiste en seleccionar, de todas las divisiones de las variables candidatas seleccionadas aleatoriamente, la división que minimiza la impureza de Gini (en el caso de la clasificación) y el SSE (en caso de regresión). Árboles de inferencia condicional implementan un mecanismo de división alternativo que ayude a reducir este sesgo de selección de variables.

Para aumentar la eficiencia computacional, las reglas de división se pueden aleatorizar donde solo se considera un subconjunto aleatorio de posibles valores de división para una variable. Si solo se selecciona al azar un único valor de división aleatorio, entonces llamamos a este procedimiento árboles extremadamente aleatorios. Debido a la aleatoriedad adicional de los puntos de división, este método tiende a no tener ninguna mejora, o a menudo tiene un impacto negativo, en la precisión predictiva.

Con respecto al tiempo de ejecución, los árboles extremadamente aleatorios son los más rápidos ya que los puntos de corte se dibujan de forma completamente aleatoria, seguidos del bosque aleatorio clásico, mientras que para los bosques de inferencia condicionales el tiempo de ejecución es el más grande.

5 Estrategias de ajuste

Con una mayor cantidad de hiperparámetros, se debe llevar mayor estrategia con el ajuste. Se considera cómo avanzar en una búsqueda en la cuadrícula. Las búsquedas de cuadrículas cartesianas completas evalúan cada combinación de hiperparámetros de interés. El siguiente bloque de código busca en \(120\) combinaciones de configuraciones de hiperparámetros:

# cuadrícula de hiperparámetros
hyper_grid <- expand.grid(
  mtry = floor(n_features * c(.05, .15, .25, .333, .4)),
  min.node.size = c(1, 3, 5, 10),
  replace = c(TRUE, FALSE),                               
  sample.fraction = c(.5, .63, .8),                       
  rmse = NA                                               
)

# búsqueda cartesiana de cuadrícula
for(i in seq_len(nrow(hyper_grid))) {
  # fit model for ith hyperparameter combination
  fit <- ranger(
    formula         = Cell.size ~ ., 
    data            = BCrec_train, 
    num.trees       = n_features * 10,
    mtry            = hyper_grid$mtry[i],
    min.node.size   = hyper_grid$min.node.size[i],
    replace         = hyper_grid$replace[i],
    sample.fraction = hyper_grid$sample.fraction[i],
    verbose         = FALSE,
    seed            = 123,
    respect.unordered.factors = 'order',
  )
  
  # exportar error OOB  
  hyper_grid$rmse[i] <- sqrt(fit$prediction.error)
}

# modelos top
hyper_grid %>%
  arrange(rmse) %>%
  mutate(perc_gain = (default_rmse - rmse) / default_rmse * 100) %>%
  head(10)
##    mtry min.node.size replace sample.fraction     rmse   perc_gain
## 1     0             5    TRUE            0.63 1.240676 -0.04078014
## 2     3             5    TRUE            0.63 1.240676 -0.04078014
## 3     0            10    TRUE            0.63 1.243332 -0.25491073
## 4     3            10    TRUE            0.63 1.243332 -0.25491073
## 5     0            10   FALSE            0.80 1.245002 -0.38956505
## 6     3            10   FALSE            0.80 1.245002 -0.38956505
## 7     0             3    TRUE            0.50 1.247997 -0.63104506
## 8     3             3    TRUE            0.50 1.247997 -0.63104506
## 9     0            10   FALSE            0.50 1.249266 -0.73337182
## 10    3            10   FALSE            0.50 1.249266 -0.73337182

Al mirar los resultados, los \(10\) mejores modelos están todos cerca o por debajo de un rmse de \(1.25\). En estos resultados, el valor predeterminado de mtry es \([\frac{No. features}{3}⌋=3\) y los tamaños de nodo más pequeños (árboles más profundos) funcionan mejor. El muestreo de menos del \(100%\) agrega aleatoriedad adicional en el procedimiento, lo que ayuda a descorrelacionar aún más los árboles.

Sin embargo, a más hiperparámetros, los conjuntos de datos se hacen más grandes, y una búsqueda cartesiana completa puede volverse exhaustiva y computacionalmente costosa. Además de la búsqueda cartesiana completa, el paquete h2o proporciona una búsqueda de cuadrícula aleatoria que permite saltar de una combinación aleatoria a otra y también proporciona reglas de detención temprana que permiten detener la búsqueda de cuadrícula una vez que se cumple una determinada condición. Aunque es probable que el uso de una ruta de búsqueda discreta aleatoria no encuentre el modelo óptimo, por lo general hace un buen trabajo al encontrar un modelo muy bueno.

Para ajustar un modelo de bosque aleatorio con h2o , primero se inicia sesión de h2o.

h2o.no_progress()
h2o.init(max_mem_size = "5g")
##  Connection successful!
## 
## R is connected to the H2O cluster: 
##     H2O cluster uptime:         17 minutes 51 seconds 
##     H2O cluster timezone:       America/Mexico_City 
##     H2O data parsing timezone:  UTC 
##     H2O cluster version:        3.32.1.3 
##     H2O cluster version age:    2 months and 10 days  
##     H2O cluster name:           H2O_started_from_R_juan_acuna_dmg384 
##     H2O cluster total nodes:    1 
##     H2O cluster total memory:   4.94 GB 
##     H2O cluster total cores:    4 
##     H2O cluster allowed cores:  4 
##     H2O cluster healthy:        TRUE 
##     H2O Connection ip:          localhost 
##     H2O Connection port:        54321 
##     H2O Connection proxy:       NA 
##     H2O Internal Security:      FALSE 
##     H2O API Extensions:         Amazon S3, XGBoost, Algos, AutoML, Core V3, TargetEncoder, Core V4 
##     R Version:                  R version 4.1.0 (2021-05-18)

6 Importancia de las características

El cálculo de la importancia de las características y los efectos de las características para bosques aleatorios, además de la medida de la importancia de la característica basada en impurezas, también se incluye una función de permutación medida de importancia. En el enfoque basado en permutación, para cada árbol, la muestra OOB se transmite por el árbol y se registra la precisión de la predicción. Luego, los valores de cada variable (uno a la vez) se permutan aleatoriamente y se vuelve a calcular la precisión. La disminución en la precisión como resultado de esta mezcla aleatoria de valores de características se promedia sobre todos los árboles para cada predictor. Las variables con la mayor disminución promedio en la precisión se consideran las más importantes.

Por ejemplo, podemos calcular ambas medidas de importancia de características con ranger estableciendo el argumento importance.

# vip basada en impurezas
rf_impurity <- ranger(
  formula = Cell.size ~ ., 
  data = BCrec_train, 
  num.trees = 2000,
  mtry = 3,
  min.node.size = 1,
  sample.fraction = .80,
  replace = FALSE,
  importance = "impurity",
  respect.unordered.factors = "order",
  verbose = FALSE,
  seed  = 123
)

# vip basada en permutaciones
rf_permutation <- ranger(
  formula = Cell.size ~ ., 
  data = BCrec_train, 
  num.trees = 2000,
  mtry = 3,
  min.node.size = 1,
  sample.fraction = .80,
  replace = FALSE,
  importance = "permutation",
  respect.unordered.factors = "order",
  verbose = FALSE,
  seed  = 123
)

Los VIP resultantes se muestran en la Figura . Normalmente, no se verá el mismo orden de importancia en variables entre las dos opciones; sin embargo, a menudo se verán variables similares en la parte superior de los gráficos (y también en la parte inferior). En consecuencia, parece haber suficiente evidencia para sugerir que tres variables se destacan como las más influyentes:

  • Cell.shape
  • Class
  • Epith.c.size
p1 <- vip::vip(rf_impurity, num_features = 25, bar = FALSE)
p2 <- vip::vip(rf_permutation, num_features = 25, bar = FALSE)

gridExtra::grid.arrange(p1, p2, nrow = 1)

7 Conclusión

Los bosques aleatorios proporcionan un algoritmo que a menudo tiene una gran precisión predictiva. Vienen con todos los beneficios de los árboles de decisión (con la excepción de las divisiones sustitutas) y el bagging, pero reducen en gran medida la inestabilidad y la correlación entre árboles. Y debido al atributo de selección de variable de división agregado, los bosques aleatorios también son más rápidos que el bagging, ya que tienen un espacio de búsqueda de características más pequeño en cada división de árbol. Sin embargo, los bosques aleatorios todavía sufren de una velocidad computacional lenta a medida que las bases de datos se hacen grandes, pero, al igual que el bagging, el algoritmo se basa en pasos independientes y la mayoría de las implementaciones modernas (ranger, h2o) permiten la paralelización para mejorar el tiempo de entrenamiento.

Referencias

Boehmke, Bradley, and Brandon Greenwell. 2020. Hands-on Machine Learning with r. CRC-Press. https://bradleyboehmke.github.io/HOML/.
Breiman, Leo. 2001. “Random Forests.” Machine Learning 45 (1): 5–32.
LeDell, Erin, Navdeep Gill, Spencer Aiello, Anqi Fu, Arno Candel, Cliff Click, Tom Kraljevic, et al. 2021. H2o: R Interface for the ’H2o’ Scalable Machine Learning Platform. https://CRAN.R-project.org/package=h2o.
Wright, Marvin N., and Andreas Ziegler. 2017. ranger: A Fast Implementation of Random Forests for High Dimensional Data in C++ and R.” Journal of Statistical Software 77 (1): 1–17. https://doi.org/10.18637/jss.v077.i01.