Receiver Operating Characteristic (ROC) Curve
Une courbe caractéristique de fonctionnement du récepteur (ROC) représente le taux de vrais positifs en fonction du taux de faux positifs pour chaque seuil. Découvre comment la lire, l’AUC et des exemples de code.
Une courbe caractéristique de fonctionnement du récepteur (ROC) est un graphique qui montre la capacité d’un classifieur binaire à séparer deux classes pour chaque seuil de décision possible. Elle trace le taux de vrais positifs sur l’axe des y en fonction du taux de faux positifs sur l’axe des x, avec un point pour chaque seuil appliqué aux scores du modèle ; un seul graphique couvre ainsi tous les compromis entre la détection des positifs et le déclenchement de fausses alertes. Son aire, l’aire sous la courbe (AUC), condense ces informations en un seul nombre indépendant du seuil. Le nom vient de l’ingénierie radar de la Seconde Guerre mondiale, où la courbe mesurait la capacité des opérateurs radar à distinguer les avions ennemis du bruit, et la méthode s’est ensuite répandue en médecine, en psychologie et en apprentissage automatique (ML).
Construction d’une courbe ROC#
Chaque point de la courbe provient d’une matrice de confusion calculée à un seuil donné :
- Taux de vrais positifs (TPR), aussi appelé rappel ou sensibilité : TP / (TP + FN), proportion de positifs réels détectés par le modèle.
- Taux de faux positifs (FPR) : FP / (FP + TN), proportion de négatifs réels signalés à tort. Le FPR est égal à 1 moins la spécificité.
En faisant varier le seuil de haut en bas, on trace la courbe de (0, 0), où rien n’est signalé, à (1, 1), où tout l’est. Un classifieur qui classe tous les positifs au-dessus de tous les négatifs passe par le coin supérieur gauche, un modèle qui devine au hasard suit la diagonale, et une courbe sous la diagonale indique que les scores sont inversés. L’AUC correspond à la probabilité qu’un positif choisi au hasard reçoive un score supérieur à celui d’un négatif choisi au hasard ; 0,5 correspond donc au hasard et 1,0 à une séparation parfaite. Pour construire la courbe, il faut des scores continus, comme des probabilités prédites ou des valeurs de confiance, car des étiquettes binaires 0/1 ne donnent qu’un seul point. L’exemple suivant entraîne un classifieur de régression logistique sur un jeu de données déséquilibré et calcule sa courbe avec la fonction roc_curve de scikit-learn :
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, roc_curve
from sklearn.model_selection import train_test_split
# Binary task where 10% of samples are positive
X, y = make_classification(n_samples=5000, weights=[0.9], class_sep=0.8, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0)
# ROC analysis needs continuous scores, not hard 0/1 predictions
scores = LogisticRegression().fit(X_train, y_train).predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, scores)
print(f"AUC: {roc_auc_score(y_test, scores):.3f}")
# Each threshold is one point on the curve: higher recall costs more false alarms
for target in (0.80, 0.95):
i = np.argmax(tpr >= target) # first point that reaches the target TPR
print(f"Threshold {thresholds[i]:.2f}: TPR {tpr[i]:.2f}, FPR {fpr[i]:.2f}")Le modèle atteint une AUC de 0,963. Avec un seuil de 0,44, il détecte 82 % des positifs avec un FPR de 0,01, tandis qu’atteindre un rappel de 95 % nécessite d’abaisser le seuil à 0,02, ce qui fait bondir le FPR à 0,26. Sur ce lot de test composé de 130 positifs et de 1 120 négatifs, détecter 18 positifs de plus entraîne 279 fausses alertes supplémentaires.
Choisir un point de fonctionnement#
La courbe montre le coût de chaque seuil, et c’est l’application qui détermine le coût acceptable :
- Dépistage médical : un cancer manqué coûte bien plus cher qu’un examen de suivi ; les outils de dépistage fonctionnent donc à un point de TPR élevé et acceptent davantage de faux positifs que les radiologues examineront.
- Détection de fraude : chaque paiement légitime bloqué frustre un client ; les banques choisissent donc un point qui détecte la plupart des fraudes tout en maintenant le FPR à un niveau supportable pour leurs équipes de vérification et leurs clients.
- Coûts égaux : lorsque les deux erreurs ont le même coût, la statistique J de Youden, TPR moins FPR, sélectionne le point le plus éloigné au-dessus de la diagonale.
Les modèles de classification Ultralytics YOLO26 renvoient une probabilité pour chaque classe dans results[0].probs.data, soit les scores continus nécessaires à l’analyse ROC ; recueillir la probabilité d’une classe sur un jeu de validation annoté donne donc la courbe ROC un contre tous de cette classe et permet de choisir un seuil adapté aux coûts du déploiement.
Courbe ROC et outils apparentés#
Une courbe précision-rappel trace la précision en fonction du rappel plutôt que le TPR en fonction du FPR. Comme le TPR et le FPR sont chacun calculés au sein d’une classe, une courbe ROC reste inchangée lorsque les positifs deviennent plus rares, contrairement à la précision, qui diminue. Lorsque les positifs sont rares, comme dans le dépistage de fraude ou la détection d’anomalies, un faible FPR peut tout de même entraîner plus de fausses alertes que de détections réelles ; une étude de PLOS ONE sur les jeux de données déséquilibrés a conclu que les courbes précision-rappel étaient plus informatives dans ce cas. La détection d’objets est évaluée à l’aide de courbes précision-rappel et de la précision moyenne (mAP) pour une raison similaire : les régions d’arrière-plan qui constitueraient des vrais négatifs sont sans limites, de sorte que le FPR n’a pas de dénominateur fixe.
- Précision et score F1 : nombres uniques mesurés à un seul seuil, tandis que la courbe ROC et son AUC résument l’ensemble des seuils.
- Classification d’images multiclasse : chaque classe dispose d’une courbe ROC un contre tous, et les AUC par classe sont moyennées pour obtenir un score macro ou pondéré par la prévalence, calculé dans scikit-learn par
roc_auc_scoreavecmulti_class="ovr".










