ML Atlas

Machine learning from zero · Lesson 2.3 · 10 of 33 lessons ready · 12 min

Thresholds, ROC and PR: every threshold at once

In short

ROC and precision–recall curves show every threshold at once. When a class is rare, ROC AUC hardly moves while AP falls, because false alarms eat precision.

Warm-up Two questions from earlier lessons. A minute, and it never blocks the lesson.
From lesson 2.2: Evaluating a classifier: beyond accuracy A screening model misses many cancers but almost never raises a false alarm. Which number is low?

Show the answer

Correct answer: Recall, i.e. the share of cancers detected

Recall is the share of real positives that were caught, so many misses mean low recall.

From lesson 2.1: Classification: drawing a boundary You raise the threshold from 0.5 to 0.8. What happens to the boundary?

Show the answer

Correct answer: It shifts parallel to itself, so fewer penguins are classed as species B

Each threshold corresponds to a line with the same direction; a stricter threshold shifts it so that only confident cases pass.

In lesson 2.2 you moved one threshold and watched the confusion matrix change. Every threshold is a different decision, although the model’s scores stay the same. Today you will see every threshold at once, on two curves: ROC and precision–recall (PR). You will also check which of them tells the truth when the sick are few. The model is weak on purpose: a logistic regression that sees only one measurement, the mean texture of the tumour, so the curves do not stick to the corners.

Predict, then check The same model scores the same 357 benign biopsies. First there are 212 malignant ones (37% of the data), then only 19 (5%). What happens to ROC AUC (the area under the ROC curve), and what to AP, the area under the PR curve?
How sure are you?

Interactive lab

This interactive lab runs in a browser with JavaScript enabled. The text around it describes what the lab shows.

Show the answer

Correct answer: ROC AUC hardly changes (about 0.77), while AP falls to about 0.14

ROC AUC is 0.773 with all malignant biopsies and 0.772 on average with 19 (over 200 draws of which malignant ones stay). AP falls from 0.591 to 0.137 on average. The model forgot nothing; only the class proportions changed. ROC does not see it, the PR curve does.

Try it

  • Start with 212 malignant and threshold 0.5. Read recall and FPR, then move the threshold to 0.3. Which way does the point go on both curves?
  • Switch to 19 malignant at threshold 0.3. There are as many false alarms as a moment ago. What happened to precision?
  • Compare ROC AUC and AP at 212, 40 and 19 malignant.

Once you have checked it in the lab, let’s name what you see.

The idea in plain words

The model does not say “malignant” or “benign”. It gives a score from 0 to 1, and a threshold makes the decision. Each threshold gives a different pair of numbers: how many malignant biopsies were found (recall) and how many benign ones got a false alarm. The ROC curve draws these pairs for every threshold at once: the share of false alarms among benign biopsies (FPR) across, recall up. A high threshold is the bottom left corner (few alarms), a low one is the top right (an alarm for every biopsy).

The precision–recall (PR) curve shows the same thresholds differently: recall across, precision up, that is, the share of alarms that are right. The area under the ROC curve is ROC AUC, the area under the PR curve is AP (average precision). Each of them sums up every threshold at once, so it does not depend on the threshold you pick.

On all 569 biopsies the model has ROC AUC 0.773 and AP 0.591. Random guessing would give ROC AUC 0.5 and an AP equal to the share of malignant biopsies, 0.373. The model is clearly better than guessing, but far from the ideal, which is 1.

Jak to policzono
  • Biopsies: Breast Cancer Wisconsin (Diagnostic), UCI, licence CC BY 4.0. 569 biopsies (357 benign, 212 malignant). The model sees one measurement: the mean texture of the tumour.
  • Scores from 5-fold cross-validation (StratifiedKFold(5) in scikit-learn): every biopsy is scored by a model that never saw it.
  • Rarer class: all 357 benign stay, with 40 or 19 malignant drawn at random. The chart shows one draw; the means are over 200 draws.
  • Model: standardization and logistic regression (C = 1).

