Overfitting and Evaluation

Why a model can ace its training data and still fail on new cases, how to test it honestly, and what accuracy, precision and recall each tell you.

15 minLesson 4 of 7
On this page
  1. Memorizing versus learning
  2. Test on data the model has never seen
  3. Accuracy can lie
  4. Precision and recall
  5. Common mistakes
  6. Try it yourself
  7. Check yourself
  8. What to practise next

A student gets hold of last year's exam and memorizes every answer. On a practice run with those same questions, they score 100%. On the real exam, with new questions on the same topics, they fail. They learned the answers, not the subject.

Models do the same thing, and it is one of the most common ways machine learning goes wrong. A model that scores brilliantly on the examples it trained on can be useless on the next one. You need a way to catch that before your users do.

Memorizing versus learning

Go back to the delivery data from What Is Machine Learning. The rule "late if at least 6 km" got 6 of 7 past deliveries right. The odd one out was a 10 km trip that arrived on time.

Now consider a fussier rule: "late if 6 to 9 km, or 11 km and over". It gets all 7 right. It is also nonsense. It says a 10 km delivery will be on time while 9 km and 11 km ones will be late, all because of one lucky day. The fussier rule fits the training data better and will predict new deliveries worse.

This is overfitting: the model has learned the noise in its examples along with the pattern. The opposite problem, underfitting, is a model too simple to capture the pattern at all, like predicting "on time" for every delivery.

error model flexibility: simple → complex sweet spot underfitting overfitting training error error on new data
As a model gets more flexible, its error on training data keeps falling. Its error on new data falls, then rises again once it starts memorizing noise.

Test on data the model has never seen

The fix is simple and strict. Before training, split your labelled examples into separate piles:

  • Training set (often around 70 to 80%). The model learns from these.
  • Validation set. You use these to compare settings: which features, how flexible a model, which learning rate.
  • Test set. Locked away until the very end. You score the final model on it once, and that score is your honest estimate of real-world performance.

Two piles are not enough. Every time you look at a score and change something, you are fitting a little to that data. Try twenty settings on the validation set and keep the best, and that best score is a bit flattering. The test set has not been part of any choice, so its score is not.

When data is scarce, cross-validation makes better use of it: split the training data into, say, 5 parts, train 5 times, each time holding out a different part for scoring, and average the 5 scores.

Accuracy can lie

With a fair test set in hand, the obvious number to measure is accuracy: the share of predictions that were right. It hides a trap.

Picture a fraud detector checking 10 card payments, of which 3 are fraud. A lazy model that always says "fine" is right 7 times out of 10: 70% accuracy, while catching zero fraud. In real data, fraud is far rarer than 3 in 10, so the lazy model can score 99.9%.

To see what a classifier really does, sort every prediction into one of four boxes. This table is called a confusion matrix:

  • True positive: fraud, and flagged. Good.
  • False negative: fraud, but not flagged. A missed case.
  • False positive: fine, but flagged. A false alarm.
  • True negative: fine, and not flagged. Good.
Filling a confusion matrix for 10 paymentsUse the arrows or ← → keys
  1. actual
    0Fi
    1ok
    2ok
    3F
    4ok
    5ok
    6ok
    7F
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud10
    fine00

    1Payment 0 is fraud and the model flagged it. True positive.

  2. actual
    0F
    1oki
    2ok
    3F
    4ok
    5ok
    6ok
    7F
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud10
    fine01

    2Payment 1 is fine and was not flagged. True negative.

  3. actual
    0F
    1ok
    2oki
    3F
    4ok
    5ok
    6ok
    7F
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud10
    fine11

    3Payment 2 is fine, but the model flagged it. False positive: a customer gets an annoying call from the bank.

  4. actual
    0F
    1ok
    2ok
    3Fi
    4ok
    5ok
    6ok
    7F
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud11
    fine11

    4Payment 3 is fraud, and the model let it through. False negative: real money is lost.

  5. actual
    0F
    1ok
    2ok
    3F
    4ok
    5ok
    6oki
    7F
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud11
    fine14

    5Payments 4, 5 and 6 are fine and pass. Three more true negatives.

  6. actual
    0F
    1ok
    2ok
    3F
    4ok
    5ok
    6ok
    7Fi
    8ok
    9ok
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud21
    fine14

    6Payment 7 is fraud and flagged. Second true positive.

  7. actual
    0F
    1ok
    2ok
    3F
    4ok
    5ok
    6ok
    7F
    8ok
    9oki
    flagged
    0yes
    1no
    2yes
    3no
    4no
    5no
    6no
    7yes
    8no
    9no
    flaggednot flagged
    fraud21
    fine16

    7Payments 8 and 9 are fine and pass. The matrix is complete: every payment is in exactly one box.

  8. flaggednot flagged
    fraud21
    fine16
    accuracy
    8/10

    8Accuracy: the diagonal boxes are correct, 2 + 6 = 8 out of 10, so 80%.

  9. flaggednot flagged
    fraud21
    fine16
    accuracy
    8/10
    precision
    2/3
    recall
    2/3

    9Precision: of the 3 payments flagged, 2 were fraud (2/3). Recall: of the 3 frauds, 2 were caught (2/3).

  10. flaggednot flagged
    fraud03
    fine07
    accuracy
    7/10
    recall
    0/3

    10Compare the lazy model that never flags anything: 70% accuracy, only 10 points lower, but recall is 0/3. It catches nothing. Accuracy alone hid that.

