Classical ML
Bias-Variance Tradeoff
Separate systematic error from sensitivity to the training sample. Read an error decomposition without assuming every model follows a U-shaped curve.
Jump to a section
Why this matters
A model can fail because its assumptions are too restrictive or because its fit changes too much with the training sample. Bias describes systematic error; variance describes sensitivity to the sampled data.
For squared error, the expected prediction error separates into bias squared, variance, and irreducible noise under the usual assumptions. The familiar U-shaped curve is a useful illustration, not a universal rule for every model.
Build the mental model
The dartboard analogy: bias is how far your cluster of shots sits from the bullseye; variance is how spread out the shots are. Four regimes:
- Low bias, low variance: tight cluster on target (the goal).
- High bias, low variance: tight cluster, wrong spot (underfit).
- Low bias, high variance: centered on average but scattered (overfit).
- High bias, high variance: scattered and off-target (worst case).
Read the trade-off, not just its name
Notice where the falling bias term and rising variance term give the lowest total error.
The visual loads as you reach this section.
Inspect the plotted values
x,y,series 1,4,Bias squared 1,0.035,Variance 1,0.3,Noise 1,4.335,Total 2,2,Bias squared 2,0.14,Variance 2,0.3,Noise 2,2.44,Total 3,1.3333333333333333,Bias squared 3,0.31500000000000006,Variance 3,0.3,Noise 3,1.9483333333333335,Total 4,1,Bias squared 4,0.56,Variance 4,0.3,Noise 4,1.86,Total 5,0.8,Bias squared 5,0.8750000000000001,Variance 5,0.3,Noise 5,1.9750000000000003,Total 6,0.6666666666666666,Bias squared 6,1.2600000000000002,Variance 6,0.3,Noise 6,2.2266666666666666,Total 7,0.5714285714285714,Bias squared 7,1.715,Variance 7,0.3,Noise 7,2.5864285714285713,Total 8,0.5,Bias squared 8,2.24,Variance 8,0.3,Noise 8,3.04,Total 9,0.4444444444444444,Bias squared 9,2.8350000000000004,Variance 9,0.3,Noise 9,3.5794444444444444,Total 10,0.4,Bias squared 10,3.5000000000000004,Variance 10,0.3,Noise 10,4.2,Total
As complexity rises, total error traces a U-shape: it falls while shrinking bias dominates, hits a minimum, then climbs as growing variance takes over.
Work through the math
For a true function with noise , , and a model trained on a random dataset, the expected squared error at a point decomposes exactly as:
Reading each term:
- Bias: how far the average prediction (over many training sets) is from the truth. Simple models (linear fit to a curve) have high bias.
- Variance: how much the prediction wobbles as the training set changes. Flexible models (a degree-15 polynomial on 20 points) have high variance.
- Irreducible: , the noise in the data itself; no model can beat it.
The expectation is over random draws of the training set. The tradeoff is structural: you cannot drive both the first two terms to zero with finite data.
Key takeaway: You diagnose from two numbers: high training error means bias; a large train-to-validation gap means variance. The fix always trades one for the other, and irreducible noise caps what any model can achieve.
Diagnosis to prescription:
| Symptom | Diagnosis | Levers |
|---|---|---|
| High train error, high val error | High bias (underfit) | Bigger model, better features, train longer |
| Low train error, large gap to val | High variance (overfit) | More data, regularization (L1/L2, dropout), simpler model |
| Both errors near the noise floor | Well-fit | Ship it |
Read the implementation
A direct demonstration: fit polynomials of increasing degree and watch train error fall while test error makes a U.
1import numpy as np
2from numpy.polynomial import polynomial as P
3
4rng = np.random.default_rng(0)
5
6def true_f(x):
7 return np.sin(2 * np.pi * x)
8
9def make_data(n):
10 x = rng.uniform(0, 1, n)
11 y = true_f(x) + rng.normal(0, 0.2, n) # signal + irreducible noise
12 return x, y
13
14x_train, y_train = make_data(20)
15x_test, y_test = make_data(500)
16
17for degree in (1, 3, 9, 15):
18 coeffs = P.polyfit(x_train, y_train, degree)
19 train_mse = np.mean((P.polyval(x_train, coeffs) - y_train) ** 2)
20 test_mse = np.mean((P.polyval(x_test, coeffs) - y_test) ** 2)
21 print(f"degree {degree:>2}: train={train_mse:.3f} test={test_mse:.3f}")
22
23# degree 1: high train + high test -> underfit (high bias)
24# degree 3: low train + low test -> sweet spot
25# degree 15: ~0 train + high test -> overfit (high variance)Questions and trade-offs
- Conceptual: Define bias and variance in one sentence each, in terms of training-set randomness. (Bias: error of the average prediction vs. truth. Variance: how much the prediction changes across different training sets.)
- Implementation: Given training error and validation error, how do you diagnose bias vs. variance? (High training error → high bias/underfit. Low training error but large train-val gap → high variance/overfit.)
- Applied: You're overfitting. Name three levers and which side of the tradeoff each moves. (More data, regularization, or simpler model → reduce variance, slightly raise bias.)
- Systems-level: How does cross-validation relate to this decomposition? (It estimates expected test error: averaging over folds approximates the expectation, exposing high-variance models that look great on one split.)
- Failure modes: Does the classic U-curve always hold? (Not always: deep, overparameterized nets can show "double descent," where test error falls again past the interpolation threshold. Know the exception exists.)
Check your understanding
From memory: write the three-term error decomposition, define each term in words, and state which way bias and variance move as model complexity increases. Then name one real exception to the U-curve. Check against Stages 3 and 5.
Oliver Perrin
Machine Learning Engineer · Founder, LiminalML
LiminalML brings together concept explanations, mathematical examples, and working visuals. Find more writing by Oliver on the LiminalML Substack.
Keep exploring the idea.
Ask a follow-up in a guided session, work through the self-check, or open the visual in Studio to build on it.