Está en la página 1de 39

TEMA 4.

BOOSTING
Máxima Formación

Contents
1 Introducción 2
1.1. Algoritmos de Boosting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2. Diferencias entre el Bagging y el Boosting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Ventajas y Desventajas: Modelo Boosting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2. GBM con la Librería dismo 3


2.1. Parámetros función: gbm.step() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2. Caso práctico: Regresión. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

3. GBM con la Librería gbm 16


3.1. Parámetros función: gbm() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2. Caso práctico: Clasificación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3. Grid Search con Caret . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4. XGBoost 28
4.1. Parámetros función: XGBoost. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.2. Caso práctico: Regresión. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.3. Grid Search con Caret . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

1
1 Introducción
Otro de los métodos para reducir la variabilidad de los árboles individuales y aumentar el poder predictivo
son los métodos Boosting. Si en el caso de los métodos de Bagging buscábamos hacer un árbol individual
en paralelo dividiendo el conjunto de entrenamiento en varios subconjuntos, en los métodos de Boosting se
crean los árboles de forma secuencial.
El método fue propuesto por Freund y Schapire en 1996, y consiste en ir ajustando modelos de árboles de
decisión, de modo que cada árbol aprende del error del modelo anterior. En el Boosting se van creando
árboles de forma secuencial y no paralela, donde cada árbol que se crea tiene como objetivo reducir los
errores del árbol anterior. Cada árbol que se crea en la continuación de la secuencia aprenderá de una
versión actualizada de los residuos.
Los árboles iniciales se denominan aprendices débiles (weak learners) cuyo sesgo es grande (no ajusta bien a
los datos) y su poder predictivo es bajo, pero la unión secuencial de estos aprendices débiles permite crear
un modelo final donde se reduce tanto el sesgo como la varianza.
El método de Boosting se desarrolla en 3 pasos:

1. Se define un modelo inicial F0 para predecir la variable Y , que se asociará con un residuo (Y − F0 ).
2. Se define un nuevo modelo h1 que se ajusta a los residuos del paso anterior.
3. Se combinan F0 con h1 para crear un modelo F1 el cual su error cuadrático medio será menor que el
de F0 .
F1 = F0 (x) + h1 (x)
Este proceso se puede realizar n veces hasta que los residuos se minimicen lo máximo posible.

F n(x) = Fn − 1(x) + hn (x)

1.1. Algoritmos de Boosting.

Dentro del método de Boosting podemos encontrar tres tipos de algoritmos: Gradient Boosting, Stochastic
Gradient Boosting y AdaBoost.
El algoritmo de AdaBoost es uno de los más utilizados para problemas de clasificación binaria. Este algoritmo
utiliza los siguientes pasos:

1. A todas las observaciones del conjunto de datos se les asigna el mismo peso: wi = N1 donde N es el
número de observaciones.
2. Se entrena el modelo (weak learner), se predice con él y se identifican los casos erróneos.
3. Se incrementan los pesos en aquellos casos que se identifican erróneos. Se asigna un peso total al
weak learner según los aciertos obtenidos (cuantos más aciertos, más peso en el modelo conjunto) y se
entrena un nuevo modelo con los nuevos pesos.

Este proceso se repite M veces, es decir el número de interacciones indicado. Por último, la predicción final
se realiza por la mayor frecuencia.
El Gradient Boosting es una modificación del algoritmo de AdaBoost que nos permite utilizarlo en numerosos
problemas. Mientras que el algoritmo de AdaBoost solo se puede utilizar para problemas de clasificación
binaria, el Gradient Boosting nos permite utilizarlo en problemas de regresión, multiclasificación y binarios.
Por último, Stochastic Gradient Boosting se basa en los dos anteriores, pero además se divide el conjunto de
entrenamiento en subconjuntos y cada árbol (weak learner) se ajusta al subconjunto de datos, estimando un
error (out of bag).

2
1.2. Diferencias entre el Bagging y el Boosting.

Aunque el Bagging y el Boosting son métodos que se basan en combinar modelos individuales, existen
diferencias entre ambos métodos. En el Bagging se realizan modelos individuales en paralelo, es decir, son
independientes entre ellos. Además, los árboles se dejan crecer sin ninguna limitación, creando árboles muy
profundos.
En el Boosting se van creando modelos a partir del anterior de forma secuencial. El nuevo árbol aprende de
los errores de los anteriores. Los árboles son menos profundos que en el Bagging pero unidos suelen aumentar
su poder predictivo.
Uno de los problemas del Boosting es el overfiting a los datos de entrenamiento mientras que en el método
de Bagging este problema no se produce.
En R podemos encontrar varios paquetes con los que se puede realizar Boosting. Empezaremos con un
problema de regresión con el paquete dismo y haremos un problema de clasificación con el paquete gbm.

1.3. Ventajas y Desventajas: Modelo Boosting.

Las ventajas que tienen los modelos de Boosting son:

• Alto poder predictivo.


• No necesita un preprocesamiento de los datos, aunque este puede mejorar su poder predictivo.
• No necesita imputación de datos faltantes.
• Se puede optimizar con diferentes funciones de pérdida y parámetros.
• Nos facilitan las variables más importantes para la construcción del modelo.

Las desventajas de estos modelos son:

• Pueden mejorar hasta crear overfitting.


• Difíciles de interpretar.
• Al tener tantos parámetros que se pueden utilizar en su afinamiento, hace que sean algoritmos com-
plejos.
• Alto costes computacionales.

2. GBM con la Librería dismo


El paquete dismo nos permite realizar Stochastic Gradient Boosting. Para poder utilizarlo tenemos que
instalar tanto el paquete dismo como gbm y cargar las librerías.

#install.packages("dismo")
#install.packages(gbm)
library(dismo)
library(gbm)

Utilizaremos la función gbm.step(), que busca el número óptimo de árboles por validación cruzada que hacen
que se reduzca la desvianza residual.

3
2.1. Parámetros función: gbm.step()

Antes de empezar con el ejemplo práctico vamos a definir los principales parámetros que se utiliza con la
función gbm.step() del paquete dismo que vamos a utilizar.

• data: Conjunto de datos.

• gbm.x: Variables independientes o predictoras.

• gbm.y: Variable dependiente o a predecir.


• learning.rate: Peso de los árboles individuales sobre el modelo final. Se recomienda utilizar el valor
más pequeño posible.
• bag.fraction: Porcentaje de observaciones para entrenar los árboles individuales. Por defecto es 0.5. 1
• family: Puede ser bernoulli para problemas binomiales, poisson para problemas de conteo, gaussian
para problemas de regresión.
• cv.folds: Número de validaciones cruzadas que realizará.
• n.trees: Número inicial de árboles del modelo.
• max.trees: Máximo número de árboles antes de parar.
• tree.complexity: profundidad de los árboles.

2.2. Caso práctico: Regresión.

Utilizamos nuestro conjunto de datos sobre casas de Boston y con la que hemos ido realizando diferentes
algoritmos a lo largo del curso, que contiene información de la vivienda en los suburbios de Boston. Nuestro
objetivo es predecir la variable medv que es el valor medio de las casas ocupadas por el dueño en $1000s.
Realizamos un modelo de Boosting que utiliza el algoritmo de Stochastic Gradient Boosting, que como ya
hemos comentado. No utiliza todo el conjunto de datos, sino que crea subconjunto con los que se van
entrenando los weak learner. Utilizamos el paquete dismo que nos permite realizar y ajustar el Boosted
Regresion Trees.
A continuación, cargamos los datos. Es un dataset pequeño donde tenemos 506 observaciones y 6 variables
independientes.

#Cargamos los datos.


library(MASS)
data(Boston)
#Estadisticos basicos
summary(Boston)

## crim zn indus chas


## Min. : 0.00632 Min. : 0.00 Min. : 0.46 Min. :0.00000
## 1st Qu.: 0.08204 1st Qu.: 0.00 1st Qu.: 5.19 1st Qu.:0.00000
## Median : 0.25651 Median : 0.00 Median : 9.69 Median :0.00000
## Mean : 3.61352 Mean : 11.36 Mean :11.14 Mean :0.06917
## 3rd Qu.: 3.67708 3rd Qu.: 12.50 3rd Qu.:18.10 3rd Qu.:0.00000
## Max. :88.97620 Max. :100.00 Max. :27.74 Max. :1.00000
## nox rm age dis
## Min. :0.3850 Min. :3.561 Min. : 2.90 Min. : 1.130
## 1st Qu.:0.4490 1st Qu.:5.886 1st Qu.: 45.02 1st Qu.: 2.100
## Median :0.5380 Median :6.208 Median : 77.50 Median : 3.207
## Mean :0.5547 Mean :6.285 Mean : 68.57 Mean : 3.795
1 Página web de Kaggle

