En la lección anterior mejoramos un modelo conteniéndolo con regularización. Esta lección toma el camino opuesto: mejorar combinando muchos modelos para que sus errores individuales se cancelen entre sí. Es una de las ideas más rentables de todo el machine learning: los métodos de ensemble (conjunto) dominan las competiciones sobre datos tabulares y son el estándar de facto en producción para problemas como el churn de MercaFresh. Aquí entenderás por qué combinar funciona, conocerás las tres grandes familias — bagging, boosting y stacking —, estudiarás Random Forest en detalle y lo enfrentarás al árbol de decisión solitario de 04-03 sobre nuestro dataset de churn, con la misma validación cruzada del módulo 6.

Contenido

  1. Por qué combinar modelos funciona
  2. Bagging: bootstrap + agregación
  3. Random Forest en detalle
  4. Random Forest sobre el churn de MercaFresh
  5. Voting classifiers: hard y soft
  6. Stacking: un modelo que aprende a combinar
  7. Boosting: la familia secuencial (presentación)
  8. Tabla comparativa de las tres familias
  9. El precio: interpretabilidad

Por qué combinar modelos funciona

Imagina que preguntas a 1.000 personas cuántas naranjas caben en un palé de MercaFresh. Cada estimación individual será mala, pero la media de todas suele ser sorprendentemente buena: los que se pasan compensan a los que se quedan cortos. Es la "sabiduría de las multitudes", y funciona bajo dos condiciones:

  1. Cada estimador es mejor que el azar (aunque sea por poco).
  2. Los errores son independientes (o al menos, poco correlacionados): no todos se equivocan en la misma dirección.

Con modelos pasa exactamente igual. Si tienes 100 clasificadores que aciertan el 70% de las veces y sus errores fueran independientes, la votación por mayoría acertaría mucho más del 70%: para que la mayoría falle, tendrían que equivocarse a la vez más de 50, algo improbable si cada uno falla en clientes distintos.

La conexión con 06-05 es directa: promediar modelos reduce la varianza. Un árbol de decisión profundo tiene bajo sesgo pero varianza altísima (cambia por completo con otra muestra de entrenamiento); si promediamos muchos árboles distintos, el componente aleatorio de cada uno se cancela y queda la señal común. La condición crítica — y la más difícil — es la descorrelación: cien copias del mismo modelo no aportan nada, igual que preguntar mil veces a la misma persona. Todo el ingenio de los métodos de ensemble está en fabricar diversidad:

Fuente de diversidad Técnica que la usa
Entrenar cada modelo con datos distintos (remuestreo) Bagging / Random Forest
Limitar las features que ve cada modelo Random Forest (feature bagging)
Usar algoritmos de familias distintas Voting, Stacking
Entrenar en secuencia, cada uno sobre los errores del anterior Boosting

Bagging: bootstrap + agregación

Bagging = Bootstrap AGGregatING. La receta:

  1. Bootstrap: genera B muestras del conjunto de entrenamiento, cada una del mismo tamaño que el original pero muestreada con reemplazo (un cliente puede aparecer varias veces; otros, ninguna). Ya usamos esta idea en inferencia estadística (02-04); aquí sirve para fabricar B "versiones alternativas" del dataset.
  2. Entrenamiento: entrena un modelo (típicamente un árbol profundo, sin podar) en cada muestra.
  3. Agregación: para predecir, en clasificación se vota; en regresión se promedia.

Cada muestra bootstrap deja fuera, en media, un 37% de las observaciones (la probabilidad de que un ejemplo concreto no salga en n extracciones con reemplazo es $(1-1/n)^n \approx e^{-1} \approx 0.37$). Ese 37% excluido tendrá un papel estelar enseguida: la evaluación out-of-bag.

