Seuil de classification et calibration des probabilités en R
Cessez de faire confiance au seuil de 0.5 — choisissez le seuil adapté à vos coûts, et vérifiez si vos probabilités prédites sont honnêtes
Un guide pratique de deux compétences de classification que les manuels sous-estiment : choisir le seuil de décision (0.5 est rarement le bon) et calibrer les probabilités prédites. Balayez le seuil pour arbitrer entre sensibilité et spécificité, choisissez-le par le J de Youden ou par une règle de coût avec probably::threshold_perf(), puis lisez une courbe de calibration (fiabilité) et recalibrez avec cal_estimate_logistic() / cal_apply() — appliqué sur PimaIndiansDiabetes2 avec le package probably de tidymodels.
Date de publication
25 juin 2026
Modifié
7 juillet 2026
AstuceL’essentiel à retenir
0.5 est rarement le bon seuil. Le seuil par défaut qui transforme une probabilité en étiquette yes/no est une décision métier, pas une loi — il arbitre entre la sensibilité (détecter plus de positifs) et la spécificité (déclencher moins de fausses alertes).
Balayez le seuil sur toute sa plage et observez les métriques évoluer avec probably::threshold_perf(), puis choisissez le seuil que votre problème exige — par le J de Youden (équilibrer sensibilité et spécificité) ou par une règle de coût explicite lorsqu’une erreur coûte plus cher que l’autre.
Une courbe de calibration (fiabilité) pose une question différente : les probabilités sont-elles honnêtes ? Quand le modèle annonce 0.8, l’événement survient-il réellement environ 80 % du temps ? Tracez-la avec cal_plot_breaks() (par classes) ou cal_plot_logistic() (lissée).
Un modèle peut avoir une excellente AUC et rester mal calibré — l’AUC ne se soucie que du classement, pas de la véracité numérique des probabilités. Si vous agissez sur la probabilité elle-même (coût attendu, un seuil de 0.7), la calibration compte.
Recalibrez un modèle mal calibré avec cal_estimate_logistic() (apprendre la correction) + cal_apply() (l’appliquer) — les probabilités deviennent plus honnêtes sans réajuster le modèle.
Le seuillage et la calibration sont deux réglages distincts : calibrez pour rendre les chiffres véridiques, puis seuillez sur les probabilités (désormais honnêtes).
Vous ajustez un classifieur, il renvoie une probabilité pour chaque cas, et R transforme discrètement chacune en une classe avec la même règle que tout le monde utilise : « positif si probabilité > 0.5 ». Ce choix par défaut n’est presque jamais le bon. Un dépistage du cancer qui manque un vrai cas est bien pire qu’une fausse alerte — vous voudriez signaler plus de personnes, ce qui implique un seuil plus bas. Un filtre anti-spam qui met en quarantaine un vrai e-mail est agaçant — vous voudriez un seuil plus haut. Le seuil de 0.5 encode une hypothèse (les deux erreurs coûtent la même chose) que votre problème viole presque certainement.
Il y a une seconde question, plus discrète, que le seuil de 0.5 masque entièrement : les probabilités prédites sont-elles seulement honnêtes ? Quand le modèle annonce « 0.8 de chance de diabète », le diabète survient-il réellement chez environ 80 % de ces patients — ou « 0.8 » est-il en réalité plutôt 0.6 ? Un modèle peut classer les cas magnifiquement (AUC élevée) tout en étant mal calibré, en rapportant des chiffres qu’on ne peut pas prendre pour argent comptant. Dès l’instant où vous agissez sur la probabilité elle-même — un calcul de coût attendu, un seuil de risque de 0.7, un chiffre montré à un clinicien — la calibration cesse d’être un sujet purement académique.
Cette leçon couvre ces deux compétences de terrain, toutes deux avec le package probably de tidymodels :
Sélection du seuil — balayer le seuil, observer l’arbitrage sensibilité/spécificité, et choisir le seuil par le J de Youden ou par une règle de coût.
Calibration des probabilités — lire une courbe de fiabilité et recalibrer lorsque les probabilités sont fausses.
Nous travaillons sur le même jeu de données PimaIndiansDiabetes2 que l’évaluation d’un modèle de classification — commencez par là si vous avez d’abord besoin de la matrice de confusion, de la ROC et de l’AUC ; cette leçon reprend là où l’évaluation par défaut à 0.5 s’arrête.
NoteRéglez le seuil sur des données de validation, pas sur le jeu de test
Choisir un seuil est une décision de modélisation — prenez-la sur un pli de validation/entraînement, puis rapportez la performance finale sur le jeu de test mis de côté. Ici nous utilisons une seule division entraînement/test pour rester concentrés sur la mécanique de probably ; en production, balayez le seuil à l’intérieur de la validation croisée (voir validation croisée).
Les données et un modèle rapide
Nous utilisons PimaIndiansDiabetes2 (le package mlbench) : 768 femmes d’origine amérindienne Pima avec des prédicteurs cliniques (glucose, IMC, âge, …) et un résultat binaire diabetes — pos (test positif) ou neg. Nous supprimons les lignes contenant des valeurs manquantes, définissons pos comme le second niveau de facteur (l’événement qui nous intéresse), et divisons en 80/20 :
library(mlbench)library(rsample)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos")) # "pos" = the event of interestset.seed(123)split <-initial_split(pima, prop =0.80, strata = diabetes)train_data <-training(split)test_data <-testing(split)c(train =nrow(train_data), test =nrow(test_data))
train test
313 79
Ajustons maintenant une régression logistique rapide et obtenons, pour chaque patient du jeu de test, la probabilité prédite d’être positif. probably travaille sur un data frame ordonné contenant la colonne de vérité ainsi que les colonnes de probabilité .pred_<class>, alors assemblons exactement cela :
Chaque ligne porte le vrai statut du patient et les probabilités prédites par le modèle. Tout ce qui suit est construit à partir de cet unique data frame.
Le seuil par défaut de 0.5 — et ce qu’il coûte
D’abord, la référence que tout le monde obtient gratuitement : étiqueter un patient pos si .pred_pos > 0.5, et regarder la matrice de confusion.
À 0.5, le modèle détecte 15 diabétiques réels sur 26 (les autres passent à travers comme faux négatifs) — une sensibilité de seulement ~0.58. Pour un test de dépistage, manquer quatre cas sur dix est le chiffre inquiétant. Le remède n’est pas un meilleur modèle ; c’est un meilleur seuil.
Balayer le seuil
probably::threshold_perf() réalise tout le balayage en un seul appel : passez-lui la vérité et la probabilité de la classe positive, un vecteur de seuils candidats, et les métriques qui vous intéressent. Comme notre événement (pos) est le second niveau de facteur, nous passons event_level = "second". Nous suivons trois métriques — sensibilité, spécificité et j_index (J de Youden = sensibilité + spécificité − 1, qui atteint son maximum au seuil du meilleur équilibre) :
Le résultat est au format long : une ligne par paire (seuil, métrique), donc il se trace directement. Chaque colonne a sa propre histoire — à mesure que le seuil monte, la sensibilité baisse (vous déclarez moins de personnes positives) tandis que la spécificité monte (moins de fausses alertes). Le graphique rend l’arbitrage évident.
library(mlbench)library(rsample)library(yardstick)library(probably)library(ggplot2)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos"))set.seed(123)split <-initial_split(pima, prop =0.80, strata = diabetes)train_data <-training(split); test_data <-testing(split)model <-glm(diabetes ~ ., data = train_data, family = binomial)prob_pos <-predict(model, test_data, type ="response")preds <-data.frame(diabetes = test_data$diabetes, .pred_pos = prob_pos)sweep <-threshold_perf( preds, truth = diabetes, estimate = .pred_pos,thresholds =seq(0.05, 0.95, by =0.05),event_level ="second",metrics =metric_set(sensitivity, specificity, j_index))ggplot(sweep, aes(x = .threshold, y = .estimate, color = .metric)) +geom_vline(xintercept =0.5, linetype ="dashed", color ="gray60") +geom_line(linewidth =1) +geom_point(size =1.6) +scale_color_manual(values =c(sensitivity ="#3a86d4",specificity ="#e29400",j_index ="#1f9e6b")) +labs(x ="Probability threshold", y ="Metric value", color =NULL,subtitle ="Sensitivity vs specificity across every cutoff (0.5 marked)") +theme_minimal()
Lisez l’image : les deux courbes se croisent — il n’y a pas de repas gratuit, seulement un arbitrage que vous choisissez. La ligne en pointillés à 0.5 est le défaut ; elle se situe bien à droite du maximum du J de Youden (en vert). Le seuil qui maximise J est le point de la courbe ROC le plus éloigné au-dessus de la diagonale du hasard — le meilleur équilibre entre les deux types d’erreur.
Choisir le seuil : le J de Youden
Pour lire le seuil du meilleur équilibre à partir du balayage, extrayez les lignes du J-index et prenez le maximum :
Le seuil du meilleur équilibre est d’environ 0.21, pas 0.5. Réétiquetez les patients à ce seuil — en le lisant directement depuis best_j$.threshold, jamais une valeur codée en dur à l’aveugle — et la matrice de confusion raconte une autre histoire :
library(mlbench)library(rsample)library(yardstick)library(probably)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos"))set.seed(123)split <-initial_split(pima, prop =0.80, strata = diabetes)train_data <-training(split); test_data <-testing(split)model <-glm(diabetes ~ ., data = train_data, family = binomial)prob_pos <-predict(model, test_data, type ="response")preds <-data.frame(diabetes = test_data$diabetes, .pred_pos = prob_pos)# Recover the best-balance (Youden's J) threshold from the sweepsweep <-threshold_perf( preds, truth = diabetes, estimate = .pred_pos,thresholds =seq(0.05, 0.95, by =0.01),event_level ="second",metrics =metric_set(sensitivity, specificity, j_index))j_rows <- sweep[sweep$.metric =="j_index", ]best_j <- j_rows[which.max(j_rows$.estimate), ]# Label at the J-optimal cutoff (not a hardcoded 0.5 or 0.25)preds$pred_j <-factor(ifelse(preds$.pred_pos >= best_j$.threshold, "pos", "neg"),levels =c("neg", "pos"))conf_mat(preds, truth = diabetes, estimate = pred_j)
Au seuil de 0.21, la sensibilité bondit à environ 0.88 (nous détectons désormais la plupart des diabétiques) tandis que la spécificité se stabilise autour de 0.68 — nous acceptons plus de fausses alertes en échange du fait de manquer bien moins de cas réels. Pour un dépistage, c’est généralement le bon arbitrage. Même modèle, mêmes probabilités — un seuil différent, délibéré.
Choisir le seuil : une règle de coût
Le J de Youden traite les deux erreurs comme aussi importantes l’une que l’autre. Souvent, elles ne le sont pas. Si un diabétique manqué (un faux négatif) est, disons, 5× plus coûteux qu’une fausse alerte (un faux positif), choisissez le seuil qui minimise directement le coût attendu. Nous balayons à nouveau, comptons les deux types d’erreur à chaque seuil, et les combinons avec les poids de coût :
library(mlbench)library(rsample)library(ggplot2)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos"))set.seed(123)split <-initial_split(pima, prop =0.80, strata = diabetes)train_data <-training(split); test_data <-testing(split)model <-glm(diabetes ~ ., data = train_data, family = binomial)prob_pos <-predict(model, test_data, type ="response")truth <- test_data$diabetescost_fn <-5# cost of a missed diabetic (false negative)cost_fp <-1# cost of a false alarm (false positive)thr <-seq(0.05, 0.95, by =0.01)cost <-sapply(thr, function(t) { pred <-ifelse(prob_pos > t, "pos", "neg") fn <-sum(pred =="neg"& truth =="pos") # said no, was yes fp <-sum(pred =="pos"& truth =="neg") # said yes, was no cost_fn * fn + cost_fp * fp})best_t <- thr[which.min(cost)]ggplot(data.frame(thr, cost), aes(thr, cost)) +geom_vline(xintercept = best_t, linetype ="dashed", color ="#e2542f") +geom_line(color ="#3a86d4", linewidth =1) +labs(x ="Probability threshold", y ="Total weighted cost",subtitle =paste0("FN 5x costlier than FP → cost-optimal cutoff ≈ ", best_t)) +theme_minimal()
Avec des faux négatifs cinq fois plus coûteux, le seuil optimal en coût descend encore plus bas que le 0.21 de Youden — le modèle devrait être empressé à signaler, parce que la pénalité d’un cas manqué écrase celle d’une fausse alerte. C’est la façon honnête de fixer un seuil : écrivez le coût relatif de chaque erreur et laissez les données choisir le seuil. Changez les coûts et le bon seuil change avec eux — ce qui explique précisément pourquoi 0.5 l’est rarement.
Les probabilités sont-elles honnêtes ? La calibration
Le seuillage a décidé où couper. La calibration pose une question plus profonde : peut-on croire le chiffre lui-même ? Un modèle est bien calibré lorsque, parmi tous les cas qu’il note ~0.8, environ 80 % sont réellement positifs ; parmi ceux qu’il note ~0.3, environ 30 % le sont. Une courbe de calibration (aussi appelée diagramme de fiabilité) vérifie cela directement : on regroupe les prédictions par probabilité prédite, et on trace le taux d’événements observé dans chaque classe face à la probabilité prédite moyenne. La calibration parfaite est la diagonale à 45 degrés.
probably::cal_plot_breaks() trace la version par classes directement à partir de notre data frame preds :
Lisez-la ainsi : chaque point est une classe de patients ; son x est ce que le modèle a dit (probabilité prédite moyenne), son y est ce qui s’est produit (fraction observée de positifs). Les points sur la diagonale en pointillés sont honnêtes ; les points au-dessus signifient que le modèle a sous-prédit le vrai risque dans cette classe (il était trop prudent), les points en dessous signifient qu’il a sur-prédit (trop confiant). Notre modèle logistique suit la diagonale d’assez près — la régression logistique est calibrée par construction sur ses données d’entraînement, et ici elle tient raisonnablement sur le jeu de test, à la fluctuation près qu’on attend d’un petit échantillon (le ruban ombré représente l’incertitude, alors ne surinterprétez pas un seul point hors diagonale).
Le graphique par classes est intuitif mais sensible au nombre de coupures. Une vue plus lissée ajuste une courbe logistique flexible à travers les points — cal_plot_logistic() :
La courbe lissée colle à la diagonale — une image rassurante. Un modèle mal calibré s’en écarterait nettement : une courbe qui est partout en dessous de la ligne est systématiquement trop confiante (ses 0.8 se comportent comme des 0.6), et vous ne devriez pas injecter ces chiffres bruts dans un calcul de coût sans les corriger d’abord.
AstuceLa calibration n’est pas l’exactitude — et l’AUC ne vous le dira pas
Un modèle peut avoir une AUC élevée (il classe bien les positifs au-dessus des négatifs) tout en étant mal calibré (ses probabilités sont numériquement fausses). L’AUC est invariante à toute compression monotone des probabilités, donc elle ne peut tout simplement pas voir un problème de calibration. Si vous ne faites jamais que seuiller les scores, une mauvaise calibration peut ne pas vous nuire ; dès l’instant où vous utilisez la probabilité comme un nombre — coût attendu, un score de risque montré à un humain, combiner des modèles — la calibration compte. Les ensembles d’arbres et les SVM sont réputés pour en avoir besoin ; la régression logistique généralement moins.
Recalibrer un modèle
Quand la courbe s’écarte de la diagonale, vous n’avez pas besoin d’un nouveau modèle — vous avez besoin de corriger les probabilités. probably apprend une correction sur des données de validation et l’applique aux nouvelles prédictions, en deux étapes :
cal_estimate_logistic() ajuste un calibreur (un modèle logistique flexible qui transforme la probabilité brute en taux observé). Des calibreurs isotonique (cal_estimate_isotonic()) et Beta (cal_estimate_beta()) existent aussi.
cal_apply() utilise ce calibreur pour réécrire les colonnes .pred_* en colonnes honnêtes.
Nous mesurons l’effet avec le score de Brier (yardstick::brier_class()) — l’erreur quadratique moyenne des probabilités, où plus bas vaut mieux — avant et après :
library(mlbench)library(rsample)library(probably)library(yardstick)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos"))set.seed(123)split <-initial_split(pima, prop =0.80, strata = diabetes)train_data <-training(split); test_data <-testing(split)model <-glm(diabetes ~ ., data = train_data, family = binomial)prob_pos <-predict(model, test_data, type ="response")preds <-data.frame(diabetes = test_data$diabetes,.pred_pos = prob_pos, .pred_neg =1- prob_pos)# 1. Learn the calibration mapping; 2. apply it to get corrected probabilitiescalib <-cal_estimate_logistic(preds, truth = diabetes,estimate = dplyr::starts_with(".pred_"),event_level ="second")preds_cal <-cal_apply(preds, calib)# Brier score (lower = more honest probabilities), before vs afterbrier_before <-brier_class(preds, diabetes, .pred_pos, event_level ="second")$.estimatebrier_after <-brier_class(preds_cal, diabetes, .pred_pos, event_level ="second")$.estimateround(c(before = brier_before, after = brier_after), 4)
before after
0.1589 0.1513
Le score de Brier baisse légèrement après la recalibration — les probabilités sont devenues un peu plus honnêtes. (Sur un modèle logistique déjà bien calibré le gain est faible, comme ici ; sur une forêt aléatoire ou un SVM trop confiant il peut être important.) Dans un vrai flux de travail, vous estimeriez le calibreur sur un jeu de validation séparé, pas sur le jeu de test, afin qu’il ne jette pas un œil aux données que vous notez — voir l’encadré ci-dessous.
NoteNe calibrez pas et n’évaluez pas sur les mêmes données
Estimer le calibreur et mesurer son effet sur les mêmes lignes est légèrement optimiste — le correcteur a vu ces étiquettes. En production, apprenez la calibration sur une division de validation dédiée (ou via cal_validate_logistic(), qui la rééchantillonne pour vous), puis appliquez-la à des données réellement inédites. Nous avons réutilisé le jeu de test ici uniquement pour limiter l’exemple à un seul data frame.
Mettre le tout ensemble : calibrer, puis seuiller
Les deux idées sont des réglages distincts que vous tournez dans l’ordre :
Calibrer d’abord — rendre les probabilités numériquement honnêtes (cal_estimate_* + cal_apply()), pour que « 0.3 » signifie réellement une chance de 30 %.
Seuiller ensuite — choisir le seuil sur ces probabilités honnêtes, par le J de Youden ou votre règle de coût (threshold_perf()).
Procéder dans cet ordre signifie que le seuil choisi (disons 0.21) renvoie à un risque réel de 21 %, pas à un nombre arbitraire sur une échelle non calibrée. Calibrez pour rendre le chiffre digne de confiance ; seuillez pour agir dessus.
NoteCe que signifie « calibration », précisément (optionnel)
Pour un résultat binaire de probabilité prédite \(\hat{p}\), le modèle est parfaitement calibré si, pour chaque valeur \(q\),
\[
\Pr(Y = 1 \mid \hat{p} = q) = q .
\]
En d’autres termes : parmi tous les cas auxquels on attribue la probabilité \(q\), une fraction exactement égale à \(q\) est positive. La courbe de calibration estime le membre de gauche (en regroupant par \(\hat{p}\) et en calculant le taux de positifs observé) et le trace face à \(q\) ; la calibration parfaite est la droite d’identité \(y = x\).
Le score de Brier résume à la fois la calibration et la finesse en un seul nombre — l’erreur quadratique moyenne des probabilités,
\[
B = \frac{1}{n} \sum_{i=1}^{n} (\hat{p}_i - y_i)^2 ,
\]
avec \(y_i \in \{0, 1\}\). Plus bas vaut mieux. Il se décompose en un terme de calibration (les probabilités sont-elles honnêtes ?) et un terme de raffinement (sont-elles décisives ?), ce qui explique pourquoi c’est la métrique avant/après naturelle pour la recalibration. Une fonction de recalibration\(g(\hat{p})\) — logistique (Platt), isotonique ou Beta — est ajustée sur des données de validation pour ramener la courbe sur la diagonale, et \(g\) est ensuite appliquée aux nouvelles prédictions.
Quel réglage, et quand
Un repère de décision rapide :
Votre situation
À utiliser
Le 0.5 par défaut manque trop de positifs (ou déclenche trop de fausses alertes)
Balayage de seuil (threshold_perf)
Les deux types d’erreur coûtent différemment
Seuil par règle de coût (minimiser le coût pondéré)
Vous voulez un seuil unique équilibré, sans information de coût
J de Youden
Vous agissez sur la valeur de la probabilité (coût attendu, score de risque)
Courbe de calibration (cal_plot_*)
La courbe s’écarte de la diagonale
Recalibrer (cal_estimate_* + cal_apply)
Les deux
Calibrer d’abord, puis seuiller
🟢 Avec un agent IA
Vous ne savez pas si le seuil de 0.5 de votre classifieur est mauvais, ni si l’on peut faire confiance à ses probabilités ? Demandez à Prova« balaie le seuil de mon classifieur, choisis le seuil adapté à mes coûts, et vérifie si les probabilités prédites sont calibrées » — elle répond avec du code R que vous pouvez exécuter sur vos propres prédictions, construit le balayage de seuil et la courbe de calibration, et vous aide à les lire. The runtime is the judge.Demander à Prova →
Problèmes courants
La sensibilité et la spécificité ressortent inversées (ou J est négatif partout).probably, comme yardstick, traite par défaut le premier niveau de facteur comme l’événement. Si votre classe positive est le second niveau (comme pos ici), passez event_level = "second" à threshold_perf() et aux appels cal_plot_* / cal_estimate_* — sinon chaque métrique est calculée pour la classe négative.
threshold_perf() dit qu’il ne trouve pas votre colonne de probabilité. Il lui faut la probabilité de la classe positive comme estimate, pas la classe prédite. Passez le .pred_pos numérique, jamais le facteur pos/neg seuillé.
La courbe de calibration paraît dentelée ou vide par endroits.cal_plot_breaks() regroupe les prédictions ; avec un petit jeu de test, certaines classes contiennent très peu de cas, donc les points sautent. Réduisez num_breaks, ou utilisez plutôt la version lissée cal_plot_logistic() — et ne surinterprétez jamais une seule classe hors diagonale à l’intérieur de son ruban de confiance.
La recalibration a aggravé les choses. Vous avez probablement estimé le calibreur et l’avez évalué sur les mêmes lignes, ou sur trop peu de données — la correction a surajusté. Apprenez-le sur une division de validation séparée (ou cal_validate_logistic()) et appliquez-le à des données inédites.
Questions fréquentes
NoteComment choisir un seuil de classification en R ?
Balayez le seuil et choisissez celui que votre problème exige. Avec probably : threshold_perf(data, truth, estimate = .pred_pos, thresholds = seq(0.05, 0.95, 0.05), metrics = metric_set(sensitivity, specificity, j_index)), puis prenez le seuil qui maximise le J de Youden (équilibré) ou minimise votre coût attendu (quand une erreur coûte plus cher). Le 0.5 par défaut est rarement le bon — il suppose que les deux erreurs coûtent la même chose.
NotePourquoi 0.5 n’est-il pas toujours le meilleur seuil de classification ?
0.5 suppose implicitement qu’un faux positif et un faux négatif sont aussi graves l’un que l’autre, et que les classes sont équilibrées. En dépistage (un cas manqué est dangereux) vous voulez un seuil plus bas pour détecter plus de positifs ; dans un contexte de haute précision vous en voulez un plus haut. Fixez le seuil à partir du coût relatif de chaque type d’erreur, pas par défaut.
NoteComment réaliser une courbe de calibration en R ?
Utilisez probably : cal_plot_breaks(data, truth, estimate = .pred_pos) pour le diagramme de fiabilité par classes, ou cal_plot_logistic(...) pour une version lissée. Les deux tracent le taux d’événements observé face à la probabilité prédite ; un modèle bien calibré suit la diagonale à 45 degrés. Passez event_level = "second" si votre classe positive est le second niveau de facteur.
NoteQu’est-ce que la calibration des probabilités et comment la corriger en R ?
Un modèle est calibré lorsque ses probabilités prédites sont honnêtes — parmi les cas qu’il note 0.8, environ 80 % sont vraiment positifs. Un modèle peut avoir une excellente AUC tout en étant mal calibré (l’AUC ne juge que le classement). Pour la corriger en R, apprenez une correction avec cal_estimate_logistic() (ou cal_estimate_isotonic() / cal_estimate_beta()) sur des données de validation et appliquez-la avec cal_apply() ; vérifiez le score de Brier avant et après.
NoteDois-je calibrer avant ou après avoir choisi un seuil ?
Calibrez d’abord, puis seuillez. La recalibration rend les probabilités numériquement honnêtes, de sorte que le seuil que vous choisissez ensuite (disons 0.21) renvoie à un risque réel de 21 % plutôt qu’à un point arbitraire sur une échelle déformée. Ce sont deux réglages indépendants : la calibration corrige les chiffres, le seuillage décide de l’action.
Testez vos connaissances
ImportantExercice : choisir un seuil à partir des coûts, et raisonner sur la calibration
Le modèle de fraude d’une banque produit P(fraud) pour chaque transaction. Bloquer une transaction légitime (un faux positif) coûte à la banque environ $5 en agacement du client ; laisser passer une transaction frauduleuse (un faux négatif) coûte environ $200. (a) Le seuil de décision doit-il être au-dessus ou en dessous de 0.5, et pourquoi ? (b) Le modèle a une AUC de 0.95 mais ses probabilités prédites sont environ 20 points trop élevées (un « 0.9 » se comporte en réalité comme un 0.7). L’AUC élevée garantit-elle que les probabilités sont dignes de confiance ? (c) Que faites-vous avant d’utiliser ces probabilités dans une règle de coût attendu ?
AstuceIndice
Un faux négatif est ici 40× plus coûteux qu’un faux positif — donc le modèle devrait être empressé à signaler. Pour (b), rappelez-vous ce que l’AUC mesure réellement. Pour (c), pensez à l’ordre des deux réglages.
AstuceSolution
(a) En dessous de 0.5. Une fraude manquée coûte 40× une fausse alerte, donc vous voulez signaler plus de transactions — échangez de la spécificité contre de la sensibilité en abaissant le seuil. Formellement, minimisez 200 * FN + 5 * FP sur le seuil (un balayage threshold_perf + la règle de coût de cette leçon) ; le minimum se situe bien en dessous de 0.5.
(b) Non. L’AUC mesure seulement si le modèle classe la fraude au-dessus de la non-fraude ; elle est inchangée par toute compression monotone des probabilités, donc une AUC de 0.95 ne dit rien sur le fait que « 0.9 » signifie vraiment 90 %. Le modèle est mal calibré.
(c) Recalibrer d’abord. Apprenez un calibreur sur des données de validation (cal_estimate_logistic()), appliquez-le (cal_apply()) pour que les probabilités soient honnêtes, puis exécutez le seuil basé sur le coût sur les scores corrigés. Une règle de coût attendu alimentée par des probabilités non calibrées optimise la mauvaise quantité.
Vérification rapide. Deux classifieurs de diabète ont tous deux une AUC de 0.84. La courbe de calibration du modèle A colle à la diagonale ; celle du modèle B se situe nettement en dessous de la diagonale partout. Vous prévoyez de multiplier chaque probabilité prédite par un coût clinique pour classer les patients en vue d’un suivi. Les probabilités de quel modèle pouvez-vous utiliser directement, et que devez-vous faire de l’autre ?
NoteAfficher la réponse
Utilisez directement le modèle A — ses probabilités sont calibrées, donc un « 0.7 » signifie vraiment ~70 % de risque et le classement par coût attendu a du sens. Le modèle B est trop confiant (courbe sous la diagonale : ses scores élevés se comportent comme des taux réels plus bas), donc ses probabilités brutes fausseraient le calcul de coût. Avant d’utiliser le modèle B, recalibrez-le (cal_estimate_logistic() + cal_apply()) et confirmez que la courbe corrigée suit désormais la diagonale. Une AUC égale n’implique pas une utilisabilité égale des valeurs de probabilité — c’est exactement ce que la calibration vérifie.
Conclusion
Vous disposez maintenant des deux compétences de terrain que le pipeline par défaut à 0.5 dissimule. Seuillage : le seuil qui transforme une probabilité en étiquette est une décision — balayez-le avec probably::threshold_perf(), puis choisissez-le par le J de Youden pour l’équilibre ou par une règle de coût explicite lorsqu’une erreur coûte davantage ; 0.5 est rarement la réponse. Calibration : les probabilités d’un modèle ne valent la peine d’être suivies que si elles sont honnêtes — vérifiez avec une courbe de calibration (cal_plot_breaks() / cal_plot_logistic()), et quand la courbe s’écarte de la diagonale, recalibrez avec cal_estimate_logistic() + cal_apply() et confirmez que le score de Brier s’améliore. Les deux sont des réglages distincts : calibrez pour rendre les chiffres véridiques, puis seuillez pour agir dessus. C’est là le levier décisif — pas seulement quelle classe le modèle prédit, mais si vous pouvez faire confiance à la probabilité qui la sous-tend, démontré de façon exécutable.
Prouvez que vous savez le faire. Maîtrisez toute la série Classification en R — suivez votre parcours, construisez des projets et obtenez un certificat.
Cette leçon est reproductible : chaque métrique et chaque figure a été produite par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.
Références
Kuhn, M., & Silge, J. Tidy Modeling with R — le package probably, la calibration et le chapitre sur le moment où l’on peut faire confiance à ses prédictions. Gratuit en ligne (CC BY-NC-SA).
La documentation du package probably — threshold_perf(), les diagrammes de fiabilité cal_plot_*, et les outils de recalibration cal_estimate_* / cal_apply().
Niculescu-Mizil, A., & Caruana, R. Predicting Good Probabilities with Supervised Learning — la référence sur les raisons pour lesquelles les modèles (surtout les arbres et les SVM) ont besoin de calibration, et comment les méthodes de Platt/isotoniques la corrigent.
@online{2026,
author = {},
title = {Seuil de classification et calibration des probabilités en R},
date = {2026-06-25},
url = {https://www.datanovia.com/learn/machine-learning/classification/probability-thresholds-and-calibration},
langid = {fr}
}