Home / Machine Learning

Cross Validation

Interactive explorer to visualize K-Fold splitting and validation logic.

Overview

What a single split cannot tell you

Hold out 20% of the data, train on the rest, and you get an accuracy figure. Change the random seed and you get a different one — sometimes several points different on a small dataset.

Neither number is wrong; both are estimates from a sample of test rows, and a sample of 200 rows carries real sampling error. With one split you have no way to tell whether a two-point difference between two models is a genuine improvement or the luck of the partition.

Configuration

5

Higher k creates more folds but requires more training iterations.


Visualization

Fold: - / -
Train Data
Test Fold

Results

Ready to start.
Click 'Run Next Fold'
Average Accuracy
--%

K-Fold Cross Validation: A Practical Guide

One train-test split gives you one number, and that number depends on which rows happened to land in the test set. K-fold cross-validation uses every row for testing exactly once and reports the spread as well as the average.

How k-fold works

Split the data into k equally sized folds. Then run k separate experiments: hold out fold 1 and train on folds 2…k, hold out fold 2 and train on the rest, and so on. Each fold serves as the test set exactly once, and each row is predicted exactly once by a model that never saw it.

You end up with k scores. Report the mean as the performance estimate and the standard deviation as a measure of how stable it is. The standard deviation is the part a single split cannot give you, and it is often the more informative number: a model scoring 0.82 ± 0.01 is a very different proposition from one scoring 0.82 ± 0.09.

k = 5 or k = 10 are the standard choices. Larger k means more training data per fold (less pessimistic bias) but k model fits and more correlated training sets. The extreme, k = n, is leave-one-out: nearly unbiased, high variance, and usually far too expensive.

Why one number is not an answer

Suppose you split 1,000 rows into 800 for training and 200 for testing, and the model scores 87%. Report that and you have said something slightly dishonest, because you have not said how much of the 87% was luck.

Shuffle the data differently and try again: 84%. Again: 89%. Again: 86%. The model did not change. What changed is which awkward rows happened to land in the test set.

The spread across those runs is the thing a single split hides. A model that scores 87% ± 1% and a model that scores 87% ± 6% are not equally trustworthy, and only the second one is going to embarrass you in production. Cross-validation exists to put the ± on the number.

It also fixes a quieter waste. With one split, 20% of your data only ever gets tested on and 80% only ever gets trained on. When data is expensive — medical records, labelled defects, anything a human had to annotate — throwing away a fifth of it is a real cost. K-fold uses every row for both jobs, just never at the same time.

A worked example with 100 rows

Take 100 rows and k = 5. Cross-validation shuffles them, cuts them into five blocks of 20, and runs five separate experiments:

RunTrained onTested onScore
1Blocks 2,3,4,5Block 10.86
2Blocks 1,3,4,5Block 20.91
3Blocks 1,2,4,5Block 30.79
4Blocks 1,2,3,5Block 40.88
5Blocks 1,2,3,4Block 50.84

The headline is the mean: (0.86 + 0.91 + 0.79 + 0.88 + 0.84) / 5 = 0.856. The standard deviation is about 0.04, so the honest summary is "roughly 0.86, give or take 0.04".

Block 3 is worth a look on its own. A fold that scores well below the others is not noise to be averaged away — it usually means those twenty rows contain something the others do not. A different time period, a different hospital, a different product line. Open them.

Note what five folds cost: five models trained instead of one. That is the whole downside, and it is why k = 5 or k = 10 are the standard choices. Ten folds give a slightly better estimate for double the compute, and beyond that the returns are tiny.

Choosing k, and the leave-one-out extreme

  • k = 5 — the everyday default. Each model trains on 80% of the data, and you pay five times the training cost.
  • k = 10 — the classic recommendation. Each model sees 90% of the data, so the estimate is a little less pessimistic, and the variance across folds is usually smaller.
  • k = n (leave-one-out) — every single row takes a turn as the test set. Almost unbiased, but you train n models, and the folds are so similar to each other that the estimate can be surprisingly unstable. Reserve it for very small datasets.

There is a real tension here. Small k means each model trains on less data, so every fold's score is pessimistic compared to the model you will finally ship on 100% of the data. Large k reduces that pessimism but makes the training sets nearly identical, which means the fold scores stop being independent estimates. Five and ten are compromises that have survived decades of practice.

For very small datasets, repeated k-fold is often the better move: run 5-fold cross-validation five times with different shuffles and average the twenty-five scores. You get a much steadier estimate without the pathologies of leave-one-out.

Nested cross-validation, and why the ordinary kind can lie

Here is the trap that catches most people once. You use cross-validation to pick hyperparameters — try thirty settings, keep whichever scored best — and then report that best score as the model's performance.

That number is optimistic, and sometimes badly so. You searched thirty options against those exact folds and kept the luckiest. The score now includes the luck.

The fix is nested cross-validation. An outer loop splits off a test fold and never touches it for tuning. Inside each outer training set, an inner cross-validation picks the hyperparameters. The outer fold then scores that tuned model on data no part of the tuning ever saw.

from sklearn.model_selection import GridSearchCV, cross_val_score, KFold

