Classical ML · CS229

Model Selection & Regularization

Bias-variance told you a sweet spot exists. This is how you actually find it — without ever cheating by peeking at the test set — and how to dial in exactly the right complexity with a single knob.

Prerequisites: Bias & Variance. We turn its theory into practice.
10
Chapters
7+
Simulations
0
Assumed Knowledge

Chapter 0: The Trap That Always Picks the Worst Model

The bias-variance lesson left you with a cliffhanger. You learned that test error is U-shaped in model complexity, so somewhere there's an optimal complexity — not too simple, not too complex. But it left the burning practical question unanswered: how do you actually find that sweet spot? You're choosing whether to fit a degree-2, degree-5, or degree-9 polynomial. Which do you pick?

Here's the obvious idea, and it's a trap so seductive that nearly everyone falls into it once: train all the candidate models, and pick the one with the lowest error. Sounds reasonable. It is catastrophically wrong. Watch what happens.

"Pick the lowest training error" — the trap in action

Each bar is a candidate model's training error. Press Select best and the procedure dutifully picks the smallest bar. Watch which model it chooses — the most complex one, every single time — even though we know from the last lesson that the highest-degree model is usually a wild overfitting disaster. The faint test error markers reveal the truth: the "winner" is actually one of the worst.

picked:

The reason is exactly the bias-variance story in reverse. More complexity always fits the training data better — a degree-12 polynomial can wiggle through every point that a degree-2 can only approximate. So training error monotonically decreases with complexity and is minimized by the most complex model in your lineup. Selecting on training error doesn't find the sweet spot; it sprints straight past it to maximum overfitting. The very thing we're trying to avoid.

The fix is one of the most important ideas in all of applied machine learning, and it's beautifully simple: judge each model on data it didn't train on. Hold some data back, train on the rest, and pick the model that does best on the held-back portion — an honest preview of how it'll do on truly new data. That's cross-validation, and getting it right is the difference between a model that works in production and one that only worked in your notebook.

The plan. Ch.1: the golden rule (never select on training data) and the train/validation/test split. Ch.2–4: the cross-validation family — hold-out, k-fold, leave-one-out — for choosing complexity honestly. Ch.5–6: regularization, a continuous complexity dial, and the famous L1-vs-L2 choice that does automatic feature selection. Ch.7: a full model-selection workbench. Ch.8: the Bayesian view that reveals regularization as a prior belief. From "which model?" to "here's the principled answer."
Common misconception: "If a model has low training error, it's a good model." Low training error is necessary but wildly insufficient — the most overfit, useless model has the lowest training error of all. Training error measures memorization. It tells you the model can fit the data, not that it has learned anything generalizable. Never, ever select or evaluate a model by the error on the data it trained on.
Why does "train all candidate models and pick the one with the lowest training error" always fail for model selection?

Chapter 1: The Golden Rule — Three Sets, Three Jobs

The single most important discipline in machine learning is the separation of data into roles. To do model selection honestly, you split your data into three parts, each with a strict, non-overlapping job. Violate this separation and your reported performance is a fiction.

Training set (~60%)
Used to fit the parameters of each candidate model. The model sees this and learns from it.
Validation set (~20%)
Used to choose between models (which degree? which regularization?). The model never trains on it, so its error here is an honest comparison.
Test set (~20%)
Used once, at the very end, to report final performance. Touched by nothing during model-building. The sacred, untouched audit.

The crucial insight is why we need a separate validation set at all, distinct from the test set. When you use a held-out set to choose among many models, you are — subtly — optimizing against that set. Try 50 models and pick the one that happens to score best on your validation data, and you've partly fit to that data's quirks too. So the validation error of your chosen model is now slightly optimistic. That's why you keep a final test set, never used for any decision, to get a clean, unbiased estimate at the end.

Three sets, three roles, one rule. Train fits the model. Validation selects the model. Test reports the model. The rule that ties them together: a set used to make a decision can no longer give an unbiased estimate of that decision's quality. Each set is "spent" the moment you use it for a choice. The test set stays pristine because you make zero choices with it — you look at it exactly once, after everything is locked.

Why selecting on a set biases it

Imagine trying many random models and keeping the one that scores best on a single held-out set. Slide the number of models tried: watch the selected model's score on that set look better and better (you're cherry-picking lucky fits), while its true performance on fresh data stays flat. The gap is the optimism you bake in by selecting — and exactly why the final test set must be separate.

