Welcome to the final lesson of our first module on core machine learning concepts. In our previous session, we explored how to select and justify evaluation metrics like precision, recall, and F1-score to understand what to measure about a model's performance. Now, we will focus on how to measure it reliably.
Today’s lesson addresses a fundamental challenge in model evaluation: a single train/test split can give a misleading picture of a model's true performance due to the luck of the draw. We will introduce k-fold cross-validation, a powerful and standard technique to obtain a much more robust estimate of how your model will perform on unseen data. By the end of this lesson, you will understand the procedure and be able to implement it in Python, completing our foundational toolkit for building and evaluating models.
The Problem: Why a Single Split Isn't Enough
In our previous lessons, we've been using a simple train_test_split to create a training set and a test set. We train the model on the former and evaluate it on the latter. While straightforward, this approach has a significant weakness: the resulting performance score can be highly dependent on which specific data points ended up in the training set versus the test set.
Imagine you're evaluating a spam classifier. If, by chance, all the trickiest, most ambiguous emails land in your test set, your model's performance will look deceptively poor. Conversely, if they all land in the training set and the test set contains only easy examples, your model will look unrealistically good. The performance estimate has high variance and isn't reliable.
To build confidence in our model's evaluation, we need a method that is less sensitive to the randomness of a single split.
The following video from StatQuest provides an excellent, intuitive explanation of this problem and introduces the idea of cross-validation as the solution.
Machine Learning Fundamentals: Cross Validation
Watch the initial parts of this video to grasp the core problem.
Focus on the sections explaining why we need to compare models (which method is best) and the pitfalls of both reusing data and relying on a single, arbitrary split (a terrible approach).
The Solution: k-Fold Cross-Validation
The core idea behind k-fold cross-validation is simple but powerful: instead of performing a single split, we perform k of them. The dataset is divided into k equal-sized partitions, or "folds". We then run k iterations. In each iteration, a different fold is held out as the test set, while the remaining k-1 folds are combined to form the training set.
This process ensures that every data point gets to be in a test set exactly once and in a training set k-1 times. The k performance scores from each iteration are then averaged to produce a single, more stable estimate of the model's generalization ability.
This diagram beautifully illustrates the process for 5-fold cross-validation.

