Quant Memo
Core

K-Fold Cross-Validation

A way to estimate how a model will perform on data it hasn't seen by rotating which slice of the dataset is held out for testing, so every observation gets used for both fitting and checking without ever being tested on data it was trained on.

Prerequisites: The Bias-Variance Decomposition, In-Sample and Out-of-Sample Protocol

You have 200 days of labeled data — say, whether a stock beat its sector the next week — and you want an honest estimate of how a model will do on data it hasn't seen. Split the 200 days once into 160 training and 40 testing, and you get a single number that depends heavily on which 40 days happened to land in the test set: a calm stretch versus a volatile one can swing your accuracy estimate by several points, and you've thrown away information, since those 40 days never helped train the model. K-fold cross-validation fixes both problems by rotating which slice gets held out, so every observation eventually plays both roles.

An analogy: rotating who grades the essays

Imagine five study partners preparing for the same exam. Instead of one partner grading everyone's practice essays, the group splits into five smaller groups. Each round, four groups study together and write practice essays, and the fifth group — who studied nothing that round — grades them. Over five rounds, every group gets a turn grading and being graded, and you average the five scores into one estimate. No one's essays are graded by someone who helped write them.

The mechanics, one symbol at a time

Split the nn observations into kk roughly equal, non-overlapping groups called folds. For each fold i=1,,ki = 1, \dots, k: train the model on all data except fold ii, then measure its error on fold ii alone — data the model never touched during that round. The cross-validated error is the average across all kk rounds:

CV(k)=1ki=1kErrori.CV_{(k)} = \frac{1}{k} \sum_{i=1}^{k} \text{Error}_i .

In plain English: instead of one train/test split producing one possibly-lucky or possibly-unlucky number, you get kk independent-ish estimates and average them, which is both more stable and uses every observation for testing exactly once.

Worked example: 10 days, 5 folds

Suppose you have 10 days of return-prediction errors and use k=5k=5, so each fold holds out 2 days. Round 1 trains on days 3–10 and tests on days 1–2, getting a mean-squared error of 0.400.40. Rounds 2 through 5 rotate the held-out pair and give errors of 0.550.55, 0.350.35, 0.600.60, and 0.500.50:

CV(5)=0.40+0.55+0.35+0.60+0.505=0.48.CV_{(5)} = \frac{0.40+0.55+0.35+0.60+0.50}{5} = 0.48 .

If by chance a single 80/20 split had put the two easiest days (behind the 0.35 result) in the test set, you'd have reported 0.35 and believed the model noticeably better than it is. Averaging across rotations smooths out that luck.

Choosing k

Larger kk (up to k=nk=n, leave-one-out) trains on more data per round, giving a nearly unbiased estimate, but the resulting error estimates are highly correlated and expensive to compute. Smaller kk trains on less data each round, biasing the estimate upward, but runs fast. In practice k=5k=5 or k=10k=10 is the standard compromise.

Round 1 Round 2 Round 3 Round 4 Round 5 held-out test fold training folds
Each round trains on four-fifths of the data and tests on the remaining fifth; the red block rotates until every observation has been tested exactly once.

What this means in practice

K-fold cross-validation is the default tool for choosing hyperparameters and getting an honest read on a model's generalization error before it ever sees live data. It's also the workhorse behind grid search: you run cross-validation once per candidate hyperparameter setting and pick the one with the lowest average error. But standard k-fold assumes each observation is roughly independent of the others — a plain random shuffle of time-ordered financial data lets a model trained on data from next week "peek" at patterns and test on last week, silently leaking information across the split.

K-fold cross-validation rotates which slice of the data is held out for testing across kk rounds, using every observation for both training and testing exactly once, and averages the kk error estimates into one more stable number than any single train/test split could give.

Applying plain k-fold cross-validation to time-ordered financial data by shuffling observations randomly breaks the arrow of time: a fold can end up training on future prices to predict the past. For return series or panel data with time dependence, use a purged, embargoed, or walk-forward variant instead of a naive random split.

Related concepts

Practice in interviews

Further reading

  • Hastie, Tibshirani & Friedman, The Elements of Statistical Learning, ch. 7
ShareTwitterLinkedIn