Skip to main content

Welcome to a Grasp lesson

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

Lesson 55

Data Splitting: Train, Validate, Test

Recommended resources

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

In our last session, we dug into the bias-variance tradeoff and saw how comparing a model's performance on training data versus test data is crucial for diagnosing underfitting (high bias) and overfitting (high variance). This raises a critical question: how do we manage these datasets correctly?

If we use our test set to tune our model—for example, by trying different model settings and picking the one that performs best on the test data—we are essentially cheating. The model gains an unfair advantage by learning from the test data, and our final performance score will be an overly optimistic and unreliable estimate of how it would perform on truly new data.

Today, we'll solve this problem by learning the proper way to partition our data.

Lesson Introduction

This lesson focuses on a cornerstone of reliable model evaluation: splitting data into training, validation, and test sets. You will learn the distinct purpose of each set and how to implement this three-way split in Python using scikit-learn. Mastering this technique is fundamental to building models that you can trust to perform well in the real world.



1. The "Why": Understanding the Role of Each Dataset

Before we write any code, let's build a strong conceptual foundation for why we need three datasets, not just two. A common analogy is that of a student preparing for a final exam.

How to Split Validation and Test Set Easily

This article from Liberti Hub uses a clear student-exam analogy to explain the purpose of each data split. It's a great way to build an intuitive understanding.

Please read the section titled 'Intuitive Distinction'. As you read, think about why the student can't use the same questions they studied with (the training set) to realistically gauge their readiness.

The analogy makes it clear: you can't properly evaluate yourself on questions you've already seen the answers to. To make this more concrete in a machine learning context, let's watch a short video.

Validation data: How it works and why you need it - Machine Learning Basics Explained

This video from Galaxy Inferno Codes provides a concise explanation of the role each dataset plays in the machine learning workflow.

Watch the video from the beginning until 03:28. Pay close attention to how it defines the purpose of each set: Training set: To train the model (i.e., learn parameters). Validation set: For you, the developer, to choose the best model (i.e., tune hyperparameters). Test set: To get a true final performance estimate.

To summarize and formalize these crucial concepts:

  • Training Set: This is the bulk of your data. The model uses this set to learn the underlying patterns by adjusting its internal parameters. For a neural network, this means learning the weights and biases. For a linear regression model, it means learning the coefficients.

  • Validation Set (or Development Set): This dataset is used to tune the model's hyperparameters. Hyperparameters are the high-level configuration choices you make as a data scientist before training begins, such as the number of trees in a random forest or the learning rate in gradient descent. You train various models with different hyperparameters on the training set and then evaluate them on the validation set to see which configuration performs best on data it hasn't been trained on.

  • Test Set: This dataset is the final checkpoint. It is kept completely separate and is used only once after you have selected your final model (based on its validation set performance). The test set provides an unbiased estimate of how your model will generalize to new, unseen data. Its performance score is the one you would report to stakeholders.

This workflow is visualized perfectly in the following flowchart:

Training, Validation, and Test Data Workflow
This flowchart illustrates the iterative process of model development. A model is trained on the training set and evaluated on the validation set. This loop is repeated as the model is tweaked (hyperparameter tuning). Once the best model is found, it's evaluated a final time on the unseen test set to get a final performance measure.


2. The "How": Splitting Data with Scikit-Learn

Now that we understand the "why," let's move to the "how." In Python, the scikit-learn library provides a convenient function called train_test_split that is the standard tool for this job.

However, train_test_split only splits data into two sets. To get our desired three sets (train, validation, test), we simply apply the function twice.

Dataset Splitting for Machine Learning
This diagram shows the standard two-step procedure for creating train, validation, and test sets. First, the full dataset is split into a training set and a test set. Then, the initial training set is split again to create a smaller training set and a validation set.

Implementing the Two-Step Split

Let's walk through the code. Imagine we have our data loaded into a pandas DataFrame data and we want to split it into a 60% training, 20% validation, and 20% test set.

import pandas as pd
from sklearn.model_selection import train_test_split

# Assume 'data' is a pandas DataFrame with your features and target
# For demonstration, let's create a dummy DataFrame
data = pd.DataFrame({
    'feature1': range(100),
    'feature2': range(100, 200),
    'target': range(200, 300)
})

# Separate features (X) and target (y)
X = data[['feature1', 'feature2']]
y = data['target']

# Step 1: Split the data into training+validation (80%) and test (20%)
X_train_val, X_test, y_train_val, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Step 2: Split the train_val set into training (75% of 80%) and validation (25% of 80%)
# 0.25 * 0.80 = 0.20, so this gives us a 20% validation set from the original data.
X_train, X_val, y_train, y_val = train_test_split(
    X_train_val, y_train_val, test_size=0.25, random_state=42)

