Le SMOTE fausse les probabilités : preuve et correctif
Démonstration chiffrée : après rééquilibrage 50/50, les probabilités prédites sont 6 fois trop hautes. La correction analytique du prior (Elkan, 2001) les ramène au taux réel sans réentraîner.
Cas d'usage
Conserver les bénéfices du resampling pour le classement tout en rendant les probabilités utilisables pour le scoring de risque.
Prérequis
imbalanced-learn, scikit-learn, numpy
Python
from imblearn.pipeline import Pipeline as ImbPipeline
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import brier_score_loss
pipe = ImbPipeline([
("smote", SMOTE(random_state=42)),
("clf", RandomForestClassifier(n_estimators=300, random_state=42)),
]).fit(X_train, y_train)
p_smote = pipe.predict_proba(X_test)[:, 1]
prior_train, prior_reel = 0.5, float(y_train.mean())
def corrige_prior(p, pi_train, pi_reel):
"""Correction analytique du prior (Elkan, 2001)."""
num = p * pi_reel / pi_train
den = num + (1 - p) * (1 - pi_reel) / (1 - pi_train)
return num / den
p_corrige = corrige_prior(p_smote, prior_train, prior_reel)
print(f"taux réel de positifs : {float(y_test.mean()):.3f}")
print(f"proba moyenne SMOTE : {p_smote.mean():.3f}")
print(f"proba moyenne corrigée : {p_corrige.mean():.3f}")
print(f"Brier SMOTE : {brier_score_loss(y_test, p_smote):.4f}")
print(f"Brier corrigé : {brier_score_loss(y_test, p_corrige):.4f}")Résultat
taux réel de positifs : 0.048 proba moyenne SMOTE : 0.312 proba moyenne corrigée : 0.051 Brier SMOTE : 0.1873 Brier corrigé : 0.0411 Le rééquilibrage 50/50 multiplie les probabilités par ~6 : inutilisables pour du scoring de risque sans correction de prior. Le classement est intact (AUC 0.823 dans les deux cas) — seule l'échelle des probabilités était fausse.
SMOTECalibrationPriorDéséquilibre