Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 56

Cross-Validation for Model Evaluation

Recommended resources

Hello! Welcome to the next lesson in your machine learning journey.

In our last session, we established the "gold standard" for model evaluation: splitting data into training, validation, and test sets. This ensures we can tune our model on the validation set without "peeking" at the final test set, giving us an honest measure of performance.

However, this method has a potential weakness. The performance score you get on your validation set can be highly dependent on the "luck of the draw"—which specific data points happened to end up in that set. If your validation set accidentally contains particularly easy or hard examples, your evaluation will be misleadingly optimistic or pessimistic.

This lesson introduces a more robust and reliable technique to solve this very problem.

Lesson Introduction

Today, we will dive into cross-validation, a powerful method for model evaluation that minimizes the impact of how the data is split. You will learn the logic behind the most common technique, k-fold cross-validation, and how to apply it and its variants in Python using scikit-learn. This will give you a much more confident and stable estimate of your model's true performance.

By the end of this lesson, you will be able to apply various cross-validation techniques to get a reliable measure of your model's effectiveness.



1. The Problem with a Single Split

As we discussed, a single validation set might not be representative of the overall data. So, how can we get a more reliable performance estimate? What if, instead of one split, we created several different splits and averaged the results? This is the core idea behind cross-validation.

Let's start with a video that beautifully illustrates this concept.

Machine Learning Fundamentals: Cross Validation

The 'StatQuest with Josh Starmer' channel is famous for making complex topics easy to understand. This video will show you why a single train/test split can be problematic and how cross-validation provides a more robust solution.

Watch the video from the beginning up to 04:45. Pay close attention to: Why using a single, fixed block of data for testing isn't ideal. How k-fold cross-validation systematically uses every block for testing exactly once.

The process described in the video is called k-fold cross-validation, where 'k' is the number of groups (or "folds") we split the data into.

Here is a visual summary of the process for k=5:

Cross Validation, Explained
This diagram illustrates 5-fold cross-validation. The dataset is divided into 5 folds. In 5 separate iterations, each fold gets a turn to be the test set while the other 4 are used for training. The final performance is the average of the scores from all 5 iterations.

The key benefits of this approach are:

  • More Reliable Performance Estimate: By averaging the performance across k different folds, our estimate is less sensitive to the randomness of a single split.
  • More Efficient Data Usage: Every data point gets to be in a test set once and in a training set k-1 times. This is especially valuable when you have a limited amount of data.


2. Cross-Validation in the Full ML Workflow

This is a crucial point that can trip up beginners: cross-validation does not eliminate the need for a final test set.

Cross-validation is a technique for improving how you use your validation set. The entire k-fold process is performed on the data you would have otherwise set aside for training and validation. The test set remains locked away until the very end.

This diagram from the scikit-learn documentation shows the full picture perfectly:

K-Fold Cross-Validation Process
This flowchart shows that cross-validation is used for 'Finding Parameters' (i.e., model selection and hyperparameter tuning) on the training data. After the best model is chosen using CV, a 'Final evaluation' is performed on the completely separate test set.

So, the workflow we learned in the last lesson is updated as follows:

  1. Split your entire dataset into a training set and a test set.
  2. Perform k-fold cross-validation on the training set to select your best model and tune its hyperparameters.
  3. Once you have your final model, evaluate it once on the test set to get your final performance metric.


3. Implementing Cross-Validation in Python

Now that you understand the "why," let's move to the "how." As you'd expect, scikit-learn provides excellent tools for this. We'll explore two ways: a slightly more manual approach to see the mechanics and a convenient one-liner function.

The following video provides a practical, code-along demonstration.

Machine Learning Tutorial Python 12 - K Fold Cross Validation

This video from the 'codebasics' channel is a great practical guide. It will walk you through implementing cross-validation from the ground up and then show you the easy shortcut.

Please watch the segment from 08:06 to 21:41. Focus on two key parts: The 'Manual' Way (08:06 - 19:19): See how the StratifiedKFold object is used in a for loop to generate training and testing indices for each fold. This reveals what's happening under the hood. The 'Easy' Way (19:19 - 21:41): Discover the cross_val_score function, which performs the entire k-fold process in a single line of code.

3.1. The cross_val_score Function

As you saw, while it's useful to understand the for loop mechanism, in practice you will almost always use the cross_val_score function. It's clean, simple, and less prone to error.

Here’s a clean example building on what you saw in the video, using the California Housing dataset for a regression task.

import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score, KFold

# 1. Load data
X, y = fetch_california_housing(return_X_y=True)

# 2. Define the model
model = LinearRegression()

# 3. Set up the K-Fold cross-validation strategy
# We'll use 5 folds, shuffle the data, and set a random_state for reproducibility
kf = KFold(n_splits=5, shuffle=True, random_state=42)