1 / 10

Precision and recall

Two questions matter for almost every classifier, and each has its own number.

  • Precision: when the model raises a flag, how often is it right? It is true positives ÷ everything flagged. Low precision means many false alarms.
  • Recall: of all the real cases, how many did the model catch? It is true positives ÷ all real positives. Low recall means many misses.

They pull against each other. In Regression and Classification you saw that raising the threshold flags fewer messages. That usually raises precision (the flags you do raise are surer) and lowers recall (you miss more). Which matters more depends on the cost of each mistake. A cancer screening wants high recall. A system that blocks customer accounts automatically wants high precision.

// actual and predicted are lists of 1 (fraud) and 0 (fine).
function evaluate(actual, predicted) {
  let tp = 0, fp = 0, fn = 0, tn = 0;
  for (let i = 0; i < actual.length; i++) {
    if (predicted[i] === 1 && actual[i] === 1) tp++;
    else if (predicted[i] === 1) fp++; // flagged a fine payment
    else if (actual[i] === 1) fn++;    // missed a fraud
    else tn++;
  }
  return {
    accuracy: (tp + tn) / actual.length,
    precision: tp + fp ? tp / (tp + fp) : 0, // of the flags, how many were right
    recall: tp + fn ? tp / (tp + fn) : 0,    // of the frauds, how many we caught
  };
}
 
evaluate([1, 0, 0, 1, 0, 0, 0, 1, 0, 0],
         [1, 0, 1, 0, 0, 0, 0, 1, 0, 0]);
// { accuracy: 0.8, precision: 0.666..., recall: 0.666... }
def evaluate(actual, predicted):
    """actual and predicted are lists of 1 (fraud) and 0 (fine)."""
    tp = fp = fn = tn = 0
    for a, p in zip(actual, predicted):
        if p == 1 and a == 1:
            tp += 1
        elif p == 1:
            fp += 1   # flagged a fine payment
        elif a == 1:
            fn += 1   # missed a fraud
        else:
            tn += 1
    return {
        "accuracy": (tp + tn) / len(actual),
        "precision": tp / (tp + fp) if tp + fp else 0,
        "recall": tp / (tp + fn) if tp + fn else 0,
    }
 
evaluate([1, 0, 0, 1, 0, 0, 0, 1, 0, 0],
         [1, 0, 1, 0, 0, 0, 0, 1, 0, 0])
# {'accuracy': 0.8, 'precision': 0.666..., 'recall': 0.666...}

For regression, the usual test number is the average error on the test set, for example the mean squared error from How Models Learn, or the average absolute error, which is easier to explain: "off by 40 a month on average".

Common mistakes

Try it yourself

Try it: Find the flaws in this evaluation

A team reports on a model that predicts whether a customer will cancel their subscription next month. List everything that makes you doubt the result, and what you would ask for instead.

Show a sample answer
  • Scored on training data. 96% on the examples it learned from says little. Ask for a score on a held-out test set, ideally the most recent months, since this is a prediction about the future.
  • Accuracy with rare positives. With 4% cancelling, a model that predicts "stays" for everyone scores 96%. That is exactly the number reported. Ask for precision and recall on the customers who cancel.
  • Possible leakage. "Days since last login", measured today, is huge for customers who already cancelled, because they stopped logging in after leaving. Ask for each feature to be measured as of the date the prediction would have been made.

Any one of these could make the model look good while being useless. Together, they almost guarantee it.

Check yourself

Quick check

A model scores 99% on its training data and 71% on the test set. What is the most likely problem?

A disease affects 1 in 200 people. A test that always says "healthy" has what accuracy?

A spam filter has high precision and low recall. What do users notice?

What to practise next

To code the confusion matrix and these scores yourself, try Bruised Apple Detector. The models so far were weighted sums. Decision Trees and Ensembles learns a different kind of rule, a chain of yes-or-no questions, and shows how combining many trees fights overfitting.