Stacking (Empilement de Modèles) : Guide Complet — Principes, Exemples et Implémentation Python

Stacking (Empilement de Modèles) : Guide Complet — Principes, Exemples et Implémentation Python

Stacking (Empilement de Modèles) : Guide complet — Principes, Exemples et Implémentation Python

Résumé

Le Stacking (ou empilement de modèles, également appelé généralisation empilée) est une méthode d’apprentissage ensembliste avancée qui combine les prédictions de plusieurs modèles de base — appelés modèles de niveau 0 — à l’aide d’un méta-modèle de niveau 1 (ou meta-learner). Contrairement au bagging qui moyenne des modèles identiques ou au boosting qui corrige séquentiellement les erreurs, le stacking apprend comment combiner au mieux les prédictions de modèles hétérogènes. Cette approche, introduite par David Wolpert en 1992, constitue l’une des techniques les plus puissantes du machine learning supervisé, particulièrement prisée en compétition Kaggle où elle permet fréquemment de gagner des positions cruciales au classement.

Dans ce guide complet, nous explorerons les principes mathématiques du stacking, son intuition fondamentale, son implémentation pratique avec scikit-learn (via StackingClassifier et StackingRegressor), ses hyperparamètres clés, ainsi que quatre cas d’usage concrets.


Principe mathématique du Stacking

Le stacking repose sur une architecture à deux niveaux (parfois davantage, d’où le terme « empilement »).

Niveau 0 : les modèles de base

Soit un jeu d’entraînement D = {(x₁, y₁), …, (xₙ, yₙ)} où xᵢ ∈ ℝᵖ sont les features et yᵢ la cible. On entraîne K modèles de base différents :

f₁, f₂, …, f_K

Chaque modèle f_k est entraîné sur D et produit une prédiction f_k(x) pour une nouvelle observation x. Ces modèles sont typiquement de nature différente (algorithme, hyperparamètres, sous-ensembles de features) afin de garantir une diversité des prédictions — condition essentielle au succès du stacking.

Niveau 1 : le méta-modèle

L’idée centrale est de construire un méta-modèle g qui prend en entrée les prédictions des K modèles de base et produit la prédiction finale :

ŷ = g(f₁(x), f₂(x), …, f_K(x))

Le méta-modèle g apprend ainsi à pondérer intelligemment les avis des différents modèles de base. Par exemple, si le Random Forest excelle sur certaines régions de l’espace des features tandis que le SVM est meilleur sur d’autres, g apprendra à accorder plus de poids au modèle compétent dans chaque région.

Validation croisée pour éviter le data leakage

Un problème crucial se pose ici : si l’on entraîne g sur les mêmes données que celles utilisées pour entraîner les f_k, le méta-modèle risque d’apprendre les biais systématiques des modèles de base plutôt que leur capacité de généralisation. C’est ce qu’on appelle le data leakage.

La solution, introduite par Wolpert, repose sur la validation croisée out-of-fold :

  1. On divise D en M plis (folds) D₁, D₂, …, D_M.
  2. Pour chaque pli D_m et chaque modèle f_k :
    – On entraîne f_k sur D \ D_m (toutes les données sauf le pli m).
    – On prédit sur D_m uniquement, obtenant des prédictions hors échantillon (out-of-fold).
  3. On assemble toutes les prédictions out-of-fold pour former la méta-base Z, où chaque ligne i contient (f₁⁽⁻ᶖ⁾(xᵢ), f₂⁽⁻ᶖ⁾(xᵢ), …, f_K⁽⁻ᶖ⁾(xᵢ)).
  4. On entraîne le méta-modèle g sur Z avec les vraies étiquettes y.

Cette procédure garantit que le méta-modèle g n’a jamais vu les données sur lesquelles les modèles de base font leurs prédictions lors de l’entraînement du niveau 1. Le résultat est une estimation honnête de la performance de chaque modèle et une combinaison optimale sans surapprentissage.