models tried1
selection-set score: · true fresh-data score:
Common misconception: "I tuned my model to get 99% on the test set — great result!" If you tuned to the test set, that 99% is meaningless — you turned your test set into a validation set and have no honest estimate left. The moment the test set influences any decision (which model, which hyperparameter, when to stop), it's contaminated. The discipline feels paranoid until the day your "99%" model ships and scores 80% on real users. Then it feels like wisdom.
Why do we keep a separate test set in addition to a validation set, instead of just using one held-out set for everything?

Chapter 2: Hold-Out Cross-Validation

Now the simplest honest model-selection algorithm, called hold-out cross-validation (or simple cross-validation). It's just the golden rule turned into a recipe:

  1. Randomly split your data: a training portion (say 70%) and a held-out validation portion (the other 30%).
  2. Train every candidate model on the training portion only.
  3. Measure each trained model's error on the validation portion — the validation error.
  4. Pick the model with the lowest validation error. Optionally, retrain it on all the data for the final model.

That's it. Because each model is judged on data it never saw, the validation error is an honest estimate of its generalization — so picking the lowest validation error picks the model that genuinely generalizes best. And here's the beautiful part: the validation error, unlike training error, is U-shaped in complexity, just like the test error from the last lesson. It falls as you escape underfitting, bottoms out at the sweet spot, then rises as you start overfitting. The bottom of that U is exactly the model you want.

Cross-validation finds the sweet spot

Slide through candidate complexities. Training error falls forever (useless for selection). But validation error traces a U — and its minimum, marked in green, is the complexity cross-validation selects. Notice it lands right where the true test error is lowest, without ever touching the test set. That's the magic.

complexity (degree)2
val error: · CV's choice: degree

Hold-out cross-validation is simple and fast, and it's what you'll reach for first. But it has a real weakness: it "wastes" 30% of your data — every model is trained on only 70% of what you have, and the validation estimate rests on just that one particular 30% split. With abundant data, who cares. But when data is scarce — say you have only 20 examples total — throwing away 6 of them for validation, and trusting one arbitrary split, is painful and unreliable. The next chapter fixes exactly this.

Common misconception: "A single train/validation split gives a reliable estimate of model quality." It gives an estimate, but a noisy one — you happened to hold out those particular points. A different random split could pick a different "best" model. With plenty of data the noise is small; with little data, one split can mislead you badly. That fragility — one split, one verdict — is the motivation for k-fold cross-validation, which averages over many splits.
In hold-out cross-validation, on which data do you measure the error used to select the best model?

Chapter 3: k-Fold Cross-Validation — Don't Waste Data

Hold-out cross-validation throws away a chunk of data and trusts a single split. k-fold cross-validation fixes both problems with one elegant idea: rotate the validation set so every point gets a turn.

Here's the recipe. Split your data into k equal-sized chunks, called folds (k = 5 or k = 10 are typical). Now, for each candidate model, do k rounds of training. In each round, hold out one fold as the validation set, train on the other k−1 folds, and record the validation error on the held-out fold. After k rounds, every fold has served as validation exactly once, and you have k validation errors. Average them. That average is your estimate of the model's generalization error — far more reliable than any single split, because it's smoothed over k different held-out sets.

The folds rotate — every point validated once

The data is split into k folds (rows). In each round, one fold (highlighted orange) is held out for validation while the rest (teal) train the model. Step through the rounds and watch the held-out fold rotate top to bottom. The final estimate is the average error across all rounds — using every data point for both training and validation, just never at the same time.

number of folds k5
round 1 of 5 · avg error so far:

The win is data efficiency. Each model now trains on a generous (k−1)/k fraction of the data — 90% of it with k = 10, versus only 70% for hold-out — and the estimate averages over k different validation sets instead of trusting one. The cost is compute: you train each candidate model k times instead of once. That trade — k× the compute for a much more reliable estimate that wastes almost no data — is usually well worth it, which is why k-fold (especially 10-fold) is the workhorse of practical model selection.

Why averaging helps so much. Any single validation split gives a noisy estimate — it depends on which points landed in the held-out set. The k fold-errors are k noisy estimates of the same quantity, and averaging noisy estimates reduces their noise (the same reason a poll of 1000 people beats asking 1). So k-fold doesn't just save data — it gives a lower-variance estimate of each model's quality, so your selection is less likely to be fooled by luck.
Common misconception: "More folds is always better, so use k as large as possible." More folds means more data per training round (less bias in the estimate) but also k× more compute and, at the extreme, more correlated training sets. There's a sweet spot — k = 5 or 10 captures most of the benefit at reasonable cost. Going to the extreme (k = n, the next chapter) is reserved for when data is so scarce you can't spare even one fold's worth.
What is the key advantage of k-fold cross-validation over a single hold-out split?