The following article from MachineLearningMastery.com provides a clear, step-by-step breakdown of this procedure.
A Gentle Introduction to k-fold Cross-Validation - MachineLearningMastery.com
This article introduces the concept and outlines the procedure in a very accessible way.
Read the first section, k-Fold Cross-Validation, to solidify your understanding of the motivation and the step-by-step algorithm. Pay close attention to the point that data preparation and hyperparameter tuning must happen inside the loop to avoid data leakage.
Choosing the Value of k
A key parameter in this process is k, the number of folds. The choice of k involves a trade-off that should be familiar from our first lesson: the bias-variance trade-off.
- A small k (e.g., 2 or 3) means the training sets in each fold are significantly smaller than the whole dataset. This can lead to a more biased estimate of performance, as the model isn't learning from as much data as it could. However, the performance estimates across folds tend to have lower variance because the test folds are large.
- A large k (e.g., n, where n is the number of samples, known as Leave-One-Out CV) means the training sets are nearly identical to the full dataset. This results in a less biased estimate. However, it is computationally very expensive, and the performance estimates can have high variance because each test set is tiny (just one sample).
In practice, values of k=5 or k=10 are standard choices. They have been shown empirically to provide a good balance between bias and variance, giving a reliable estimate of model performance without being excessively computationally demanding.
Implementing k-Fold CV with scikit-learn
As a Computer Science student, you'll appreciate how scikit-learn elegantly abstracts this process. While you could write the loop yourself, the library provides high-level functions that make implementation trivial.
The codebasics channel offers a fantastic tutorial that first builds the intuition by manually implementing the loop and then shows the much simpler cross_val_score function.
Machine Learning Tutorial Python 12 - K Fold Cross Validation
This video provides a practical, code-driven walkthrough of implementing k-fold cross-validation in Python.
Watch the segment that demonstrates how the KFold object works to create splits (demonstrate what exactly it is). This shows you what's happening under the hood. Next, observe the full manual implementation that compares three different models across the folds (use stratified kfold). Finally, see how the entire process can be simplified using the cross_val_score function (a ready made method), which is the standard approach in practice.
As you saw, cross_val_score is a powerful tool. You pass it a model, your full feature matrix X, your target vector y, and specify the number of folds with the cv parameter. It handles the entire process of splitting, training, and evaluating k times, returning an array of the k scores.
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
# Load a sample dataset
X, y = load_iris(return_X_y=True)
# Create two different models
log_reg = LogisticRegression(max_iter=200)
svm_clf = SVC()
# Get 5-fold cross-validation scores for Logistic Regression
log_reg_scores = cross_val_score(log_reg, X, y, cv=5, scoring='f1_macro')
print(f"Logistic Regression F1 Scores: {log_reg_scores}")
print(f"Average F1 Score: {log_reg_scores.mean():.2f} (+/- {log_reg_scores.std():.2f})")
# Get 5-fold cross-validation scores for SVM
svm_scores = cross_val_score(svm_clf, X, y, cv=5, scoring='f1_macro')
print(f"\nSVM F1 Scores: {svm_scores}")
print(f"Average F1 Score: {svm_scores.mean():.2f} (+/- {svm_scores.std():.2f})")
Notice how we can also pass a scoring parameter. This allows us to use any of the metrics we learned about in the last lesson, such as 'precision', 'recall', or 'f1_macro'.
A Crucial Variation: Stratified K-Fold
When dealing with classification, especially with imbalanced datasets, standard k-fold has a potential flaw. A random split might result in some folds having a very different distribution of classes than the overall dataset. In an extreme case, a fold might not contain any instances of a minority class, making it impossible to calculate metrics like recall for that fold.
Stratified k-fold solves this. It's a variation that preserves the percentage of samples for each class in every fold. For example, if your overall dataset is 80% class A and 20% class B, each fold will also have an 80/20 split.
Fortunately, when you use cross_val_score with an integer cv value on a classifier, scikit-learn automatically uses StratifiedKFold by default. This is a sensible default that protects you from this common pitfall.
The scikit-learn documentation explains this concept and its importance clearly.
3.1. Cross-validation: evaluating estimator performance
This is the official documentation, providing a definitive reference for implementation and variations.
First, read the section on Computing cross-validated metrics to see more examples of cross_val_score and the related cross_validate function. Then, jump down to the subsection on Stratified K-fold to understand how it differs from regular K-fold and why it is essential for classification tasks.
The Full Picture: CV for Tuning, Test Set for Final Evaluation
It is critical to distinguish between the purpose of cross-validation and a final test set.
- Initial Split: Before anything else, you should split your entire dataset once into a training set and a final hold-out test set. This test set should not be touched until the very end.
- Cross-Validation: You use k-fold cross-validation exclusively on the training set. Its purpose is to perform model selection (e.g., comparing logistic regression vs. SVM) or to tune hyperparameters (e.g., finding the best regularization strength
C). The average cross-validation score guides these decisions. - Final Training: Once you have selected your best model and hyperparameters using cross-validation, you train this final model on the entire training set.
- Final Evaluation: Finally, you evaluate this trained model once on the hold-out test set. This score provides the ultimate, unbiased estimate of your model's performance on new, unseen data.
This image perfectly captures this workflow. The iterative k-fold process is for "Finding Parameters" (or model selection), and the final, one-time evaluation happens on the separate "Test data".

Conclusion
You have now reached the end of the first module and have assembled a complete, foundational framework for supervised machine learning. You can preprocess and train a model, regularize it to prevent overfitting, evaluate it with appropriate metrics, and now, use cross-validation to do so robustly.
Here are the key takeaways from this lesson:
- A single train/test split provides an unstable estimate of model performance.
- K-fold cross-validation offers a more robust estimate by training and testing a model k times on different subsets of the data and averaging the results.
- The choice of k=5 or k=10 typically provides a good bias-variance trade-off.
- Stratified k-fold is crucial for classification to ensure class distributions are maintained across folds.
- Cross-validation is used on the training set for model selection and tuning. The final performance is reported on a separate, held-out test set.
With these core concepts firmly in place, you are ready to move from classical linear models to the world of deep learning. In the next module, "Neural Network Fundamentals," we will begin our journey towards your goal of building advanced NLP models by constructing our very first artificial neuron: the perceptron.