Concepts / k-Fold Cross-Validation

k-Fold Cross-Validation

Stratified k-fold cross-validation keeps the proportions between classes the same in each fold as in the whole dataset.

  • Programming
Interactive lab

Try it: K-Fold Cross-Validation and Model Selection

How hold-out and k-fold cross-validation choose among candidate models: each fold takes a turn as the validation set, the fold errors are averaged, the lowest average picks the model, and only then is the untouched test set used once — while tuning on the test set would report an optimistic number.

How it works

  1. Set 10 test points aside; they take no part in any choice.
  2. Optionally shuffle the 20 development points (seeded), then cut them into k folds (hold-out: one chunk validates, the rest trains).
  3. For every round, train each candidate polynomial on the other folds and measure its validation MSE on the held-out fold.
  4. Average each candidate's fold errors (with repeated shuffling: average the p run scores; p × k fits per candidate).
  5. Select the candidate with the lowest average, retrain it on all development points, and evaluate it once on the test set.
  6. For contrast, show which candidate the test set itself would have picked — that figure is leaked and optimistic.

Default run (12 steps): 20 development points (sorted by x as they arrived) and 10 test points set aside. Candidates: polynomial degree 0, 1, 2, 3, 4, 5, 6. Plan: 5-fold cross-validation, shuffled with seed 1 → 5 fits per candidate (p × k = 1 × 5), 35 in total. … 5-fold cross-validation picks degree 3 (score 0.0768); its honest test MSE is 0.0889. Picking by test error would report degree 4 at 0.0868 — a leaked, optimistic figure.

Simplified: One fixed toy dataset (20 development + 10 test points from sin(2πx) with noise) and polynomial least squares of degree 0–6 as the candidate models. Shuffling uses a seeded mulberry32 Fisher–Yates; folds are contiguous chunks like scikit-learn's KFold. Hold-out uses the first of k chunks as the validation set.

Educational simulation

Loading the simulation…

The Recipe That Hides a Problem

Most people first meet k-fold cross-validation as a recipe. You cut the dataset into k folds, let each fold take a turn as the test set, train on the remaining folds, and average the scores. The recipe itself says nothing about which samples land in which fold, so it is easy to assume that cutting the data into consecutive pieces is harmless. This assumption breaks down when the dataset is sorted by its labels.

The misconception to watch for is this: a standard k-fold split is good enough for evaluating a classifier no matter how the classes are distributed. For a classifier, the class mix inside each test fold matters, and a plain split does not guarantee it. In this article you will trace exactly where that guarantee fails, and then see how stratified k-fold cross-validation changes what the split is required to preserve.

Tracing an Ordered Three-Fold Split

Consider a small dataset whose labels come in three consecutive blocks, similar to the iris dataset in the source. The first third of the samples is class 0, the second third is class 1, and the last third is class 2. Here is a version of that label arrangement with 18 samples, six per class, split into three folds with no shuffling. Read the code first, then work out the answer before you check the output.

python
Output (expected)
Fold 1: test classes = [0], train classes = [1 2]
Fold 2: test classes = [1], train classes = [0 2]
Fold 3: test classes = [2], train classes = [0 1]
Notes on the code
  • The labels array is ordered by class: indices 0 to 5 are class 0, 6 to 11 are class 1, and 12 to 17 are class 2.
  • KFold assigns rows by position, so each fold is one contiguous block of indices.
  • The unique-class printout makes the mismatch visible in every round.

What do you think happens?

Before you look at the output, which classes do you expect in the test set of fold 2?

  • Only class 1

  • Classes 0, 1 and 2 in equal parts

  • No classes at all

Reveal answer
Answer:

Only class 1

Fold 2 is the second contiguous block of indices, 6 to 11, and every one of those samples is class 1. Because the split is positional and the data is sorted, the test fold holds a single class.

Fold 1indices 0 to 5, class 0onlyFold 2indices 6 to 11, class 1onlyFold 3indices 12 to 17, class 2only
When the data is sorted by class and split into contiguous blocks, each fold receives only one class, so the classes missing from a test fold are the ones the training set must learn alone.

