#1.- ABSTRACT
Los bosques aleatorios son una modificación de los árboles de decisión en bagging que crean una gran colección de árboles descorrelacionados para mejorar aún más el rendimiento predictivo. Se han convertido en un algoritmo de aprendizaje listo para usar muy popular que disfruta de un buen rendimiento predictivo con relativamente poco ajuste de hiperparámetros. Existen muchas implementaciones modernas de bosques aleatorios; sin embargo, se menciona el algoritmo de Leo Breiman (Breiman 2001).
#2.- 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).
#3.- BIBLIOTECAS
library(mlbench)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.3 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ ggplot2 3.4.4 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.0
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidymodels)
## ── Attaching packages ────────────────────────────────────── tidymodels 1.1.1 ──
## ✔ broom 1.0.5 ✔ rsample 1.2.0
## ✔ dials 1.2.0 ✔ tune 1.1.2
## ✔ infer 1.0.5 ✔ workflows 1.1.3
## ✔ modeldata 1.3.0 ✔ workflowsets 1.0.1
## ✔ parsnip 1.1.1 ✔ yardstick 1.3.0
## ✔ recipes 1.0.8
## ── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
## ✖ scales::discard() masks purrr::discard()
## ✖ dplyr::filter() masks stats::filter()
## ✖ recipes::fixed() masks stringr::fixed()
## ✖ dplyr::lag() masks stats::lag()
## ✖ yardstick::spec() masks readr::spec()
## ✖ recipes::step() masks stats::step()
## • Search for functions across packages at https://www.tidymodels.org/find/
library(rpart.plot)
## Loading required package: rpart
##
## Attaching package: 'rpart'
##
## The following object is masked from 'package:dials':
##
## prune
library(vip)
##
## Attaching package: 'vip'
##
## The following object is masked from 'package:utils':
##
## vi
# bibliotecas auxiliares
library(dplyr) # manejo de datos
library(ggplot2) # mejores gráficas
# bibliotecas de modelación
library(ranger) # implementación en c++ de random forest
library(h2o) # implementación en jave-based de random forest
##
## ----------------------------------------------------------------------
##
## Your next step is to start H2O:
## > h2o.init()
##
## For H2O package documentation, ask for help:
## > ??h2o
##
## After starting H2O, you can use the Web UI at http://localhost:54321
## For more information visit https://docs.h2o.ai
##
## ----------------------------------------------------------------------
##
##
## Attaching package: 'h2o'
##
## The following objects are masked from 'package:lubridate':
##
## day, hour, month, week, year
##
## The following objects are masked from 'package:stats':
##
## cor, sd, var
##
## The following objects are masked from 'package:base':
##
## %*%, %in%, &&, ||, apply, as.factor, as.numeric, colnames,
## colnames<-, ifelse, is.character, is.factor, is.numeric, log,
## log10, log1p, log2, round, signif, trunc
~4.- MUESTRA (SAMPLE)
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)
BCrec4
## # A tibble: 683 × 10
## Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size Bare.nuclei
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5 1 1 1 2 1
## 2 5 4 4 5 7 10
## 3 3 1 1 1 2 2
## 4 6 8 8 1 3 4
## 5 4 1 1 3 2 1
## 6 8 10 10 8 7 10
## 7 1 1 1 1 2 10
## 8 2 1 2 1 2 1
## 9 2 1 1 1 2 1
## 10 4 2 1 1 2 1
## # ℹ 673 more rows
## # ℹ 4 more variables: Bl.cromatin <dbl>, Normal.nucleoli <dbl>, Mitoses <dbl>,
## # Class <fct>
BCrec_train
## # A tibble: 512 × 10
## Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size Bare.nuclei
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 10 4 4 6 2 10
## 2 4 1 2 1 2 1
## 3 4 1 1 1 2 1
## 4 1 1 1 1 2 1
## 5 8 5 6 2 3 10
## 6 7 5 6 10 5 10
## 7 10 10 10 10 10 1
## 8 3 1 1 1 2 1
## 9 8 5 5 5 2 10
## 10 2 2 2 1 1 1
## # ℹ 502 more rows
## # ℹ 4 more variables: Bl.cromatin <dbl>, Normal.nucleoli <dbl>, Mitoses <dbl>,
## # Class <fct>
BCrec_test
## # A tibble: 171 × 10
## Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size Bare.nuclei
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 3 1 1 1 2 2
## 2 4 1 1 3 2 1
## 3 8 10 10 8 7 10
## 4 2 1 2 1 2 1
## 5 2 1 1 1 2 1
## 6 7 4 6 4 6 1
## 7 4 1 1 1 2 1
## 8 7 3 2 10 5 10
## 9 1 1 1 1 2 1
## 10 3 2 1 1 1 1
## # ℹ 161 more rows
## # ℹ 4 more variables: Bl.cromatin <dbl>, Normal.nucleoli <dbl>, Mitoses <dbl>,
## # Class <fct>
summary(BCrec4)
## Cl.thickness Cell.size Cell.shape Marg.adhesion
## Min. : 1.000 Min. : 1.000 Min. : 1.000 Min. : 1.00
## 1st Qu.: 2.000 1st Qu.: 1.000 1st Qu.: 1.000 1st Qu.: 1.00
## Median : 4.000 Median : 1.000 Median : 1.000 Median : 1.00
## Mean : 4.442 Mean : 3.151 Mean : 3.215 Mean : 2.83
## 3rd Qu.: 6.000 3rd Qu.: 5.000 3rd Qu.: 5.000 3rd Qu.: 4.00
## Max. :10.000 Max. :10.000 Max. :10.000 Max. :10.00
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli
## Min. : 1.000 Min. : 1.000 Min. : 1.000 Min. : 1.00
## 1st Qu.: 2.000 1st Qu.: 1.000 1st Qu.: 2.000 1st Qu.: 1.00
## Median : 2.000 Median : 1.000 Median : 3.000 Median : 1.00
## Mean : 3.234 Mean : 3.545 Mean : 3.445 Mean : 2.87
## 3rd Qu.: 4.000 3rd Qu.: 6.000 3rd Qu.: 5.000 3rd Qu.: 4.00
## Max. :10.000 Max. :10.000 Max. :10.000 Max. :10.00
## Mitoses Class
## Min. :1.000 benign :444
## 1st Qu.:1.000 malignant:239
## Median :1.000
## Mean :1.583
## 3rd Qu.:1.000
## Max. :9.000
summary(BCrec_train)
## Cl.thickness Cell.size Cell.shape Marg.adhesion
## Min. : 1.000 Min. : 1.000 Min. : 1.000 Min. : 1.000
## 1st Qu.: 2.000 1st Qu.: 1.000 1st Qu.: 1.000 1st Qu.: 1.000
## Median : 4.000 Median : 1.000 Median : 2.000 Median : 1.000
## Mean : 4.484 Mean : 3.211 Mean : 3.281 Mean : 2.906
## 3rd Qu.: 6.000 3rd Qu.: 5.000 3rd Qu.: 5.000 3rd Qu.: 4.000
## Max. :10.000 Max. :10.000 Max. :10.000 Max. :10.000
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli
## Min. : 1.000 Min. : 1.00 Min. : 1.000 Min. : 1.000
## 1st Qu.: 2.000 1st Qu.: 1.00 1st Qu.: 2.000 1st Qu.: 1.000
## Median : 2.000 Median : 1.00 Median : 3.000 Median : 1.000
## Mean : 3.279 Mean : 3.68 Mean : 3.508 Mean : 2.979
## 3rd Qu.: 4.000 3rd Qu.: 7.00 3rd Qu.: 5.000 3rd Qu.: 4.000
## Max. :10.000 Max. :10.00 Max. :10.000 Max. :10.000
## Mitoses Class
## Min. :1.000 benign :322
## 1st Qu.:1.000 malignant:190
## Median :1.000
## Mean :1.641
## 3rd Qu.:1.000
## Max. :9.000
summary(BCrec_test)
## Cl.thickness Cell.size Cell.shape Marg.adhesion
## Min. : 1.000 Min. : 1.000 Min. : 1.000 Min. : 1.000
## 1st Qu.: 2.000 1st Qu.: 1.000 1st Qu.: 1.000 1st Qu.: 1.000
## Median : 4.000 Median : 1.000 Median : 1.000 Median : 1.000
## Mean : 4.316 Mean : 2.971 Mean : 3.018 Mean : 2.602
## 3rd Qu.: 5.000 3rd Qu.: 4.000 3rd Qu.: 4.000 3rd Qu.: 3.000
## Max. :10.000 Max. :10.000 Max. :10.000 Max. :10.000
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli
## Min. : 1.000 Min. : 1.00 Min. : 1.000 Min. : 1.000
## 1st Qu.: 2.000 1st Qu.: 1.00 1st Qu.: 1.000 1st Qu.: 1.000
## Median : 2.000 Median : 1.00 Median : 2.000 Median : 1.000
## Mean : 3.099 Mean : 3.14 Mean : 3.257 Mean : 2.544
## 3rd Qu.: 4.000 3rd Qu.: 4.00 3rd Qu.: 4.000 3rd Qu.: 3.000
## Max. :10.000 Max. :10.00 Max. :10.000 Max. :10.000
## Mitoses Class
## Min. :1.000 benign :122
## 1st Qu.:1.000 malignant: 49
## Median :1.000
## Mean :1.409
## 3rd Qu.:1.000
## Max. :9.000
#5.- EXTENSIÓN 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 mtry=p3 (regresión) y mtry=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:
Given a training data set Select number of trees to build (n_trees) for i = 1 to n_trees do Generate a bootstrap sample of the original data Grow a regression/classification tree to the bootstrapped data for each split do | Select m_try variables at random from all p variables | Pick the best variable/split-point among the m_try | Split the node into two child nodes end Use typical tree model stopping criteria to determine when a tree is complete (but do not prune) end 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.
#6.- 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.
#7.- MODELO BÁSICO
# 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
#8.- HIPER PARAMETROS
Existen varios hiperparámetros ajustables a considerar al entrenar un modelo. Los principales incluyen:
La cantidad de árboles en el bosque El número de funciones a considerar en cualquier división determinada: mtry La complejidad de cada árbol El esquema de muestreo La regla de división que se debe usar durante la construcción del árbol.
#9.- AJUSTE ÓPTIMO
# 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
h2o.no_progress()
h2o.init(max_mem_size = "5g")
## Connection successful!
##
## R is connected to the H2O cluster:
## H2O cluster uptime: 5 minutes 16 seconds
## H2O cluster timezone: Europe/Paris
## H2O data parsing timezone: UTC
## H2O cluster version: 3.42.0.2
## H2O cluster version age: 6 months and 3 days
## H2O cluster name: H2O_started_from_R_Usuario_dmg384
## H2O cluster total nodes: 1
## H2O cluster total memory: 4.09 GB
## H2O cluster total cores: 20
## H2O cluster allowed cores: 20
## H2O cluster healthy: TRUE
## H2O Connection ip: localhost
## H2O Connection port: 54321
## H2O Connection proxy: NA
## H2O Internal Security: FALSE
## R Version: R version 4.3.2 (2023-10-31 ucrt)
## Warning in h2o.clusterInfo():
## Your H2O cluster version is (6 months and 3 days) old. There may be a newer version available.
## Please download and install the latest version from: https://h2o-release.s3.amazonaws.com/h2o/latest_stable.html
# 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
)
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)
#10.- 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.