Une régression qui impose une seule contrainte : la courbe ne peut que monter, sans forme imposée. On l'utilise surtout pour calibrer un score, c'est-à-dire traduire des points ou une note en vraie probabilité. Un score de 12 devient « 38 % de risque de départ », un chiffre sur lequel on peut chiffrer une campagne.
Un escalier qu'on n'a pas le droit de descendre. On suit les taux observés marche par marche, et quand une marche est plus basse que la précédente, on fusionne les deux.
On range les clients du score le plus bas au plus haut et on regarde le taux de départ observé à chaque niveau.
Si un niveau a un taux plus bas que le précédent, on regroupe les deux et on prend leur moyenne pondérée. C'est l'algorithme PAVA (pool adjacent violators).
Le résultat est une fonction en escalier croissante : à chaque score correspond une probabilité, directement applicable aux nouveaux clients.
L'équipe fidélisation utilise une grille : 2 points par appel au support, 3 par incident, 4 pour un contrat mensuel, 4 pour moins de 6 mois d'ancienneté. Le score classe bien les clients, mais « 12 points » ne dit pas combien vont partir.
Sur le jeu d'exemple : 5,5 % de départs à 0 point, 21 % à 8 points, 38 % à 12 points, 54 % à 16 points. On peut enfin chiffrer le coût d'une campagne et le nombre de départs évités attendu.
Le Brier est l'écart moyen au carré entre probabilité annoncée et réalité (0 ou 1). Sur les clients jamais vus, il passe de 0,162 en donnant à tous le taux moyen à 0,128 avec le score calibré. On vérifie aussi, par tranche, que les taux annoncés collent aux taux observés.
Pas d'hyperparamètre à proprement parler. Noms donnés pour R (Iso) et Python (scikit-learn).
La courbe doit être apprise sur des données que le score n'a pas vues, sinon elle recopie le surapprentissage. En Python, CalibratedClassifierCV(method="isotonic") le gère par validation croisée.
Le sens de la contrainte. Par défaut, croissante. Pour un score où une valeur haute signifie un faible risque, on inverse.
Que faire d'un score hors de la plage vue à l'apprentissage. On prolonge la dernière valeur connue : out_of_bounds="clip" en Python, rule = 2 dans approxfun en R.
# Calibrer un score de churn maison : régression isotonique en R
library(Iso)
clients <- read.csv("clients_churn.csv")
# Score à points utilisé par l'équipe fidélisation : il classe, mais n'est pas une probabilité
clients$score <- 2 * clients$appels_support + 3 * clients$incidents_3m +
4 * (clients$contrat == "mensuel") + 4 * (clients$anciennete < 6)
set.seed(42)
idx <- sample(nrow(clients), round(0.7 * nrow(clients)))
train <- clients[idx, ]
test <- clients[-idx, ]
# Taux de départ observé par niveau de score, puis courbe croissante pondérée (PAVA)
taux <- aggregate(churn ~ score, data = train, FUN = mean)
effectif <- aggregate(churn ~ score, data = train, FUN = length)$churn
taux$proba <- pava(taux$churn, w = effectif)
calibre <- approxfun(taux$score, taux$proba, method = "constant", rule = 2)
print(data.frame(score = c(0, 4, 8, 12, 16, 20), proba_depart = round(100 * calibre(c(0, 4, 8, 12, 16, 20)), 1)))
proba <- calibre(test$score)
cat("Brier, taux moyen pour tous :", round(mean((mean(train$churn) - test$churn)^2), 3), "\n")
cat("Brier, score calibré :", round(mean((proba - test$churn)^2), 3), "\n")
# Calibrer un score de churn maison : régression isotonique en Python
import pandas as pd
from sklearn.isotonic import IsotonicRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import brier_score_loss
clients = pd.read_csv("clients_churn.csv")
# Score à points utilisé par l'équipe fidélisation : il classe, mais n'est pas une probabilité
clients["score"] = (2 * clients["appels_support"] + 3 * clients["incidents_3m"]
+ 4 * (clients["contrat"] == "mensuel") + 4 * (clients["anciennete"] < 6))
train, test = train_test_split(clients, test_size=0.3, random_state=42)
# Courbe croissante score -> probabilité de départ
iso = IsotonicRegression(out_of_bounds="clip").fit(train["score"], train["churn"])
proba = iso.predict(test["score"])
grille = pd.Series([0, 4, 8, 12, 16, 20])
print(pd.DataFrame({"score": grille, "proba_depart_%": (iso.predict(grille) * 100).round(1)}))
print("Brier, taux moyen pour tous :", round(brier_score_loss(test["churn"], [train["churn"].mean()] * len(test)), 3))
print("Brier, score calibré :", round(brier_score_loss(test["churn"], proba), 3))
À faire correspondre les probabilités annoncées aux fréquences réelles : parmi les clients notés 30 %, environ 30 % doivent partir. Un modèle peut bien classer sans être calibré. Dès qu'on chiffre un coût, un gain ou une provision, il faut des probabilités calibrées.
La calibration de Platt ajuste une courbe en S : elle suppose une forme, mais reste stable sur peu de données. La régression isotonique n'impose que la croissance : plus souple, elle demande plus de données pour ne pas surapprendre. Au-delà de quelques milliers d'exemples, l'isotonique est souvent meilleure.
Pool Adjacent Violators Algorithm. On parcourt les points triés ; chaque fois qu'une valeur est plus basse que la précédente, on fusionne les deux groupes et on les remplace par leur moyenne pondérée. On répète jusqu'à obtenir une suite croissante.
Appliquée au score seul, c'est la calibration de Platt : une courbe en S, moins souple, plus stable sur peu de données.
Voir la fiche → le score à pointsLa grille de points classique des banques, que l'on calibre ensuite en probabilité de défaut.
Voir la fiche → un score à calibrerSes parts de votes classent bien mais ne sont pas toujours de bonnes probabilités.
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