Pour produire la prédiction finale sur de nouvelles données, chaque modèle de base f_k est réentraîné sur l’intégralité de D, puis leurs prédictions sont combinées par g.


Intuition : le comité d’experts et son chef de projet

Imaginez la situation suivante : un cabinet de conseil doit évaluer la valeur d’une entreprise. Il convoque trois experts :

  • L’analyste financier (notre Random Forest) : excellent pour repérer les tendances passées et les patterns récurrents.
  • Le stratège industriel (notre Gradient Boosting) : fort pour comprendre la dynamique concurrentielle et les signaux faibles.
  • L’expert juridique (notre SVM) : capable de détecter les subtilités et les cas limites que les autres manquent.

Chacun donne son estimation. Mais au lieu de faire une simple moyenne (ce serait du bagging), on fait appel à un chef de projet (le méta-modèle g). Ce chef de projet a de l’expérience : il sait que l’analyste financier tend à surestimer les entreprises tech, que le stratège est excellent sur les secteurs matures, et que l’expert juridique est indispensable quand il y a des litiges en cours.

Le chef de projet ne se contente pas d’additionner les avis : il les pondère de manière contextuelle. Selon le profil de l’entreprise, il accorde plus ou moins de confiance à chaque expert. C’est exactement ce que fait le stacking : le méta-modèle apprend quand faire confiance à quel modèle.


Implémentation Python avec scikit-learn

Exemple complet : StackingClassifier

Voici une implémentation complète utilisant StackingClassifier de scikit-learn, avec trois modèles de base et une régression logistique comme méta-modèle :

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_predict
from sklearn.ensemble import (
    RandomForestClassifier,
    GradientBoostingClassifier,
    StackingClassifier,
)
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report

