SMOTE distorsiona las probabilidades: prueba y corrección
Demostración con cifras: tras el reequilibrado 50/50, las probabilidades predichas son 6 veces demasiado altas. La corrección analítica del prior (Elkan, 2001) las devuelve a la tasa real sin reentrenar.
Requisitos
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}")Resultado
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