# Print the sizes of the resulting sets
print(f"Full dataset shape: {X.shape}")
print(f"Training set shape: {X_train.shape}")
print(f"Validation set shape: {X_val.shape}")
print(f"Test set shape: {X_test.shape}")

# Expected output:
# Full dataset shape: (100, 2)
# Training set shape: (60, 2)
# Validation set shape: (20, 2)
# Test set shape: (20, 2)

Notice the calculation in Step 2. To get a 20% validation set from the original total, we take 25% of the train_val set, which constitutes 80% of the data (0.25 * 80% = 20%).

Important train_test_split Parameters

The train_test_split function has a few key parameters you should know.

Train Test Split: What it Means and How to Use It

This article from Built In provides a clear, practical guide to using train_test_split, including explanations of its important parameters.

First, read the subsection titled 'How to Implement Train Test Split in Python' to see another example. Pay special attention to the explanation of the random_state parameter. Then, read the subsection 'Methods for Splitting Data...' and focus on 'Stratified Splitting'.

Let's recap the key parameters:

  • test_size (or train_size): A float between 0.0 and 1.0 representing the proportion of the dataset to include in the split. Common splits are 70/15/15 or 80/10/10. The ideal ratio depends on the size of your dataset; with very large datasets, you might use smaller percentages for validation and testing (e.g., 98/1/1).
  • random_state: This is an integer that acts as a seed for the random number generator. By setting random_state, you ensure that the split is reproducible. Every time you run the code with the same random_state, you will get the exact same split. This is crucial for debugging and sharing your work. If you leave it as None, you'll get a different random split each time.
  • stratify: This parameter is very important for classification tasks, especially with imbalanced datasets. If you have a dataset with 90% class A and 10% class B, a random split might result in a validation set with very few or even zero examples of class B. By setting stratify=y (where y is your target labels), you ensure that the training, validation, and test sets all have the same proportion of each class as the original dataset.
Test your understanding!

You have a dataset of 1000 samples. You want to split it into a 70% training set, 15% validation set, and 15% test set. Describe the two train_test_split calls you would make. What values would you use for test_size in each call?

Show answer

Step 1: Split off the test set.
First, we split the data into a temporary training set (85% of the data) and the final test set (15% of the data).

X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.15, random_state=42)

This leaves us with 850 samples for X_train_val and 150 samples for X_test.

Step 2: Split the temporary training set into the final training and validation sets.
We need a validation set of 150 samples (15% of the original 1000). To get 150 samples from the 850 samples in X_train_val, we calculate the required proportion: 150 / 850 ≈ 0.1765.

X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.1765, random_state=42)

This gives us a final training set of approximately 700 samples (70%), a validation set of 150 samples (15%), and a test set of 150 samples (15%).



3. A Complete Workflow in Action

To see how these splits are used in a complete, (though simplified) modeling process, let's watch one more video. This will tie together what we've learned today with the concepts of model training and evaluation.

Train, Test, & Validation Sets | How to Train Machine Learning Models (Properly!!!)

This video by Greg Hogg demonstrates the entire workflow, from making the initial mistake of tuning on the test set to correctly using a validation (or 'holdout') set.

Watch the video to see the end-to-end process. Don't worry about understanding the details of the machine learning models themselves (Linear Regression, Random Forest). Instead, focus on: The problem that arises when the developer tries to improve the model using only the test set (around 12:00-14:00). How the validation set is created using train_test_split (around 16:00). How the validation error is used to choose the best model (around 19:00). How the test (holdout) set is used only at the very end to report the final, honest error (around 20:45).

This practical demonstration reinforces the importance of the three-set split for honest model building and evaluation.


Conclusion

You have now learned one of the most important procedural skills in machine learning. Properly separating your data ensures that you can train your model, tune it effectively, and, most importantly, produce a reliable estimate of its real-world performance.

Key Takeaways:

  • A three-way data split is essential for robust model development.
  • Training Set: Used by the model to learn its parameters.
  • Validation Set: Used by the data scientist to tune hyperparameters and select the best model.
  • Test Set: A "sacred," held-out set used only once for the final, unbiased performance evaluation.
  • In Python, you can achieve this by applying scikit-learn's train_test_split function twice.
  • Parameters like random_state ensure reproducibility, and stratify is crucial for maintaining class proportions in classification tasks.

Preview of the Next Lesson:
A single train-validation-test split works well, but it has a weakness: the model's performance can depend heavily on which specific data points happened to fall into the validation set. If you have a small dataset, this can be a significant problem. In our next lesson, we will explore a more robust and data-efficient technique for model evaluation called cross-validation.