Matrice de confusion et courbe ROC en R : évaluer un modèle
Lire une matrice de confusion, calculer accuracy, précision, rappel, sensibilité, spécificité et F1, et tracer la courbe ROC avec l’AUC — et pourquoi l’accuracy seule trompe
Un guide pratique pour évaluer un modèle de classification en R. Construire une matrice de confusion ; calculer accuracy, précision, rappel (sensibilité), spécificité et le score F1 ; tracer la courbe ROC et lire l’AUC ; et voir pourquoi l’accuracy seule trompe sur des données déséquilibrées (utiliser précision-rappel et le seuil de probabilité). Calculé de deux façons — le jeu de métriques yardstick de tidymodels et le caret / pROC de base — sur PimaIndiansDiabetes2 (binaire) et iris (multiclasse, avec moyennage macro/micro).
Date de publication
25 juin 2026
Modifié
11 juillet 2026
AstucePoints clés
La matrice de confusion est le tableau 2×2 des prédictions face à la vérité — vrais positifs, vrais négatifs, faux positifs, faux négatifs. Toute métrique de classification en découle, à partir de ses quatre cases.
L’accuracy = la part de prédictions correctes. Intuitive, mais elle trompe sur des données déséquilibrées : si 67 % des cas sont négatifs, un modèle qui prédit toujours « négatif » obtient déjà 0.67 tout en n’attrapant aucun positif. Comparez-la toujours à la référence de la classe majoritaire.
La précision (« quand je prédis positif, ai-je raison ? »), le rappel / la sensibilité (« parmi les vrais positifs, combien en ai-je attrapé ? »), et la spécificité (« parmi les vrais négatifs, combien en ai-je écarté ? ») révèlent le compromis que l’accuracy masque. Le F1 en est l’équilibre.
La courbe ROC trace la sensibilité face à (1 − spécificité) pour chaque seuil de probabilité ; l’ AUC (l’aire sous la courbe) résume le modèle en un seul chiffre — 0.5 = aléatoire, > 0.8 = bon, 1.0 = parfait. ROC + précision-rappel ne dépendent pas du seuil de 0.5, ils résistent donc au déséquilibre des classes.
Le seuil de probabilité est un choix, pas une loi : 0.5 est la valeur par défaut, mais l’abaisser échange de la spécificité contre de la sensibilité (attraper plus de positifs, déclencher plus de fausses alertes).
Obtenez-les tous avec yardstick (conf_mat, accuracy, sens, spec, precision, recall, f_meas, roc_auc, roc_curve) — ou en base avec caret::confusionMatrix / pROC::roc.
Vous ajustez un classifieur — régression logistique, LDA, une forêt aléatoire — et il annonce « accuracy : 0.76. » Est-ce bon ? Cela dépend entièrement du type d’erreurs qu’il commet. Un dépistage du diabète qui n’attrape que la moitié des vrais cas peut tout de même afficher une accuracy élevée si la plupart des gens sont en bonne santé. L’accuracy seule est le chiffre auquel on accorde le plus de confiance à tort, le plus trompeur en classification.
Cette leçon est la boîte à outils d’évaluation à laquelle renvoie toute la série classification. Nous construirons la matrice de confusion — le tableau dont découle chaque métrique — puis nous calculerons et interpréterons en langage clair l’accuracy, la précision, le rappel (sensibilité), la spécificité et le F1, nous verrons exactement pourquoi l’accuracy trompe sur des données déséquilibrées (et quoi utiliser à la place), et nous tracerons la courbe ROC avec son AUC — le résumé d’un classifieur indépendant du seuil. Nous terminerons par le seuil de probabilité (0.5 est une valeur par défaut, pas une fatalité) et une section multiclasse (moyennage macro/micro/pondéré, ROC un-contre-tous).
Nous calculons chaque métrique de deux façons — la voie tidymodels yardstick utilisée dans tout ce pilier, et la voie de base caret / pROC que la plupart des utilisateurs de R connaissent déjà — pour que vous puissiez lire l’une ou l’autre des sorties que vous croiserez.
NoteÉvaluez sur le jeu de test, jamais sur le jeu d’entraînement
Chaque métrique ici est calculée sur un jeu de test mis de côté que le modèle n’a jamais vu pendant l’entraînement. Évalué sur les données d’entraînement, un classifieur se flatte lui-même. Pour le pourquoi et l’alternative par rééchantillonnage, voir la validation croisée. Ici, nous utilisons une simple division entraînement/test pour garder le focus sur les métriques.
Les données et un modèle rapide
Nous utilisons le jeu de données PimaIndiansDiabetes2 (le package mlbench) : 768 femmes d’origine pima, avec des prédicteurs cliniques (glucose, IMC, âge, …) et une issue binaire diabetes — pos (testée positive) ou neg. C’est le jeu d’enseignement classique de la classification binaire. Nous supprimons les lignes comportant des valeurs manquantes, puis divisons 80/20 en entraînement et test :
library(mlbench)library(rsample)data("PimaIndiansDiabetes2", package ="mlbench")pima <-na.omit(PimaIndiansDiabetes2)pima$diabetes <-factor(pima$diabetes, levels =c("neg", "pos")) # "pos" is 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
Nous fixons levels = c("neg", "pos") pour que la classe positive (pos, les cas de diabète qui nous intéressent) soit le deuxième niveau — cela comptera dans un instant, lorsque nous indiquerons à yardstick quelle classe est l’« événement ».
Ajustons maintenant une régression logistique rapide sur le jeu d’entraînement et obtenons, pour chaque patiente du test, la probabilité prédite par le modèle d’être positive :
library(mlbench)library(rsample)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)# Predicted P(diabetes = pos) for every test patientprob_pos <-predict(model, test_data, type ="response")head(round(prob_pos, 3))
Pour obtenir les classes prédites, nous appliquons un seuil — le 0.5 standard : une patiente est étiquetée pos si sa probabilité prédite dépasse 0.5. (Nous reviendrons sur ce seuil plus tard ; c’est un choix, pas une loi.)
library(mlbench)library(rsample)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")predicted <-factor(ifelse(prob_pos >0.5, "pos", "neg"), levels =c("neg", "pos"))observed <- test_data$diabetestable(head(predicted)) # a peek at the predicted labels
neg pos
4 2
Nous avons maintenant tout ce dont l’évaluation a besoin : les vraies étiquettes (observed), les étiquettes prédites (predicted) et les probabilités prédites (prob_pos).
La matrice de confusion
La matrice de confusion est le point de départ de toute l’évaluation en classification. Elle croise ce que le modèle a prédit avec ce qui était réellement vrai — quatre comptages dont découle toute autre métrique. La version d’une ligne en base R est simplement table() :
Lisez le tableau par ses quatre cases (avec pos comme classe positive — les cas de diabète) :
Vrais positifs (TP) = 15 — prédits pos, et ils étaient pos. Diabète correctement attrapé.
Vrais négatifs (TN) = 45 — prédits neg, et ils étaient neg. Correctement écartés.
Faux positifs (FP) = 8 — prédits pos, mais ils étaient neg. Une fausse alerte (erreur de type I).
Faux négatifs (FN) = 11 — prédits neg, mais ils étaient pos. Un cas manqué (erreur de type II).
La diagonale (TP + TN = 60) correspond aux prédictions correctes ; la hors-diagonale (FP + FN = 19) aux erreurs. Dans un dépistage médical, les deux erreurs ne sont pas aussi coûteuses l’une que l’autre — un faux négatif (un diabétique manqué) est habituellement bien pire qu’une fausse alerte — ce qui explique précisément pourquoi nous regardons au-delà d’un seul chiffre d’accuracy.
yardstick donne le même tableau avec conf_mat(), et une heatmap avec autoplot() :
library(mlbench)library(rsample)library(yardstick)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")eval_df <-data.frame(truth = test_data$diabetes,predicted =factor(ifelse(prob_pos >0.5, "pos", "neg"), levels =c("neg", "pos")))autoplot(conf_mat(eval_df, truth, predicted), type ="heatmap") +scale_fill_gradient(low ="#eaf2fb", high ="#3a86d4") +theme_minimal()
La diagonale sombre correspond aux prédictions correctes ; les cases hors-diagonale plus claires aux erreurs. Un coup d’œil vous dit où le modèle se trompe — ici, il manque plus de positifs (11) qu’il ne déclenche de fausses alertes (8).
L’accuracy — et pourquoi elle trompe sur des données déséquilibrées
L’accuracy est la proportion de prédictions qui étaient correctes : (TP + TN) / total. La métrique la plus intuitive — et celle à laquelle on accorde le plus de confiance à tort.
Notre modèle obtient une accuracy ≈ 0.76 — il classe correctement environ trois patientes de test sur quatre. Cela paraît correct. Voici le piège. Le diabète est minoritaire ici : seuls ~33 % du jeu de test sont pos, donc ~67 % sont neg. Un modèle qui ne fait rien, prédisant simplement « négatif » pour tout le monde, obtiendrait déjà :
library(mlbench)library(rsample)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)test_data <-testing(split)# "Always predict the majority class (neg)" accuracy = prevalence of negmean(test_data$diabetes =="neg")
[1] 0.6708861
Une accuracy de 0.67 tout en n’attrapant aucun diabétique. Le 0.76 de notre modèle n’est que ~9 points au-dessus d’un modèle qui ne fait rien d’utile — et c’est cet écart, pas le 0.76, qui est le vrai signal. Voilà pourquoi l’accuracy seule trompe sur des données déséquilibrées : elle récompense la prédiction de la classe majoritaire et masque si vous attrapez la classe rare qui compte habituellement le plus. La solution consiste à regarder des métriques qui séparent les deux types d’erreur — précision, rappel et spécificité — et des résumés indépendants du seuil (ROC/AUC, précision-rappel) qui ne dépendent pas du seuil de 0.5.
Précision, rappel (sensibilité), spécificité et F1
Ce sont les métriques qui révèlent ce que l’accuracy masque. Chacune répond à une question différente et concrète :
Précision = TP / (TP + FP) — « quand le modèle prédit positif, à quelle fréquence a-t-il raison ? » Une précision élevée signifie peu de fausses alertes. Surveillez-la quand agir sur un positif coûte cher (une transaction signalée déclenche une vérification coûteuse).
Rappel, alias sensibilité ou taux de vrais positifs = TP / (TP + FN) — « parmi tous les vrais positifs, combien le modèle en a-t-il attrapé ? » Un rappel élevé signifie peu de cas manqués. Surveillez-le quand manquer un positif est dangereux (une maladie non diagnostiquée).
Spécificité, le taux de vrais négatifs = TN / (TN + FP) — « parmi tous les vrais négatifs, combien le modèle en a-t-il correctement écartés ? » Le miroir du rappel, pour la classe négative.
Score F1 = la moyenne harmonique de la précision et du rappel — un seul chiffre qui n’est élevé que lorsque les deux sont élevés. Le résumé de référence quand la classe positive compte et que les données sont déséquilibrées.
Il y a un compromis inhérent : pousser le rappel vers le haut (attraper chaque positif) fait habituellement baisser la précision et la spécificité (plus de fausses alertes), et inversement. Aucune métrique unique n’est « la » réponse — vous choisissez selon l’erreur qui coûte le plus cher. Calculez-les toutes d’un coup avec un metric_set() de yardstick, en lui indiquant que l’ événement est le deuxième niveau de facteur (pos) :
Lisez chaque chiffre en langage clair — c’est tout l’intérêt :
Sensibilité / rappel ≈ 0.58 — le modèle n’attrape qu’environ 58 % des vrais diabétiques ; à peu près quatre sur dix passent à travers en faux négatifs. Pour un test de dépistage, c’est le chiffre inquiétant.
Spécificité ≈ 0.85 — il écarte correctement 85 % des patientes en bonne santé. Peu de fausses alertes parmi les négatifs.
Précision ≈ 0.65 — quand il dit « diabète », il a raison environ 65 % du temps.
F1 ≈ 0.61 — l’équilibre précision/rappel. Modeste, tiré vers le bas par le faible rappel — et un résumé bien plus honnête que l’accuracy de 0.76, car il ignore la majorité de négatifs faciles à classer.
Remarquez l’histoire que le seul chiffre d’accuracy ne pouvait pas raconter : ce modèle est bon pour écarter les personnes en bonne santé mais médiocre pour attraper les diabétiques. Dans un dépistage médical, vous voudriez probablement une sensibilité plus élevée, même au prix de plus de fausses alertes — ce que nous pouvons obtenir en abaissant le seuil de probabilité (ci-dessous).
Les mêmes métriques à la base : caret::confusionMatrix
Si vous venez de caret, confusionMatrix() affiche la matrice de confusion et toute la batterie de métriques en un seul appel. Passez positive = "pos" pour qu’il sache quelle classe est l’événement :
Confusion Matrix and Statistics
Reference
Prediction neg pos
neg 45 11
pos 8 15
Accuracy : 0.7595
95% CI : (0.6502, 0.8486)
No Information Rate : 0.6709
P-Value [Acc > NIR] : 0.05705
Kappa : 0.4389
Mcnemar's Test P-Value : 0.64636
Sensitivity : 0.5769
Specificity : 0.8491
Pos Pred Value : 0.6522
Neg Pred Value : 0.8036
Prevalence : 0.3291
Detection Rate : 0.1899
Detection Prevalence : 0.2911
Balanced Accuracy : 0.7130
'Positive' Class : pos
Les mêmes chiffres, en plus grand nombre. L’accuracy en tête d’affiche est 0.76 ; le Kappa ≈ 0.44 est l’accuracy corrigée du hasard (0 = pas mieux que deviner, 1 = parfait — un compagnon utile, sensible au déséquilibre, de l’accuracy brute). Sensitivity (0.58), Specificity (0.85) et Pos Pred Value (= la précision, 0.65) correspondent exactement aux valeurs de yardstick — même évaluation, deux interfaces.
La courbe ROC et l’AUC
Chaque métrique jusqu’ici dépendait du seuil de 0.5. Mais celui-ci est arbitraire — changez-le et la précision, le rappel et le reste bougent tous. La courbe ROC (Receiver Operating Characteristic) vous libère de tout seuil unique : elle trace la sensibilité (taux de vrais positifs) face à 1 − spécificité (taux de faux positifs) à chaque seuil possible, retraçant toute la courbe de compromis du modèle.
Un modèle qui ne fait pas mieux que le hasard donne la ligne diagonale. Un bon modèle s’incurve vers le coin supérieur gauche — sensibilité élevée et spécificité élevée à la fois. L’AUC (Area Under the Curve) ramène toute la courbe à un seul chiffre :
AUC = 0.5 → tirage au hasard (la diagonale).
AUC > 0.8 → un bon classifieur.
AUC = 1.0 → séparation parfaite.
Surtout, la ROC et l’AUC sont calculées à partir des probabilités prédites, et non des classes seuillées à 0.5 — elles ne dépendent donc pas du seuil et résistent au déséquilibre des classes bien mieux que l’accuracy. yardstick calcule l’AUC avec roc_auc() et la courbe avec roc_curve() + autoplot() :
L’AUC ≈ 0.84 — au-dessus de la barre « bon » de 0.8. Elle se lit ainsi : prenez un diabétique au hasard et une patiente en bonne santé au hasard, et le modèle attribue au diabétique la probabilité prédite la plus élevée environ 84 % du temps. Un verdict bien plus robuste que l’accuracy de 0.76, car il ignore entièrement le seuil. Maintenant, la figure phare — la courbe elle-même :
library(mlbench)library(rsample)library(yardstick)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")eval_df <-data.frame(truth = test_data$diabetes, prob_pos = prob_pos)roc_curve(eval_df, truth = truth, prob_pos, event_level ="second") |>autoplot() +annotate("text", x =0.62, y =0.30, label ="AUC = 0.84",color ="#3a86d4", fontface ="bold", size =5) +theme_minimal()
La courbe monte fortement vers le coin supérieur gauche et reste bien au-dessus de la diagonale — la signature visuelle d’un bon classifieur. Le classifieur idéal épouserait exactement le coin supérieur gauche (sensibilité 1, spécificité 1) ; la diagonale est un pile ou face. Chaque point de la courbe est un seuil possible, ce qui est la passerelle vers le choix d’un seuil ensuite.
La même courbe à la base : pROC
L’outil de référence en base R pour l’analyse ROC est pROC. roc() construit l’objet et plot() la dessine avec l’AUC imprimée dessus :
library(mlbench)library(rsample)library(pROC)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")res_roc <-roc(test_data$diabetes, prob_pos, levels =c("neg", "pos"), direction ="<")plot(res_roc, print.auc =TRUE, col ="#3a86d4", legacy.axes =TRUE)
pROC rapporte la même AUC ≈ 0.84. Les deux moteurs s’accordent exactement — yardstick pour les pipelines tidymodels, pROC pour la voie base R et ses extras supplémentaires (intervalles de confiance, tests de DeLong pour comparer deux courbes).
AstuceROC vs précision-rappel sur des données déséquilibrées
La ROC/AUC résistent au déséquilibre, mais lorsque les positifs sont très rares (fraude, maladie rare), la courbe précision-rappel est encore plus informative — elle se concentre sur la classe positive et n’est pas flattée par l’énorme réservoir de négatifs faciles. Dans yardstick : pr_curve() et pr_auc(), utilisés exactement comme roc_curve()/roc_auc(). Rapportez les deux quand les classes sont fortement déséquilibrées.
Choisir le seuil de probabilité
Tout ce qui précède utilisait le seuil par défaut de 0.5 pour transformer les probabilités en classes. Mais 0.5 est une valeur par défaut, pas une loi. Le point faible de notre modèle était le rappel — il manquait 42 % des diabétiques. Abaissez le seuil (déclarez plus de patientes positives) et vous en attrapez davantage — au prix de plus de fausses alertes (spécificité plus basse). La courbe ROC est précisément le menu de ces compromis.
pROC::coords() trouve le seuil qui équilibre le mieux sensibilité et spécificité (le « meilleur » point sur la courbe ROC, selon l’indice de Youden — le seuil le plus éloigné au-dessus de la diagonale) :
Le seuil du meilleur équilibre est d’environ 0.21, et non 0.5 — à ce seuil, la sensibilité bondit à ~0.88 (attrapant bien plus de diabétiques) tandis que la spécificité se stabilise autour de ~0.68. Pour un test de dépistage, c’est souvent le bon choix : vous acceptez plus de fausses alertes pour manquer moins de vrais cas. La leçon : le seuil est une décision que vous prenez à partir du coût de chaque type d’erreur — pas un nombre à laisser à 0.5 par défaut.
Évaluation multiclasse
Tout jusqu’ici était binaire. Avec trois classes ou plus, les idées se transposent, mais chaque métrique est calculée par classe (un-contre-tous : cette classe comme positive, toutes les autres comme négatives) puis moyennée entre les classes. Nous utiliserons le jeu de données iris — prédire l’espèce Species (trois classes : setosa, versicolor, virginica) — avec un modèle LDA rapide :
La matrice de confusion est désormais 3×3 — la diagonale correspond aux prédictions correctes, les cases hors-diagonale montrent quelles espèces sont confondues avec quelles autres :
setosa est parfaitement séparée (elle l’est toujours) ; la seule confusion est une virginica lue à tort comme une versicolor — les deux espèces qui se chevauchent réellement. Maintenant, les métriques moyennées. Le nouvel argument clé est estimator, qui contrôle la façon dont les scores par classe sont combinés :
"macro" — la moyenne non pondérée des métriques par classe. Chaque classe compte également, même une classe rare. Utilisez-la quand la performance sur la classe minoritaire compte autant que celle de la majorité.
"micro" — met en commun tous les TP/FP/FN des classes en un seul comptage global, puis calcule la métrique une seule fois. Elle est dominée par les classes fréquentes (pour les métriques de type accuracy, micro = accuracy globale).
"macro_weighted" — macro, mais chaque classe pondérée par son support (le nombre de cas vrais qu’elle possède) — un compromis.
library(rsample)library(MASS)library(yardstick)data("iris")set.seed(123)split <-initial_split(iris, prop =0.80, strata = Species)train_data <-training(split); test_data <-testing(split)iris_model <-lda(Species ~ ., data = train_data)iris_pred <-predict(iris_model, test_data)iris_eval <-data.frame(truth = test_data$Species, predicted = iris_pred$class)# Overall accuracy + macro-averaged precision/recall/F1# (yardstick:: qualifies the calls in case caret is also attached — both export precision/recall)yardstick::accuracy(iris_eval, truth, predicted)
L’accuracy globale ≈ 0.97, et les précision/rappel/F1 moyennés en macro ≈ 0.97 aussi — excellent, et parce que les trois classes d’iris sont équilibrées, macro et micro donnent presque la même réponse (elles divergent seulement quand les classes sont déséquilibrées). Pour un problème déséquilibré, rapportez le macro afin qu’un modèle ne puisse pas cacher une mauvaise performance sur la classe minoritaire derrière une classe majoritaire forte.
La ROC/AUC s’étendent aussi au multiclasse via le un-contre-tous : roc_auc() calcule une courbe par classe et les moyenne. Les probabilités de classe prédites (colonnes .pred_<class>) la pilotent :
library(rsample)library(MASS)library(yardstick)data("iris")set.seed(123)split <-initial_split(iris, prop =0.80, strata = Species)train_data <-training(split); test_data <-testing(split)iris_model <-lda(Species ~ ., data = train_data)iris_pred <-predict(iris_model, test_data)# Assemble truth + the three class-probability columns yardstick expectsprobs <-as.data.frame(iris_pred$posterior)names(probs) <-paste0(".pred_", names(probs))iris_prob <-cbind(truth = test_data$Species, probs)# Multiclass AUC: macro-averaged one-vs-allyardstick::roc_auc(iris_prob, truth = truth, .pred_setosa, .pred_versicolor, .pred_virginica,estimator ="macro")
Une AUC multiclasse de 1.0 — le modèle LDA sépare les trois espèces essentiellement à la perfection sur ce jeu de test. (L’estimateur multiclasse par défaut de yardstick est celui de Hand & Till, une moyenne un-contre-tous apparentée ; estimator = "macro" rend le moyennage explicite.)
NoteL’arithmétique de la matrice de confusion (optionnel)
Chaque métrique binaire est un rapport des quatre cases de la matrice de confusion. Avec TP, TN, FP, FN, écrivons l’accuracy comme \(A\), la précision comme \(P\), le rappel (sensibilité) comme \(R\), la spécificité comme \(S\), et le score \(F_1\) comme \(F_1\) :
\[
A = \frac{TP + TN}{TP + TN + FP + FN}, \qquad
P = \frac{TP}{TP + FP},
\]
\[
R = \frac{TP}{TP + FN}, \qquad
S = \frac{TN}{TN + FP},
\]
\[
F_1 = 2 \cdot \frac{P \cdot R}{P + R}.
\]
En injectant nos comptages de diabète (TP = 15, TN = 45, FP = 8, FN = 11) : accuracy = (15 + 45)/79 ≈ 0.76 ; précision = 15/(15 + 8) ≈ 0.65 ; rappel = 15/(15 + 11) ≈ 0.58 ; spécificité = 45/(45 + 8) ≈ 0.85 — exactement les chiffres que yardstick et caret ont rapportés. Le taux de faux positifs tracé sur l’axe des x de la ROC est simplement 1 − specificity = FP/(TN + FP), et l’axe des y est le rappel. Balayer le seuil de probabilité déplace chaque comptage, traçant toute la courbe ROC.
🟢 Avec un agent IA
Vous ne savez pas si votre classifieur est réellement bon, ou si l’accuracy cache un problème ? Demandez à Prova« évalue mon modèle de classification — matrice de confusion, précision/rappel, ROC et AUC — et dis- moi si l’accuracy m’induit en erreur » — elle répond avec du code R que vous pouvez exécuter sur vos propres prédictions, construit la matrice de confusion, calcule chaque métrique, trace la courbe ROC, et vous aide à les lire. The runtime is the judge.Demandez à Prova →
Problèmes courants
La précision/le rappel sortent « à l’envers » (ou en NA).yardstick traite par défaut le premier niveau de facteur comme l’événement. Si votre classe positive est le deuxième niveau (comme pos ici), passez event_level = "second" — sinon il évalue la classe négative comme l’événement. Dans caret, fixez positive = "pos". Vérifiez toujours de quelle classe vos métriques parlent.
Votre courbe ROC paraît à l’envers (AUC < 0.5). Les probabilités sont orientées vers la mauvaise classe — vous avez fourni P(negative) là où P(positive) était attendue, ou pROC a deviné la direction. Passez la probabilité de la classe positive, et dans pROC fixez levels = c("neg", "pos") + direction = "<" pour corriger l’ orientation. Une AUC de, disons, 0.16 signifie habituellement un 0.84 inversé.
Une excellente accuracy en réalité inutile. Sur des données déséquilibrées, l’accuracy peut se situer près de la prévalence de la classe majoritaire tandis que le modèle n’attrape aucun cas de la minorité. Comparez toujours l’accuracy à la référence d’absence de discrimination (mean(truth == majority_class)), et rapportez le rappel, le F1 et l’AUC à côté.
ROC/AUC calculées à partir des classes prédites au lieu des probabilités. La ROC a besoin de la probabilité continue (ou du score), pas des étiquettes 0/1 — passez prob_pos, jamais le facteur predicted seuillé, à roc_auc() / roc().
Questions fréquentes
NoteComment faire une matrice de confusion en R ?
Croisez les vraies étiquettes et les étiquettes prédites. Base R : table(observed, predicted). tidymodels : yardstick::conf_mat(data, truth = truth_col, estimate = pred_col), avec autoplot(..., type = "heatmap") pour une visualisation. Le caret::confusionMatrix(predicted, observed, positive = "pos") de base affiche la matrice et l’accuracy, la sensibilité, la spécificité et plus encore en un seul appel.
NoteComment tracer une courbe ROC en R ?
À partir des probabilités prédites (et non des classes). tidymodels : yardstick::roc_curve(data, truth, prob_col) |> autoplot(). Base R : library(pROC); roc_obj <- roc(observed, prob_col); plot(roc_obj, print.auc = TRUE). La courbe trace la sensibilité face à 1 − spécificité pour tous les seuils de probabilité.
NoteComment calculer l’AUC en R ?
L’AUC est l’aire sous la courbe ROC. tidymodels : yardstick::roc_auc(data, truth, prob_col, event_level = "second"). Base R : pROC::auc(roc(observed, prob_col)), ou ajoutez print.auc = TRUE lors du tracé. L’AUC va de 0.5 (aléatoire) à 1.0 (parfait) ; au-dessus de 0.8 est généralement considéré comme bon. Calculez-la toujours à partir des probabilités prédites, jamais à partir des classes seuillées.
NoteQuelle est la différence entre précision et rappel en R ?
Précision = TP / (TP + FP) — parmi tout ce que le modèle a prédit positif, quelle part était vraiment positive (peu de fausses alertes). Rappel (= sensibilité) = TP / (TP + FN) — parmi tous les vrais positifs, combien ont été attrapés (peu de manqués). Calculez-les avec yardstick::precision() et yardstick::recall() (ou l’alias sens() de recall()). Le score F1 (f_meas()) est leur moyenne harmonique — élevé seulement quand les deux sont élevés.
NoteQuelle est la différence entre sensibilité et spécificité en R ?
La sensibilité (le taux de vrais positifs, = rappel) est la proportion de vrais positifs que le modèle attrape correctement : yardstick::sens(). La spécificité (le taux de vrais négatifs) est la proportion de vrais négatifs qu’il écarte correctement : yardstick::spec(). Un dépistage médical privilégie habituellement la sensibilité (ne pas manquer un patient malade) ; un test de confirmation privilégie la spécificité (ne pas alarmer une personne en bonne santé). Les deux viennent de la même matrice de confusion.
Testez vos connaissances
ImportantExercice : évaluer un modèle et décider quelle métrique privilégier
Un détecteur de fraude est testé sur 10 000 transactions, dont seulement 100 sont des fraudes. Il prédit « pas de fraude » pour chaque transaction. (a) Quelle est son accuracy ? (b) Quels sont son rappel et sa précision pour la classe fraude ? (c) Quelle métrique devriez-vous rapporter, et qu’est-ce que cela enseigne sur l’accuracy sur des données déséquilibrées ?
AstuceIndice
Avec 9 900 transactions légitimes et 100 fraudes, « toujours pas-de-fraude » classe correctement chaque légitime et se trompe sur chaque fraude. Écrivez TP, TN, FP, FN pour la classe fraude (positive), puis appliquez les formules : accuracy = (TP + TN)/total, rappel = TP/(TP + FN), précision = TP/(TP + FP).
AstuceSolution
Pour la classe fraude (positive) : TP = 0, FP = 0, TN = 9 900, FN = 100.
(b) Rappel = 0 / (0 + 100) = 0 — il n’attrape aucune fraude. La précision = 0 / (0 + 0) est indéfinie (il ne prédit jamais positif), rapportée en NA.
(c) Rapportez le rappel (et le F1, l’AUC), jamais l’accuracy. Une accuracy de 99 % avec un rappel nul est inutile — le modèle ne sert à rien précisément là où cela importe. C’est la leçon canonique : sur des données déséquilibrées, l’accuracy suit la classe majoritaire ; vérifiez toujours le rappel, le F1 et l’AUC, et comparez l’accuracy à la référence d’absence de discrimination (ici, 0.99).
Vérification rapide. Vous évaluez deux classifieurs du diabète. Le modèle A a une accuracy de 0.78 et une AUC de 0.72 ; le modèle B a une accuracy de 0.75 et une AUC de 0.86. Les classes sont déséquilibrées (la plupart des patientes sont négatives). Quel modèle privilégieriez-vous pour un outil de dépistage, et pourquoi ?
NoteAfficher la réponse
Privilégiez le modèle B. Sur des données déséquilibrées, l’accuracy est gonflée par la majorité facile (les négatifs), donc l’accuracy légèrement plus élevée du modèle A n’est pas digne de confiance. L’AUC est indépendante du seuil et résiste au déséquilibre, et le 0.86 vs 0.72 du modèle B indique qu’il classe les diabétiques au-dessus des patientes en bonne santé bien plus fiablement à tous les seuils — exactement ce dont un outil de dépistage a besoin. Vous choisiriez ensuite, sur le modèle B, un seuil de probabilité qui donne la sensibilité que votre dépistage exige. Jugez les classifieurs par l’AUC (et le rappel/F1), pas par l’accuracy seule, quand les classes sont déséquilibrées.
Conclusion
Vous disposez maintenant de toute la boîte à outils d’évaluation d’un classifieur en R. La matrice de confusion est la source de tout ; l’accuracy est intuitive mais trompe sur des données déséquilibrées — comparez-la toujours à la référence de la classe majoritaire. La précision, le rappel (sensibilité), la spécificité et le F1 séparent les deux types d’erreur pour que vous puissiez juger le modèle selon l’erreur qui coûte le plus cher. La courbe ROC et son AUC résument la performance sur tous les seuils et résistent au déséquilibre, et le seuil de probabilité est une décision que vous prenez à partir de ces coûts — pas un nombre figé à 0.5. Pour trois classes ou plus, les mêmes métriques s’étendent via le moyennage un-contre-tous (macro/micro/pondéré). Calculez le tout avec yardstick dans un pipeline tidymodels, ou caret / pROC en base R — les chiffres s’accordent.
Pour transformer une seule évaluation entraînement/test en une estimation honnête et à faible variance, rééchantillonnez avec la validation croisée. Pour construire les classifieurs que vous évaluez, partez de construire des modèles avec tidymodels.
Leçons connexes
Construire des modèles avec tidymodels — le workflow division → recipe → ajustement → prédiction qui produit les prédictions que vous évaluez ici. · Validation croisée — remplacer une seule division de test bruitée par une estimation rééchantillonnée stable. · Régression logistique pénalisée — un classifieur dont vous évaluerez la ROC/AUC avec exactement ces métriques. · Régression logistique — ajuster et interpréter le modèle derrière ces prédictions (Biostatistique).
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 ont été produites par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.
Références
James, G., Witten, D., Hastie, T., & Tibshirani, R. An Introduction to Statistical Learning — le traitement canonique de la classification, des courbes ROC et de l’AUC. Gratuit en ligne.
Kuhn, M., & Silge, J. Tidy Modeling with R — le chapitre sur les métriques de yardstick (métriques de classification, courbes ROC et PR). Gratuit en ligne (CC BY-NC-SA).
Robin, X., et al. pROC: an open-source package for R and S+ to analyze and compare ROC curves.BMC Bioinformatics (2011) — la référence pour le package pROC.
@online{2026,
author = {},
title = {Matrice de confusion et courbe ROC en R : évaluer un modèle},
date = {2026-06-25},
url = {https://www.datanovia.com/learn/machine-learning/classification/classification-model-evaluation},
langid = {fr}
}