# 1. Génération des données
X, y = make_classification(
    n_samples=2000,
    n_features=20,
    n_informative=12,
    n_redundant=4,
    n_classes=2,
    random_state=42,
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 2. Définition des modèles de base (niveau 0)
estimators = [
    ("rf", RandomForestClassifier(n_estimators=100, random_state=42)),
    ("gb", GradientBoostingClassifier(n_estimators=100, random_state=42)),
    ("svm", SVC(probability=True, random_state=42)),
]

# 3. Empilement avec méta-modèle (niveau 1)
stacking_clf = StackingClassifier(
    estimators=estimators,
    final_estimator=LogisticRegression(max_iter=1000),
    cv=5,                     # Validation croisée à 5 plis
    stack_method="predict_proba",  # Utilise les probabilités
    n_jobs=-1,                # Parallélisation
    passthrough=False,        # N'ajoute pas les features originales
)

# 4. Entraînement et évaluation
stacking_clf.fit(X_train, y_train)
y_pred = stacking_clf.predict(X_test)

print("=== StackingClassifier ===")
print(f"Précision test : {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))

# 5. Comparaison avec les modèles individuels
print("\n=== Performances individuelles ===")
for name, clf in estimators:
    clf.fit(X_train, y_train)
    pred = clf.predict(X_test)
    acc = accuracy_score(y_test, pred)
    print(f"{name:20s} : {acc:.4f}")

Démonstration de cross_val_predict

Pour bien comprendre le mécanisme de validation croisée sous-jacent, voici comment reconstruire manuellement la méta-base avec cross_val_predict :

from sklearn.model_selection import cross_val_predict

# Prédictions hors échantillon pour chaque modèle de base
meta_features_train = np.column_stack([
    cross_val_predict(clf, X_train, y_train, cv=5, method="predict_proba")[:, 1]
    for _, clf in estimators
])

print(f"Forme de la méta-base d'entraînement : {meta_features_train.shape}")
# Résultat : (1600, 3) --- 1600 échantillons, 3 modèles de base

# Entraînement du méta-modèle sur ces prédictions
meta_model = LogisticRegression(max_iter=1000)
meta_model.fit(meta_features_train, y_train)

StackingRegressor

Le même principe s’applique à la régression via StackingRegressor :

from sklearn.ensemble import StackingRegressor
from sklearn.linear_model import Ridge, Lasso
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error

estimators_reg = [
    ("ridge", Ridge(alpha=1.0)),
    ("lasso", Lasso(alpha=0.1)),
    ("dt", DecisionTreeRegressor(max_depth=5)),
]

stacking_reg = StackingRegressor(
    estimators=estimators_reg,
    final_estimator=Ridge(alpha=1.0),
    cv=5,
)

stacking_reg.fit(X_train_reg, y_train_reg)
y_pred_reg = stacking_reg.predict(X_test_reg)
rmse = np.sqrt(mean_squared_error(y_test_reg, y_pred_reg))
print(f"RMSE Stacking : {rmse:.4f}")

Hyperparamètres clés

estimators

Liste de tuples (nom, estimateur) définissant les modèles de niveau 0. La diversité est la clé : utilisez des algorithmes de nature différente. Un stacking avec trois Random Forest aura peu d’intérêt, car ils feront des erreurs similaires.

estimators = [
    ("rf", RandomForestClassifier(n_estimators=200, max_depth=10)),
    ("gb", GradientBoostingClassifier(n_estimators=200, learning_rate=0.05)),
    ("svm", SVC(C=1.0, kernel="rbf", probability=True)),
    ("xgb", XGBClassifier(n_estimators=200, learning_rate=0.05)),  # Avec XGBoost
]

final_estimator

Le méta-modèle qui combine les prédictions du niveau 0. Par défaut, scikit-learn utilise LogisticRegression pour la classification et Ridge pour la régression. En pratique :

  • LogisticRegression / Ridge : choix par défaut, robuste, peu sujet au surapprentissage.
  • GradientBoosting / XGBoost : plus puissant mais risque de surapprentissage si le dataset est petit.
  • Réseau de neurones : envisageable pour les très grands ensembles de données.

cv

Nombre de plis pour la validation croisée. Par défaut à 5. Augmenter à 10 réduit la variance des prédictions out-of-fold au prix d’un temps de calcul accru. Pour les petits datasets (n < 1000), cv=10 est recommandé.

stack_method

Détermine quelle méthode des modèles de base est utilisée comme feature pour le méta-modèle :

  • "predict_proba" (Classification) : Probabilités de chaque classe — recommandé.
  • "decision_function" (Classification) : Score brut avant transformation en probabilité.
  • "predict" (Classification/Régression) : Prédiction directe (classification dure).

Pour la régression, stack_method est ignoré car seule predict existe.

passthrough

  • passthrough=False (par défaut) : les features du méta-modèle sont uniquement les prédictions des modèles de base.
  • passthrough=True : les features originales X sont ajoutées aux prédictions des modèles de base. Cela peut être utile si les modèles de base ne capturent pas toute l’information disponible, mais augmente le risque de surapprentissage du méta-modèle.

Avantages et limites du Stacking

Avantages

  1. Performance supérieure : Le stacking surpasse généralement chaque modèle individuel en tirant parti de leurs forces complémentaires.
  2. Flexibilité algorithmique : On peut combiner n’importe quels estimateurs — arbres, SVM, réseaux de neurones, modèles linéaires.
  3. Robustesse : Moins sensible au surapprentissage qu’un modèle unique très complexe, grâce à la validation croisée intégrée.
  4. Pas d’hypothèse restrictive : Contrairement au boosting qui suppose que les erreurs sont corrigeables séquentiellement, le stacking ne fait aucune supposition sur la structure des erreurs des modèles de base.
  5. Transparence relative : Si le méta-modèle est linéaire (LogisticRegression), les coefficients révèlent l’importance relative de chaque modèle de base.

Limites

  1. Complexité computationnelle : Entraîner K modèles en validation croisée multiplie le temps de calcul par K × cv (par exemple, 3 modèles × 5 plis = 15 entraînements au niveau 0).
  2. Risque de surapprentissage du méta-modèle sur les prédictions du niveau 0, surtout avec un petit dataset ou un final_estimator trop complexe.
  3. Difficulté d’interprétation : L’architecture à deux niveaux rend le modèle global difficile à interpréter, même si le méta-modèle est simple.
  4. Sensibilité à la qualité des modèles de base : Si tous les modèles de base sont médiocres, le méta-modèle ne pourra pas faire de miracle. Le stacking amplifie la diversité performante, il ne crée pas la performance.
  5. Maintenance en production : Le déploiement nécessite de maintenir K + 1 modèles, ce qui augmente la complexité opérationnelle.

4 cas d’usage concrets

1. Compétitions de data science (Kaggle)

Le stacking est l’arme secrète des compétitions Kaggle. Les équipes gagnantes combinent fréquemment 5 à 10 modèles hétérogènes (Random Forest, XGBoost, LightGBM, CatBoost, réseaux de neurones) via un méta-modèle. Sur des datasets tabulaires, le stacking à deux ou trois niveaux peut offrir un gain de 2 à 5 % en métrique par rapport au meilleur modèle individuel — suffisant pour passer de la 50ᵉ à la 5ᵉ place.

2. Détection de fraude financière

Dans la détection de fraude, les modèles individuels capturent des signaux différents : les arbres détectent des règles métier complexes, les SVM identifient les anomalies dans des sous-espaces, les réseaux de neurones captent des motifs temporels. Le stacking combine ces signaux de manière optimale, réduisant à la fois les faux positifs (transactions légitimes bloquées) et les faux négatifs (fraudes non détectées).

3. Diagnostic médical assisté

Plusieurs algorithmes analysent les mêmes données médicales (images, biomarqueurs, dossiers patients) : un CNN pour l’imagerie, un modèle séquentiel pour les séries temporelles de constantes vitales, un modèle tabulaire pour les données démographiques et cliniques. Le stacking agrège ces analyses en une recommandation unique, plus fiable que chaque source isolée — essentiel quand une décision engage la vie du patient.

4. Prédiction de la demande et prévision des ventes

Dans la supply chain, différents modèles capturent différentes saisons et tendances : Prophet pour les saisonnalités calendaires, Random Forest pour les promotions et événements, régression pour les tendances de fond. Le stacking produit une prévision consensus plus robuste, réduisant les ruptures de stock et le surstockage.


Stacking à plusieurs niveaux

Rien n’empêche d’empiler davantage : les prédictions du niveau 1 peuvent elles-mêmes servir de features pour un niveau 2, etc. C’est ce qu’on appelle le multi-level stacking ou super learning. En pratique :

  • Niveau 1 : 5 modèles de base → méta-modèle A
  • Niveau 2 : Les prédictions de A + modèles individuels → méta-modèle B (final)

Cette approche est courante en compétition mais rarement justifiée en production, où la complexité supplémentaire n’apporte qu’un gain marginal.


Bonnes pratiques

  1. Diversifiez les modèles de base : Choisissez des algorithmes fondamentalement différents. Trois forêts aléatoires avec des graines différentes apportent peu.
  2. Évaluez individuellement chaque modèle : Incluez uniquement les modèles qui surpassent un baseline raisonnable. Supprimez les modèles « bruiteurs ».
  3. Gardez un méta-modèle simple : LogisticRegression ou Ridge sont d’excellents choix par défaut. Évitez les méta-modèles complexes sauf si vous avez beaucoup de données.
  4. Utilisez la validation croisée imbriquée pour estimer honnêtement la performance finale sans biais optimiste.
  5. Attention au déséquilibre des classes : Utilisez stratify dans le split et des métriques adaptées (F1-score, AUC-ROC, Average Precision) plutôt que la simple exactitude.
  6. Scalez les features pour les modèles sensibles à l’échelle (SVM, régression) avant l’entraînement des modèles de base.

Voir aussi


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.