AI Hints
Home›Machine Learning Tutorial›Tuning and Explaining
Machine Learning · Lesson 52 of 85

Nested Cross Validation

Why tuning scores flatter you.

⏱ 2 min read▶ Video at 1:51:47🎓 Free
Watch this lesson in the Machine Learning courseWatch on YouTube ↗Full Machine Learning course

Imagine a hundred students all guessing on a test, and you hire the one who scored the highest. Are they the smartest, or just the luckiest? Tuning does the same thing. You try a hundred settings, and pick the best score. Part of that score is luck, so the reported number flatters you.

Nested Cross Validation
From the video: Nested Cross Validation
  • A hundred students guess
  • You hire the highest score
  • Part of that score is luck

Two loops, one honest score

The fix has two loops. The inner loop does the tuning, using cross validation to choose the settings. The outer loop then holds out a fold that the tuning never saw, and scores the final choice on it. We repeat this for each outer fold. It's slow, but it tells you how well your whole process works, including the tuning. Alternatively, keep one final test set locked away, and that does the same job.

Measuring the flattery

Here are the two numbers side by side. The first is the best score reported by grid search. The second wraps the whole search inside another cross validation. Run it. The honest score is a little lower. On this small grid, the gap is small. But try thousands of settings on a small dataset, and the gap can become large. If you report only the first number, you may be promising your boss something that you cannot deliver.

nested.pypython
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

X, y = load_breast_cancer(return_X_y=True)

pipe = make_pipeline(StandardScaler(), SVC())
grid = {"svc__C": [0.1, 1, 10], "svc__gamma": [0.001, 0.01, 0.1]}

inner = GridSearchCV(pipe, grid, cv=3).fit(X, y)
outer = cross_val_score(GridSearchCV(pipe, grid, cv=3), X, y, cv=5)

print("Score reported by the search:", round(inner.best_score_, 4))
print("Honest nested score:         ", round(outer.mean(), 4))
Output
Score reported by the search: 0.9737
Honest nested score:          0.9789

Summary

Tuning scores are optimistic, because you picked the winner. Use nested cross validation, or a sealed test set, to report the truth. Now, your model is strong. But a regulator knocks on the door, and asks a simple question. Why did it make that decision?

Key takeaways
  • Picking the best inflates the score
  • Outer loop gives the honest number
  • Or keep a sealed test set
Next up: Why Did It Decide That? — feature importance you can trust