ml-models / Core ML concepts / The bias–variance tradeoff core

The bias–variance tradeoff

The expected squared error of a model on a new point splits exactly into three parts: bias (it is systematically wrong), variance (it changes a lot when the training set changes) and noise (nobody can predict it). This page derives the split, measures it by refitting the same model on hundreds of fresh training sets, and shows what learning and validation curves reveal about each part.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. The decomposition
  5. Measuring it
  6. Reading the parts from curves
  7. Averaging cuts variance
  8. Worked example
  9. Splitting the error of polynomial fits
  10. Two ways to cut the variance of degree 9
  11. Implementation
  12. Tradeoffs
  13. Check yourself
  14. Connections
  15. Read first
  16. Read next
  17. References

Problem

The overfitting page said that too simple a model underfits and too flexible a model overfits. That is a description. To reason about it, to say why an ensemble helps or why a penalty helps, we need the error written as parts that each respond differently to the choices we make.

The question: for a fixed query point, if we could redraw the training set many times and refit, what does the average squared error consist of?

Intuition

Picture a dart player throwing many rounds at one target. Bias is how far the centre of the cluster of darts is from the bullseye. Variance is how spread out the darts are around their own centre. A steady player who aims a little left has high bias and low variance; a shaky player who aims correctly on average has low bias and high variance. Noise is a target that itself jitters: even a perfect player misses by that much.

Each training set is one round of throws, and the fitted model's prediction at a point is the dart. A straight line fitted to a sine wave lands in nearly the same wrong place every time (high bias, low variance). A degree-9 polynomial tracks whichever noise it was shown (low bias, high variance). Ways to cut variance without raising bias much, such as averaging many models or penalizing large coefficients, are what make flexible models usable.

Expected error split into bias squared, variance and noise Stacked bars for polynomial fits of a noisy sine wave, 25 training rows, noise variance 0.09. Degree 1 has bias squared 0.196 and variance 0.031. Degree 3 has bias squared 0.005 and variance 0.021. Degree 5 has variance 0.093. Degree 7 has variance 2.065, far off the chart. Degree 9 with a ridge penalty of 0.01 brings the variance back down to 0.070. degree 1 degree 3 degree 5 degree 7 degree 9, ridge 0.01 0 0.6 (bars clipped) plain: bias², shaded: variance, grey: noise 0.09

Bias² falls as the degree rises while variance climbs; the ridge penalty pulls the degree-9 variance back down.

Mechanics

The decomposition

Let the data be y = f(x) + ε with E[ε] = 0 and Var(ε) = σ². Let f̂ be the model fitted on a random training set D. At a fixed x, the expectation below is over D and over the noise in the test label y:

E[(y − f̂(x))²]
  = E[(f(x) + ε − f̂)²]
  = σ²  +  E[(f(x) − f̂)²]                      (ε is independent of f̂ and has mean 0)
  = σ²  +  (f(x) − E[f̂])²  +  E[(f̂ − E[f̂])²]  (add and subtract E[f̂]; the cross term is 0)
  = noise + bias² + variance

Two steps do all the work. First, the noise in the new label is independent of anything the model learned, so its contribution separates. Second, adding and subtracting the average prediction E[f̂] makes the cross term vanish because E[f̂ − E[f̂]] = 0. Averaging over x as well gives a single number for each part. The result holds for squared error; other losses do not split this cleanly (see loss functions). The expectation and variance page has the rules used here.

Average fit against the true curve for a line and a cubic The true curve is a sine wave over x from 0 to 1. The average of 100 straight-line fits stays far from it, bending the wrong way near the peaks (bias squared 0.196). The average of 100 cubic fits lies almost on top of the sine wave (bias squared 0.0045). dashed grey: true sine solid: mean of line fits dotted: mean of cubic fits x from 0 to 1

Bias is the gap between a model family's average fit and the truth, not the error of any one fit.

Measuring it

On real data f is unknown and there is only one training set, so the three parts cannot be read off directly. They can be estimated when the data are simulated, as on this page: draw many training sets, fit each, and at each test x compute the mean prediction, the squared gap of that mean from f (bias²), and the spread of the predictions around it (variance). On real data the practical stand-ins are curves, covered next.

Reading the parts from curves

  • High bias. Training and validation error are both high and close together. A learning curve is flat: more rows do not help, because the model family cannot represent the pattern.
  • High variance. Training error is low, validation error is far above it. The learning curve's gap narrows as rows are added, since more data pins the fit down.
  • The validation curve over complexity is U shaped: the left arm is bias, the right arm is variance, the floor is noise plus whatever the best model leaves.

The fixes are asymmetric. Variance falls with more data, simpler models, regularization and averaging many models. Bias falls with richer models and better features, and does not fall with more data.

Averaging cuts variance

If M predictions each have variance v and pairwise correlation ρ, their average has variance ρv + (1 − ρ)v/M. Averaging leaves the bias of each member unchanged (the mean of the average is the average of the means) and shrinks the second term towards zero as M grows. Bagging makes the members differ by fitting each on a bootstrap resample of the rows; the more decorrelated the members, the more variance disappears. Boosting works the other way, mostly reducing bias, as the gradient boosting page showed.