Chapter 4: Leave-One-Out — The Extreme Case

Push k-fold to its logical extreme. What if you set k equal to n, the total number of data points? Then each "fold" is a single example. You train on all n−1 other points, test on the one left out, and repeat n times — leaving out each point exactly once. This is leave-one-out cross-validation (LOOCV), and it's the most data-thrifty validation possible.

Every model trains on n−1 points — almost the entire dataset — so the validation estimate barely "wastes" any data at all. This makes LOOCV the natural choice when data is desperately scarce: a medical study with 20 patients, an expensive experiment with 15 trials. When every single example is precious, you can't afford to hold out a whole fold; LOOCV holds out the bare minimum — one point — each time.

Leave-one-out: each point gets a turn being the test

A small dataset. Step through: each round, one point (ringed orange) is held out, the model is fit to all the others (teal), and we measure the error on that one held-out point. After n rounds, every point has been the held-out test exactly once; the average of those n errors is the LOOCV estimate.

point 1/12 · LOOCV error:

So why isn't LOOCV always the answer? Two costs. First, compute: you retrain n times, which is brutal when n is large (imagine retraining a model a million times). Second — more subtly — the n training sets are almost identical (they differ by just one point), so the n error estimates are highly correlated, which can make the averaged estimate itself somewhat noisy in a different way. The practical wisdom: use LOOCV when data is truly scarce and models are cheap to train; use 5- or 10-fold otherwise. It's a spectrum, and you pick the point that fits your data budget and compute budget.

MethodData held outTrainings per modelBest when
Hold-out~30% (one split)1Data abundant, speed matters
k-fold (k=10)10% per roundk = 10The default workhorse
Leave-one-out1 point per roundnData very scarce, models cheap
Common misconception: "LOOCV is the gold standard because it uses the most data." It uses the most data per fit, but that's not free — the heavy compute and the high correlation between its near-identical training sets mean it isn't universally best. For many problems, 10-fold gives a comparably good estimate at a tenth of the cost. "Most data per fold" is one consideration, not the only one. Match the method to your constraints.
When is leave-one-out cross-validation the right choice?

Chapter 5: Regularization — A Continuous Complexity Dial

Cross-validation lets you choose among discrete models — degree 2 vs 3 vs 4. But complexity isn't really a staircase; it's a smooth ramp. What if you could adjust complexity continuously, with a single real-valued knob, and use cross-validation to set that knob precisely? That's regularization, and it's the most widely used complexity-control technique in machine learning.

The idea is to change what you're optimizing. Instead of minimizing just the training loss, you minimize the loss plus a penalty on the model's complexity:

Jλ(θ) = J(θ) + λ · R(θ)

Here J(θ) is the usual training loss (fit the data well), R(θ) is a regularizer — a measure of how complex or "large" the model is — and λ (lambda) is the regularization strength, the knob. The optimizer now has to balance two desires: fit the data (small J) and stay simple (small R). The parameter λ sets the exchange rate between them.

Watch what λ does, and notice it's a smooth version of the complexity slider:

One knob, the whole bias-variance spectrum

A flexible (high-degree) model fit with regularization strength λ. Slide λ from zero (a frantic overfit wiggle) up through smooth, well-generalizing fits, to over-regularized near-flat underfitting. The same expressive model, continuously tuned from overfit to underfit by one number. The validation error finds the best λ.

regularization λ0.00
validation error:  

This is why λ is so beloved: it decouples expressiveness from complexity. You can use a big, powerful model (so it can capture the truth — low bias potential) and then dial λ to control how much of that power it actually uses (taming variance). And since λ is just one continuous number, you tune it exactly the way you'd choose a discrete model: try a range of values, and let cross-validation pick the one with the lowest validation error.

Common misconception: "Regularization and more data are interchangeable ways to fight overfitting." They both reduce variance, but differently: more data reduces variance without adding bias (strictly better, but costly), while regularization reduces variance by adding a little bias (a trade, but free). When you can't get more data — the usual situation — regularization is how you buy variance reduction with a small bias payment. They're allies, not substitutes.
In the regularized objective J(θ) + λR(θ), what does increasing λ do?

Chapter 6: L2 vs L1 — Shrinking vs. Selecting