4
## 3rd Qu.:0.6240 3rd Qu.:6.623 3rd Qu.: 94.08 3rd Qu.: 5.188
## Max. :0.8710 Max. :8.780 Max. :100.00 Max. :12.127
## rad tax ptratio black
## Min. : 1.000 Min. :187.0 Min. :12.60 Min. : 0.32
## 1st Qu.: 4.000 1st Qu.:279.0 1st Qu.:17.40 1st Qu.:375.38
## Median : 5.000 Median :330.0 Median :19.05 Median :391.44
## Mean : 9.549 Mean :408.2 Mean :18.46 Mean :356.67
## 3rd Qu.:24.000 3rd Qu.:666.0 3rd Qu.:20.20 3rd Qu.:396.23
## Max. :24.000 Max. :711.0 Max. :22.00 Max. :396.90
## lstat medv
## Min. : 1.73 Min. : 5.00
## 1st Qu.: 6.95 1st Qu.:17.02
## Median :11.36 Median :21.20
## Mean :12.65 Mean :22.53
## 3rd Qu.:16.95 3rd Qu.:25.00
## Max. :37.97 Max. :50.00

#Cinco primeros datos.


head(Boston,5)

## crim zn indus chas nox rm age dis rad tax ptratio black
## 1 0.00632 18 2.31 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90
## 2 0.02731 0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90
## 3 0.02729 0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83
## 4 0.03237 0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63
## 5 0.06905 0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90
## lstat medv
## 1 4.98 24.0
## 2 9.14 21.6
## 3 4.03 34.7
## 4 2.94 33.4
## 5 5.33 36.2

#Dimensiones del conjunto de datos.


dim(Boston)

## [1] 506 14

Nuestra variable dependiente es medv que es de tipo continua, por lo tanto, nuestro problema es de regresión,
donde tenemos que predecir los precios de las viviendas.
Dividimos nuestros datos en el conjunto de entrenamiento y de validación en 80%-20%.

index <- sample(1:nrow(Boston), size = 0.8 * nrow(Boston))


train_boston <- Boston[index,]
test_boston <- Boston[-index,]

Ahora nuestro conjunto de entrenamiento tiene 404 y nuestro conjunto de validación es de 102.
Creamos nuestro primer modelo GBM. Como estamos ante un problema de regresión utilizamos el parámetro
family = “gaussian”.

5
set.seed(1979)
gbm_lr001 <- gbm.step(data = train_boston,
gbm.x = 1:13, #Columna de predictores.
gbm.y = "medv", #Columna a predecir.
family = "gaussian", #Regresión.
tree.complexity = 5, #Profundidad de los árboles
learning.rate = 0.01, #Complejidad
bag.fraction = 0.7) #Datos para construir los árboles.

##
##
## GBM STEP - version 2.9
##
## Performing cross-validation optimisation of a boosted regression tree model
## for NA and using a family of gaussian
## Using 404 observations and 13 predictors
## creating 10 initial models of 50 trees
##
## folds are unstratified
## total mean deviance = 85.058
## tolerance is fixed at 0.0851
## ntrees resid. dev.
## 50 46.2057
## now adding trees...
## 100 29.7607
## 150 22.4395
## 200 18.7142
## 250 16.531
## 300 15.2537
## 350 14.3935
## 400 13.7416
## 450 13.2554
## 500 12.8687
## 550 12.5592
## 600 12.2986
## 650 12.1037
## 700 11.9065
## 750 11.763
## 800 11.6536
## 850 11.537
## 900 11.4458
## 950 11.333
## 1000 11.2587
## 1050 11.1801
## 1100 11.0963
## 1150 11.0248
## 1200 10.985
## 1250 10.9111
## 1300 10.8701
## 1350 10.8275
## 1400 10.7867
## 1450 10.7544
## 1500 10.7148

6
## 1550 10.6938
## 1600 10.6645
## 1650 10.6404
## 1700 10.6079
## 1750 10.5969
## 1800 10.5931
## 1850 10.584
## 1900 10.5615
## 1950 10.5236
## 2000 10.5286
## 2050 10.5273
## 2100 10.5149
## 2150 10.4974
## 2200 10.4852
## 2250 10.4753
## 2300 10.4665
## 2350 10.4594
## 2400 10.4564
## 2450 10.4509
## 2500 10.4345
## 2550 10.4291
## 2600 10.4179
## 2650 10.4253
## 2700 10.4089
## 2750 10.3978
## 2800 10.3888

## fitting final gbm model with a fixed number of 2800 trees for NA

7
NA, d − 5, lr − 0.01
50
40
holdout deviance

30
20
10

0 500 1000 1500 2000 2500

no. of trees

##
## mean total deviance = 85.058
## mean residual deviance = 0.909
##
## estimated cv deviance = 10.389 ; se = 1.734
##
## training data correlation = 0.995
## cv correlation = 0.935 ; se = 0.013
##
## elapsed time - 0.69 minutes

El algoritmo va añadiendo árboles indicando como se reduce la desvianza residual, calculada por medio
de validación cruzada. La desvianza es una medida de la variación residual de un modelo. Mide cuánto
se dispersa las predicciones del modelo respecto a la variable respuesta (medv), por lo tanto, buscamos
minimizarla.
La desvianza se reduce hasta un punto en el que empieza a aumentar (no puede reducir más el error). Esto
sucede con 2800 árboles (línea vertical verde del gráfico), que es donde se obtiene el óptimo de los árboles.
Podemos solicitar el número óptimo de árboles que coincide con la línea verde del gráfico.

gbm_lr001$n.trees

## [1] 2800

La función summary() nos indica las variables más importantes para la creación del modelo.

8
summary(gbm_lr001, las = 2)

lstat
rm
dis
nox
crim
ptratio
age
black
tax
indus
chas
rad
zn
0

10

20

30

40
Relative influence

## var rel.inf
## lstat lstat 43.36023063
## rm rm 30.77350912
## dis dis 7.03825538
## nox nox 4.48715277
## crim crim 4.00666983
## ptratio ptratio 3.18812405
## age age 3.02473499
## black black 1.55171808
## tax tax 1.33177754
## indus indus 0.58292232
## chas chas 0.30322952
## rad rad 0.29672210
## zn zn 0.05495369

Las variables más importantes son lstat y rm con influencia relativa del 43% y 30% respectivamente. Para
calcular la influencia relativa, el modelo en cada división de cada árbol, calcula la mejora en el criterio de
división (MSE para regresión). Luego promedia la mejora realizada por cada variable en todos los árboles que
utiliza la variable. Las variables con la mayor disminución promedio en MSE se consideran más importantes.
Nuestro modelo ha aprendido muy lento debido a que se recomienda que se alcance la mínima desvianza resid-
ual con cerca de 1000 árboles y en nuestro primer modelo no alcanza el mínimo. Ajustamos el learning.rate
para que aprenda más rápido aumentando su valor.

9
set.seed(1979)
gbm_lr00195 <- gbm.step(data = train_boston,
gbm.x = 1:13, #Columna de predictores.
gbm.y = 14, #Columna a predecir.
family = "gaussian", #Regresión.
tree.complexity = 5, #Profundidad de los árboles
learning.rate = 0.03, #Complejidad
bag.fraction = 0.7) #Datos para construir los árboles.

##
##
## GBM STEP - version 2.9
##
## Performing cross-validation optimisation of a boosted regression tree model
## for medv and using a family of gaussian
## Using 404 observations and 13 predictors
## creating 10 initial models of 50 trees
##
## folds are unstratified
## total mean deviance = 85.058
## tolerance is fixed at 0.0851
## ntrees resid. dev.
## 50 22.4324
## now adding trees...
## 100 15.2638
## 150 13.1899
## 200 12.2677
## 250 11.6119
## 300 11.2928
## 350 11.0123
## 400 10.8233
## 450 10.6676
## 500 10.5774
## 550 10.515
## 600 10.4227
## 650 10.3408
## 700 10.3137
## 750 10.2811
## 800 10.2512
## 850 10.2705
## 900 10.2633
## 950 10.2344
## 1000 10.2347
## 1050 10.2299
## 1100 10.2332
## 1150 10.2189
## 1200 10.1995
## 1250 10.2165
## 1300 10.2445
## 1350 10.237
## 1400 10.2114
## 1450 10.2276
## 1500 10.2087

10
## fitting final gbm model with a fixed number of 1200 trees for medv

medv, d − 5, lr − 0.03
25
holdout deviance

20
15
10

0 500 1000 1500

no. of trees

##
## mean total deviance = 85.058
## mean residual deviance = 0.616
##
## estimated cv deviance = 10.2 ; se = 1.71
##
## training data correlation = 0.996
## cv correlation = 0.937 ; se = 0.013
##
## elapsed time - 0.37 minutes

gbm_lr00195$n.trees

## [1] 1200

Nuestro modelo obtiene el óptimo con aproximadamente 1200 árboles. Obtenemos las variables más impor-
tantes del nuevo modelo.

summary(gbm_lr00195, las = 2)

11
lstat
rm
dis
nox
crim
ptratio
age
black
tax
indus
rad
chas
zn
0

10

20

30

40
Relative influence

## var rel.inf
## lstat lstat 44.83861455
## rm rm 29.20565842
## dis dis 7.73050444
## nox nox 4.43325426
## crim crim 3.75504242
## ptratio ptratio 3.10389424
## age age 2.53372598
## black black 1.82668591
## tax tax 1.33350670
## indus indus 0.47043429
## rad rad 0.36387169
## chas chas 0.32987997
## zn zn 0.07492712

