Machine learning fundamentalsMiddleLesson 1155 min read

Measuring a classifier: precision and recall

Accuracy can look great while the model misses every case that matters. The confusion matrix, precision and recall show what really happened.

Lesson in motion

In 60 seconds

Measuring a classifier: precision and recall

Accuracy can look great while the model misses every case that matters. The confusion matrix, precision and recall show what really happened.

1/3
In simple words
Imagine a smoke alarm that never rings. In a year with no fire, it is "right" every single day. But the one day there is a fire, it stays quiet. Being right most days is not enough; it must catch the rare day that matters. Precision and recall help us check this.
Suppose 2 in every 100 transactions are fraud. A "model" that always says "not fraud" is right 98% of the time. Its accuracy is 98%, and it catches zero fraud. When one group is rare, accuracy hides the mistakes that matter.

The confusion matrix

For two groups, every prediction lands in one of four boxes. We call the rare, important group "positive".
Predicted: negativePredicted: positive
Actually negativeTrue negative (TN): correctly left aloneFalse positive (FP): a false alarm
Actually positiveFalse negative (FN): a missed caseTrue positive (TP): correctly caught

Precision and recall

  • Precision = TP / (TP + FP). Of all the alarms raised, how many were real? Low precision means many false alarms.
  • Recall = TP / (TP + FN). Of all the real cases, how many did we catch? Low recall means many missed cases.
  • F1 combines the two into one number (their harmonic mean). It is only high when both are high.
Python: count the boxes by hand and with scikit-learnpython
from sklearn.metrics import confusion_matrix, precision_score, recall_score, accuracy_score

# 1 = positive (the rare case we care about), 0 = negative. Made-up labels.
actual    = [0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
predicted = [0, 0, 0, 0, 0, 1, 0, 1, 1, 0]

tn, fp, fn, tp = confusion_matrix(actual, predicted).ravel()
print("TN", tn, " FP", fp, " FN", fn, " TP", tp)
print("accuracy ", accuracy_score(actual, predicted))
print("precision", tp / (tp + fp), "=", precision_score(actual, predicted))
print("recall   ", tp / (tp + fn), "=", recall_score(actual, predicted))

Which one matters more?

SituationWorse mistakeLean towards
Screening for a serious disease, followed by a proper testMissing a sick person (FN)Higher recall
Flagging bank transactions for a quick checkMissing fraud (FN), within a limit of checks staff can handleRecall, with precision kept workable
Spam filterA real email hidden in spam (FP)Higher precision
Auto-blocking an account with no human reviewBlocking an innocent person (FP)Higher precision, or add human review
You usually trade one for the other by moving the threshold. Lower threshold: more alarms, higher recall, lower precision. Higher threshold: fewer alarms, higher precision, lower recall. The right point depends on the real cost of each mistake, which is a decision for people, not the model.
Watch out
A model used to screen for disease should support doctors, not replace them. The numbers here are about teaching measurement, not medical practice.

Other habits for rare classes

  • Split with stratify=y so the rare class appears in every set.
  • Always report the confusion matrix next to any single number.
  • Compare against the "always say negative" baseline.
  • Some models accept class_weight="balanced", which makes mistakes on the rare class count more in training.
Try this. In the code, change the last prediction to 1. Which boxes change, and what happens to precision and recall?

Watch and read more

Lab

A made-up fraud dataset where a do-nothing model looks 98% accurate, a real model measured with precision and recall, and the effect of a lower threshold.

~25 min

The problem

The starter generates made-up transactions with a fixed seed. About 2% are fraud. The numbers are invented for practice and do not describe any real bank.

  1. Run it. It trains a scaled logistic regression and prints accuracy.
  2. For the do-nothing model that always predicts 0, set dumb_acc and dumb_recall on the test rows.
  3. For model, set lr_precision and lr_recall on the test rows.
  4. Make low_pred: predict 1 when the model's fraud probability is at least 0.2. Set low_precision and low_recall.
  5. Print the confusion matrix for both thresholds. In two sentences, say which threshold you would pick if each flagged transaction gets a quick human check, and why.

Practice lab

Edit the code and run it. Python runs in your browser — nothing is sent to our server.

Output

No output. Add print(...) to see values.

You are done when

Hard questions

Try to answer before you reveal. If you can answer these, you understood the lesson.

Q1Why is choosing the 0.2 threshold by looking at these test results not fully honest, and what would you do instead?Reveal

Any threshold chosen by looking at the test results is tuned to that test set, so the reported numbers become optimistic. Choose the threshold on a validation set, or with cross-validated probabilities on the training data, then apply it once to the test set.

Q2The test set has only a few dozen fraud cases. What does that mean for the recall number?Reveal

Each fraud case changes recall by several percentage points, so the number is rough. Report the counts (for example 20 of 24 caught), not just the percentage, and be careful about small differences between models. More fraud examples, or repeated splits, give steadier numbers.

Please sign in to continue.

Questions people ask

Which class should be "positive"?

By habit, the rare class you care about catching: fraud, disease, defect. Precision and recall are then about that class. scikit-learn treats label 1 as positive by default.

Is there a single best measure?

No. Choose the measure that matches the cost of each mistake in your situation, and always show the confusion matrix so others can see the full picture.

What is a precision-recall curve?

A chart of precision against recall at every possible threshold. It shows the whole trade-off at once, and helps you pick a threshold. scikit-learn has precision_recall_curve.

Can I choose the threshold on the test set?

No, that is a choice like any other. Choose it with validation data or cross-validation, then report the test result once.

Lesson test

5 questions. Get 3 right (60%) to pass and complete this lesson.

Sign in to take the test and save your progress