flowchart TB
    D[Dataset de entrenamiento] --> B1[Muestra bootstrap 1]
    D --> B2[Muestra bootstrap 2]
    D --> B3[Muestra bootstrap B]
    B1 --> A1[Árbol 1]
    B2 --> A2[Árbol 2]
    B3 --> A3[Árbol B]
    A1 --> V[Votación / Promedio]
    A2 --> V
    A3 --> V
    V --> P[Predicción final]

Random Forest en detalle

Random Forest es bagging de árboles más un truco extra que multiplica la diversidad: el feature bagging. En cada división de cada árbol, en lugar de evaluar todas las features (como hacía el árbol de 04-03), solo se considera un subconjunto aleatorio (por defecto, $\sqrt{p}$ features en clasificación, controlado por max_features).

¿Por qué es tan importante? Sin él, si una feature es muy dominante — en nuestro churn, recencia lo es —, todos los árboles la elegirían para la primera división y acabarían pareciéndose demasiado: errores correlacionados, poca ganancia al promediar. Al vetar features al azar en cada nodo, se obliga a los árboles a explorar caminos distintos y sus errores se descorrelacionan.

Tres regalos adicionales de Random Forest:

  • OOB score (out-of-bag): cada árbol se evalúa con el 37% de observaciones que no vio en su bootstrap. Agregando esas predicciones se obtiene una estimación de rendimiento en datos no vistos gratis, sin partición de validación aparte. Es un complemento (no un sustituto) de la validación cruzada de 06-03.
  • Feature importance: cuánto reduce la impureza cada feature, promediado sobre todos los árboles y todas las divisiones. Un ranking de relevancia inmediato (con matices que veremos en Errores Comunes).
  • Robustez sin apenas ajuste: más árboles (n_estimators) nunca provoca overfitting por sí mismo — solo estabiliza el promedio; el coste es tiempo de cómputo. Los mandos que sí regulan la complejidad de cada árbol son los ya conocidos de 04-03 (max_depth, min_samples_leaf).

Random Forest sobre el churn de MercaFresh

Repitamos el experimento estrella del módulo 6: predecir el churn con las features RFM que construimos en el módulo 3, comparando el árbol solitario de 04-03 con el bosque, con la misma StratifiedKFold de 06-03.

import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

# --- Dataset de churn de MercaFresh (features RFM del módulo 3) ---
rng = np.random.default_rng(42)
n = 1000
recencia = rng.gamma(2, 15, n)            # días desde el último pedido
frecuencia = rng.poisson(5, n) + 1        # pedidos en el último trimestre
monetario = rng.gamma(3, 40, n)           # gasto trimestral en €
antiguedad = rng.uniform(1, 60, n)        # meses como cliente
incidencias = rng.poisson(0.5, n)         # reclamaciones

# Probabilidad de churn: sube con recencia e incidencias, baja con frecuencia
logits = 0.05 * recencia - 0.4 * frecuencia + 0.6 * incidencias - 0.01 * antiguedad - 0.5
churn = (rng.random(n) < 1 / (1 + np.exp(-logits))).astype(int)

X = pd.DataFrame({"recencia": recencia, "frecuencia": frecuencia,
                  "monetario": monetario, "antiguedad": antiguedad,
                  "incidencias": incidencias})
y = churn

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# --- Árbol solitario (04-03) vs Random Forest ---
arbol = DecisionTreeClassifier(max_depth=5, random_state=42)
bosque = RandomForestClassifier(n_estimators=300, oob_score=True,
                                random_state=42, n_jobs=-1)

for nombre, modelo in [("Árbol (04-03)", arbol), ("Random Forest", bosque)]:
    f1 = cross_val_score(modelo, X, y, cv=cv, scoring="f1")
    print(f"{nombre:15s} F1: {f1.mean():.3f} ± {f1.std():.3f}")

# OOB score y feature importance (entrenando sobre todo el conjunto)
bosque.fit(X, y)
print(f"\nOOB accuracy: {bosque.oob_score_:.3f}")
print(pd.Series(bosque.feature_importances_, index=X.columns)
        .sort_values(ascending=False).round(3))

