Hello! Let's dive into our next lesson in the "Core Machine Learning Concepts" module.
Introduction
In our last lesson, we established a crucial protocol for model evaluation: splitting data into training, validation, and test sets. We learned that this three-way split allows us to train our model, tune its hyperparameters, and finally get an unbiased estimate of its real-world performance.
However, this method has two potential weaknesses:
- Data Inefficiency: In scenarios where data is scarce, holding out a fixed portion for validation and testing means we have less data available for training the model itself.
- Sensitivity to the Split: What if we get "unlucky" with our random split? The validation set might accidentally contain particularly easy or hard examples, giving us a misleadingly optimistic or pessimistic view of our model's capabilities. As you saw in the last lesson's code examples, changing the
random_statecan lead to different splits and, potentially, different performance metrics.
Today, we'll learn a more robust and data-efficient technique to address these issues. Our learning outcome is to implement K-fold cross-validation to assess model generalization. This is the standard method for obtaining a reliable estimate of a model's performance in professional and academic ML.
In this lesson, you will learn:
- The core principle of K-fold cross-validation.
- How to implement it using Python's scikit-learn library, from high-level functions to the underlying mechanics.
- How to interpret the results to get a robust measure of performance.
- Important variations like
StratifiedKFoldfor classification tasks.
The Principle of K-Fold Cross-Validation
The core idea of K-fold cross-validation is simple yet powerful: instead of a single train/validation split, we create K of them.
Tutorial 6: Model Selection: Cross-validation
Let's start with a formal definition. The Neuromatch Academy tutorial 'Model Selection: Cross-validation' provides an excellent, concise explanation of the process.
Please read Section 1, 'Cross-validation'. Focus on understanding the vocabulary (folds, splits) and the overall procedure of rotating the held-out fold.
To summarize the process, which is also shown in the diagram below:
- The training data (everything except the final test set) is partitioned into K equal-sized subsets, or folds.
- We iterate K times. In each iteration:
- One fold is held out as a temporary validation set.
- The model is trained on the remaining K-1 folds.
- The model is evaluated on the held-out validation fold, and the score (e.g., accuracy, MSE) is recorded.
- After K iterations, we have K performance scores. We then compute the average and standard deviation of these scores. The average provides a robust estimate of the model's performance, while the standard deviation tells us how much the performance varies across different subsets of the data.