The penalty R(θ) measures "how big" the model is — but there are two famous ways to measure size, and they produce dramatically different behavior. This is one of the most practically important choices in machine learning.

L2 (ridge / weight decay): shrink everything

L2 regularization penalizes the sum of squared parameters. As you crank up λ, it shrinks all the weights smoothly and proportionally toward zero — but rarely makes any of them exactly zero. Every feature stays in the model, just with a smaller, gentler weight. In deep learning this is called weight decay (because each gradient step literally multiplies the weights by a shrink factor slightly less than one). L2 is the safe, smooth default: it tames variance by keeping the whole model modest.

L1 (lasso): zero things out

L1 regularization penalizes the sum of absolute parameters. This sounds like a tiny change, but it has a remarkable consequence: as λ grows, L1 drives many weights to exactly zero, one by one. A weight that hits zero means that feature is completely removed from the model. So L1 does automatic feature selection — it hands you a sparse model that uses only the handful of features that truly matter, and tells you which ones they are. Invaluable when you have thousands of features and suspect only a few are useful.

L1 zeros out, L2 shrinks — watch the weights

Each bar is one feature's weight. Slide the regularization strength and toggle the penalty type. Under L2, all bars shrink together but stay non-zero. Under L1, bars snap to exactly zero one at a time — the model is selecting which features to keep. The faint markers show the true (sparse) weights: L1 recovers them, L2 blurs them.

regularization strength0.00
non-zero weights:

Why the difference? It comes down to geometry. The absolute-value penalty has a sharp corner at zero, and that corner "catches" weights and pins them exactly at zero. The squared penalty is smooth at zero, so it pushes weights toward zero but never quite parks them there. (The picture is the famous diamond-vs-circle constraint region: the diamond's pointy corners poke out along the axes, where coordinates are zero, so the solution tends to land on them.) The practical upshot: use L2 when you think all features contribute a little; use L1 when you suspect most features are useless and want the model to find the important few. And you can blend them — that's the "elastic net."

Common misconception: "L1 and L2 are basically the same since both just shrink weights." Their behaviors diverge sharply where it matters: L1 produces sparse models (exact zeros, automatic feature selection), L2 produces dense models (everything small but present). If your goal is an interpretable model that names the few features that matter, only L1 gives it. If you just want to control variance and keep all features, L2 is smoother and usually easier to optimize. The choice encodes a genuine belief about your problem.
What distinctive thing does L1 regularization do that L2 does not?

Chapter 7: The Model-Selection Workbench

Let's run the whole pipeline end to end, the way you would on a real problem. Below is a workbench that does honest model selection live: it takes a dataset, runs k-fold cross-validation across a range of complexities (or regularization strengths), plots the validation curve, and automatically picks the winner — the model with the lowest cross-validated error. This is the machine that turns the bias-variance theory into a concrete, defensible choice.

What you're watching:
  • For each candidate complexity, the workbench runs full k-fold CV and plots the averaged validation error (with its spread across folds).
  • The green marker is cross-validation's automatic choice — the lowest point of the validation curve.
  • Change k or the amount of noise/data and watch the chosen complexity shift — more noise → CV picks a simpler model; more data → it can afford a more complex one.
  • The left panel shows the actual fitted model at the cross-validated choice — the model you'd ship.
Cross-Validation Workbench

Left: the data and the model CV selected. Right: the k-fold validation error across all candidate complexities, with the auto-selected minimum marked. Press Run k-fold CV to evaluate every candidate; adjust noise and data to see the choice adapt.

folds k5
data noise0.09
CV picks degree

Sit with what just happened: you chose the right model complexity without ever looking at the test set, purely from the training data, by being disciplined about what trains and what validates. That's the entire job. A practitioner who internalizes this — cross-validate to choose, regularize to fine-tune, never contaminate the test set — will out-perform someone with fancier models but sloppy methodology, every time. (No quiz — the workbench is the test. If you can predict how CV's choice shifts when you add noise, you've got it.)

Chapter 8: Regularization Is a Prior — The Bayesian View

There's a deeper story under regularization that connects it to the probabilistic thread running through this whole course. It turns out that adding a regularizer is mathematically identical to expressing a prior belief about the parameters — and recognizing this gives regularization a principled foundation, not just an "it works" justification.

Recall maximum likelihood from the earlier lessons: we chose the parameters that made the observed data most probable. That's the frequentist view — the true parameters are fixed-but-unknown constants, and we estimate them. The Bayesian view says something bolder: treat the parameters themselves as random, and before seeing any data, specify a prior distribution over them — your belief about what values are plausible. Then, after seeing the data, you update to a posterior via Bayes' rule (the same Bayes' rule from the generative-learning lesson). Choosing the most probable parameters under that posterior is called MAP estimation (maximum a posteriori).

And here's the punchline that ties it all together. When you work through the MAP math, the prior shows up as exactly a regularization term added to the log-likelihood:

The prior behind the penalty

A regularizer corresponds to a prior distribution over a weight. The Gaussian prior (behind L2) is smooth and round — it mildly prefers small weights. The Laplace prior (behind L1) has a sharp spike at zero — it strongly believes weights are exactly zero, which is why it produces sparsity. Toggle between them; the regularization strength λ is just how confident the prior is (how narrow the peak).

prior confidence (λ)1.0
The unification. Maximum likelihood with no prior gives you the unregularized fit (it's the λ = 0 case). Add a prior belief that weights should be small, and maximum-a-posteriori estimation hands you back exactly the regularized objective — with λ controlling how strongly you hold that prior belief. So "regularize to prevent overfitting" and "encode a prior that the model should be simple" are the same statement, viewed from two angles. Regularization isn't a hack bolted onto learning — it's what learning looks like when you admit you had beliefs before you saw the data.
Common misconception: "The Bayesian view is just philosophy — it doesn't change what you compute." It changes both your understanding and your toolkit. It tells you which regularizer to choose based on your actual beliefs (sparse problem → Laplace/L1), it justifies the strength λ as a degree of confidence, and it opens the door to full Bayesian methods that quantify uncertainty in predictions, not just point estimates. The philosophy has teeth.
From the Bayesian view, what does adding an L2 regularizer correspond to?

Chapter 9: Connections & Cheat Sheet

You now have the complete practical toolkit for the question that haunts every project: how complex should my model be, and how do I know? The answer — cross-validate to choose, regularize to fine-tune, never touch the test set — is the daily discipline of every serious practitioner.

The whole lesson on one page

ConceptWhat it means
Golden ruleNever select or evaluate a model on data it trained on. Train fits, validation selects, test reports.
Hold-out CVOne train/validation split. Simple, fast, but wastes ~30% of data and trusts one split.
k-fold CVRotate the validation fold k times, average the errors. Data-efficient, lower-variance estimate. The default (k=5 or 10).
Leave-one-outk = n. Holds out one point at a time. For very scarce data; expensive (n retrainings).
RegularizationMinimize J(θ) + λR(θ). A continuous complexity dial; λ trades bias for variance.
L2 (ridge / weight decay)Penalize squared weights. Shrinks all weights smoothly; keeps every feature. ≡ Gaussian prior.
L1 (lasso)Penalize absolute weights. Drives weights to exactly zero → sparse model, feature selection. ≡ Laplace prior.
MAP estimationRegularization = a prior belief that the model is simple. λ = how strongly you hold it.

The selection pipeline in code

python
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.linear_model import Ridge, Lasso

# k-fold CV to choose the regularization strength λ (called alpha here):
grid = GridSearchCV(
    Ridge(),
    {'alpha': [0.001, 0.01, 0.1, 1, 10, 100]},  # candidate λ values
    cv=10,                      # 10-fold cross-validation
    scoring='neg_mean_squared_error'
)
grid.fit(X_train, y_train)        # trains on TRAIN only
print(grid.best_params_)          # the CV-selected λ

# Only NOW, once, touch the test set for the final honest number:
final_score = grid.score(X_test, y_test)

# Lasso for sparse feature selection (L1):
lasso = Lasso(alpha=0.1).fit(X_train, y_train)
print((lasso.coef_ != 0).sum(), "features kept")  # the rest are exactly 0

Where to go next

You can now teach this. Never select on training error — it always picks the most complex model. Split data into train (fit), validation (select), test (report once). Cross-validation — hold-out, k-fold, leave-one-out — estimates generalization honestly by rotating what's held out. Regularization is a continuous complexity dial: L2 shrinks all weights (ridge/weight decay), L1 zeros them out (lasso/feature selection). And both are secretly priors — regularization is just admitting you believed the model should be simple before you saw the data. The discipline that makes everything else work.

"Everything should be made as simple as possible, but not simpler." — attributed to Einstein. Cross-validation finds the "as simple as possible"; regularization enforces it; the prior explains why it was the right idea all along.