Fuite corrigée : sélection de features sur tout le dataset
Sélectionner les features corrélées à la cible AVANT la cross-validation produit des AUC mirobolantes sur du bruit pur — démonstration chiffrée, puis correction par pipeline.
Cas d'usage
Comprendre pourquoi un score de CV à 0.9 peut être un artefact complet sur données larges (génomique, finance).
Prérequis
scikit-learn, numpy
Python
import numpy as np
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
rng = np.random.default_rng(0)
X_bruit = rng.normal(size=(200, 5000)) # bruit pur, AUCUN signal
y_bruit = rng.integers(0, 2, 200)
# INCORRECT : sélection sur TOUT le dataset, puis CV
X_sel = SelectKBest(f_classif, k=20).fit_transform(X_bruit, y_bruit)
auc_triche = cross_val_score(LogisticRegression(), X_sel, y_bruit,
cv=5, scoring="roc_auc").mean()
# CORRECT : la sélection vit dans le pipeline, refittée par fold
pipe = make_pipeline(SelectKBest(f_classif, k=20), LogisticRegression())
auc_vraie = cross_val_score(pipe, X_bruit, y_bruit,
cv=5, scoring="roc_auc").mean()
print(f"AUC avec fuite : {auc_triche:.3f} <- signal fantôme")
print(f"AUC corrigée : {auc_vraie:.3f} <- ~0.5, comme attendu")Résultat
AUC avec fuite : 0.852 <- signal fantôme AUC corrigée : 0.497 <- ~0.5, comme attendu >>> X_bruit.shape, float(y_bruit.mean()) ((200, 5000), 0.515)
Fuite de donnéesFeature selectionAnti-patternDémonstration