The index map shows the root of the problem. Fold 1 contains only class 0, fold 2 contains only class 1, and fold 3 contains only class 2. The output confirms the same thing from the other direction: whenever a fold is the test set, the training set contains the other two classes and never sees the class being tested.

Round 1test: fold 1, class 0 onlyRound 2test: fold 2, class 1 onlyRound 3test: fold 3, class 2 onlynext foldnext fold
Each round holds out one contiguous block as the test set. The class in that block never appears in training during that round, so every round is a different and incomplete test of the classifier.

Stratification as a Proportion Rule

Stratified k-fold cross-validation fixes the problem by changing what the split is required to preserve. The split is still into k folds, and each fold still takes a turn as the test set. The difference is the rule that governs how samples are assigned. Each fold must mirror the class composition of the full dataset, rather than whatever happens to sit in a given block of rows.

Definition

Stratified k-fold cross-validation is a form of k-fold cross-validation in which the proportions between classes are kept the same in each fold as they are in the whole dataset.

The source gives a simple ratio to make the rule concrete. If 90% of the samples belong to class A and 10% belong to class B, stratified cross-validation ensures that in each fold 90% of the samples are class A and 10% are class B. The composition of every fold is anchored to the composition of the whole dataset, which is the property a fair test set needs.

Rule

In this statement, $n_{c}$ is the number of samples of class c in the full dataset, $N$ is the total number of samples, $n_{c,\text{fold}}$ is the number of class c samples in one fold, and $n_{\text{fold}}$ is the number of samples in that fold. The approximately-equal sign is deliberate. With real datasets the counts cannot always divide evenly, so the goal is that each fold is representative, not that every fold has an identical count.

Worked example

Applying the proportion rule to a 90/10 dataset

A dataset has 100 samples: 90 belong to class A and 10 belong to class B. You want five stratified folds. How many samples of each class should each fold contain?

  1. 1
    Find the fold size

    Divide the total by the number of folds: 100 divided by 5 gives 20 samples per fold.

  2. 2
    Apply the class share to class A

    Class A is 90 out of 100, which is 90%. Its share of each 20-sample fold should also be 90%, so 0.9 times 20 gives 18 samples of class A.

  3. 3
    Apply the class share to class B

    Class B is 10 out of 100, which is 10%. Its share of each fold should be 10%, so 0.1 times 20 gives 2 samples of class B.

  4. 4
    Check the totals

    18 plus 2 gives 20, which matches the fold size, so every fold is a representative miniature of the full dataset.

Result

Each fold contains 18 samples of class A and 2 samples of class B.

Why does this matter for evaluation? Suppose only 10% of the samples belong to class B. With a standard k-fold split, one fold can easily contain only samples of class A. Using that fold as the test set tells you little about overall performance, because the classifier was never asked to handle class B on that split. A stratified split guarantees that every test fold contains a fair share of class B, so each round asks the classifier the same kind of question.

Tracing the Same Data with a Stratified Split

Now run the same 18-sample, ordered label arrangement through a stratified splitter. The only change in the code is the class name. The labels are still sorted by class, but the stratified splitter assigns samples so that each class is shared across the folds.

python
Output (expected)
Fold 1: test counts per class = [2 2 2], train counts per class = [4 4 4]
Fold 2: test counts per class = [2 2 2], train counts per class = [4 4 4]
Fold 3: test counts per class = [2 2 2], train counts per class = [4 4 4]
Notes on the code
  • Each test fold holds two samples of every class, matching the 1:1:1 mix of the full dataset.
  • np.bincount counts how many samples of each class appear, which is easier to compare than the class list.
  • The training folds contain four samples of each class, so no class is missing from any training set.
Plain fold 1 test6 of class 0, 0 of class 1,0 of class 2Stratified fold 1test2 of class 0, 2 of class 1,2 of class 2Full dataset6 of class 0, 6 of class 1,6 of class 2Plain fold 2 test0 of class 0, 6 of class 1,0 of class 2Stratified fold 2test2 of class 0, 2 of class 1,2 of class 2
Plain k-fold gives test folds with a lopsided class mix, while stratified k-fold gives each test fold the same proportions as the full dataset.