Observaciones típicas del resultado:

  • El bosque mejora el F1 medio del árbol y, sobre todo, reduce la desviación entre pliegues: menos varianza, tal como prometía la teoría. No necesitamos afinar max_depth con curvas de validación como en 06-05: los árboles del bosque crecen profundos y el promedio absorbe su sobreajuste individual.
  • El oob_score_ da una cifra coherente con la CV sin haber gastado ni un pliegue.
  • feature_importances_ señala recencia, frecuencia e incidencias como las variables dominantes — coherente con lo que el equipo de retención de MercaFresh ya intuía y con cómo generamos los datos.

Nota práctica: los árboles no necesitan escalado (04-03), así que aquí el pipeline puede prescindir del StandardScaler; si el ensemble incluyera modelos sensibles a escala (SVM, K-NN, logística), el Pipeline del módulo 3 vuelve a ser obligatorio.

Voting classifiers: hard y soft

El bagging fabrica diversidad remuestreando datos; otra vía es combinar algoritmos de naturaleza distinta. En 06-03 organizamos un torneo entre los clasificadores del módulo 4; el VotingClassifier los convierte de rivales en equipo:

  • Hard voting: cada modelo emite su clase y gana la mayoría.
  • Soft voting: se promedian las probabilidades (predict_proba) y gana la clase con mayor media. Suele ser mejor porque incorpora la confianza de cada modelo, pero exige que todos los miembros produzcan probabilidades calibradas razonablemente.
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# Miembros heterogéneos: logística (04-02), K-NN (04-05), Naive Bayes (04-06)
voting = VotingClassifier(
    estimators=[
        ("logreg", Pipeline([("sc", StandardScaler()),
                             ("m", LogisticRegression(max_iter=1000))])),
        ("knn",    Pipeline([("sc", StandardScaler()),
                             ("m", KNeighborsClassifier(n_neighbors=15))])),
        ("nb",     GaussianNB()),
    ],
    voting="soft",
)

f1 = cross_val_score(voting, X, y, cv=cv, scoring="f1")
print(f"Voting (soft) F1: {f1.mean():.3f} ± {f1.std():.3f}")

Cada miembro sensible a escala lleva su propio pipeline con escalado: la disciplina anti-fugas de 06-01 se mantiene dentro del ensemble. El voting brilla cuando los miembros tienen rendimientos parecidos pero fallan en clientes distintos; si un modelo es claramente peor que el resto, puede lastrar la votación (mejor quitarlo o ponderarlo con weights).

Stacking: un modelo que aprende a combinar

La votación combina con una regla fija (mayoría o media). El stacking va un paso más allá: entrena un meta-modelo que aprende de los datos cómo combinar las predicciones de los modelos base. Por ejemplo, podría aprender que "cuando K-NN y la logística discrepan, la logística suele tener razón en clientes antiguos".

El detalle fino es que el meta-modelo debe entrenarse con predicciones out-of-fold (obtenidas por validación cruzada interna), no con predicciones sobre los mismos datos en que se entrenaron los modelos base — de lo contrario heredaría su sobreajuste, una fuga del tipo que aprendimos a temer en 06-01. StackingClassifier lo gestiona automáticamente:

from sklearn.ensemble import StackingClassifier

stacking = StackingClassifier(
    estimators=voting.estimators,               # los mismos modelos base
    final_estimator=LogisticRegression(),        # meta-modelo sencillo
    cv=5,                                        # CV interna para las meta-features
)
f1 = cross_val_score(stacking, X, y, cv=cv, scoring="f1")
print(f"Stacking F1: {f1.mean():.3f} ± {f1.std():.3f}")

El stacking es el arma final de las competiciones (a menudo con varias capas de meta-modelos), pero en proyectos reales su ganancia sobre un buen voting o un buen bosque suele ser pequeña y su coste de mantenimiento, alto. Úsalo cuando cada décima de métrica valga dinero.

