ML Train/Test Split
To know if a model really works, you must test it on data it has never seen.
Why Split the Data?
Imagine a teacher gives students the exact exam questions to practise. Everyone gets full marks, but nobody has really learned. The marks tell you nothing.
Models are the same. If you test a model on the data it trained on, it can simply remember the answers. So we keep some data aside:
| Set | Used for | Typical size |
|---|---|---|
| Training set | The model learns from it with fit() | 70% to 80% |
| Test set | Measures performance on unseen data | 20% to 30% |
The test set is the "final exam". The model must never see it during training.
Using train_test_split
scikit-learn has a ready-made function for this:
Example
from sklearn.model_selection import train_test_split
X = [[i] for i in range(10)] # 10 examples, 1 feature
y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print("Training rows:", len(X_train))
print("Test rows:", len(X_test))
print("Test data:", X_test, y_test)Training rows: 8 Test rows: 2 Test data: [[8], [1]] [1, 0]
The function shuffles the rows and returns four parts, always in this order: X_train, X_test, y_train, y_test.
Important parameters
| Parameter | Meaning |
|---|---|
test_size=0.2 | Put 20% of rows in the test set |
random_state=42 | Fix the shuffle so you get the same split every run |
stratify=y | Keep the same mix of classes in both sets |
shuffle=True | Shuffle before splitting (the default) |
Use random_state while learning. Without it, the split changes every time you run the code, and your results change too, which is confusing.
A Full Example: Train, Then Test
This example uses the Iris dataset, which comes with scikit-learn. It has measurements of 150 flowers from 3 species.
Example
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=1, stratify=y
)
model = KNeighborsClassifier(n_neighbors=5)
model.fit(X_train, y_train) # learn only from training data
print("Test accuracy:", round(model.score(X_test, y_test), 2))Test accuracy: 0.95
The model was right on 95% of flowers it had never seen. That number is a fair estimate of how it will do on new flowers.
Keeping Classes Balanced with stratify
If your data has 3 classes in equal numbers, a random split might put too many of one class in the test set. stratify=y prevents this:
Example
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0, stratify=y
)
print("Whole data:", np.bincount(y))
print("Test set: ", np.bincount(y_test))Whole data: [50 50 50] Test set: [10 10 10]
Stratifying matters most when one class is rare, such as fraud cases that are only 1% of transactions.
Common Mistakes
- Preparing data before splitting. If you fit a scaler on all the data, information from the test set leaks into training. Split first, then
fitthe scaler on the training set only. - Testing again and again on the same test set while you tune your model. You slowly "fit" the test set. Use cross-validation for tuning (see Overfitting).
- Shuffling time data. For sales or weather over time, train on the past and test on the future, not a random mix.
Exercise
Exercise
What does train_test_split return, in order?
