Concepts / Train-Test Splits and Their Limitations

Train-Test Splits and Their Limitations

The training set is the material the model is built from.

  • Programming

Running a Complete Evaluation First

This article starts by running a complete evaluation and then works backward through it. The program below loads the iris dataset, creates a logistic regression model, and asks scikit-learn to evaluate that model with cross-validation. Read the code once, then look at what it prints before reading the explanation that follows.

python
Notes on the code
  • X holds the features (the data), one row per example
  • y holds the labels (the target), the known answer for each row
  • LogisticRegression() is the model that will be evaluated
  • cross_val_score receives the model, the data, and the target, and returns an array

Before you move on, notice the three objects in the call: the model, the data, and the labels. Those three inputs are all that cross_val_score needs to run the evaluation. The next question is what it hands back.

What do you think happens?

Before reading on, make a guess: will the printed result be a single accuracy number, or something with more than one value? If more than one, how many?

  • A single accuracy number for the whole procedure

  • A list with one value per fold, three values by default

Reveal answer
Answer:

A list with one value per fold, three values by default

By default cross_val_score performs three-fold cross-validation and returns three accuracy values, one for each fold. The printed result is an array of per-fold results, not one overall score.

Reading the Returned Array

The general form of the call takes the model to evaluate as its first positional argument, the data (the features) as its second, and the target (the ground-truth labels) as its third. A fourth argument, cv, is optional and controls how many folds are used. When you do not pass cv, the default is three folds.

Cross-validation evaluates a model by splitting the data into folds and then training and testing on different subsets. In K-fold cross-validation, the data is split into k subsets. The model is evaluated once for each fold, so the function produces one score per fold. Each value in the returned array is the result of one held-out fold. The array is therefore a list of per-fold results rather than a single overall score.

Round 1test: fold 1, train: folds2 and 3Score 1accuracy from round 1Round 2test: fold 2, train: folds1 and 3Score 2accuracy from round 2Round 3test: fold 3, train: folds1 and 2Score 3accuracy from round 3producesproducesproduces
In the three-fold default, each fold is held out exactly once. In each round the model is trained on the other two folds and scored on the held-out one, so three rounds produce three values in fold order.

Read the diagram as a rule rather than a picture. Each fold is held out once, the model is trained on the remaining folds each time, and the output array holds one accuracy value per held-out fold, in fold order. A single number is not the result of the whole procedure. It is the list of per-fold results.

Key point

The length of the returned array equals the number of folds. Check the length first whenever the output looks different from what you expected.

Changing the Number of Folds

The cv parameter changes the number of folds. Keep the same model and data, and pass cv=5 instead of relying on the default. The rule from the previous section still applies: the number of folds sets the length of the returned array.

python
Notes on the code
  • cv=5 sets the number of folds to five
  • len(scores) counts the values in the returned array

What do you think happens?

Using the rule that the array length matches the number of folds, how many values do you expect len(scores) to print when cv=5?

Reveal answer
Answer:

5

The cv parameter sets the number of folds, and the length of the returned array matches that number. Five folds give five per-fold results.

Worked example

Predicting the effect of changing cv from 3 to 5

A call uses cross_val_score with the default cv, and then the same call is changed to cv=5. What changes in the output?

  1. 1
    Identify the default

    With no cv argument, the default is three-fold cross-validation, which returns three values.

  2. 2
    Apply the new cv value

    With cv=5 the data is split into five folds, so each fold is held out once across five rounds.

  3. 3
    Count the returned values

    Each held-out fold produces one value, so the array now has five entries instead of three.

  4. 4
    Note what stays the same

    The model and the data are unchanged. Only the fold structure, and therefore the number of per-fold results, has changed.

Result

The array grows from three values to five, one per held-out fold.

Tracing Each Round of the Loop

Cross-validation is a repeated train-and-evaluate loop. The same steps run once for every fold, and the loop's output is collected in order. Following one round from start to finish makes the numbers in the returned array easier to interpret.

Fit on training foldsScore on held-outfoldRecord scoreNext foldthenthenthenrepeat for remaining folds
For each fold, the model is fit on the training folds, scored on the held-out fold, and that score is recorded before the loop moves to the next fold.