Ahora vamos a simplificar nuestro modelo eliminando variables que no aportan información al mismo. Para
ello utilizamos la función gbm.simplify() y le indicamos que elimine hasta 6 variables. Podríamos indicarle
que elimine más variables, pero cuanto más variables le indiquemos más tiempo tardará en su computo.

gbm_lr00195_simpli <- gbm.simplify(gbm_lr00195,


n.drops = 6)

## gbm.simplify - version 2.9


## simplifying gbm.step model for medv with 13 predictors and 404 observations
## original deviance = 10.1995(1.7097)

12
## a fixed number of 6 drops will be tested

## creating initial models...

## dropping predictor: 1 2 3 4 5 6
## processing final dropping of variables with full data
## 1-zn
## 2-rad
## 3-chas
## 4-indus
## 5-tax
## 6-black

RFE deviance − medv − folds = 10


change in predictive deviance

1.5
1.0
0.5
0.0
−0.5

0 1 2 3 4 5 6

variables removed

La función nos indica que el óptimo es eliminar 3 variables por lo que procedemos a quedarnos con todas
menos zn y rad, que son las dos primeras que nos indica el resultado.

gbm_lr00195_simpli$pred.list[[3]]

## [1] 1 3 5 6 7 8 10 11 12 13

Creamos nuestro nuevo modelo simplificado.

set.seed(1979)
gbm_lr00195_simply <- gbm.step(data = train_boston,

13
gbm.x = gbm_lr00195_simpli$pred.list[[3]], #Columna de predictores.
gbm.y = 14, #Columna a predecir.
family = "gaussian", #Regresión.
tree.complexity = 5, #Profundidad de los árboles
learning.rate = 0.02, #Complejidad
bag.fraction = 0.7)

##
##
## GBM STEP - version 2.9
##
## Performing cross-validation optimisation of a boosted regression tree model
## for medv and using a family of gaussian
## Using 404 observations and 10 predictors
## creating 10 initial models of 50 trees
##
## folds are unstratified
## total mean deviance = 85.058
## tolerance is fixed at 0.0851
## ntrees resid. dev.
## 50 29.7864
## now adding trees...
## 100 18.5463
## 150 15.2335
## 200 13.7199
## 250 12.8314
## 300 12.235
## 350 11.8278
## 400 11.5586
## 450 11.3058
## 500 11.123
## 550 10.9838
## 600 10.8569
## 650 10.7606
## 700 10.6503
## 750 10.5829
## 800 10.5788
## 850 10.5203
## 900 10.4775
## 950 10.4079
## 1000 10.4003
## 1050 10.4061
## 1100 10.4007
## 1150 10.3718
## 1200 10.3351
## 1250 10.3185
## 1300 10.308
## 1350 10.2885
## 1400 10.2463
## 1450 10.2508
## 1500 10.2537
## 1550 10.2465
## 1600 10.2407

14
## 1650 10.2487
## 1700 10.2338
## 1750 10.2271
## 1800 10.2185
## 1850 10.2209
## 1900 10.2284
## 1950 10.2244
## 2000 10.2267

## fitting final gbm model with a fixed number of 1800 trees for medv

medv, d − 5, lr − 0.02
30
holdout deviance

25
20
15
10

0 500 1000 1500 2000

no. of trees

##
## mean total deviance = 85.058
## mean residual deviance = 0.602
##
## estimated cv deviance = 10.219 ; se = 1.651
##
## training data correlation = 0.997
## cv correlation = 0.936 ; se = 0.013
##
## elapsed time - 0.44 minutes

gbm_lr00195_simply$n.trees

## [1] 1800

15
Hemos reducido tres variables con respecto al modelo inicial y la desvianza estimada por validación cruzada
es similar.
Por medio de la función gbm.interactions() Observamos que variables interactúan con más fuerza entre sí.

gbm.interactions(gbm_lr00195_simply)$rank.list

## var1.index var1.names var2.index var2.names int.size


## 1 8 ptratio 4 rm 832.64
## 2 10 lstat 4 rm 780.11
## 3 10 lstat 6 dis 678.77
## 4 4 rm 3 nox 359.02
## 5 10 lstat 3 nox 311.91

Vemos que interactúan con más fuerza la variable ptratio con rm, y la variable lstat con rm y dis.
Predecimos sobre nuestro conjunto de validación.

predict_gbm <- predict(gbm_lr00195_simply, test_boston,


n.trees = gbm_lr00195_simply$gbm.call$best.trees,
type="response")
error <- mean((predict_gbm - test_boston$medv)^2)
paste("El error del modelo en el conjunto de validación es: ",error)

## [1] "El error del modelo en el conjunto de validación es: 7.52186935883396"

3. GBM con la Librería gbm


Otro de los paquetes que nos permite realizar modelos de AdaBoost, Gradient Boosting y Stochastic Gradient
Boosting es gbm. Su utilización es diferente a como hemos entrenado el modelo con el paquete dismo. En
este caso no buscamos el óptimo de árboles, sino que buscaremos los parámetros óptimos para el modelo.

3.1. Parámetros función: gbm()

La configuración de la función gbm() tiene una serie de parámetros muy parecidos a la función gbm.step()
del paquete dismo, estos son:

• data: Conjunto de datos.


• n.trees: Número de árboles que se crean.
• distribution: Distribución a utilizar. Si no se indica la función la identificará automáticamente.
bernoulli para problemas binarios, gaussian para problemas de regresión, multinomial para proble-
mas de clasificación con más de dos clases y adaboost para problemas binarios donde se utiliza el
algoritmo original.
• cv.folds: Número de validaciones cruzadas que realizará.
• interaction.depth: Profundidad de los árboles individuales.
• n.minobsinnode: Número mínimo de observaciones en los nodos terminales.
• shrinkage: Tasa de aprendizaje. Peso de los árboles individuales sobre el modelo final. Se recomienda
utilizar el valor más pequeño posible.
• bag.fraction: Porcentaje de observaciones para entrenar los árboles individuales. Por defecto es 0.5.
• n.cores: El número de núcleos de CPU a utilizar.

16
3.2. Caso práctico: Clasificación.

Utilizamos el conjunto de datos de Pima Indians Diabetes, que se compone de datos médicos de mujeres
mayores de 21 años de la población indígena Pima que viven cerca de Phoenix, Arizona. Se les realizaron
pruebas de diabetes según los criterios de la OMS. Los datos fueron recolectados por el National Institute
of Diabetes and Digestive and Kidney Diseases de EEUU. La variable dependiente es Outcome que indica
si después de la toma de una medicación la paciente tuvo en los siguientes 5 años diabetes (1) o no (0). Si
queremos ampliar información sobre el conjunto de datos podemos ir a la web de UCI.
Cargamos la librería, los datos, obtenemos las dimensiones del dataset y observamos los 5 primeros casos.

#Cargamos la librería.
library(gbm)
#Cargamos los datos.
pima <- read.csv("Datasets/diabetes.csv")
#Dimensiones del dataset
dim(pima)

## [1] 768 9

#Cargamos las primeras 5 observaciones.


head(pima,5)

## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI


## 1 6 148 72 35 0 33.6
## 2 1 85 66 29 0 26.6
## 3 8 183 64 0 0 23.3
## 4 1 89 66 23 94 28.1
## 5 0 137 40 35 168 43.1
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 5 2.288 33 1

Dividimos nuestros datos entre conjunto de entrenamiento y conjunto de validación.

index <- sample(1:nrow(pima), size = 0.8 * nrow(pima))


train_pima <- pima[index,]
test_pima <- pima[-index,]

Creamos nuestro primer modelo con validación cruzada. Utilizamos todos los cores del ordenador (n.cores
= NULL), le indicamos que realice 5 pliegues de validación cruzada para no aumentar demasiado el tiempo
de computación e iniciamos el modelo con 2.000 árboles.

set.seed(1979)
model_gbm <- gbm(Outcome~.,
data = pima,
distribution = "bernoulli",
n.trees = 2000,
shrinkage = 0.01,

17
interaction.depth = 1,
cv.folds = 5, #Validación cruzada.
n.cores = NULL, #Utiliza todos los cores por defecto.
verbose = F)

gbm.perf(model_gbm)
1.3
1.2
Bernoulli deviance

1.1
1.0
0.9
0.8

0 500 1000 1500 2000

Iteration

## [1] 805

La línea azul nos indica el árbol que minimiza el error de prueba por validación cruzada. La línea negra es la
desviación en el conjunto de entrenamiento y la línea verde es el error en el conjunto de prueba. Buscamos
el número de árboles óptimo que disminuye la desvianza en el conjunto de prueba.
Podemos obtener las variables más importantes con summary(), para ellos le pedimos las 5 más variables
más influyentes en el modelo. Tenemos dos alternativas:

• Influencia Relativa: En cada división de cada árbol, el modelo calcula la mejora en el criterio de
división, luego promedia la mejora realizada por cada variable en todos los árboles en que se utiliza la
variable.
• Permutación OOB: Para cada árbol se obtiene la precisión de la predicción en OOB. Después los valores
de cada variable se permutan aleatoriamente y la precisión se calcula nuevamente. La disminución en
la precisión se promedia en todos los árboles de la variable.

Por defecto, la función summary toma la influencia relativa. Vamos a obtener de las dos formas las variables
más importantes para ver las diferencias.

