Cómo crear un ensemble Stacking en Machine Learning: paso a paso
Este tutorial muestra cómo crear un ensemble Stacking en Machine Learning para combinar varios modelos y mejorar el rendimiento de clasificación. Stacking es particularmente útil cuando modelos individuales tienen puntos fuertes distintos (por ejemplo, uno capta bien relaciones lineales, otro capta interacciones no lineales) y queremos obtener un rendimiento global más robusto y frecuentemente con menor error que cada modelo por separado.
Pre-requisitos
- Python 3.8+
- scikit-learn instalado (pip install scikit-learn)
- pandas y numpy para manipulación de datos
- nociones básicas de clasificación y validación cruzada
Tener un entorno con CPU razonable ayuda (Stacking con cv=5 y varios estimadores puede consumir tiempo). Para conjuntos de datos mayores, considera usar n_jobs=-1 para paralelizar. Los ejemplos abajo usan el conjunto Iris, pequeño y didáctico.
Paso 1: Entender qué es Stacking y cuándo usarlo
Stacking es un ensemble que entrena varios modelos base (base models) y luego un modelo final (meta-modelo) que aprende a combinar las predicciones de esos base models. En lugar de simplemente votar o hacer average, el meta-modelo observa las predicciones de los base models (normalmente las predicciones out-of-fold) y aprende pesos/combinaciones, reduciendo sesgo y/o varianza.
Cuándo usar:
- Si tienes modelos con comportamientos complementarios (p. ej.: RandomForest captura interacciones, LogisticRegression es estable para relaciones lineales).
- Si quieres mejorar la generalización sin recurrir solo a una intensa hiperparametrización.
- Cuando dispones de datos suficientes para crear meta-features sin sobreajustar — por ejemplo, para Iris (150 muestras) funciona bien como demostración; en problemas reales normalmente tienes cientos o miles de muestras.
Técnica clave: usar validación cruzada (cv) para generar predicciones out-of-fold de los base models, evitando fugas de información del entrenamiento directo al meta-modelo.
Paso 2: Preparar los datos
Carga un conjunto simple (p. ej.: Iris) y divide en train/test. Asegurar limpieza y codificación de variables categóricas si existen. Importante: para clasificadores sensibles a la escala (KNN, SVM, LogisticRegression) aplicar StandardScaler dentro de un pipeline.
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
data = load_iris()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target)
# Com test_size=0.2 e 150 amostras -> 120 treino / 30 teste
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
Stratify mantiene las proporciones de las clases en train/test. Para conjuntos mayores, considera validación adicional con cross_val_score para medir robustez.
Paso 3: Elegir modelos base y meta-modelo
Elige modelos diversos para captar diferentes tipos de patrones. Ejemplo práctico: LogisticRegression (lineal), RandomForestClassifier (ensemble de árboles) y KNeighborsClassifier (método basado en distancia). El meta-modelo suele ser simple — LogisticRegression es una buena elección inicial para evitar sobreajuste.
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import StackingClassifier
estimators = [
('rf', RandomForestClassifier(n_estimators=50, random_state=42)),
('knn', KNeighborsClassifier(n_neighbors=5)),
('lr', LogisticRegression(max_iter=200))
]
meta_clf = LogisticRegression()
Notas: n_estimators=50 se elige por rapidez; en producción usar 100-500 según presupuesto. KNN con k=5 es típico; prueba k entre 3 y 15. meta_clf simple evita que el ensemble se vuelva demasiado complejo.
Paso 4: Construir y entrenar el Stacking
Usa StackingClassifier de scikit-learn: define estimators y final_estimator. El parámetro cv determina cómo se generan los meta-features (por defecto, cada base model hace predicciones out-of-fold en un cv de 5 folds, produciendo un conjunto de predicciones usadas para entrenar el meta-modelo).
stack = StackingClassifier(estimators=estimators, final_estimator=meta_clf, cv=5, n_jobs=-1)
stack.fit(X_train, y_train)
Explicación práctica: con cv=5, cada base model hace 5 entrenamientos en el 80% de los datos y predice el 20% restante; las predicciones concatenadas forman las features para el meta-modelo. Para clasificaciones múltiples puedes usar predict_proba (genera más columnas) o predict (una columna por modelo), según el parámetro del StackingClassifier.
Paso 5: Evaluar el ensemble y comparar con modelos individuales
Evalúa con métricas: accuracy, precision, recall, f1 y matriz de confusión. Compara el Stacking con cada modelo base para confirmar mejora. Ejemplos típicos con Iris: la accuracy del Stacking puede subir a 0.96-0.98 mientras los modelos individuales están entre 0.90 y 0.96 — ganancias modestas pero consistentes.
from sklearn.metrics import accuracy_score, classification_report
# Previsiones
y_pred_stack = stack.predict(X_test)
print('Stacking accuracy:', accuracy_score(y_test, y_pred_stack))
print(classification_report(y_test, y_pred_stack))
# Avaliar modelos individuais
for name, est in estimators:
est.fit(X_train, y_train)
y_pred = est.predict(X_test)
print(f"{name} accuracy:", accuracy_score(y_test, y_pred))
Interpreta resultados: si el Stacking no mejora, verifica si los modelos base son demasiado similares o si el meta-modelo está sobreajustando.
Paso 6: Evitar overfitting y errores comunes
Errores comunes: usar el mismo fold de entrenamiento para generar meta-features sin validación adecuada, no normalizar features cuando es necesario, y usar un meta-modelo demasiado complejo (p. ej., un árbol muy profundo) que aprenda ruido. Buenas prácticas:
- Usar pipelines con StandardScaler para KNN y LogisticRegression.
- Validar el ensemble con cross_val_score para estimar robustez (p. ej.: mean cross-val accuracy con cv=5).
- Experimentar con predict_proba en el StackingClassifier para ofrecer más información al meta-modelo.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score
# Exemplo com pipeline para KNN
estimators_pipe = [
('rf', RandomForestClassifier(n_estimators=50, random_state=42)),
('knn', make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))),
('lr', make_pipeline(StandardScaler(), LogisticRegression(max_iter=200)))
]
stack_pipe = StackingClassifier(estimators=estimators_pipe, final_estimator=LogisticRegression(), cv=5, n_jobs=-1)
print('Cross-val accuracy (stack):', np.mean(cross_val_score(stack_pipe, X_train, y_train, cv=5)))
Verificar el resultado
Confirma que el Stacking tiene accuracy igual o superior a los modelos individuales y que la matriz de confusión distribuye errores de forma aceptable. Usa cross_val_score para validar robustez y verifica si el meta-modelo aporta valor — por ejemplo, observando los coeficientes si es LogisticRegression (valores mayores corresponden a mayor peso en las predicciones de ciertos base models).
Conclusión
Stacking permite combinar fuerzas de varios modelos para mejorar predicciones; el siguiente paso es probar con conjuntos mayores o con modelos diferentes (p. ej., XGBoost, LightGBM) y experimentar meta-modelos alternativos. Consejo práctico: empieza con modelos simples y cv=5, mide ganancias porcentuales (p. ej., +1-3% de accuracy) y solo después aumenta la complejidad para no introducir overfitting. ¿Qué combinación vas a probar a continuación?