Gradient Boosting y XGBoost
Árboles pequeños que se corrigen unos a otros siguiendo el gradiente de la pérdida: el algoritmo detrás de XGBoost y LightGBM, implementado desde cero y puesto a prueba con resultados reales.
01Qué es
El gradient boosting construye un modelo sumando muchos árboles de decisión pequeños, uno detrás de otro. Cada árbol nuevo se entrena para corregir los errores que todavía comete la suma de los anteriores. Jerome Friedman lo formuló en 2001 como un descenso de gradiente en el espacio de funciones.
XGBoost, LightGBM, CatBoost y el HistGradientBoosting de scikit-learn son implementaciones de esta idea con optimizaciones de velocidad y regularización. Con datos tabulares, es la familia de modelos que más suele ganar.
02Intuición
Piensa en un golfista: el primer golpe deja la bola lejos del hoyo, y cada golpe siguiente se da desde donde quedó, apuntando a lo que falta. El primer «golpe» es predecir la media; cada árbol siguiente apunta al residuo que queda.
La diferencia con Random Forest es de fondo. El bosque entrena árboles grandes e independientes y los promedia para reducir la varianza. El boosting entrena árboles pequeños (poco profundos, con mucho sesgo) en serie para reducir el sesgo. Por eso cada árbol aporta solo una fracción de su predicción, la tasa de aprendizaje: pasos pequeños para no pasarse.
03Fundamento matemático
Se busca una función F que minimice una pérdida L. En vez de mover pesos, se mueve la propia función, sumándole en cada paso algo que apunte en contra del gradiente:
Esos son los pseudo-residuos. Se ajusta un árbol a ellos y se avanza un paso de tamaño η:
Con la pérdida cuadrática , el gradiente negativo es exactamente : el residuo de toda la vida. Con otras pérdidas (log loss para clasificación, Huber para robustez) cambia solo esta fórmula.
XGBoost añade un término de regularización a cada árbol y usa también la segunda derivada (aproximación de Newton):
donde T es el número de hojas, el valor de cada hoja, y la primera y segunda derivada de la pérdida.
04Algoritmo paso a paso
- Empieza prediciendo una constante: la media (regresión) o el log-odds de la clase positiva (clasificación).
- Calcula, para cada ejemplo, el gradiente negativo de la pérdida respecto a la predicción actual.
- Entrena un árbol poco profundo (2 a 6 niveles) para predecir esos pseudo-residuos.
- Suma a la predicción la salida de ese árbol multiplicada por la tasa de aprendizaje.
- Repite hasta el número de árboles fijado o hasta que el error de validación deje de bajar.
05Pseudocódigo
función GRADIENT_BOOSTING(X, y, M, η, profundidad):
F ← media(y)
árboles ← []
para m ← 1 hasta M:
r ← −∂L(y, F)/∂F # con pérdida cuadrática: r = y − F
h ← ÁRBOL_REGRESIÓN(X, r, profundidad)
F ← F + η · h(X)
añadir h a árboles
devolver media(y), árboles
función PREDECIR(x):
devolver F₀ + η · Σ h(x) para h en árboles06Implementación desde cero en Python
import numpy as np
from sklearn.tree import DecisionTreeRegressor
class GradientBoosting:
def __init__(self, n_estimators=100, learning_rate=0.1, max_depth=2):
self.n, self.lr, self.depth = n_estimators, learning_rate, max_depth
def fit(self, X, y):
self.f0 = y.mean() # modelo inicial: la media
pred = np.full(len(y), self.f0)
self.arboles = []
for _ in range(self.n):
residuo = y - pred # = −gradiente de ½(y − f)²
arbol = DecisionTreeRegressor(max_depth=self.depth).fit(X, residuo)
pred += self.lr * arbol.predict(X) # un paso pequeño en esa dirección
self.arboles.append(arbol)
return self
def predict(self, X):
return self.f0 + self.lr * sum(a.predict(X) for a in self.arboles)Usa los árboles de regresión de scikit-learn como pieza base, pero el boosting en sí son esas 15 líneas. En el problema no lineal de la sección de evaluación obtiene un R² de 0,929, idéntico al de GradientBoostingRegressor con los mismos parámetros.
07Implementación con Scikit-learn y XGBoost
from sklearn.ensemble import GradientBoostingRegressor, HistGradientBoostingRegressor
import xgboost as xgb
gbr = GradientBoostingRegressor(n_estimators=300, learning_rate=0.05, max_depth=2, random_state=42)
hgb = HistGradientBoostingRegressor(max_iter=300, learning_rate=0.05, max_depth=3) # rápido con datos grandes
xgbm = xgb.XGBRegressor(n_estimators=300, learning_rate=0.05, max_depth=2, subsample=0.8, reg_lambda=1.0)
for m in (gbr, hgb, xgbm):
m.fit(X_tr, y_tr)HistGradientBoosting* agrupa cada variable en hasta 255 intervalos antes de buscar cortes, lo que lo hace muchísimo más rápido con datos grandes; admite valores faltantes y variables categóricas de forma nativa, como LightGBM. XGBoost ofrece además entrenamiento en GPU y una regularización más fina.
08Datasets de ejemplo
Se usan dos problemas de regresión para ver cuándo compensa el boosting y cuándo no:
- Diabetes (442 pacientes, 10 variables), el mismo del artículo de regresión lineal: pocos datos y una relación casi lineal.
- Friedman #1, un problema sintético clásico (Friedman, 1991) con interacciones y curvas, 2.000 ejemplos y 10 variables de las que solo 5 influyen:
from sklearn.datasets import make_friedman1
# y = 10·sin(π·x₁·x₂) + 20·(x₃ − 0,5)² + 10·x₄ + 5·x₅ + ruido (x₆…x₁₀ no influyen)
X, y = make_friedman1(n_samples=2000, noise=1.0, random_state=42)09Entrenamiento
Más árboles siempre bajan el error de entrenamiento, pero no el de test. Con 300 árboles y η = 0,05 en Diabetes:
La solución estándar es el early stopping: separar una parte de validación y parar cuando deja de mejorar.
m = GradientBoostingRegressor(n_estimators=2000, learning_rate=0.05, max_depth=2,
validation_fraction=0.2, n_iter_no_change=20, random_state=42)
m.fit(X_tr, y_tr)
print(m.n_estimators_) # 170: se detiene solo cuando la validación deja de mejorar10Evaluación
Problema no lineal (Friedman #1, 500 test)
| Modelo | RMSE | R² | Tiempo de entrenamiento |
|---|---|---|---|
| Regresión lineal | 2,475 | 0,752 | < 0,01 s |
| Random Forest (300 árboles) | 1,855 | 0,861 | 1,8 s |
| Boosting desde cero (500, η = 0,05, prof. 3) | 1,323 | 0,929 | 2,6 s |
| GradientBoostingRegressor (mismos parámetros) | 1,324 | 0,929 | 2,5 s |
| XGBoost (+ subsample 0,8) | 1,259 | 0,936 | 0,27 s |
Problema casi lineal y con pocos datos (Diabetes, validación cruzada de 5)
| Modelo | R² medio | Desviación |
|---|---|---|
| Regresión lineal | 0,478 | 0,085 |
| XGBoost | 0,441 | 0,084 |
| GradientBoostingRegressor | 0,436 | 0,096 |
| Random Forest | 0,427 | 0,095 |
Las dos tablas cuentan la historia completa. Cuando hay interacciones y curvas, el boosting reduce el error a la mitad respecto al modelo lineal y supera al bosque. Cuando los datos son pocos y la relación es casi lineal, la regresión lineal gana. No existe un algoritmo mejor en todos los problemas; por eso se valida siempre contra una línea base.
11Métricas
Las mismas que en cualquier regresión (RMSE, MAE, R², ver el artículo de regresión lineal) o clasificación (log loss, ROC-AUC). La diferencia práctica es que el boosting optimiza directamente la pérdida que elijas, así que conviene que la pérdida de entrenamiento coincida con la métrica que te importa: loss="absolute_error" si mides MAE, loss="huber" si hay valores atípicos.
12Visualización
13Ventajas
- Suele ser el modelo más preciso en datos tabulares.
- Captura no linealidades e interacciones sin ingeniería de variables.
- Flexible: cualquier pérdida diferenciable.
- Las implementaciones modernas manejan valores faltantes y categóricas, y escalan a millones de filas.
- No necesita escalar las variables.
14Limitaciones
- Más hiperparámetros que un Random Forest y más sensibles; sin early stopping sobreajusta.
- Entrenamiento secuencial: cada árbol depende del anterior (se paraleliza dentro de cada árbol, no entre árboles).
- Como todos los árboles, no extrapola fuera del rango de entrenamiento.
- Con pocos datos o relaciones lineales puede ser peor que un modelo lineal, como se ha visto.
15Hiperparámetros
| Parámetro | scikit-learn (GradientBoosting) | XGBoost 3.2 | Efecto |
|---|---|---|---|
n_estimators | 100 | 100 | Número de árboles. Fíjalo alto y usa early stopping. |
learning_rate | 0.1 | 0.3 | Tamaño de cada paso. Más bajo necesita más árboles pero generaliza mejor. |
max_depth | 3 | 6 | Profundidad de cada árbol; controla qué interacciones puede capturar. |
subsample | 1.0 | 1.0 | Fracción de filas por árbol (stochastic boosting). 0,5–0,8 reduce varianza. |
reg_lambda / l2_regularization | — / 0.0 (Hist) | 1.0 | Penalización L2 de los valores de las hojas. |
min_samples_leaf / min_child_weight | 1 | 1 | Tamaño mínimo de hoja; subirlo suaviza. |
n_iter_no_change / early_stopping_rounds | None | None | Activa el early stopping. |
16Casos de uso
- Competiciones de Kaggle con datos tabulares.
- Riesgo de crédito, fraude y scoring.
- Ranking en buscadores y recomendadores (LambdaMART).
- Previsión de demanda con variables de calendario y retardos.
17Aplicaciones industriales
- Mantenimiento predictivo: probabilidad de fallo a partir de sensores. Ver el proyecto.
- Energía: previsión de producción renovable y demanda.
- Telecomunicaciones: predicción de bajas y de incidencias en red.
- Logística: tiempos de entrega y roturas de stock.
18Ejercicio práctico
Comprueba cómo el early stopping compensa una tasa de aprendizaje demasiado alta: ¿cuántos árboles elige para cada η y cuál es el error final?
# Ejercicio: sobreajuste con learning_rate alto y cómo lo arregla el early stopping.
for lr in (0.05, 0.3, 1.0):
m = GradientBoostingRegressor(n_estimators=2000, learning_rate=lr, max_depth=2,
validation_fraction=0.2, n_iter_no_change=20, random_state=42)
# ... entrena, imprime m.n_estimators_ y el RMSE en test. ¿Cuántos árboles usa cada uno?19Código completo
Todos los experimentos, incluidos los dos datasets y las comparaciones:
20Referencias y papers
- J. H. Friedman (2001). «Greedy Function Approximation: A Gradient Boosting Machine». The Annals of Statistics, 29(5), 1189–1232.
- Y. Freund, R. E. Schapire (1997). «A Decision-Theoretic Generalization of On-Line Learning and an Application to Boosting». Journal of Computer and System Sciences, 55(1), 119–139.
- J. H. Friedman (1991). «Multivariate Adaptive Regression Splines». The Annals of Statistics, 19(1), 1–67. (Origen del problema Friedman #1).
- T. Chen, C. Guestrin (2016). «XGBoost: A Scalable Tree Boosting System». Proceedings of KDD 2016. Ficha en la biblioteca.
- G. Ke et al. (2017). «LightGBM: A Highly Efficient Gradient Boosting Decision Tree». NeurIPS 2017.