18
#Influencia Relativa.
summary(model_gbm,
method = relative.influence,
cBars = 5,
las =2)

Glucose

BMI

Age

betesPedigreeFunction

Pregnancies
0

10

20

30

40
Relative influence

## var rel.inf
## Glucose Glucose 40.056237
## BMI BMI 18.500300
## Age Age 15.452388
## DiabetesPedigreeFunction DiabetesPedigreeFunction 10.363039
## Pregnancies Pregnancies 4.889923
## Insulin Insulin 4.815164
## BloodPressure BloodPressure 3.908451
## SkinThickness SkinThickness 2.014499

#Permutación OOB.
summary(model_gbm,
method = permutation.test.gbm,
cBars = 5,
las =2)

19
Glucose

BMI

Age

betesPedigreeFunction

Pregnancies
0

10

20

30

40

50
Relative influence

## var rel.inf
## 1 Glucose 57.4506207
## 2 BMI 20.5241341
## 3 Age 11.0809586
## 4 DiabetesPedigreeFunction 6.1147091
## 5 Pregnancies 1.9154690
## 6 BloodPressure 1.5303775
## 7 Insulin 0.9468881
## 8 SkinThickness 0.4368430

Como vemos no hay diferencias significativas en ranking de importancia entre los dos métodos. Predecimos
con el modelo en nuestro conjunto de validación. Podemos indicarle el número de árboles a utilizar n.trees
= 805 pero por defecto utiliza los óptimos.
#Predecimos con el modelo.
predict_gbm <- predict(model_gbm, test_pima, type = "response")

## Using 805 trees...

predict_gbm_p <- ifelse(predict_gbm>0.5,1,0)


(m <-table(predict_gbm_p, test_pima$Outcome))

##
## predict_gbm_p 0 1
## 0 88 27
## 1 9 30

20
Nuestro modelo tiene una tasa de acierto de 76%.
Estudiamos ahora las parcelas de dependencia parcial, como indicábamos en los modelos de random forest,
su interpretación es complicada. Por medio de las dependencias parciales podemos ver como varía nuestra
variable dependiente (Outcome) cuando variamos la variable predictora, manteniendo el resto de constantes
Vamos a ver un ejemplo con la variable Glucose.

plot(model_gbm, i.var ="Glucose",


main = "Outcome ~ Glucose",
ylab = "Outcome")

Outcome ~ Glucose

0
Outcome

−1

−2

0 50 100 150 200

Glucose

En el gráfico observamos que la variable Glucose tiene una relación positiva con la variable dependiente
Outcome. Además, vemos que la relación entre las dos variables no es lineal.

3.3. Grid Search con Caret

Como hemos visto en los temas anteriores, con el paquete caret también podemos buscar los mejores hiper-
parámetros para el modelo. Además, vamos a paralelizar nuestra búsqueda para que utilice todos los cores
de nuestro ordenador y el tiempo de computación se reduzca. Utilizamos el paquete doParallel. Hay otros
paquetes que paralelizan de manera más eficiente, pero este paquete tiene la ventaja que es multiplataforma.

#install.packages("doParallel")
library(caret)
library(doParallel)

21
Iniciamos el clúster para que detecte el número de cores del ordenador. También le podemos indicar el
número de cores a utilizar manualmente.

clu <- makeCluster(detectCores())


registerDoParallel(clu)

Para obtener todos los parámetros que podemos configurar del modelo, utilizamos la función getModelInfo()
que nos proporciona los parámetros modificables.

getModelInfo()$gbm$parameters

## parameter class label


## 1 n.trees numeric # Boosting Iterations
## 2 interaction.depth numeric Max Tree Depth
## 3 shrinkage numeric Shrinkage
## 4 n.minobsinnode numeric Min. Terminal Node Size

Esta misma función nos permite saber todos los modelos que están disponibles para entrenar en la librería
caret. También los podemos consultar en la documentación del paquete

names(getModelInfo())

## [1] "ada" "AdaBag" "AdaBoost.M1"


## [4] "adaboost" "amdai" "ANFIS"
## [7] "avNNet" "awnb" "awtan"
## [10] "bag" "bagEarth" "bagEarthGCV"
## [13] "bagFDA" "bagFDAGCV" "bam"
## [16] "bartMachine" "bayesglm" "binda"
## [19] "blackboost" "blasso" "blassoAveraged"
## [22] "bridge" "brnn" "BstLm"
## [25] "bstSm" "bstTree" "C5.0"
## [28] "C5.0Cost" "C5.0Rules" "C5.0Tree"
## [31] "cforest" "chaid" "CSimca"
## [34] "ctree" "ctree2" "cubist"
## [37] "dda" "deepboost" "DENFIS"
## [40] "dnn" "dwdLinear" "dwdPoly"
## [43] "dwdRadial" "earth" "elm"
## [46] "enet" "evtree" "extraTrees"
## [49] "fda" "FH.GBML" "FIR.DM"
## [52] "foba" "FRBCS.CHI" "FRBCS.W"
## [55] "FS.HGD" "gam" "gamboost"
## [58] "gamLoess" "gamSpline" "gaussprLinear"
## [61] "gaussprPoly" "gaussprRadial" "gbm_h2o"
## [64] "gbm" "gcvEarth" "GFS.FR.MOGUL"
## [67] "GFS.LT.RS" "GFS.THRIFT" "glm.nb"
## [70] "glm" "glmboost" "glmnet_h2o"
## [73] "glmnet" "glmStepAIC" "gpls"
## [76] "hda" "hdda" "hdrda"
## [79] "HYFIS" "icr" "J48"
## [82] "JRip" "kernelpls" "kknn"
## [85] "knn" "krlsPoly" "krlsRadial"
## [88] "lars" "lars2" "lasso"

22
## [91] "lda" "lda2" "leapBackward"
## [94] "leapForward" "leapSeq" "Linda"
## [97] "lm" "lmStepAIC" "LMT"
## [100] "loclda" "logicBag" "LogitBoost"
## [103] "logreg" "lssvmLinear" "lssvmPoly"
## [106] "lssvmRadial" "lvq" "M5"
## [109] "M5Rules" "manb" "mda"
## [112] "Mlda" "mlp" "mlpKerasDecay"
## [115] "mlpKerasDecayCost" "mlpKerasDropout" "mlpKerasDropoutCost"
## [118] "mlpML" "mlpSGD" "mlpWeightDecay"
## [121] "mlpWeightDecayML" "monmlp" "msaenet"
## [124] "multinom" "mxnet" "mxnetAdam"
## [127] "naive_bayes" "nb" "nbDiscrete"
## [130] "nbSearch" "neuralnet" "nnet"
## [133] "nnls" "nodeHarvest" "null"
## [136] "OneR" "ordinalNet" "ordinalRF"
## [139] "ORFlog" "ORFpls" "ORFridge"
## [142] "ORFsvm" "ownn" "pam"
## [145] "parRF" "PART" "partDSA"
## [148] "pcaNNet" "pcr" "pda"
## [151] "pda2" "penalized" "PenalizedLDA"
## [154] "plr" "pls" "plsRglm"
## [157] "polr" "ppr" "PRIM"
## [160] "protoclass" "qda" "QdaCov"
## [163] "qrf" "qrnn" "randomGLM"
## [166] "ranger" "rbf" "rbfDDA"
## [169] "Rborist" "rda" "regLogistic"
## [172] "relaxo" "rf" "rFerns"
## [175] "RFlda" "rfRules" "ridge"
## [178] "rlda" "rlm" "rmda"
## [181] "rocc" "rotationForest" "rotationForestCp"
## [184] "rpart" "rpart1SE" "rpart2"
## [187] "rpartCost" "rpartScore" "rqlasso"
## [190] "rqnc" "RRF" "RRFglobal"
## [193] "rrlda" "RSimca" "rvmLinear"
## [196] "rvmPoly" "rvmRadial" "SBC"
## [199] "sda" "sdwd" "simpls"
## [202] "SLAVE" "slda" "smda"
## [205] "snn" "sparseLDA" "spikeslab"
## [208] "spls" "stepLDA" "stepQDA"
## [211] "superpc" "svmBoundrangeString" "svmExpoString"
## [214] "svmLinear" "svmLinear2" "svmLinear3"
## [217] "svmLinearWeights" "svmLinearWeights2" "svmPoly"
## [220] "svmRadial" "svmRadialCost" "svmRadialSigma"
## [223] "svmRadialWeights" "svmSpectrumString" "tan"
## [226] "tanSearch" "treebag" "vbmpRadial"
## [229] "vglmAdjCat" "vglmContRatio" "vglmCumulative"
## [232] "widekernelpls" "WM" "wsrf"
## [235] "xgbDART" "xgbLinear" "xgbTree"
## [238] "xyf"

Un paso previo para realizar nuestro modelo con caret, es transformar la variable dependiente en factor.

23
#Transformamos la variable a predecir en factor
train_pima$Outcome <- as.factor(train_pima$Outcome)
test_pima$Outcome <- as.factor(test_pima$Outcome)

Ahora que tenemos preparado nuestro conjunto de datos, vamos a buscar los parámetros óptimos con 2.000
árboles, con validación cruzada y paralelizando el proceso.