ComponentShape/ParamsMeaningSource
learning_curve train_sizes, cv Train and validation scores against training set size: flat and together means bias, a closing gap means variance. _validation.py
_translate_train_sizes fractions or counts Turns the requested sizes into absolute row counts, rejecting sizes that do not fit. _validation.py
validation_curve param_name, param_range Scores one hyperparameter at each value with cross-validation: the U-shaped complexity curve. _validation.py

Worked example

Splitting the error of polynomial fits

Data: y = sin(2πx) + ε, ε ~ N(0, 0.3²), x uniform on 0 to 1, so σ² = 0.090. Each model is fitted on 100 different random training sets of 25 rows, and judged on 40 evenly spaced test points against the true sine (noise is added back as σ²). Averages over those points:

ModelBias²VarianceNoiseTotal
degree 10.19590.0310.0900.317
degree 30.00450.0210.0900.115
degree 50.00090.0930.0900.184
degree 70.03662.0650.0902.191
degree 9, ridge 0.010.00530.0700.0900.165

The straight line has bias² 0.196: no line fits a sine, however much data it sees. The cubic cuts bias² to 0.0045 at about the same variance, so it is the best row here, total 0.115, near the noise floor. Beyond that, bias² is already near zero and variance takes over: 0.093 at degree 5 and 2.065 at degree 7. The total is each row's bias² + variance + noise; the table adds exactly.

Two ways to cut the variance of degree 9

A degree-9 polynomial with almost no penalty (α = 0.001) has bias² 0.0034 and variance 0.226. Two remedies, same data and test points:

ModelBias²Variance
degree 9, α = 0.0010.00340.226
same, bagged over 15 bootstrap fits0.00160.176
degree 9, α = 0.010.00530.070

Bagging lowers the variance by 22% and leaves the bias nearly unchanged, as the averaging formula predicts. The members are strongly correlated (they see overlapping rows and fit the same family), so it does not approach 1/15. A ten-times larger penalty cuts the variance by 69% at the price of a little extra bias. Both are variance remedies; they differ in cost: bagging multiplies fitting time by 15, the penalty costs nothing extra.

Implementation

import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures

rng = np.random.default_rng(0)
grid = np.linspace(0.0125, 0.9875, 40).reshape(-1, 1)
truth = np.sin(2 * np.pi * grid[:, 0])

preds = []
for _ in range(200):                       # 200 fresh training sets
    x = rng.uniform(0, 1, (25, 1))
    y = np.sin(2 * np.pi * x[:, 0]) + rng.normal(0, 0.3, 25)
    model = make_pipeline(PolynomialFeatures(3), Ridge(alpha=1e-3)).fit(x, y)
    preds.append(model.predict(grid))
preds = np.array(preds)                    # (200, 40)

bias2 = ((preds.mean(axis=0) - truth) ** 2).mean()
variance = preds.var(axis=0).mean()
print(bias2, variance, 0.3 ** 2)           # error = sum of the three

This only works when the truth is known. For real data, fit with learning_curve and validation_curve from sklearn.model_selection and read the diagnosis from the shapes described above.

Tradeoffs

  • Squared error only. The clean three-way split needs squared loss. Classification with 0–1 loss has several competing decompositions, and none is as tidy.
  • Not a strict ladder. The "U" shape is typical for classical models but not universal. Very large networks and boosted ensembles can reach zero training error and still improve on new data as they grow (double descent; see generalization), so variance does not always rise with capacity.
  • Hard to measure for real. With one dataset you cannot redraw training sets; bootstrap resampling approximates it, and learning curves give indirect evidence. The numbers on this page come from a known truth.
  • Remedies trade against each other. A bigger penalty cuts variance and raises bias; the best setting is found by validation, not by theory.
  • Even-degree polynomials look odd here. The sine is an odd function around the middle of the interval, so degree 2 adds variance and no bias reduction over degree 1; the table sticks to odd degrees.

Check yourself

A model has training error 0.30 and validation error 0.31, with a noise floor of 0.05. Which term dominates, and will 10× more data help?

Bias: both errors are high and together. More data will not help; the model family needs more capacity or better features.

Why does averaging 100 identical copies of one model not reduce its variance?

The members are perfectly correlated (ρ = 1), so ρv + (1 − ρ)v/M is just v. Averaging only helps to the extent that the members make different errors.

In E[(y − f̂)²] = σ² + bias² + variance, which term can a better model never remove?

The noise σ²: it is the variance of the label around f(x), which no function of x can predict.

Connections

References

  • Hastie, Tibshirani and Friedman. The Elements of Statistical Learning, section 7.3: the bias–variance decomposition.
  • Geman, Bienenstock and Doursat. Neural networks and the bias/variance dilemma. Neural Computation, 1992.
  • Belkin, Hsu, Ma and Mandal. Reconciling modern machine-learning practice and the classical bias–variance trade-off. PNAS 2019. arXiv:1812.11118.
  • Breiman. Bagging predictors. Machine Learning, 1996.
  • scikit-learn 1.9.1: learning_curve, validation_curve.