inner = KFold(n_splits=5, shuffle=True, random_state=0)
outer = KFold(n_splits=5, shuffle=True, random_state=1)

search = GridSearchCV(model, param_grid, cv=inner)      # picks the settings
scores = cross_val_score(search, X, y, cv=outer)        # scores them honestly

print(scores.mean(), scores.std())

It costs k×k model fits, so it is not free. Use it when you are reporting a number someone will make a decision on. For day-to-day iteration, plain k-fold plus one genuinely untouched hold-out set is the pragmatic compromise.

The same discipline applies to preprocessing. Fit your scaler, your imputer and your feature selector inside each fold, by putting them in a Pipeline. Scaling the whole dataset before splitting leaks the test folds' means and standard deviations into training, and quietly inflates every score you produce.

One split lies; five splits argue

The same model and the same data, scored by many different single splits and then by cross-validation. The spread between splits is the point.

example_01.pyscikit-learn
Output

Guided experiments

  1. Watch each fold take its turn. Set Folds (k) to 5 and press Run Next Fold repeatedly. A different block becomes the test set each time, and the remaining blocks train.
  2. Compare the fold scores. Press Auto-Run All and look at the individual results, not just the average. The spread between the best and worst fold is exactly the uncertainty a single split would have hidden.
  3. Increase k. Set Folds (k) to 10 and run again. Each model trains on 90% of the data instead of 80%, and there are twice as many fits to pay for.
  4. Shuffle first. Press Shuffle and re-run. If the data arrived sorted by class, unshuffled folds can be wildly unrepresentative — which is why stratification exists.

Stratified, grouped, and time series

Plain k-fold splits at random, which is wrong in three common situations:

  • Imbalanced classes — a random fold might contain almost none of the minority class. Stratified k-fold preserves the class proportions in every fold, and should be the default for classification.
  • Grouped data — multiple rows per patient, user or session. If rows from the same group land in both train and test, the model has effectively seen the answer. GroupKFold keeps each group entirely on one side.
  • Time series — random folds train on the future to predict the past, which is leakage in its purest form. Use a forward-chaining split where each fold trains only on data preceding its test window.

Where this goes wrong

  • Preprocessing before splitting. Fitting a scaler, imputer or feature selector on the whole dataset leaks test information into training and inflates every fold. Put the preprocessing inside a Pipeline so it is refitted within each fold.
  • Random folds on time series. Produces excellent scores and a model that fails in production.
  • Reporting only the mean. The standard deviation is what tells you whether a difference between two models is meaningful.
  • Tuning hyperparameters on the same CV used to report the score. Selecting on those folds means the reported number is optimistic; nested cross-validation is the correct fix.
  • Forgetting to shuffle sorted data. Folds become unrepresentative before stratification can help.

What to remember

K-fold cross-validation trains k models, each holding out a different fold, so every row is tested once and you get a mean and a spread rather than one seed-dependent number. Use stratified folds for classification, grouped folds when rows share a subject, and forward-chaining for time series. Whatever the variant, fit every preprocessing step inside the fold — doing it beforehand leaks the test set and quietly invalidates the whole exercise.

Questions people ask

Do I still need a separate test set? Yes, if you can afford one. Cross-validation is for choosing between options; a final untouched hold-out is for the number you tell other people. Every decision you make while looking at the cross-validation scores erodes their independence a little.

Which model do I actually deploy? Not any of the k fold models. Once cross-validation has told you which method and which settings to use, retrain on all the data and ship that. The fold scores were an estimate of how that final model will behave.

Should I shuffle? Usually yes — data that arrived sorted by class or by date makes unshuffled folds wildly unrepresentative. The exception is time series, where shuffling destroys the point entirely.

What if the folds disagree a lot? Treat it as a finding. Wide spread means your dataset is small, heterogeneous, or contains groups the split is cutting through. Look at the worst fold's rows before you look at a different algorithm.

Is cross-validation useful for deep learning? Rarely, and only for small datasets. Training a large network five times is often unaffordable, and with hundreds of thousands of examples a single well-constructed validation set is stable enough.

Can I cross-validate on imbalanced data? Yes, with StratifiedKFold, which keeps each fold's class proportions the same as the whole. Without it, a rare class can be absent from a fold entirely and the score for that fold becomes meaningless.

Recap in one screen

  • One split gives one number; cross-validation gives a mean and a spread, and the spread is the part you were missing.
  • k-fold trains k models, testing each row exactly once. k = 5 or 10 is the normal choice.
  • Stratify for classification, group for repeated subjects, and never shuffle time series.
  • Fit every preprocessing step inside the fold, using a pipeline, or you leak.
  • Tuning on the same folds you report inflates the result — nest the loops when the number matters.
  • Retrain on everything before you deploy.

Check yourself

0 of 3

Answer without scrolling back up.

  1. What problem does k-fold cross-validation solve?

  2. For an imbalanced dataset, which variant should you reach for?

  3. What is the main cost of cross-validation?

Cheat sheet

K-Fold Cross Validation

Hold out 20% of the data, train on the rest, and you get an accuracy figure. Change the random seed and you get a different one — sometimes several points different on a small dataset.

MACHINE LEARNING · vizlearn.in/machine_learning/cross_validation.html

Further reading

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.