Compare the two outputs. The plain split produced test folds that each contained one class and none of the others. The stratified split produced test folds whose class mix matches the full dataset, which here is an equal three-way mix. The divergence you traced earlier, where the plain split drifted away from the expected mix in its very first round, is gone. The row order of the data has not changed; what changed is the rule that assigns samples to folds.

Mistakes When Choosing a Splitting Strategy

  • Common mistake

    Assuming a standard k-fold split is fine for any classifier

    What it looks like

    Splitting sorted iris-style labels into three plain folds and trusting the resulting scores.

    Why it fails

    For a classifier, the class mix inside each test fold matters, and a plain split does not guarantee it. Ordered data can leave whole classes out of a test fold entirely.

    Do this instead

    Check the class counts in each fold, and use stratified k-fold when the classes must be represented in each test fold.

  • Common mistake

    Believing stratification changes the order of the rows

    What it looks like

    Expecting the stratified folds to look like consecutive blocks of the original sorted array.

    Why it fails

    Stratification is defined by class proportions, not by position. The folds are built so each one mirrors the overall class composition.

    Do this instead

    Inspect the class counts per fold rather than the row positions when you want to confirm the split is representative.

  • Common mistake

    Expecting identical counts in every fold

    What it looks like

    Requiring each fold of an uneven dataset to contain exactly the same number of class B samples.

    Why it fails

    Real datasets often cannot divide evenly. The rule is that each fold is representative of the overall proportions, not that every fold is identical.

    Do this instead

    Accept counts that are as close as the division allows, and focus on whether each fold reflects the class shares of the whole dataset.

  • Common mistake

    Using stratification for every kind of prediction

Choosing Stratified or Plain k-Fold

The source recommends stratified k-fold cross-validation as the usual choice for evaluating a classifier, because it results in more reliable estimates of generalization performance. Stratification is about class proportions, so it applies to classification. Scikit-learn uses stratified k-fold for classification by default and standard k-fold for regression by default.

For regression, the target is a continuous value rather than a set of classes, so there are no class proportions to preserve in the same way. The source notes that it would be possible to try to make each fold representative of the different values the regression target takes, but that this is not a commonly used strategy and would surprise most users. The practical lesson is to match the splitting strategy to the kind of prediction being evaluated.

Predicting acategory?classification taskClasses imbalanced?for example 90 and 10percentStratified k-foldkeeps class proportions perfoldStratified k-foldstill the classificationdefaultPredicting acontinuous value?regression taskStandard k-foldthe regression defaultyesyesnonoyes
The task type and class balance decide whether stratified or plain k-fold is the sensible starting point.

Check Your Understanding

MEDIUM

A dataset has 60 samples: 45 belong to class X and 15 belong to class Y. You plan to use three stratified folds. How many samples of each class should each test fold contain? Then explain in one sentence why a plain ordered split of this dataset could leave a test fold with no class Y samples.

Hints
  • Start by finding the fold size: 60 samples divided into 3 folds.
  • Class X is 45 out of 60, which is 75 percent. Apply that share to the fold size.
  • A plain split assigns rows by position, so if the class Y rows sit in one block, a whole fold can fall inside that block.

Key Takeaways

  • A standard k-fold split assigns rows by position, so when data is ordered by class label, a fold can contain only one class, and a classifier can be scored on a test set that never shows it the other classes.
  • Stratified k-fold cross-validation keeps the proportions between classes the same in each fold as in the whole dataset, so every test fold mirrors the overall class mix.
  • A zero or very low score from an ordered three-fold split can reflect the split itself rather than the classifier, so checking class counts per fold is a useful diagnostic.
  • Use stratified k-fold as the default for evaluating a classifier, especially with imbalanced classes; plain k-fold is the usual default for regression, where class proportions do not apply.

Related concepts

More concepts starting with K