En la lección anterior dejamos presentado el boosting como la familia secuencial de los ensembles: modelos que se entrenan uno tras otro, cada cual corrigiendo los errores del anterior. Esta lección la dedicamos por completo a su representante estrella, el gradient boosting, probablemente el algoritmo más ganador de la última década en datos tabulares: domina las competiciones de Kaggle y resuelve en producción problemas exactamente como el churn y la demanda de MercaFresh. Vas a entender su mecánica con un ejemplo numérico a mano, aprenderás a manejar sus dos mandos principales (learning rate y número de estimadores), conocerás las implementaciones que importan — sklearn, HistGradientBoosting, XGBoost, LightGBM — y lo enfrentarás al Random Forest de la lección anterior.
Contenido
- La idea: aprender de los residuos
- Ejemplo numérico a mano: 3 iteraciones
- Learning rate y n_estimators: el equilibrio
- Gradient boosting en scikit-learn
- HistGradientBoosting: la versión moderna
- XGBoost y LightGBM: los dominadores del tablero
- Boosting sobre el churn de MercaFresh: duelo con Random Forest
- El talón de Aquiles: overfitting y sus frenos
- Interpretación: feature importance y SHAP
La idea: aprender de los residuos
Random Forest entrena árboles independientes y promedia. Gradient boosting hace lo contrario: entrena árboles pequeños y débiles en cadena, donde cada uno tiene una única misión: predecir el error que dejaron los anteriores.
En regresión, con error cuadrático, el proceso es muy intuitivo:
- Empieza con una predicción trivial: la media de
y(nuestro viejo conocidoDummyRegressorde 06-02). - Calcula los residuos:
residuo = y_real - predicción_actual. - Entrena un árbol pequeño que prediga esos residuos a partir de las features.
- Actualiza:
predicción_nueva = predicción_actual + η · predicción_del_árbol, donde η (eta) es el learning rate, un factor que encoge cada corrección. - Vuelve al paso 2, tantas veces como diga
n_estimators.
El nombre "gradient" viene de que los residuos son, matemáticamente, el gradiente (con signo cambiado) de la función de pérdida cuadrática: cada árbol da un paso de descenso de gradiente — el mismo que usamos en 04-01 para ajustar la regresión lineal — pero en el espacio de las predicciones en lugar del espacio de los coeficientes. Con otras pérdidas (p. ej. logística para clasificación) se sigue el mismo esquema sustituyendo el residuo por el "pseudo-residuo" correspondiente; por eso el método generaliza a clasificación sin cambiar de filosofía.
flowchart LR
M0[Predicción inicial: media] --> R1[Residuos 1]
R1 --> A1[Árbol 1 aprende residuos]
A1 --> M1[Predicción += eta x árbol 1]
M1 --> R2[Residuos 2 más pequeños]
R2 --> A2[Árbol 2]
A2 --> M2[Predicción += eta x árbol 2]
M2 --> R3[Residuos 3 aún menores]
Ejemplo numérico a mano: 3 iteraciones
Tomemos 4 clientes de MercaFresh y su gasto mensual (en €). Para poder seguir las cuentas usaremos learning rate η = 1 (sin encogimiento) y "árboles" simplificados que predicen la media del residuo de su grupo (clientes de frecuencia alta vs. baja):
| Cliente | Frecuencia | Gasto real (y) |
|---|---|---|
| Ana | alta | 120 |
| Bruno | alta | 100 |
| Carla | baja | 40 |
| David | baja | 60 |
Iteración 0. Predicción inicial = media global = (120+100+40+60)/4 = 80 para todos.
| Cliente | Predicción | Residuo (y − pred) |
|---|---|---|
| Ana | 80 | +40 |
| Bruno | 80 | +20 |
| Carla | 80 | −40 |
| David | 80 | −20 |
Iteración 1. El árbol 1 divide por frecuencia y predice la media del residuo de cada grupo: +30 para frecuencia alta, −30 para baja. Actualizamos:
| Cliente | Predicción nueva | Residuo nuevo |
|---|---|---|
| Ana | 80 + 30 = 110 | +10 |
| Bruno | 80 + 30 = 110 | −10 |
| Carla | 80 − 30 = 50 | −10 |
| David | 80 − 30 = 50 | +10 |
El error se ha reducido drásticamente: de residuos ±40/±20 a ±10.
Iteración 2. Dentro de cada grupo los residuos ya suman cero, así que un árbol que solo viera la frecuencia predice 0; supongamos que el árbol 2 encuentra otra feature (p. ej. antigüedad) que separa a Ana de Bruno y a David de Carla, y predice +10/−10 según corresponda:
| Cliente | Predicción final | Residuo |
|---|---|---|
| Ana | 110 + 10 = 120 | 0 |
| Bruno | 110 − 10 = 100 | 0 |
| Carla | 50 − 10 = 40 | 0 |
| David | 50 + 10 = 60 | 0 |
Tres pasos (predicción base + 2 árboles) y el train está clavado. Y aquí está la moraleja doble: la suma de correctores débiles alcanza una precisión que ninguno lograría solo… pero también acabamos de ver lo rápido que este método puede memorizar el conjunto de entrenamiento — con residuo cero en train, todo lo que quede es ajuste al ruido. En la práctica, η = 1 casi nunca se usa: encoger cada paso es el primer freno contra el overfitting.
Learning rate y n_estimators: el equilibrio
Los dos mandos principales están acoplados:
learning_rate(η): cuánto aporta cada árbol. Valores típicos: 0.01–0.3.n_estimators: cuántos árboles se encadenan.
La relación es de compensación: si reduces η a la mitad, necesitas aproximadamente el doble de árboles para llegar al mismo punto. ¿Por qué molestarse entonces en bajar η? Porque muchos pasos pequeños generalizan mejor que pocos pasos grandes: cada corrección suave deja margen a los árboles siguientes para enmendar el rumbo, mientras que un paso grande puede perseguir ruido irreversiblemente.
| Configuración | Comportamiento |
|---|---|
| η alto (0.3) + pocos árboles | Rápido de entrenar; riesgo de overfitting y de pasos bruscos |
| η bajo (0.01–0.05) + muchos árboles | Más lento; mejor generalización; necesita early stopping para no pasarse |
| η alto + muchos árboles | Receta casi segura de overfitting |
A diferencia del Random Forest — donde más árboles solo estabilizaba —, en boosting n_estimators sí es un mando de complejidad: cada árbol adicional sigue reduciendo el error de train, y a partir de cierto punto empieza a subir el de validación. Es exactamente la silueta de las curvas de 06-05, y su antídoto natural será el early stopping que veremos enseguida.
Gradient boosting en scikit-learn
La implementación clásica: GradientBoostingRegressor y GradientBoostingClassifier.
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(
n_estimators=300, # árboles encadenados
learning_rate=0.05, # aporte de cada árbol
max_depth=3, # árboles deliberadamente pequeños (débiles)
subsample=0.8, # cada árbol ve el 80% de las filas (toque de bagging)
random_state=42,
)Fíjate en max_depth=3: en boosting los árboles son enanos a propósito (1–4 niveles). El aprendiz débil es una decisión de diseño: la potencia viene de la secuencia, no del individuo. subsample < 1 añade aleatoriedad al estilo bagging ("stochastic gradient boosting"), que suele mejorar la generalización.
HistGradientBoosting: la versión moderna
Desde scikit-learn 0.21 existe una reimplementación mucho más rápida: HistGradientBoostingClassifier/Regressor. Su truco es discretizar cada feature en un histograma de a lo sumo 255 contenedores, de modo que buscar la mejor división ya no requiere ordenar valores continuos: se recorren los contenedores. En datasets de decenas de miles de filas es órdenes de magnitud más rápida, y además:
- Maneja valores faltantes de forma nativa (aprende hacia qué rama enviar los NaN, sin imputación previa — aunque lo aprendido en 03-02 sigue siendo necesario para los demás modelos).
- Incorpora early stopping integrado (
early_stopping=True) con una fracción de validación interna. - Soporta features categóricas nativas (
categorical_features).
from sklearn.ensemble import HistGradientBoostingClassifier
hgb = HistGradientBoostingClassifier(
learning_rate=0.05,
max_iter=1000, # equivalente a n_estimators
early_stopping=True, # para de añadir árboles cuando validación deja de mejorar
validation_fraction=0.15,
n_iter_no_change=20, # paciencia: 20 iteraciones sin mejora
random_state=42,
)Regla práctica actual: para gradient boosting dentro del ecosistema sklearn, usa la versión Hist por defecto; la clásica queda para datasets pequeños o por compatibilidad.
XGBoost y LightGBM: los dominadores del tablero
Fuera de sklearn viven las dos bibliotecas que popularizaron el gradient boosting moderno (se instalan aparte: pip install xgboost lightgbm):
| XGBoost (2014) | LightGBM (2017) | |
|---|---|---|
| Aportación clave | Regularización L1/L2 en los árboles, manejo de faltantes, paralelización eficiente | Crecimiento por hojas (leaf-wise) e histogramas: aún más rápido y con menos memoria |
| Fama | El algoritmo que "ganaba todos los Kaggle" a mediados de los 2010 | Estándar actual en industria para tabular grande |
| API | Propia + wrapper compatible con sklearn (XGBClassifier) |
Ídem (LGBMClassifier) |
¿Por qué esta familia domina en datos tabulares (tablas de clientes, transacciones, sensores…)? Porque los árboles capturan sin esfuerzo interacciones y no-linealidades, ignoran la escala de las features, digieren mezclas de variables numéricas y categóricas, y el boosting exprime hasta la última gota de señal — todo con tiempos de entrenamiento de segundos o minutos. En este terreno suelen batir incluso a las redes profundas, como discutiremos con honestidad en 07-04.
Ambas bibliotecas ofrecen early stopping contra un conjunto de validación explícito, la forma profesional de fijar n_estimators:
# Ejemplo con la API sklearn de XGBoost
from xgboost import XGBClassifier
xgb = XGBClassifier(n_estimators=2000, learning_rate=0.05, max_depth=3,
early_stopping_rounds=50, eval_metric="logloss")
xgb.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
print(xgb.best_iteration) # cuántos árboles se usaron realmenteSe pide un n_estimators deliberadamente alto y se deja que la validación decida dónde parar: si tras 50 rondas la métrica no mejora, se detiene y se queda con la mejor iteración. Observa que reaparece la disciplina de 06-01: el conjunto de validación usado para parar no puede ser el test final.
Boosting sobre el churn de MercaFresh: duelo con Random Forest
Enfrentemos al campeón de la lección anterior con el nuevo aspirante, sobre el mismo dataset de churn con features RFM y la misma validación cruzada estratificada de 06-03.
import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.ensemble import (RandomForestClassifier,
HistGradientBoostingClassifier)
# Dataset de churn de MercaFresh (idéntico al de 07-02)
rng = np.random.default_rng(42)
n = 1000
recencia = rng.gamma(2, 15, n)
frecuencia = rng.poisson(5, n) + 1
monetario = rng.gamma(3, 40, n)
antiguedad = rng.uniform(1, 60, n)
incidencias = rng.poisson(0.5, n)
logits = 0.05 * recencia - 0.4 * frecuencia + 0.6 * incidencias - 0.01 * antiguedad - 0.5
y = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)
X = pd.DataFrame({"recencia": recencia, "frecuencia": frecuencia,
"monetario": monetario, "antiguedad": antiguedad,
"incidencias": incidencias})
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
modelos = {
"Random Forest (07-02)": RandomForestClassifier(
n_estimators=300, random_state=42, n_jobs=-1),
"HistGradientBoosting": HistGradientBoostingClassifier(
learning_rate=0.05, max_iter=500, max_depth=3,
early_stopping=True, random_state=42),
}
for nombre, modelo in modelos.items():
f1 = cross_val_score(modelo, X, y, cv=cv, scoring="f1")
print(f"{nombre:24s} F1: {f1.mean():.3f} ± {f1.std():.3f}")En datasets pequeños y con señal sencilla como este, ambos suelen quedar muy parejos (a veces gana el bosque). El patrón general en la práctica es:
- Random Forest: excelente resultado con cero ajuste; difícil de estropear. La baseline fuerte.
- Gradient boosting: techo más alto — con sus hiperparámetros bien elegidos suele arañar unos puntos más de métrica, especialmente en datasets grandes con interacciones complejas — pero exige ese ajuste (que automatizaremos en 07-05) y vigilancia del overfitting.
Para MercaFresh la lectura de negocio es concreta: si el bosque da F1 0.82 sin esfuerzo y el boosting bien afinado da 0.85, esos tres puntos son decenas de clientes recuperables cada mes correctamente priorizados por la campaña de retención — normalmente merece el esfuerzo.
El talón de Aquiles: overfitting y sus frenos
El boosting persigue residuos, y los residuos acaban siendo ruido: si lo dejas correr, memoriza el train (lo vimos en el ejemplo a mano, que llegó a residuo cero). Es el alumno aventajado de las patologías de 06-05, y por eso viene equipado con más frenos que ningún otro algoritmo:
| Freno | Parámetro típico | Efecto |
|---|---|---|
| Learning rate bajo | learning_rate=0.01–0.1 |
Pasos pequeños, correcciones suaves |
| Early stopping | early_stopping / early_stopping_rounds |
Corta la secuencia cuando validación deja de mejorar |
| Árboles pequeños | max_depth=2–4 (o num_leaves bajo en LightGBM) |
Limita la complejidad de cada corrector |
| Submuestreo de filas | subsample<1 |
Aleatoriedad estilo bagging, descorrelaciona errores |
| Submuestreo de columnas | colsample_bytree<1 (XGBoost/LightGBM) |
Feature bagging, como en Random Forest |
| Regularización en hojas | reg_alpha, reg_lambda (XGBoost/LightGBM) |
El L1/L2 de 07-01 aplicado a los valores de las hojas |
Fíjate en la última fila: la regularización de 07-01 reaparece dentro de los árboles. Las técnicas de este módulo no son compartimentos estancos, son piezas combinables. La receta defensiva estándar: learning rate bajo + early stopping + árboles poco profundos, y validar siempre con la CV del módulo 6.
Interpretación: feature importance y SHAP
Como el Random Forest, los modelos de boosting ofrecen feature_importances_ (en sklearn clásico y en XGBoost/LightGBM) con las mismas virtudes y los mismos sesgos que discutimos en 07-02: útiles como ranking global, peligrosas con features correlacionadas.
Para explicaciones serias, el estándar actual es SHAP (SHapley Additive exPlanations, biblioteca shap): asigna a cada feature de cada predicción individual una contribución con signo — "a este cliente el modelo le da un 78% de churn: la recencia de 45 días aporta +0.20, sus 2 incidencias +0.12 y su antigüedad de 5 años −0.08". Eso es oro para el equipo de retención de MercaFresh, que necesita saber no solo quién se va sino por qué. No lo desarrollamos aquí — es materia de interpretabilidad avanzada y reaparecerá en el contexto ético de 08-04 —, pero conviene que sepas que existe y que se lleva especialmente bien con los modelos de árboles.
Errores Comunes y Consejos
- Subir
n_estimators"porque más es mejor". Cierto en Random Forest, falso en boosting: aquí cada árbol extra añade complejidad. Fija un tope alto y deja que el early stopping decida. - Ajustar el learning rate sin tocar el número de árboles. Van acoplados: si bajas η de 0.1 a 0.01, multiplica el presupuesto de árboles por ~10 o el modelo quedará infraentrenado.
- Usar árboles profundos como base.
max_depth=10en boosting suele ser una receta de sobreajuste; la fuerza está en la secuencia de correctores débiles, no en correctores fuertes. - Hacer early stopping contra el conjunto de test. El conjunto que decide cuándo parar participa en el entrenamiento a todos los efectos: es una fuga de las de 06-01. Usa una partición de validación o la validación interna del propio algoritmo, y reserva el test intacto.
- Escalar las features "por si acaso". No hace daño, pero es innecesario: como todos los métodos de árboles, el boosting es insensible a la escala. Ahorra ese paso del pipeline (y recuerda que sí es obligatorio para SVM, K-NN o los modelos regularizados de 07-01).
- Consejo: en proyectos reales, entrena primero un Random Forest (baseline fuerte, 07-02) y después un HistGradientBoosting/LightGBM con early stopping. Si el boosting no supera claramente al bosque, quédate con el bosque: menos hiperparámetros que mantener.
Ejercicios
- Boosting a mano, cuarta iteración. Repite el ejemplo numérico de la lección pero con learning rate η = 0.5: recalcula predicciones y residuos de las iteraciones 1 y 2 y añade una iteración 3 (asume que cada árbol sigue prediciendo la media del residuo de su grupo, alternando la división por frecuencia y la que separa dentro de cada grupo). ¿Se llega a residuo cero? ¿Qué ilustra esto sobre η?
- El acoplamiento η–n_estimators. Sobre el churn de MercaFresh, entrena
GradientBoostingClassifier(max_depth=3, subsample=0.8)con las combinaciones (η=0.3, 50 árboles), (η=0.1, 150), (η=0.03, 500) y (η=0.3, 500). Evalúa F1 con la CV estratificada. ¿Confirman los resultados la tabla de la sección 3? - Curva de early stopping. Con
train_test_split(estratificado) separa un 20% de validación del churn. EntrenaGradientBoostingClassifier(n_estimators=400, learning_rate=0.1, max_depth=3)y usastaged_predict_probapara calcular la log-loss en train y en validación tras cada árbol. Dibuja ambas curvas: ¿en qué iteración habría parado un early stopping con paciencia 20? ¿Qué forma de 06-05 reconoces?
Soluciones
- Con η = 0.5, la iteración 1 suma la mitad de la corrección: predicciones 95/95/65/65 y residuos +25/+5/−25/−5. La iteración 2 (corrección por grupos de frecuencia: media de residuos ±15, aplicada al 50%) deja predicciones 102.5/102.5/57.5/57.5 y residuos +17.5/−2.5/−17.5/+2.5; la iteración 3 sigue reduciendo pero no llega a cero. Ilustración: con η < 1 el modelo se aproxima asintóticamente al ajuste perfecto en train — nunca lo clava de golpe —, y eso da margen para detener la secuencia (early stopping) en el punto de mejor generalización antes de memorizar el ruido.
- Las tres primeras combinaciones (presupuesto equilibrado η·n_estimators ≈ 15) dan F1 similares, con ligera ventaja habitual para η bajo + muchos árboles y menor desviación entre pliegues. La cuarta (η=0.3 con 500 árboles, presupuesto 150) rinde peor en validación pese a ser la que menor error de train tiene: overfitting puro. Confirma la tabla: lo que importa es el producto equilibrado, y pasarse de presupuesto pasa factura.
- La log-loss de train desciende monótonamente (el boosting siempre puede seguir limando residuos). La de validación baja, toca mínimo — típicamente entre las iteraciones 80 y 200 con estos datos — y luego asciende suavemente: la silueta clásica de la curva de validación de 06-05, ahora con "número de árboles" en el eje x. El early stopping con paciencia 20 habría cortado unas 20 iteraciones después del mínimo, quedándose con el modelo de la mejor iteración. Este ejercicio es exactamente lo que
early_stopping=Trueautomatiza por dentro.
Conclusión
El gradient boosting materializa la promesa del boosting: una predicción inicial trivial más una cadena de árboles enanos, cada uno entrenado sobre los residuos del conjunto anterior, con el learning rate encogiendo cada paso — descenso de gradiente en el espacio de las predicciones. Has visto sus dos mandos acoplados (η y n_estimators), sus implementaciones por orden de modernidad (clásica de sklearn, HistGradientBoosting, XGBoost y LightGBM), su duelo con Random Forest sobre el churn de MercaFresh y, sobre todo, su carácter: el algoritmo más potente en datos tabulares es también el más propenso a memorizar, y por eso viaja rodeado de frenos — learning rate bajo, early stopping, árboles pequeños, submuestreo y la regularización de 07-01 aplicada a sus hojas. Con árboles y ensembles hemos llegado muy lejos partiendo de modelos sencillos; la siguiente lección explora la otra gran vía hacia la potencia: apilar capas de neuronas hasta construir redes profundas, el deep learning que la MLP de 04-07 dejó anunciado.
Curso de Machine Learning
Módulo 1: Introducción al Machine Learning
- ¿Qué es el Machine Learning?
- Historia y evolución del Machine Learning
- Tipos de Machine Learning
- Aplicaciones del Machine Learning
- El flujo de trabajo de un proyecto de Machine Learning
Módulo 2: Fundamentos de Estadística y Probabilidad
- Conceptos básicos de estadística
- Distribuciones de probabilidad
- Correlación y covarianza
- Inferencia estadística
- Teorema de Bayes
Módulo 3: Preprocesamiento de Datos
- Limpieza de datos
- Manejo de datos faltantes
- Transformación de datos
- Codificación de variables categóricas
- Normalización y estandarización
- Ingeniería de características
Módulo 4: Algoritmos de Machine Learning Supervisado
- Regresión lineal
- Regresión logística
- Árboles de decisión
- Máquinas de soporte vectorial (SVM)
- K-Vecinos más cercanos (K-NN)
- Naive Bayes
- Redes neuronales
Módulo 5: Algoritmos de Machine Learning No Supervisado
- Clustering: K-means
- Clustering jerárquico
- Análisis de componentes principales (PCA)
- Análisis de agrupamiento DBSCAN
- Visualización de datos con t-SNE y UMAP
Módulo 6: Evaluación y Validación de Modelos
- División de datos: entrenamiento, validación y prueba
- Métricas de evaluación
- Validación cruzada
- Curva ROC y AUC
- Overfitting y underfitting
Módulo 7: Técnicas Avanzadas y Optimización
- Regularización: Ridge, Lasso y Elastic Net
- Ensemble Learning
- Gradient Boosting
- Redes neuronales profundas (Deep Learning)
- Optimización de hiperparámetros
Módulo 8: Implementación y Despliegue de Modelos
- Frameworks y bibliotecas populares
- Implementación de modelos en producción
- Mantenimiento y monitoreo de modelos
- Consideraciones éticas y de privacidad
Módulo 9: Proyectos Prácticos
- Proyecto 1: Predicción de precios de viviendas
- Proyecto 2: Clasificación de imágenes
- Proyecto 3: Análisis de sentimientos en redes sociales
- Proyecto 4: Detección de fraudes
- Proyecto 5: Segmentación de clientes