Boosting: la familia secuencial (presentación)

Las técnicas anteriores entrenan los modelos en paralelo e independientes. El boosting los entrena en secuencia: cada nuevo modelo se concentra en los ejemplos (o errores) en que los anteriores fallaron. En lugar de reducir varianza promediando modelos fuertes, el boosting reduce sesgo sumando muchos modelos deliberadamente débiles (árboles pequeñísimos) que se corrigen unos a otros.

Esa inversión de filosofía tiene consecuencias importantes (más potencia, más riesgo de overfitting, imposible paralelizar la secuencia) y ha producido los algoritmos más dominantes en datos tabulares: Gradient Boosting, XGBoost, LightGBM. Es materia de la próxima lección (07-03); aquí basta con situarlo en el mapa.

Tabla comparativa de las tres familias

Bagging / Random Forest Boosting Stacking
Entrenamiento Paralelo, independiente Secuencial Paralelo + meta-modelo
Modelos base Fuertes (árboles profundos), homogéneos Débiles (árboles pequeños), homogéneos Heterogéneos
Reduce sobre todo Varianza Sesgo Ambos (según miembros)
Riesgo de overfitting Bajo Medio-alto (necesita frenos) Medio (fugas si la CV interna se hace mal)
Sensibilidad a hiperparámetros Baja Alta Media
Ejemplo sklearn RandomForestClassifier GradientBoostingClassifier (07-03) StackingClassifier

El precio: interpretabilidad

El árbol único de 04-03 tenía una virtud que ningún ensemble conserva: se podía dibujar y explicar — "si recencia > 30 días y frecuencia < 3 pedidos, riesgo alto". Un bosque de 300 árboles es una caja mucho más opaca: nadie puede seguir 300 votaciones a la vez.

Los paliativos existen — feature_importances_ da un ranking global, y en 07-03 mencionaremos SHAP para explicaciones por cliente —, pero la decisión de fondo es de negocio: si el equipo de retención de MercaFresh necesita justificar ante dirección por qué se llama a cada cliente, quizá un árbol interpretable con F1 0.78 valga más que un bosque opaco con 0.82. Si lo único que importa es acertar, el ensemble gana. Este trade-off rendimiento-interpretabilidad reaparecerá en las consideraciones éticas de 08-04.

Errores Comunes y Consejos

  • Combinar modelos casi idénticos. Tres logísticas con semillas distintas no forman multitud: sin diversidad no hay cancelación de errores. Mezcla familias de algoritmos o usa remuestreo/feature bagging.
  • Tratar n_estimators como un mando de complejidad. En Random Forest, más árboles no sobreajusta: solo estabiliza (y encarece). Los frenos de complejidad son los de cada árbol (max_depth, min_samples_leaf). En boosting, ojo: allí n_estimators aumenta el riesgo de overfitting, como veremos en 07-03.
  • Fiarse ciegamente de feature_importances_. La importancia por impureza favorece a las features continuas o de alta cardinalidad y se reparte arbitrariamente entre features correlacionadas. Contrasta con permutation_importance sobre datos de validación antes de sacar conclusiones de negocio.
  • Usar soft voting con modelos sin probabilidades fiables. Un SVM sin probability=True no da predict_proba, y algunos modelos dan probabilidades mal calibradas que distorsionan la media. Verifica los miembros antes de votar.
  • Evaluar el stacking con la misma CV que genera sus meta-features sin cuidado. Deja que StackingClassifier gestione su CV interna y evalúa el conjunto completo con una CV externa (como hicimos aquí): es la manera limpia de evitar fugas.
  • Consejo: empieza siempre por RandomForestClassifier con parámetros por defecto como "baseline fuerte". Es difícil de estropear y te dice rápidamente cuánta señal hay en los datos; luego decide si merece la pena algo más sofisticado.

