CTCaliberTek
Course: Machine Learning with Python: End to End · Lesson 5 of 7Log in to track your progress and earn a certificate when you finish the course.
AI & Machine Learning

Overfitting, Cross-Validation, and Hyperparameter Tuning

Published July 8, 2026·Updated October 29, 2026·9 min read
Like SaveLog in to like, save, and track progress.

Introduction

Overfitting is when a model learns the noise in the training data and fails on new data. A single train/test split can also be lucky or unlucky. This lesson shows how to spot overfitting, get a more reliable estimate with cross-validation, and tune hyperparameters without peeking at the test set.

Prerequisites

  • Evaluation metrics lesson
  • A scikit-learn classifier and the breast cancer data from earlier lessons

Step-by-Step Guide

Step 1: Spot overfitting

Compare training and test scores. A large gap is the classic sign. An unconstrained decision tree is a good demonstration because it can memorize the training set.

from sklearn.tree import DecisionTreeClassifier

tree = DecisionTreeClassifier(random_state=42).fit(X_train, y_train)
print("Train:", tree.score(X_train, y_train))   # likely 1.0
print("Test: ", tree.score(X_test, y_test))     # noticeably lower

shallow = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X_train, y_train)
print("Shallow test:", shallow.score(X_test, y_test))

Step 2: Estimate performance with cross-validation

K-fold cross-validation splits the data into k parts, trains on k-1 of them, tests on the remaining one, and repeats so every part is used for testing once. The average and spread of the scores are far more trustworthy than one split.

from sklearn.model_selection import cross_val_score

scores = cross_val_score(model, X, y, cv=5, scoring="f1")
print(scores.round(3))
print("Mean:", scores.mean().round(3), "Std:", scores.std().round(3))

Step 3: Tune hyperparameters with a grid search

Hyperparameters are settings you choose before training, such as tree depth. GridSearchCV tries every combination using cross-validation on the training data only, then refits the best one.

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

param_grid = {
    "n_estimators": [100, 300],
    "max_depth": [None, 5, 10],
}

grid = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid, cv=5, scoring="f1", n_jobs=-1,
)
grid.fit(X_train, y_train)

print(grid.best_params_)
print("CV score:", grid.best_score_)

Step 4: Touch the test set once

After tuning, evaluate the final model on the test set a single time. If you keep adjusting based on test results, the test set becomes part of training and your estimate becomes optimistic.

print("Final test score:", grid.score(X_test, y_test))

Step 5: Reduce overfitting

Common remedies are getting more data, simplifying the model (less depth, fewer features), adding regularization, and using ensembles. Try these in order of how cheap they are, and measure each change with cross-validation.

FAQs

What is the difference between a parameter and a hyperparameter?
Parameters are learned from the data during training, such as coefficients. Hyperparameters are settings you choose beforehand that control learning, such as tree depth or the number of trees.
When is grid search too slow?
The number of combinations multiplies quickly. For larger searches, try RandomizedSearchCV, which samples a fixed number of combinations.

Summary

Check the train/test gap for overfitting, use cross-validation for stable estimates, tune with GridSearchCV on the training data only, and use the test set once at the end.

Related tutorials

Comments

Log in or create a free account to join the discussion.

  • No comments yet. Be the first to comment.