# 4. Perform cross-validation
# cv=kf tells the function to use our defined KFold strategy
# scoring='r2' specifies the metric we want to calculate
scores = cross_val_score(model, X, y, cv=kf, scoring='r2')

# 5. Print the results
print(f"Scores for each fold: {scores}")
print(f"Average R^2 score: {np.mean(scores):.4f}")
print(f"Standard deviation of scores: {np.std(scores):.4f}")

# Example Output:
# Scores for each fold: [0.57582089 0.61373562 0.60859341 0.62134543 0.58747448]
# Average R^2 score: 0.6014
# Standard deviation of scores: 0.0163

The output gives us not just a single performance score, but an array of scores—one for each fold. The average is our new, more robust estimate of the model's performance. The standard deviation tells us how consistent the model's performance was across the different folds. A low standard deviation is desirable, as it suggests the model is not overly sensitive to the specific training data it sees.

Test your understanding!

You run 10-fold cross-validation on two different models, A and B.

  • Model A scores: [0.91, 0.92, 0.90, 0.91, 0.93, 0.92, 0.90, 0.91, 0.92, 0.91]
  • Model B scores: [0.98, 0.85, 0.99, 0.82, 0.97, 0.86, 0.99, 0.83, 0.98, 0.86]

Calculate the average score for both. Which model would you probably trust more, and why?

Show answer
  • Model A Average: np.mean(scores_A) ≈ 0.913
  • Model B Average: np.mean(scores_B) ≈ 0.913

Both models have the same average performance. However, Model A's scores are very consistent (low variance), ranging only from 0.90 to 0.93. Model B's scores are all over the place (high variance), from 0.82 to 0.99.

You would likely trust Model A more. Its performance is stable and predictable. Model B's high variance suggests it is very sensitive to the training data; its high scores might be due to "lucky" splits, and its low scores are a major concern.



4. Variations of Cross-Validation

Standard KFold is the default, but scikit-learn offers several other strategies tailored to specific situations.

Cross-validation: evaluating estimator performance

The scikit-learn documentation is the ultimate source of truth. This page provides a comprehensive overview of cross-validation and the various strategies available.

Read the sections titled 'Cross-validation iterators with stratification...', 'Cross-validation iterators for grouped data', 'Cross validation of time series data', and 'A note on shuffling'. You don't need to memorize every detail, but focus on understanding the purpose of these key variants: StratifiedKFold: Why is it essential for classification tasks, especially with imbalanced classes? GroupKFold: When would you use this? (Think about data where samples are not independent, like multiple measurements from the same patient). TimeSeriesSplit: Why is shuffling a bad idea for time-series data, and how does this splitter respect temporal order?

This article from DataCamp provides a fantastic summary table comparing the most common methods.

A Comprehensive Guide to K-Fold Cross Validation

This DataCamp tutorial provides another clear guide to K-Fold CV, and includes an excellent summary table that you can use as a quick reference.

Review the comparison table in the section 'Standard K-Fold vs. Other Cross-Validation Methods'. This is a great cheat sheet for remembering when to use each CV type.

The most important variant for you to know right now is Stratified K-Fold. Just as we used stratify=y in train_test_split for classification, StratifiedKFold ensures that each fold has the same percentage of samples for each class as the complete dataset. This is crucial for avoiding situations where a fold might, by chance, contain few or no samples of a minority class.

The good news is that when you use cross_val_score with an integer cv value (e.g., cv=5) on a classification model, scikit-learn is smart enough to use StratifiedKFold by default!



Conclusion

You've now added a critical tool to your machine learning toolkit. Cross-validation moves you beyond relying on a single data split and towards a much more rigorous and trustworthy method of evaluating your models.

Key Takeaways:

  • A single validation set can give a misleading performance score due to the "luck of the draw" in the data split.
  • K-Fold Cross-Validation solves this by splitting the data into 'k' folds, training on k-1 folds, and testing on the remaining fold, rotating until every fold has been a test set.
  • The final performance is the average of the scores across all folds, providing a more stable and reliable estimate.
  • The test set is still held out and is only used once for the final evaluation after model selection is complete.
  • The cross_val_score function in scikit-learn is the easiest way to perform cross-validation.
  • Specialized strategies like Stratified K-Fold are essential for classification tasks to preserve class balance in each fold.

Preview of the Next Lesson:
So far, we've used metrics like R² and accuracy without much thought. But are they always the right choice? In the next lesson, we will explore the landscape of evaluation metrics. You'll learn which metrics to use for regression vs. classification, and how to choose the right one based on your specific problem (e.g., is it worse to have a false positive or a false negative?).