#Búsqueda en cuadricula
set.seed(1979)
control <- trainControl(method = "cv", #Método: Validación cruzada.
number = 5, #Número de conjuntos por validación cruzada.
allowParallel = T) #Indica que se paralelice.
gbmGrid <- expand.grid(n.trees = 2000,
interaction.depth = 1:5,
shrinkage = c(0.005,0.01),
n.minobsinnode = c(1,5,10))

model_gbm_cv <- train(Outcome~.,


data = train_pima,
trControl = control, #Parámetros de control
metric = "Accuracy", #Métrica.
distribution = "bernoulli", #Distribución
method ="gbm", #Método
tuneGrid = gbmGrid, #Hiperparámetros
verbose=F)
model_gbm_cv

## Stochastic Gradient Boosting


##
## 614 samples
## 8 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 491, 491, 491, 492, 491
## Resampling results across tuning parameters:
##
## shrinkage interaction.depth n.minobsinnode Accuracy Kappa
## 0.005 1 1 0.7671998 0.4596315
## 0.005 1 5 0.7720512 0.4713219
## 0.005 1 10 0.7606691 0.4431380
## 0.005 2 1 0.7558043 0.4383356
## 0.005 2 5 0.7606824 0.4496838
## 0.005 2 10 0.7525123 0.4291397
## 0.005 3 1 0.7525523 0.4343618
## 0.005 3 5 0.7590430 0.4491391
## 0.005 3 10 0.7509130 0.4313714
## 0.005 4 1 0.7509130 0.4301132
## 0.005 4 5 0.7508996 0.4277268
## 0.005 4 10 0.7460216 0.4191410
## 0.005 5 1 0.7460083 0.4185758
## 0.005 5 5 0.7443689 0.4134218

24
## 0.005 5 10 0.7362255 0.3978323
## 0.010 1 1 0.7606824 0.4477476
## 0.010 1 5 0.7623084 0.4520699
## 0.010 1 10 0.7574037 0.4416399
## 0.010 2 1 0.7427696 0.4128187
## 0.010 2 5 0.7427562 0.4128447
## 0.010 2 10 0.7460349 0.4184209
## 0.010 3 1 0.7443956 0.4125680
## 0.010 3 5 0.7346128 0.3935380
## 0.010 3 10 0.7345995 0.3941522
## 0.010 4 1 0.7427429 0.4135991
## 0.010 4 5 0.7329601 0.3917157
## 0.010 4 10 0.7248301 0.3769777
## 0.010 5 1 0.7411435 0.4144568
## 0.010 5 5 0.7248167 0.3749097
## 0.010 5 10 0.7280821 0.3832271
##
## Tuning parameter 'n.trees' was held constant at a value of 2000
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were n.trees = 2000,
## interaction.depth = 1, shrinkage = 0.005 and n.minobsinnode = 5.

Detenemos el clúster. Hay que recordar que mientras está encendido utiliza toda la memoria del CPU.

stopCluster(clu)

Obtenemos el Accuracy de cada uno de los folds que hemos realizado por validación cruzada.

model_gbm_cv$resample

## Accuracy Kappa Resample


## 1 0.6991870 0.2861176 Fold2
## 2 0.8048780 0.5397568 Fold1
## 3 0.7723577 0.4879572 Fold5
## 4 0.8114754 0.5600502 Fold4
## 5 0.7723577 0.4827275 Fold3

Y con la función confusionMatrix() obtenemos la media de los accuracy.

confusionMatrix(model_gbm_cv)

## Cross-Validated (5 fold) Confusion Matrix


##
## (entries are percentual average cell counts across resamples)
##
## Reference
## Prediction 0 1
## 0 57.3 14.5
## 1 8.3 19.9
##
## Accuracy (average) : 0.772

25
Dibujamos las interacciones de los distintos parámetros que hemos solicitado, calculando para cada una de
las combinaciones la precisión (Accuracy) por validación cruzada.

plot(model_gbm_cv)

Min. Terminal Node Size


1 5 10
1 2 3 4 5

shrinkage: 0.005 shrinkage: 0.010

0.77
Accuracy (Cross−Validation)

0.76

0.75

0.74

0.73

1 2 3 4 5

Max Tree Depth

En el gráfico observamos que la mayor precisión se obtiene con los parámetros shrinkage = 0.005, Max Tree
Depth = 1 y Min.Terminal Node Size = 5, con una precisión superior al 77%.
Solicitamos cuales son las variables más importantes del modelo. Obtenemos las mismas variables y en la
misma posición que en el primer modelo creado.

varImp(model_gbm_cv)

## gbm variable importance


##
## Overall
## Glucose 100.000
## BMI 35.011
## Age 26.949
## DiabetesPedigreeFunction 20.532
## BloodPressure 5.291
## Pregnancies 5.037
## Insulin 1.882
## SkinThickness 0.000

Y realizamos un gráfico con ellas.

26
plot(varImp(model_gbm_cv),main="Importancia de las Variables - GBM")

Importancia de las Variables − GBM

Glucose

BMI

Age

DiabetesPedigreeFunction

BloodPressure

Pregnancies

Insulin

SkinThickness

0 20 40 60 80 100

Importance

Predecimos con el modelo que hemos creado.

predict_gbm_cv <- predict(model_gbm_cv, test_pima,type = "raw")


confusionMatrix(predict_gbm_cv,test_pima$Outcome)

## Confusion Matrix and Statistics


##
## Reference
## Prediction 0 1
## 0 86 30
## 1 11 27
##
## Accuracy : 0.7338
## 95% CI : (0.6566, 0.8017)
## No Information Rate : 0.6299
## P-Value [Acc > NIR] : 0.004152
##
## Kappa : 0.3869
##
## Mcnemar's Test P-Value : 0.004937
##
## Sensitivity : 0.8866
## Specificity : 0.4737

27
## Pos Pred Value : 0.7414
## Neg Pred Value : 0.7105
## Prevalence : 0.6299
## Detection Rate : 0.5584
## Detection Prevalence : 0.7532
## Balanced Accuracy : 0.6801
##
## 'Positive' Class : 0
##

En este caso nuestra tasa de acierto en el conjunto de validación es muy inferior a la tasa de acierto del
conjunto de entrenamiento obtenida con validación cruzada y a la tasa de acierto obtenida con el modelo
de la librería GBM, por lo que deberíamos subir el número de folds de cross validation y aumentar las
posibilidades de búsqueda de los hiperparámetros.

4. XGBoost
Xgboost es un algoritmo para problemas de aprendizaje supervisado que se puede utilizar tanto para prob-
lemas de regresión como de clasificación y que suele aumentar la precisión de predicción frente a otros
algoritmos de Machine Learning.
Realiza un procesamiento secuencial de los datos con una función de pérdida o coste, la cual, minimiza
el error iteración tras iteración, haciéndolo de esta manera, un pronosticador fuerte, elevando el poder de
predicción frente a otros modelos de árboles secuenciales.
Es un algoritmo cuya versión inicial nació en 2014 por parte de Tianqi Chen, estudiante de la Universidad
de Washington basado en el Stochastic Gradient Boosting y que ha ido ganando importancia desde su
lanzamiento, siendo uno de los algoritmos que dominan las competiciones de Kaggle2 . Entre sus ventajas
se encuentra su potencia, la escalabilidad que hace que su aprendizaje sea más rápido y su uso eficiente
de la memoria. Incorpora validación cruzada y un tratamiento de valores faltantes de los cuales aprende si
hay alguna tendencia en ellos. Deja crecer al máximo de profundidad (max_depth) los árboles y desde esa
profundidad poda hacia atrás siempre que mejore la función de pérdida.

4.1. Parámetros función: XGBoost.

El algoritmo de XGBoost tiene muchos parámetros que podemos modificar, aunque los valores que vienen
por defecto suelen dar predicciones muy altas. Podemos mejorar éstas si entendemos para que sirve cada
uno de los parámetros.

• objective: Indicamos el tipo de problema que tenemos. Podemos indicar reg:linear para regresión,
binary:logistic para regresión logística (clasificación binaria), multi:softmax para problemas de clasifi-
cación con más de dos clases que nos devuelve las clases predichas (no probabilidades) y multi:softprob
para problemas de clasificación que nos devuelve las probabilidades de cada clase.
• eval_metric: Nos permite indicar la métrica a utilizar. Por defecto utiliza el RMSE para los proble-
mas de regresión y error (tasa de error) para los problemas de clasificación, pero podemos utilizar el
MAE(regresión), Logloss(clasificación), AUC(clasificación) y mlogloss(clasificación)
• nrounds: Es el número máximo de iteraciones del modelo que podríamos asimilar al número de árboles
en los problemas de Bagging. El valor por defecto es 100 iteraciones y puede ir desde 1 hasta el valor
máximo que le queramos dar.
2 Página web de Kaggle

28
• eta: Es la tasa de aprendizaje de nuestro modelo o dicho de otra manera como de rápido queremos que
aprenda nuestro modelo de los datos. Con valores bajos el modelo aprenderá lentamente y deberemos
subir el nrounds, mejorando la predicción de entrenamiento, pero puede que se adapte demasiado a
estos datos surgiendo el overfitting. Valores altos necesitaran menores nrounds y el aprendizaje será
más rápido, lo que puede hacer que nos saltemos el mínimo global del error. El valor por defecto es
0.3, y su rango es de entre 0 y 1.
• gamma: Es la reducción de pérdida mínima requerida para realizar una partición adicional en un nodo
de hoja del árbol. El valor por defecto es 0, pudiéndose ajustar entre 0 e infinito. Valores bajos harán
que el modelo se ajuste más a los datos de entrenamiento, mientras que valores altos harán un modelo
más conservador. Se recomienda empezar con una gamma en 0 y ver si el resultado del conjunto de
entrenamiento por validación cruzada es superior al de validación, si es así debemos subir la gamma
para intentar reducir el overfitting.
• max_depth: Es la profundidad máxima del árbol. Valores altos crea modelos más complejos que se
adaptan a los datos de entrenamiento creando overfitting. El valor por defecto es 6, siendo el rango de
ajuste entre 0 e infinito.
• colsample_bytree: Es el número de variables que le suministramos a un árbol. Se asemeja al mtry en
el algoritmo Random Forest. Su valor por defecto es 1 y se puede ajustar entre 0 y 1.
• subsample: Es el número de observaciones que le facilitamos a un árbol para que aprenda. Su valor
por defecto es 1, siendo su rango entre 0 y 1.
• min_child_weight: Es la suma mínima de instancias requeridas en un nodo secundario. Se asemeja al
número de observaciones en un nodo terminal. Su valor por defecto es 1 y se puede ajustar entre 0 e
infinito.

Normalmente se suele empezar por entrenar un modelo xgboost con los parámetros originales e intentar
ajustar la profundidad (max_depth) entre 1 y 10 (recordamos que los modelos Boosting se basan en modelos
poco profundos) y una vez que tenemos la profundidad óptima ajustamos el resto de parámetros.

4.2. Caso práctico: Regresión.

Empezamos por instalar y cargar la librería xgboost que nos permite entrenar el algoritmo xgboost tanto
para problemas de clasificación como de regresión.

#install.packages("xgboost")
library(xgboost)

Utilizamos los datos de AmesHousing pertenecientes al paquete AmesHousing donde encontramos 82 vari-
ables para predecir el precio de las casas. Si queremos más información sobre el dataset podemos encontrarla
aquí. Lo primero que vamos a hacer es cargar los datos y ver como se estructuran.

#Intalamos y cargamos la librería.


#install.packages("AmesHousing")
library(AmesHousing)
#Caragamos los datos.
AmesHousing <- make_ames()
#Número de observaciones y variables
dim(AmesHousing)

## [1] 2930 81

#Estructura de las variables.


str(AmesHousing)

29
## Classes 'tbl_df', 'tbl' and 'data.frame': 2930 obs. of 81 variables:
## $ MS_SubClass : Factor w/ 16 levels "One_Story_1946_and_Newer_All_Styles",..: 1 1 1 1 6 6 12 1
## $ MS_Zoning : Factor w/ 7 levels "Floating_Village_Residential",..: 3 2 3 3 3 3 3 3 3 3 ...
## $ Lot_Frontage : num 141 80 81 93 74 78 41 43 39 60 ...
## $ Lot_Area : int 31770 11622 14267 11160 13830 9978 4920 5005 5389 7500 ...
## $ Street : Factor w/ 2 levels "Grvl","Pave": 2 2 2 2 2 2 2 2 2 2 ...
## $ Alley : Factor w/ 3 levels "Gravel","No_Alley_Access",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ Lot_Shape : Factor w/ 4 levels "Regular","Slightly_Irregular",..: 2 1 2 1 2 2 1 2 2 1 ...
## $ Land_Contour : Factor w/ 4 levels "Bnk","HLS","Low",..: 4 4 4 4 4 4 4 2 4 4 ...
## $ Utilities : Factor w/ 3 levels "AllPub","NoSeWa",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Lot_Config : Factor w/ 5 levels "Corner","CulDSac",..: 1 5 1 1 5 5 5 5 5 5 ...
## $ Land_Slope : Factor w/ 3 levels "Gtl","Mod","Sev": 1 1 1 1 1 1 1 1 1 1 ...
## $ Neighborhood : Factor w/ 28 levels "North_Ames","College_Creek",..: 1 1 1 1 7 7 17 17 17 7 ..
## $ Condition_1 : Factor w/ 9 levels "Artery","Feedr",..: 3 2 3 3 3 3 3 3 3 3 ...
## $ Condition_2 : Factor w/ 8 levels "Artery","Feedr",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ Bldg_Type : Factor w/ 5 levels "OneFam","TwoFmCon",..: 1 1 1 1 1 1 5 5 5 1 ...
## $ House_Style : Factor w/ 8 levels "One_and_Half_Fin",..: 3 3 3 3 8 8 3 3 3 8 ...
## $ Overall_Qual : Factor w/ 10 levels "Very_Poor","Poor",..: 6 5 6 7 5 6 8 8 8 7 ...
## $ Overall_Cond : Factor w/ 10 levels "Very_Poor","Poor",..: 5 6 6 5 5 6 5 5 5 5 ...
## $ Year_Built : int 1960 1961 1958 1968 1997 1998 2001 1992 1995 1999 ...
## $ Year_Remod_Add : int 1960 1961 1958 1968 1998 1998 2001 1992 1996 1999 ...
## $ Roof_Style : Factor w/ 6 levels "Flat","Gable",..: 4 2 4 4 2 2 2 2 2 2 ...
## $ Roof_Matl : Factor w/ 8 levels "ClyTile","CompShg",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ Exterior_1st : Factor w/ 16 levels "AsbShng","AsphShn",..: 4 14 15 4 14 14 6 7 6 14 ...
## $ Exterior_2nd : Factor w/ 17 levels "AsbShng","AsphShn",..: 11 15 16 4 15 15 6 7 6 15 ...
## $ Mas_Vnr_Type : Factor w/ 5 levels "BrkCmn","BrkFace",..: 5 4 2 4 4 2 4 4 4 4 ...
## $ Mas_Vnr_Area : num 112 0 108 0 0 20 0 0 0 0 ...
## $ Exter_Qual : Factor w/ 4 levels "Excellent","Fair",..: 4 4 4 3 4 4 3 3 3 4 ...
## $ Exter_Cond : Factor w/ 5 levels "Excellent","Fair",..: 5 5 5 5 5 5 5 5 5 5 ...
## $ Foundation : Factor w/ 6 levels "BrkTil","CBlock",..: 2 2 2 2 3 3 3 3 3 3 ...
## $ Bsmt_Qual : Factor w/ 6 levels "Excellent","Fair",..: 6 6 6 6 3 6 3 3 3 6 ...
## $ Bsmt_Cond : Factor w/ 6 levels "Excellent","Fair",..: 3 6 6 6 6 6 6 6 6 6 ...
## $ Bsmt_Exposure : Factor w/ 5 levels "Av","Gd","Mn",..: 2 4 4 4 4 4 3 4 4 4 ...
## $ BsmtFin_Type_1 : Factor w/ 7 levels "ALQ","BLQ","GLQ",..: 2 6 1 1 3 3 3 1 3 7 ...
## $ BsmtFin_SF_1 : num 2 6 1 1 3 3 3 1 3 7 ...
## $ BsmtFin_Type_2 : Factor w/ 7 levels "ALQ","BLQ","GLQ",..: 7 4 7 7 7 7 7 7 7 7 ...
## $ BsmtFin_SF_2 : num 0 144 0 0 0 0 0 0 0 0 ...
## $ Bsmt_Unf_SF : num 441 270 406 1045 137 ...
## $ Total_Bsmt_SF : num 1080 882 1329 2110 928 ...
## $ Heating : Factor w/ 6 levels "Floor","GasA",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ Heating_QC : Factor w/ 5 levels "Excellent","Fair",..: 2 5 5 1 3 1 1 1 1 3 ...
## $ Central_Air : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 2 2 2 2 ...
## $ Electrical : Factor w/ 6 levels "FuseA","FuseF",..: 5 5 5 5 5 5 5 5 5 5 ...
## $ First_Flr_SF : int 1656 896 1329 2110 928 926 1338 1280 1616 1028 ...
## $ Second_Flr_SF : int 0 0 0 0 701 678 0 0 0 776 ...
## $ Low_Qual_Fin_SF : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Gr_Liv_Area : int 1656 896 1329 2110 1629 1604 1338 1280 1616 1804 ...
## $ Bsmt_Full_Bath : num 1 0 0 1 0 0 1 0 1 0 ...
## $ Bsmt_Half_Bath : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Full_Bath : int 1 1 1 2 2 2 2 2 2 2 ...
## $ Half_Bath : int 0 0 1 1 1 1 0 0 0 1 ...
## $ Bedroom_AbvGr : int 3 2 3 3 3 3 2 2 2 3 ...
## $ Kitchen_AbvGr : int 1 1 1 1 1 1 1 1 1 1 ...
## $ Kitchen_Qual : Factor w/ 5 levels "Excellent","Fair",..: 5 5 3 1 5 3 3 3 3 3 ...

