10.3 Boosting

Es una familia de algoritmos de machine learning cuya idea es la de utilizar métodos de aprendizaje débiles (weak learners) para crear un método de aprendizaje fuerte con alto poder predictivo. Es uno de los algoritmos de aprendizaje que mayor impacto han tenido en los últimos 20 años. Robert E. Schapire y Yoav Freund recibieron el premio Gödel en 2003 por su trabajo sobre boosting. La mayoría de los ganadores recientes en Kaggle, utilizan boosting. Buscadores como Yahoo utilizan versiones propias de algoritmos de boosting.

El algoritmo sería

  • Inicializar: \(\omega_1^{(i)}=\frac{1}{n} \text{ con } i = 1, \ldots , n\) -Repetir \(t = 1, \ldots, T\):
    • Seleccionar un clasificador \(h_t(x_i)\) para los datos de entrenamiento usando \(\omega_t^{(i)}\)
    • Calcular \(\alpha_t\)
    • Calcular \(\omega_{t+1}^{(i)} \text{ para } i = 1, \ldots, n\)
  • Modelo final: \[H(x) = sign\left(\sum_{t=1}^T\alpha_th_t(x)\right)\]

Lo descrito anteriormente se denomina AdaBoost y se aplica solamente para problema de tipo binario. Para poder aplicarlo a cualquier tipo de problema, tenemos el gradient boosting, donde se utiliza el algoritmo del descenso del gradiente:

El algoritmo gradient boosting queda de la siguiente forma

  • Inicializar: \(H_0 \equiv 0\)
  • Repetir para \(t = 1, \ldots, T\):
    • Calcular para \(i = 1, \ldots, n\) \[r_{it}=-\left[ \frac{\partial L(y_i, H(x_i))}{\partial H(x_i)}\right]_{H=H_{t-1}}\]
    • Ajustar \(h_t\) a \(r_{it}\)
    • Elegir \(\alpha_t >0\) tal que \[\alpha_t = \underset{\alpha_t}{\arg\min} \sum_{i= 1}^n L(x_i, H_{t-1}(x_i)+\alpha_th_t(x_i))\]

10.3.1 Optimización de parámetros

Como con cualquier otro modelo, lleva asociados una serie de parámetros que necesitan ser ajustados para encontrar el mejor modelo. Dos formas básicas de hacerlo son

  • Hacer un barrido con un número elevado de combinaciones.
  • Hacer una búsqueda orientada de los metaparámetros óptimos.

Paso 1. Modelo inicial.

Hacer un primer lanzamiento dejando las opciones por defecto o elegir una combinación inicial.

Por ejemplo,

  • profundidad = 5.
  • submuestra = 0.8.
  • shrinkage = 0.1.

Paso 2. Profundidad del árbol

Una vez que tenemos un modelo base, realizamos pruebas con distintas profundidades de árbol.

Una buena idea suele ser elegir profundidades entre 2 y 10.

Paso 3. Submuestra (y muestra de columnas)

El siguiente paso es elegir un porcentaje de observaciones que se utilizarán, aleatoriamente, en cada iteración.

Se suelen elegir valores 0.6, 0.7, 0.8, 0.9.

A veces, si el volumen de datos es muy grande, se pueden elegir submuestras por debajo de 0.5 sin que se vea afectado el poder predictivo.

En caso de que el software lo permita, se puede probar a la vez el muestreo sobre columnas (idea de random forest)

Paso 4. Shrinkage

Por último, nos ocupamos del shrinkage, probablemente el valor que más puede ayudar a tener un mejor modelo.

En función de la estructura de los datos, podemos hacer un barrido inicial de, por ejemplo, 0.1, 0.01 y 0.001. En este caso es importante cambiar el número de iteraciones a un valor alto y elegir el número óptimo de estas mediante parada temprana.

10.3.2 En R

library(xgboost)

dtrain <- xgb.DMatrix(agaricus.train$data, label = agaricus.train$label)
dtest <- xgb.DMatrix(agaricus.test$data, label = agaricus.test$label)
watchlist <- list(eval = dtest, train = dtrain)

xgb_grid = expand.grid(
  eta                 = c(0.1, 0.05, 0.01, 0.001),
  max_depth           = c(3, 6, 8, 10),
  colsample_bytree    = 1,
  subsample  = c(0.6, 0.75, 1)
)


modelos.xgb <- data.frame()
tiempo.ini <- Sys.time()
for (model in 1:nrow(xgb_grid)){
  set.seed(1234)
  boosting <- xgb.train(data                = dtrain,
                        nrounds             = 10,
                        objective           = "binary:logistic", 
                        booster             = "gbtree",
                        eval_metric         = "auc",
                        params              = as.list(xgb_grid[model,]),
                        watchlist           = watchlist,
                        early_stopping_rounds    = 3
  )
  
  modelos.xgb <- rbind(modelos.xgb, data.frame( xgb_grid[model,],
                                                iteracion = boosting$best_iteration,
                                                ntree = boosting$best_ntreelimit,
                                                error = boosting$best_score))
  
  print(paste('modelo', model, ' de ', nrow(xgb_grid)))
  print(max(modelos.xgb$error))
  print(paste('Tiempo ',Sys.time() - tiempo.ini))
}

pred <- predict(boosting, agaricus.test$data, ntreelimit = 8)