Why it works

ROC counts false alarms against the benign biopsies: FPR is the false alarms divided by 357. Recall counts hits against the malignant ones. When you take malignant biopsies away at random, both fractions stay roughly the same, so the ROC curve and the area under it hardly move. At threshold 0.3 the model raises an alarm for 144 of 357 benign biopsies (FPR 40.3%), whether there are 212 malignant or 19.

Precision counts false alarms against all alarms. With 212 malignant and threshold 0.3 the model finds 177, so precision is 55.1%. With 19 malignant it finds 17, and the 144 false alarms stay: precision falls to 10.6%, that is 8.5 false alarms per correct one. The PR curve falls with precision, and AP from 0.591 to 0.137 on average. The random-guessing line on PR falls too, to the share of malignant biopsies: 0.051.

This is not luck of one draw or one way of cutting the data into parts. With 40 malignant (10%) ROC AUC is 0.773 on average and AP 0.232. In the code below the same model on 20 other cuts into parts has, with 19 malignant, an AP below half of its full-data AP every single time.

Common mistake

True or false: “a high ROC AUC means few false alarms”. False. ROC AUC measures how well the model sorts: the probability that a random malignant biopsy gets a higher score than a random benign one. It does not say how many alarms will be false, because that depends on how rare the class is. In the lab, with 19 malignant ROC AUC is 0.772 on average, nearly as much as with 212, and at threshold 0.3 there are 8.5 false alarms per correct one. When a class is rare, look at the PR curve and at the precision at the threshold you will really use.

A warning the other way: AP is not a number “about the model alone” either. It depends on the share of the class in the data, so the APs of two datasets with different proportions cannot be compared directly. Compare AP with the random-guessing line on the same data.

Quiz

Three questions to check yourself. Pick an answer, or open the answer under a question. Keyboard: arrow keys choose, Enter or Space confirms.

1. What does the ROC curve show?
Show the answer

Correct answer: Recall against the share of false alarms, for every threshold at once

Each point of the curve is one threshold. A high threshold is the bottom left corner, a low one the top right. The area under the curve, ROC AUC, sums up every threshold in one number: here 0.773.

2. Malignant biopsies are 5% instead of 37%. ROC AUC hardly moves. Why does AP fall?
Show the answer

Correct answer: Because the same false alarms are now a much larger part of all alarms

FPR divides false alarms by the number of benign biopsies, which does not change. Precision divides them by all alarms. At threshold 0.3 with 19 malignant, there are 144 false alarms for 17 correct ones.

3. When does the PR curve tell you more than the ROC curve?
Show the answer

Correct answer: When the positive class is rare and what counts is how many alarms are right

With a rare class the ROC curve looks the same while precision falls. In the lab with 19 malignant, AP falls to 0.137 on average although ROC AUC hardly moves.

Key takeaways

  • The ROC curve and the PR curve show every threshold at once; you pick the threshold afterwards, for the cost of mistakes.
  • ROC AUC measures how well the model sorts and barely depends on the class proportions.
  • When the positive class is rare, the same false alarms eat precision: look at the PR curve and AP.
  • Compare AP with the random-guessing line, the share of the positive class in the same data.

Going further: formula, code, practice

The math

At threshold t the model says “malignant” when its score s ≥ t. Then recall = TP / (TP + FN), FPR = FP / (FP + TN), precision = TP / (TP + FP).

FPR has only benign biopsies in its denominator, recall only malignant ones. When you take malignant biopsies away at random, neither fraction changes systematically, so the ROC curve stays. Precision has both kinds of alarm in its denominator. At threshold 0.3: 177 / (177 + 144) ≈ 0.551 with 212 malignant and 17 / (17 + 144) ≈ 0.106 with 19.