A common choice for K is 5 or 10. This approach uses the available data much more effectively than a single validation set.
Why a Single Split Can Be Misleading
Before we dive into the implementation, let's see a practical demonstration of why relying on a single train-test or train-validation split can be unreliable.
A Comprehensive Guide to Cross-Validation with Scikit-Learn and Python
The video 'A Comprehensive Guide to Cross-Validation' from Ryan & Matt Data Science does a great job of motivating the need for cross-validation by showing just how much a model's score can change based on a single random split.
Please watch from the beginning until 09:25. The video sets up a simple dataset and shows that changing the random_state in train_test_split from 11 to 25 changes the model's accuracy from 71% to 80%. This volatility is exactly the problem cross-validation solves.
This demonstration should make it clear that a single performance score can be misleading. Cross-validation smooths out this variance by averaging over multiple splits, giving us a much more trustworthy assessment of our model's true generalization ability.
Implementing K-Fold Cross-Validation in Scikit-Learn
With your background in Python, you'll find that scikit-learn makes implementing cross-validation straightforward. There are two main ways to approach it: a high-level function for convenience and a lower-level approach for more control and understanding.
The Easy Way: cross_val_score
The most direct way to perform K-fold cross-validation is with the cross_val_score function. It handles the entire looping process for you.
A Comprehensive Guide to Cross-Validation with Scikit-Learn and Python
Let's continue with the same video, which now introduces the cross_val_score function.
Watch from 09:25 to 12:24. This segment shows you how to use cross_val_score with a model, your data (X and y), and the number of folds (cv). Pay attention to the output: an array of scores, and how to compute their mean and standard deviation.
Here's the key takeaway in code:
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
import numpy as np
# Assuming X and y are your features and labels
# 1. Initialize your model
model = LogisticRegression()
# 2. Use cross_val_score
# cv=10 means 10-fold cross-validation
scores = cross_val_score(model, X, y, cv=10)
print(f"Scores for each fold: {scores}")
print(f"Average score: {np.mean(scores):.4f}")
print(f"Standard deviation: {np.std(scores):.4f}")
The average score is your primary estimate of the model's performance. The standard deviation is also crucial:
- A low standard deviation means the model's performance is stable and consistent across different subsets of the data.
- A high standard deviation suggests that the model's performance is sensitive to the specific data it's trained on, which might indicate instability or that the data itself is highly varied.
Under the Hood: The KFold Splitter
While cross_val_score is convenient, understanding what it does internally is important, especially given your CS background. The function uses a "cross-validation splitter" object. The most basic one is KFold.
A splitter object's main job is to generate the indices of the data points that will be in the training and validation sets for each fold.
Machine Learning Tutorial Python 12 - K Fold Cross Validation
To see how this works, let's look at a different video from codebasics, which explicitly demonstrates the use of a KFold object to generate these indices.
Watch the segment from 08:06 to 10:28. The presenter shows how to create a KFold object and loop through kf.split(data) to get train_index and test_index arrays for each iteration. This is the manual loop that cross_val_score automates.
Here's a simple demonstration of that concept:
from sklearn.model_selection import KFold
import numpy as np
# Create a dummy array of 10 data points
data = np.arange(10)
# Initialize a 5-fold splitter
kf = KFold(n_splits=5)
# The .split() method returns an iterator
for fold_num, (train_indices, val_indices) in enumerate(kf.split(data)):
print(f"--- Fold {fold_num+1} ---")
print(f"Validation Indices: {val_indices}")
print(f"Training Indices: {train_indices}\n")
# --- Fold 1 ---
# Validation Indices: [0 1]
# Training Indices: [2 3 4 5 6 7 8 9]
# --- Fold 2 ---
# Validation Indices: [2 3]
# Training Indices: [0 1 4 5 6 7 8 9]
# ...and so on for 5 folds.
This manual loop gives you full control. You can perform custom operations within each fold, which is useful for more complex evaluation scenarios. You can pass a splitter object like kf directly to cross_val_score's cv parameter.
An Essential Variation: Stratified K-Fold
In the previous lesson, we discussed stratify for dealing with imbalanced classes in a simple train-test split. The same problem exists for cross-validation. If you have an imbalanced dataset (e.g., 90% class A, 10% class B), a standard KFold split might, by chance, create a validation fold with no examples of the minority class B, making evaluation impossible for that fold.
Stratified K-Fold is the solution. It ensures that each fold has approximately the same percentage of samples for each class as the original dataset.
A Comprehensive Guide to Cross-Validation with Scikit-Learn and Python
Let's return to the Ryan & Matt video to see a demonstration of StratifiedKFold.
Watch from 16:28 to 19:09. This section introduces StratifiedKFold and shows its implementation, which is very similar to KFold. It's the go-to method for classification tasks.
For classification problems, you should almost always use StratifiedKFold. In fact, scikit-learn is smart about this: if you pass an integer cv value to cross_val_score for a classification model, it automatically uses StratifiedKFold by default.
Cross-Validation for the Entire Pipeline
A critical point that connects to our previous discussion on data leakage: cross-validation must be performed on the entire machine learning pipeline, including any preprocessing steps like scaling or feature engineering.
If you scale your entire dataset before passing it to cross_val_score, you have leaked information from the validation folds into the training process of the scaler.
The correct way is to create a Pipeline object that contains both the scaler and the model. When you pass this pipeline to cross_val_score, it correctly re-fits the scaler on only the training portion of each fold.
A Comprehensive Guide to Cross-Validation with Scikit-Learn and Python
The Ryan & Matt video concludes with this exact point, demonstrating how to use cross_val_score on a scikit-learn Pipeline.
Watch from 19:09 to 22:39. This is a crucial practical skill that ensures your validation is truly unbiased and avoids a common source of data leakage.
Test your understanding!
You are tasked with choosing between a LogisticRegression model and a RandomForestClassifier for a given classification problem. You run 10-fold cross-validation on both.
- Logistic Regression yields an average accuracy of
0.85with a standard deviation of0.08. - Random Forest yields an average accuracy of
0.86with a standard deviation of0.02.
Which model would you choose and why?
Show answer
You would likely choose the Random Forest model.
While its average accuracy is only slightly higher (0.86 vs. 0.85), its standard deviation is much lower (0.02 vs. 0.08). This indicates that the Random Forest's performance is far more stable and consistent across different subsets of the data. The Logistic Regression model, while performing well on average, is more erratic, suggesting its performance is more dependent on the specific training data it sees. A more stable model is generally more reliable and trustworthy.
Conclusion
You've now learned one of the most important and widely-used techniques in a machine learning practitioner's toolkit. K-fold cross-validation is the gold standard for assessing how well a model will generalize to new, unseen data.
Key Takeaways:
- Cross-validation provides a more robust and reliable estimate of model performance than a single train/validation split by averaging results over multiple folds.
- It is more data-efficient, as every data point gets used for both training and validation.
- Scikit-learn provides the convenient
cross_val_scorefunction for easy implementation. - The results should be analyzed by looking at both the mean (overall performance) and standard deviation (stability).
- For classification tasks,
StratifiedKFoldis essential to handle class imbalances correctly. - To prevent data leakage, cross-validation should be applied to the entire pipeline, including all preprocessing steps.
Preview of the next lesson:
Cross-validation is a powerful diagnostic tool. The scores it produces can tell us a lot about our model's behavior. A model that performs poorly across all folds might be too simple for the data (underfitting). A model that has high training accuracy but a much lower cross-validation score is likely memorizing the training data instead of generalizing (overfitting).
In our next lesson, we will delve deeper into these concepts and learn how to formally "Diagnose and mitigate overfitting and underfitting." We'll explore techniques to identify these problems and strategies to fix them, with cross-validation serving as our primary tool for diagnosis.