Ejercicios

  1. El valor de la descorrelación. Simula 500 predicciones binarias de un "comité" de 25 clasificadores que aciertan individualmente el 65% (usa rng.random((500, 25)) < 0.65 como matriz de aciertos independientes) y calcula el acierto de la votación por mayoría. Repite haciendo que 20 de los 25 sean copias exactas del mismo clasificador. Compara ambos resultados.
  2. Curva de estabilización del bosque. Sobre el churn de MercaFresh, evalúa RandomForestClassifier con n_estimators en [1, 5, 10, 25, 50, 100, 200, 400] usando la CV estratificada de la lección. Dibuja F1 medio y desviación estándar frente al número de árboles. ¿A partir de cuántos árboles deja de compensar añadir más? ¿Se parece esta curva a una curva de overfitting como las de 06-05?
  3. Torneo de ensembles. Compara con la misma CV: (a) el mejor modelo individual del módulo 4 que obtuviste en 06-03, (b) VotingClassifier soft con tres modelos heterogéneos, (c) RandomForestClassifier(n_estimators=300). Presenta la tabla F1 medio ± desviación y decide razonadamente cuál desplegaría MercaFresh, teniendo en cuenta también la interpretabilidad.

Soluciones

  1. Con 25 clasificadores independientes al 65%, la mayoría (≥13 aciertos de 25) acierta en torno al 93-94% de los casos: la multitud pulveriza al individuo. Con 20 copias idénticas, la "votación" la decide en la práctica ese único clasificador repetido y el acierto vuelve a ~65%. Moraleja numérica de toda la lección: sin errores descorrelacionados, el ensemble es teatro.
  2. El F1 medio sube con fuerza hasta ~50-100 árboles y después se aplana; la desviación entre pliegues se reduce de forma continua. A partir de ~200 árboles las mejoras son ruido y solo pagas cómputo. No es una curva de overfitting: a diferencia de max_depth en 06-05, aquí no hay tramo descendente por muchos árboles que añadas — la curva converge, no se da la vuelta.
  3. Resultado típico: el bosque encabeza la tabla, el voting queda cerca (a veces empata si sus miembros son buenos y diversos) y el mejor modelo individual queda por detrás con mayor desviación entre pliegues. La decisión de despliegue no es automática: con diferencias de pocas centésimas de F1, el argumento de interpretabilidad puede inclinar la balanza hacia el modelo simple; con diferencias claras, el bosque gana y las importancias de features sirven como explicación global para negocio.

Conclusión

Has visto por qué la combinación de modelos funciona — errores descorrelacionados que se cancelan, varianza que se desploma al promediar — y las tres formas de fabricarla: bagging (bootstrap + agregación, culminando en Random Forest con su feature bagging, su OOB score y sus importancias), voting y stacking (diversidad por heterogeneidad de algoritmos), y boosting, la familia secuencial que dejamos presentada. Sobre el churn de MercaFresh, el bosque superó al árbol solitario de 04-03 con menos varianza entre pliegues y sin apenas ajuste, a cambio de sacrificar la interpretabilidad del árbol dibujable. Queda pendiente el miembro más potente de la familia: el boosting, donde cada modelo nace para corregir los errores del anterior y que hoy domina el machine learning sobre datos tabulares. Es el tema de la siguiente lección: Gradient Boosting.

Curso de Machine Learning

Módulo 1: Introducción al Machine Learning

Módulo 2: Fundamentos de Estadística y Probabilidad

Módulo 3: Preprocesamiento de Datos

Módulo 4: Algoritmos de Machine Learning Supervisado

Módulo 5: Algoritmos de Machine Learning No Supervisado

Módulo 6: Evaluación y Validación de Modelos

Módulo 7: Técnicas Avanzadas y Optimización

Módulo 8: Implementación y Despliegue de Modelos

Módulo 9: Proyectos Prácticos

Módulo 10: Recursos Adicionales

© Copyright 2026. Todos los derechos reservados