ROC AUC is the probability that a random malignant biopsy gets a higher score than a random benign one: AUC = P(s₊ > s₋), ties counting half. AP is the mean precision weighted by the steps in recall: AP = Σₖ (Rₖ − Rₖ₋₁) · Pₖ. Random scores give AUC 0.5 and an AP equal to the share of the positive class: 212 / 569 ≈ 0.373 or 19 / 376 ≈ 0.051.

In code (Python)

The same experiment in scikit-learn: cross-validated scores, ROC AUC and AP with 212, 40 and 19 malignant biopsies (means over 1000 draws), then the same on 20 other cuts into parts.

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, average_precision_score

X, y = load_breast_cancer(return_X_y=True)
X, y = X[:, [1]], 1 - y  # mean texture only, as in the lab; 1 = malignant
model = make_pipeline(StandardScaler(), LogisticRegression())
def scores(cv):  # validation scores: every biopsy is scored by a model that never saw it
    return cross_val_predict(model, X, y, cv=cv, method="predict_proba")[:, 1]
ben, mal = np.flatnonzero(y == 0), np.flatnonzero(y == 1)
def rare(p, keep, draws, seed):  # all 357 benign + `keep` random malignant, mean over the draws
    rng = np.random.default_rng(seed)
    rows = [np.r_[ben, rng.choice(mal, keep, replace=False)] for _ in range(draws)]
    return np.mean([(roc_auc_score(y[i], p[i]), average_precision_score(y[i], p[i])) for i in rows], axis=0)
p = scores(StratifiedKFold(5))
print(f"212 malignant ({212 / 569:.0%}): ROC AUC {roc_auc_score(y, p):.3f}, AP {average_precision_score(y, p):.3f}")
for keep in (40, 19):
    auc, ap = rare(p, keep, 1000, 0)
    print(f"{keep} malignant ({keep / (357 + keep):.0%}), mean of 1000 draws: ROC AUC {auc:.3f}, AP {ap:.3f}")
r = []
for s in range(20):  # 20 other ways to cut the data into 5 parts
    q = scores(StratifiedKFold(5, shuffle=True, random_state=s))
    r.append([roc_auc_score(y, q), average_precision_score(y, q), *rare(q, 19, 200, s)])
r = np.array(r)
print(f"20 other fold splits, 212 -> 19 malignant: ROC AUC {r[:, 0].mean():.3f} -> {r[:, 2].mean():.3f}, AP {r[:, 1].mean():.3f} -> {r[:, 3].mean():.3f}; AP below half in {(r[:, 3] < r[:, 1] / 2).sum()} of 20")

Output

212 malignant (37%): ROC AUC 0.773, AP 0.591
40 malignant (10%), mean of 1000 draws: ROC AUC 0.773, AP 0.231
19 malignant (5%), mean of 1000 draws: ROC AUC 0.774, AP 0.140
20 other fold splits, 212 -> 19 malignant: ROC AUC 0.774 -> 0.775, AP 0.595 -> 0.140; AP below half in 20 of 20

With all malignant biopsies the numbers are the same as in the lab. With 40 and 19 the code draws malignant biopsies differently from the lab and makes 1000 draws instead of 200, so the means differ in the third decimal place: AP 0.140 instead of 0.137. On 20 other cuts into parts the picture is the same: ROC AUC stays, and AP falls below half every time.

For practitioners
  • In scikit-learn the numbers come from roc_auc_score and average_precision_score, and RocCurveDisplay and PrecisionRecallDisplay draw the curves. Compute them on validation scores (cross_val_predict(..., method="predict_proba")), not on the learning data.
  • With a rare class (fraud, rare diseases, failures) report AP next to ROC AUC, and next to AP the random-guessing line, the share of the positive class.
  • A curve is a menu of thresholds. Pick the threshold on validation for the cost of mistakes, for example “recall at least 90% with the highest possible precision”, and only then check it on the test set.
  • ROC AUC of two datasets with different class proportions can be compared, but it will not tell you how many alarms will be false; AP tells you more about that, but compare it only at the same proportions.

Go deeper in the Atlas

Encyclopedia entries on the ideas in this lesson: