Quand 7 % des assurés ont un sinistre, un modèle apprend vite à dire « aucun sinistre » à tout le monde. SMOTE crée des exemples synthétiques de la classe rare en interpolant entre ses cas réels, pour rééquilibrer l'entraînement. Utile, mais souvent surestimé : une simple pondération des classes fait fréquemment aussi bien.
Dans un sondage où un groupe est sous-représenté, on ne se contente pas de recopier ses réponses : on crée des profils intermédiaires entre des répondants proches.
Un assuré sinistré, décrit par ses variables mises à l'échelle.
Parmi ses k plus proches voisins sinistrés (5 par défaut), on en tire un au hasard.
Le nouvel exemple est placé au hasard sur le segment qui relie les deux cas. On répète jusqu'à la proportion voulue, en général l'équilibre.
Âge du conducteur, puissance, bonus-malus, durée d'observation. Environ 7 % des contrats ont eu un sinistre. L'objectif : repérer à la souscription les profils à surveiller.
Sans correction, au seuil de 50 %, le modèle ne signale presque aucun contrat. Avec SMOTE, il en signale beaucoup plus et repère une bonne part des sinistrés, au prix de nombreuses fausses alertes. La pondération des classes donne un résultat très proche.
On compare rappel et précision au seuil retenu. L'AUC, qui mesure le classement, bouge très peu avec SMOTE : le rééquilibrage déplace surtout le seuil. C'est le signe qu'un simple ajustement du seuil aurait pu suffire.
Noms donnés pour R (smotefamily) et Python (imbalanced-learn).
Nombre de voisins de la classe rare parmi lesquels on tire. 5 par défaut. Plus petit, les points restent près des cas réels ; plus grand, ils s'étalent davantage.
Quantité d'exemples créés. Viser l'équilibre parfait n'est pas obligatoire : un ratio de 1 pour 3 ou 1 pour 2 suffit souvent.
SMOTE calcule des distances : sans mise à l'échelle, la variable aux plus grandes valeurs décide seule des voisins.
SMOTE doit être appliqué dans chaque pli de validation croisée, sur l'entraînement seulement. En Python, le Pipeline d'imbalanced-learn le garantit.
# Assurés sinistrés, classe rare : SMOTE en R
library(smotefamily)
contrats <- read.csv("sinistres.csv")
contrats$sinistre <- as.integer(contrats$nb_sinistres > 0) # environ 7 % de sinistrés
vars <- c("age_conducteur", "puissance_cv", "bonus_malus", "exposition")
set.seed(42)
idx <- sample(nrow(contrats), round(0.7 * nrow(contrats)))
# SMOTE mesure des distances : mise à l'échelle avec les paramètres de l'entraînement
X_train <- scale(contrats[idx, vars])
X_test <- as.data.frame(scale(contrats[-idx, vars], center = attr(X_train, "scaled:center"), scale = attr(X_train, "scaled:scale")))
y_test <- contrats$sinistre[-idx]
# Sinistrés synthétiques créés sur l'entraînement seulement, jusqu'à l'équilibre
equilibre <- SMOTE(X = as.data.frame(X_train), target = contrats$sinistre[idx], K = 5, dup_size = 0)$data
equilibre$class <- as.integer(equilibre$class)
cat("Part de sinistrés après SMOTE :", round(mean(equilibre$class), 2), "\n")
brut <- glm(class ~ ., data = data.frame(X_train, class = contrats$sinistre[idx]), family = binomial)
avec_smote <- glm(class ~ ., data = equilibre, family = binomial)
# Au seuil de 50 % : part des vrais sinistrés repérés (rappel) et volume signalé
for (m in list(brut, avec_smote)) {
pred <- predict(m, X_test, type = "response") > 0.5
cat("Rappel :", round(mean(pred[y_test == 1]), 2), "| part des contrats signalés :", round(mean(pred), 2), "\n")
}
# Assurés sinistrés, classe rare : SMOTE en Python
import pandas as pd
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import recall_score, precision_score, roc_auc_score
contrats = pd.read_csv("sinistres.csv")
X = contrats[["age_conducteur", "puissance_cv", "bonus_malus", "exposition"]]
y = (contrats["nb_sinistres"] > 0).astype(int) # environ 7 % de sinistrés
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# Pipeline imblearn : SMOTE n'agit qu'au fit, donc jamais sur le jeu de test
modeles = {
"sans correction": make_pipeline(StandardScaler(), LogisticRegression()),
"SMOTE": make_pipeline(StandardScaler(), SMOTE(k_neighbors=5, random_state=42), LogisticRegression()),
"class_weight": make_pipeline(StandardScaler(), LogisticRegression(class_weight="balanced")),
}
for nom, modele in modeles.items():
modele.fit(X_train, y_train)
pred = modele.predict(X_test) # seuil de 50 %
print(nom, "| rappel :", round(recall_score(y_test, pred), 2),
"| précision :", round(precision_score(y_test, pred, zero_division=0), 2),
"| AUC :", round(roc_auc_score(y_test, modele.predict_proba(X_test)[:, 1]), 3))
Après, et sur l'entraînement uniquement. Appliqué avant, il crée des points synthétiques à partir de cas qui se retrouvent ensuite dans le test : l'évaluation devient trop optimiste. En validation croisée, il faut le refaire dans chaque pli.
Les deux corrigent le déséquilibre et donnent souvent des résultats proches. class_weight ne crée aucune donnée et ne coûte rien ; c'est le premier essai. SMOTE devient intéressant quand le modèle n'accepte pas de pondération, ou quand il améliore nettement les résultats en validation.
Non. Il augmente en général le rappel au seuil par défaut, mais l'AUC change peu, car le classement des cas change peu. Choisir un seuil plus bas sur le modèle d'origine donne souvent un résultat équivalent, sans données artificielles.
Avec class_weight, elle compense le déséquilibre sans créer de données.
Voir la fiche → quand les exemples manquentDétecte les cas atypiques sans étiquettes : utile quand la classe rare est à peine observée.
Voir la fiche → gère le déséquilibre seulLe paramètre scale_pos_weight rééquilibre les classes sans suréchantillonnage.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus