Overfitting, Underfitting, and Bias-Variance Tradeoff

A model that aces the training set but fails on new data is not "smart" — it memorized. The whole point of learning is generalization: good predictions on data the model has never seen. This topic exists because a model can look great on training data and still fail on unseen data.

Intuition

Imagine studying for an exam. Underfitting is only reading the chapter titles: you miss detail, so you score poorly on practice and on the real test. Overfitting is memorizing the answer key for last year's quiz: practice scores soar, but a reworded question on exam day wrecks you.

In ML terms:

Roughly:

Expected error ~= Bias^2 + Variance + Noise

You rarely minimize bias and variance at once. Bigger models and longer training cut bias but can inflate variance unless you regularize, add data, or stop early.

How it works

Detect with curves, not vibes. Plot training loss/metric and validation loss/metric over epochs (or model complexity).

Plain-English idea When to use it
Both train and val error high, gap small Underfitting — model too simple
Train error low, val error high, gap growing Overfitting — model memorizing training quirks
Both train and val error low, gap modest Healthy fit — model generalizing well

A rising validation loss while training loss still falls is the classic overfit signal. In LLMs and classifiers, watch validation perplexity or F1 the same way.

Capacity knobs. More parameters, deeper trees, higher polynomial degree, longer fine-tuning -> more capacity -> lower bias risk, higher overfit risk. Less capacity or stronger constraints -> opposite.

What helps.

avg_score = (score_1 + score_2 + ... + score_k) / k
flowchart TB subgraph under ["Underfit: high bias"] U1[Simple model] --> U2[High train error] U2 --> U3[High val error] end subgraph over ["Overfit: high variance"] O1[Complex model] --> O2[Low train error] O2 --> O3[High val error] end subgraph good ["Sweet spot"] G1[Right capacity + data] --> G2[Low train and val error] end

Bias-variance is a lens, not a single number you compute daily. In deep learning the picture is richer: double descent can show test error rising then falling again as models grow past the interpolation threshold, and large models sometimes fit train data near-perfectly yet still generalize (benign overfitting). Those phenomena refine the classical U-shaped curve; they do not retire the practical checklist. Still compare train vs held-out metrics, still react when the gap balloons on your task and data regime.

In code

Fit polynomials of increasing degree to a noisy sine wave. Low degree underfits; high degree overfits the noise.

import numpy as np

rng = np.random.default_rng(0)
n = 30
x = np.linspace(0, 1, n)
y_true = np.sin(2 * np.pi * x)
y = y_true + 0.3 * rng.normal(size=n)

# Hold out last 8 points as "validation"
x_train, y_train = x[:22], y[:22]
x_val, y_val = x[22:], y[22:]

def mse(y_hat, y):
    return float(np.mean((y_hat - y) ** 2))

print(f"{'degree':>6}  {'train MSE':>10}  {'val MSE':>10}")
for degree in [1, 3, 9, 15]:
    # Vandermonde design matrix
    Phi_tr = np.vander(x_train, N=degree + 1, increasing=True)
    Phi_va = np.vander(x_val, N=degree + 1, increasing=True)
    # Least squares
    coef, *_ = np.linalg.lstsq(Phi_tr, y_train, rcond=None)
    tr = mse(Phi_tr @ coef, y_train)
    va = mse(Phi_va @ coef, y_val)
    print(f"{degree:6d}  {tr:10.4f}  {va:10.4f}")

Expect degree 1: both MSEs mediocre (underfit). Mid degrees: both improve. Very high degree: train MSE near zero while validation MSE blows up (overfit). That gap is the lesson.

What goes wrong

One-line summary

Underfitting is a model too simple for the pattern (high bias); overfitting is a model that memorizes training quirks (high variance) — watch the train/validation gap and aim for the capacity sweet spot.

Key terms