30
## $ TotRms_AbvGrd : int 7 5 6 8 6 7 6 5 5 7 ...
## $ Functional : Factor w/ 8 levels "Maj1","Maj2",..: 8 8 8 8 8 8 8 8 8 8 ...
## $ Fireplaces : int 2 0 0 2 1 1 0 0 1 1 ...
## $ Fireplace_Qu : Factor w/ 6 levels "Excellent","Fair",..: 3 4 4 6 6 3 4 4 6 6 ...
## $ Garage_Type : Factor w/ 7 levels "Attchd","Basment",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Garage_Finish : Factor w/ 4 levels "Fin","No_Garage",..: 1 4 4 1 1 1 1 3 3 1 ...
## $ Garage_Cars : num 2 1 1 2 2 2 2 2 2 2 ...
## $ Garage_Area : num 528 730 312 522 482 470 582 506 608 442 ...
## $ Garage_Qual : Factor w/ 6 levels "Excellent","Fair",..: 6 6 6 6 6 6 6 6 6 6 ...
## $ Garage_Cond : Factor w/ 6 levels "Excellent","Fair",..: 6 6 6 6 6 6 6 6 6 6 ...
## $ Paved_Drive : Factor w/ 3 levels "Dirt_Gravel",..: 2 3 3 3 3 3 3 3 3 3 ...
## $ Wood_Deck_SF : int 210 140 393 0 212 360 0 0 237 140 ...
## $ Open_Porch_SF : int 62 0 36 0 34 36 0 82 152 60 ...
## $ Enclosed_Porch : int 0 0 0 0 0 0 170 0 0 0 ...
## $ Three_season_porch: int 0 0 0 0 0 0 0 0 0 0 ...
## $ Screen_Porch : int 0 120 0 0 0 0 0 144 0 0 ...
## $ Pool_Area : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Pool_QC : Factor w/ 5 levels "Excellent","Fair",..: 4 4 4 4 4 4 4 4 4 4 ...
## $ Fence : Factor w/ 5 levels "Good_Privacy",..: 5 3 5 5 3 5 5 5 5 5 ...
## $ Misc_Feature : Factor w/ 6 levels "Elev","Gar2",..: 3 3 2 3 3 3 3 3 3 3 ...
## $ Misc_Val : int 0 0 12500 0 0 0 0 0 0 0 ...
## $ Mo_Sold : int 5 6 6 4 3 6 4 1 3 6 ...
## $ Year_Sold : int 2010 2010 2010 2010 2010 2010 2010 2010 2010 2010 ...
## $ Sale_Type : Factor w/ 10 levels "COD","Con","ConLD",..: 10 10 10 10 10 10 10 10 10 10 ...
## $ Sale_Condition : Factor w/ 6 levels "Abnorml","AdjLand",..: 5 5 5 5 5 5 5 5 5 5 ...
## $ Sale_Price : int 215000 105000 172000 244000 189900 195500 213500 191500 236500 189000 ...
## $ Longitude : num -93.6 -93.6 -93.6 -93.6 -93.6 ...
## $ Latitude : num 42.1 42.1 42.1 42.1 42.1 ...

#Columnas con NAs


colSums(is.na.data.frame(AmesHousing))

## MS_SubClass MS_Zoning Lot_Frontage


## 0 0 0
## Lot_Area Street Alley
## 0 0 0
## Lot_Shape Land_Contour Utilities
## 0 0 0
## Lot_Config Land_Slope Neighborhood
## 0 0 0
## Condition_1 Condition_2 Bldg_Type
## 0 0 0
## House_Style Overall_Qual Overall_Cond
## 0 0 0
## Year_Built Year_Remod_Add Roof_Style
## 0 0 0
## Roof_Matl Exterior_1st Exterior_2nd
## 0 0 0
## Mas_Vnr_Type Mas_Vnr_Area Exter_Qual
## 0 0 0
## Exter_Cond Foundation Bsmt_Qual
## 0 0 0
## Bsmt_Cond Bsmt_Exposure BsmtFin_Type_1
## 0 0 0

31
## BsmtFin_SF_1 BsmtFin_Type_2 BsmtFin_SF_2
## 0 0 0
## Bsmt_Unf_SF Total_Bsmt_SF Heating
## 0 0 0
## Heating_QC Central_Air Electrical
## 0 0 0
## First_Flr_SF Second_Flr_SF Low_Qual_Fin_SF
## 0 0 0
## Gr_Liv_Area Bsmt_Full_Bath Bsmt_Half_Bath
## 0 0 0
## Full_Bath Half_Bath Bedroom_AbvGr
## 0 0 0
## Kitchen_AbvGr Kitchen_Qual TotRms_AbvGrd
## 0 0 0
## Functional Fireplaces Fireplace_Qu
## 0 0 0
## Garage_Type Garage_Finish Garage_Cars
## 0 0 0
## Garage_Area Garage_Qual Garage_Cond
## 0 0 0
## Paved_Drive Wood_Deck_SF Open_Porch_SF
## 0 0 0
## Enclosed_Porch Three_season_porch Screen_Porch
## 0 0 0
## Pool_Area Pool_QC Fence
## 0 0 0
## Misc_Feature Misc_Val Mo_Sold
## 0 0 0
## Year_Sold Sale_Type Sale_Condition
## 0 0 0
## Sale_Price Longitude Latitude
## 0 0 0

Nuestro conjunto de datos tiene 2930 observaciones con 81 variables, no tenemos valores faltantes en ninguna
de las variables y tenemos variables numéricas y factores.
Dividimos nuestros datos en un conjunto de entrenamiento y otro de validación (80%-20%).

index <- sample(1:nrow(AmesHousing), size = 0.8 * nrow(AmesHousing))


train_Ames <- AmesHousing[index,]
test_Ames <- AmesHousing[-index,]

Una de las características del algoritmo XGBoost es que solo funciona con variables numéricas por lo que
tendremos que transformar aquellas variables que no lo son por medio de la función sparse.model.matrix()
del paquete Matrix, que transforma las variables factor en binarias.

#install.packages("Matrix")
library(Matrix)

Extraemos de nuestros datos la variable a predecir y la eliminamos de nuestros conjuntos de datos.

labels_train <- train_Ames$Sale_Price


train_Ames$Sale_Price <- NULL

32
labels_test <- test_Ames$Sale_Price
test_Ames$Sale_Price <- NULL

Transformamos nuestros datos en numéricos y le quitamos la primera columna que son todos “unos”. No
transformamos la variable Sale_Price al ser nuestra variable dependiente. También podríamos haber uti-
lizado el paquete Dummies como se indicó en el primer tema, para transformar las variables factor en
binarias.

#Transformamos en numérico
x_train <- sparse.model.matrix(~.-1, data = train_Ames)
x_test <- sparse.model.matrix(~.-1, data = test_Ames)

Transformamos nuestros datos en una matriz con la función xgb.Dmatrix() del propio paquete xgboost.

dtrain <- xgb.DMatrix(x_train,label = labels_train)


dtest <- xgb.DMatrix(x_test, label = labels_test)

Creamos nuestro primer modelo básico con la función xgb.cv() que incorpora validación cruzada frente a la
función xgb.train().

parametros <- list(booster = "gbtree",


max_depth = 6,
eta = 0.03,
min_child_weight = 1,
nthread = 1,
subsample = 1,
colsample_bytree = 1,
objective = "reg:linear",
eval_metric = "rmse")

xgb_model_cv <- xgb.cv(data = dtrain,


params = parametros,
nfold = 5,
showsd = TRUE,
nrounds = 1000,
print_every_n = 50,
early.stop.round = 10,
verbose = 1
)

## [1] train-rmse:193147.959375+1405.096655 test-rmse:193133.015625+5673.518167


## Multiple eval metrics are present. Will use test_rmse for early stopping.
## Will train until test_rmse hasn't improved in 10 rounds.
##
## [51] train-rmse:48410.817188+367.608155 test-rmse:53433.166406+3204.969731
## [101] train-rmse:16804.968359+52.764294 test-rmse:28716.640235+2180.869036
## [151] train-rmse:10285.972461+143.983804 test-rmse:25454.790234+1723.303042
## [201] train-rmse:8424.568164+198.401567 test-rmse:24668.531250+1492.212141
## [251] train-rmse:7518.177051+164.663171 test-rmse:24414.388281+1478.982356
## [301] train-rmse:6875.276367+172.027124 test-rmse:24256.635156+1476.650776
## [351] train-rmse:6381.395508+146.778500 test-rmse:24155.981640+1496.470272
## [401] train-rmse:5951.954492+145.300900 test-rmse:24087.949219+1503.314596

33
## [451] train-rmse:5536.319434+121.706303 test-rmse:24035.552344+1514.418954
## [501] train-rmse:5175.528027+88.871124 test-rmse:23983.727734+1519.640122
## [551] train-rmse:4829.248926+65.914722 test-rmse:23946.645312+1511.765379
## [601] train-rmse:4524.881153+53.402320 test-rmse:23914.873047+1507.334899
## [651] train-rmse:4224.307812+58.409545 test-rmse:23885.728516+1501.837465
## [701] train-rmse:3975.167187+65.685885 test-rmse:23868.355078+1497.199742
## [751] train-rmse:3738.158789+61.974672 test-rmse:23846.751953+1492.352449
## [801] train-rmse:3499.402832+60.254416 test-rmse:23830.083594+1494.650833
## [851] train-rmse:3284.513574+57.798865 test-rmse:23816.142578+1499.164213
## [901] train-rmse:3089.364746+48.721813 test-rmse:23802.726172+1507.245346
## [951] train-rmse:2922.604785+47.105310 test-rmse:23792.610547+1508.856588
## Stopping. Best iteration:
## [971] train-rmse:2850.742138+50.097935 test-rmse:23786.514453+1505.683076