The loop has no hidden side effects that change the answer. It trains on the training folds, evaluates on the held-out fold, keeps the result, and moves on. When the loop finishes, the collected results are what cross_val_score returns.

Tracing an Unexpected Output

When an output does not match your expectation, the useful question is where the divergence began. Work through the chain in order: the inputs you passed, the value of cv, the number of values in the array, and then the values themselves. Checking in this order lets you find the failing step instead of rereading the whole program.

  1. 1
    Check the inputs: confirm that the model, the data, and the target are the ones you intended.
  2. 2
    Check cv: confirm the number of folds you think was used, including the default if cv was omitted.
  3. 3
    Check the length: count the values in the array. A length mismatch usually points to the cv argument.
  4. 4
    Check the values: if the length is correct but the numbers differ, suspect the data or the model, since the same fold structure was used.

Why Judging a Model on Its Own Data Fails

Suppose you have a dataset and you want to build a machine learning model from it. A natural question follows: how will you know whether the model is any good? If you judge the model only on the same data it was built from, you are measuring how well it fits the examples it has already seen. That is not the same as measuring how well it will handle new cases.

A train-test split divides a dataset into a training set and a test set. It is the standard way to keep those two questions apart: how well the model fits known examples, and how well it works on new ones. Keep this question in mind as you read: if the model is supposed to work on new data, which part of the dataset should be used to judge it, and which part should be used to build it?

Roles of the Training Set and the Test Set

The two parts have different jobs. The training set is the material the model is built from. The test set is held back and used afterward to check the model's performance on data it was not built from. The test set contains data that is new to the model, which is exactly what makes it useful for judging generalization.

Full datasetSplitTraining setBuild modelBuilt modelTest setEvaluate modeldividedone portionheld backfeedsproducesused inused in
The full dataset is divided first. One portion feeds model building, while the test set is kept aside and reaches only the evaluation step.

Notice that the test set reaches the evaluation step but does not feed into building the model. That separation is the whole point of the technique. The training set is used to build the model, and the test set is used to evaluate it.

Mistakes That Break the Split

  • Common mistake

    Judging the model only on the data it was built from

    Why it fails

    This measures how well the model fits examples it has already seen, not how it will handle new cases.

    Do this instead

    Hold out a test set that the model never sees while it is being built, and judge the model on that.

  • Common mistake

    Using the test set to build the model

    Why it fails

    Once the test set feeds model building, it is no longer data the model has not seen, and the evaluation loses its meaning.

    Do this instead

    Let only the training set feed model building. Keep the test set aside until the model has been built.

  • Common mistake

    Assuming the array holds one overall score rather than a list of per-fold results

  • Common mistake

    Changing cv and expecting the same number of values to come back

AspectCorrect splitBroken split
Training setUsed only to build the modelAlso includes data later used for testing
Test setNew to the model, used only for evaluationAlready seen during model building
Evaluation meaningShows how the model may handle new dataMostly measures fit to known examples
Data flowTest set reaches evaluation onlyTest set feeds back into model building

A correct split keeps the test set separate from model building. A broken split lets test data influence the model.

Practice Predicting Fold Counts

EASY

A colleague calls cross_val_score with cv=4 on a dataset and prints the returned array. Without running the code, predict how many values appear, and then state which part of the output you would check first if the count surprised you.

Hints
  • Recall that the array length matches the number of folds.
  • If the count surprises you, check the cv argument before checking the numbers.

Key Takeaways

  • cross_val_score takes the model, the data, and the target as its main inputs and returns an array of per-fold results, not one overall score.
  • By default it performs three-fold cross-validation, and the cv parameter sets the number of folds, which sets the length of the returned array.
  • A train-test split separates the data used to build a model (the training set) from the data used to judge how well it generalizes (the test set).
  • The test set must stay separate from model building; using it to build the model, or judging only on training data, breaks the purpose of the split.
  • When an output looks wrong, trace the chain in order: inputs, cv, array length, then values, to find where the divergence began.

Related concepts

More concepts starting with T