El modelo se ha parado en la iteración donde se consigue el menor error de prueba (test-rmse).

#Mejor iteración.
(m_cv <- xgb_model_cv$best_iteration)

## [1] 971

#Mínimo error de prueba.


min(xgb_model_cv$evaluation_log$test_rmse_mean)

## [1] 23786.51

Creamos nuestro modelo con la función xgb.train(), utilizando la mejor iteración 971 y predecimos sobre el
conjunto de validación.

xgb_model1 <- xgb.train(data = dtrain,


params = parametros,
nrounds = m_cv,
print_every_n = 50,
early.stop.round = 10,
verbose = 1,
watchlist = list(val = dtest, train = dtrain))

## [1] val-rmse:187138.859375 train-rmse:193141.578125


## Multiple eval metrics are present. Will use train_rmse for early stopping.
## Will train until train_rmse hasn't improved in 10 rounds.
##
## [51] val-rmse:48639.089844 train-rmse:48420.847656
## [101] val-rmse:25638.658203 train-rmse:17247.603516
## [151] val-rmse:23153.058594 train-rmse:10944.022461
## [201] val-rmse:22662.066406 train-rmse:9090.769531
## [251] val-rmse:22472.900391 train-rmse:8263.637695
## [301] val-rmse:22322.226562 train-rmse:7758.497559
## [351] val-rmse:22240.822266 train-rmse:7325.917480
## [401] val-rmse:22170.662109 train-rmse:6889.916016
## [451] val-rmse:22134.113281 train-rmse:6498.423340
## [501] val-rmse:22108.134766 train-rmse:6141.195801
## [551] val-rmse:22081.845703 train-rmse:5795.507324

34
## [601] val-rmse:22063.181641 train-rmse:5462.775391
## [651] val-rmse:22041.847656 train-rmse:5179.170898
## [701] val-rmse:22016.673828 train-rmse:4889.941895
## [751] val-rmse:21982.628906 train-rmse:4612.371094
## [801] val-rmse:21952.957031 train-rmse:4361.537109
## [851] val-rmse:21922.798828 train-rmse:4118.350098
## [901] val-rmse:21902.529297 train-rmse:3924.767822
## [951] val-rmse:21882.109375 train-rmse:3715.879883
## [971] val-rmse:21874.765625 train-rmse:3643.016357

Predecimos sobre el conjunto de validación.

predict_xgb1 <- predict(xgb_model1, dtest)


(RMSExgb1 <- RMSE(predict_xgb1,labels_test))

## [1] 21874.76

Obtenemos las variables más importantes con la función xgb.importance() donde le tendremos que
indicar el nombre de las variables y realizamos un gráfico con las 10 más importantes con la función
xgb.plot.importance().

import_var <-xgb.importance(feature_names = colnames(x_train),model = xgb_model1)


xgb.plot.importance(import_var[1:10])

Garage_Cars

Gr_Liv_Area

Total_Bsmt_SF

Year_Built

Year_Remod_Add

First_Flr_SF

Second_Flr_SF

Exter_QualTypical

Fireplaces

Exter_QualGood

0.0 0.1 0.2 0.3

35
Esta función nos da la contribución relativa de la característica al modelo, calculada según la contribución
de la característica para cada árbol del modelo. Un porcentaje más alto significa una característica predic-
tiva más importante. En nuestro caso las variables más importantes son: Garage_Cars, Gr_Liv_Area y
Total_Bsmt_SF.

4.3. Grid Search con Caret

Hasta ahora hemos obtenido un modelo con casi todos los parámetros estándar y buscando la mejor iteración
con estos. Ahora por medio del paquete caret vamos a buscar los mejores hiperparámetros para el prob-
lema, paralelizando dicha búsqueda. También podríamos haber buscado mejorar la precisión del modelo
modificando manualmente los parámetros con los que está configurado
Lo primero que hacemos es ver que parámetros nos permite configurar la librería caret para los modelos de
xgboost.

modelLookup("xgbTree")

## model parameter label forReg forClass


## 1 xgbTree nrounds # Boosting Iterations TRUE TRUE
## 2 xgbTree max_depth Max Tree Depth TRUE TRUE
## 3 xgbTree eta Shrinkage TRUE TRUE
## 4 xgbTree gamma Minimum Loss Reduction TRUE TRUE
## 5 xgbTree colsample_bytree Subsample Ratio of Columns TRUE TRUE
## 6 xgbTree min_child_weight Minimum Sum of Instance Weight TRUE TRUE
## 7 xgbTree subsample Subsample Percentage TRUE TRUE
## probModel
## 1 TRUE
## 2 TRUE
## 3 TRUE
## 4 TRUE
## 5 TRUE
## 6 TRUE
## 7 TRUE

Creamos nuestra búsqueda en cuadrícula (grid search) con el nrounds que hemos obtenido anteriormente.

xgbGrid <- expand.grid(eta = c(0.02,0.03),


max_depth = 4,
colsample_bytree = c(0.5,0.9),
nrounds = m_cv,
min_child_weight = 1,
gamma = 0,
subsample = 1
)
dim(xgbGrid)

## [1] 4 7

En total vamos a buscar en 4 modelos. Solo indicamos 4 modelos porque no queremos alargar el tiempo de
computación, sino indicar cuál es el proceso que deberíamos realizar para mejorar los resultados del modelo.
Cargamos nuestra librería e iniciamos el clúster.

36
#Cargamos la librería.
library(doParallel)
#Iniciamos el clúster.
clu <- makeCluster(detectCores())
registerDoParallel(clu)

Realizamos nuestra búsqueda de los parámetros del modelo.

control <- trainControl(method = "cv", #Método: Validación cruzada.


number = 3, #Número de conjuntos por validación cruzada.
allowParallel = T) #Indica que se paralelice.
xgb_model2 <- train(x = x_train,
y = labels_train,
metric = "RMSE",
trControl = control,
tuneGrid = xgbGrid,
method = "xgbTree")
xgb_model2

## eXtreme Gradient Boosting


##
## No pre-processing
## Resampling: Cross-Validated (3 fold)
## Summary of sample sizes: 1563, 1562, 1563
## Resampling results across tuning parameters:
##
## eta colsample_bytree RMSE Rsquared MAE
## 0.02 0.5 26226.61 0.8962580 14891.37
## 0.02 0.9 26937.69 0.8905388 15293.50
## 0.03 0.5 26184.86 0.8962253 14892.33
## 0.03 0.9 26903.78 0.8911409 15086.48
##
## Tuning parameter 'nrounds' was held constant at a value of 971
##
## Tuning parameter 'min_child_weight' was held constant at a value of
## 1
## Tuning parameter 'subsample' was held constant at a value of 1
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were nrounds = 971, max_depth = 4,
## eta = 0.03, gamma = 0, colsample_bytree = 0.5, min_child_weight = 1
## and subsample = 1.

Detenemos el clúster antes de continuar.

stopCluster(clu)

Obtenemos el resultado de cada uno de los folds de validación cruzada.

xgb_model2$resample

## RMSE Rsquared MAE Resample

37
## 1 33720.33 0.8363898 16163.42 Fold1
## 2 21822.02 0.9264476 14331.93 Fold2
## 3 23012.22 0.9258386 14181.63 Fold3

Vemos que hay grandes diferencias entre los distintos pliegues, tanto en el RMSE y R2 , por lo que deberíamos
repetir nuestra búsqueda de hiperparámetros con mayor número de folds de validación cruzada para tener
un resultado más fiable.
Visualizamos como varía el RMSE en función de los parámetros que le hemos indicado.

plot(xgb_model2)

Subsample Ratio of Columns


0.5 0.9
RMSE (Cross−Validation)

26800

26600

26400

26200

0.020 0.022 0.024 0.026 0.028 0.030

Shrinkage

Realizamos gráfico con las 10 variables más importantes para el modelo.

imporXGB2 <- varImp(xgb_model2)


plot(imporXGB2, top = 10, main = "Importancia de las Variables - XGBoost")

38
Importancia de las Variables − XGBoost

Gr_Liv_Area

Garage_Cars

Year_Built

Total_Bsmt_SF

Exter_QualTypical

Garage_Area

First_Flr_SF

Second_Flr_SF

Year_Remod_Add

Fireplaces

20 40 60 80 100

Importance

Por último, vamos a predecir sobre nuestros datos de validación.

predict_xgb2 <- predict(xgb_model2, x_test)


(RMSExgb2 <- RMSE(pred = predict_xgb2,obs = labels_test))

## [1] 20543.65

Hemos pasado de un error en nuestro conjunto de validación de 21874.7597173 a 20543.654134 con los
parámetros óptimos. Aún queda margen de mejora, porque hemos entrenado muy pocos modelos.

39

También podría gustarte