📐 Section 2 · Supervised Learning 🟡 Intermediate MODULE 11

Hyperparameter Tuning — GridSearch & RandomSearch

⏱️ 23 min read
📖 Systematic Model Selection
🧩 4 Quiz Questions
🏗️ 1 Challenge · 3 tasks
Your progress in Section 286%
🎯 What you'll learn: Every algorithm in this section has had at least one hyperparameter to set by hand — alpha, C, K, n_estimators, num_leaves. Up to now, this course has said "try a few values and compare with cross-validation." This lesson makes that systematic: GridSearchCV to exhaustively try every combination, RandomizedSearchCV for when the search space is too large to check exhaustively, and how to read .best_params_ and .best_score_ once a search finishes.

Hyperparameter vs. Parameter

This distinction has come up repeatedly across Section 2, and it's worth stating cleanly now that you've seen many examples of it.

🧠
Parameter
Learned automatically FROM the data during .fit(). Examples: linear regression's .coef_ and .intercept_ (Lesson 6), an SVM's support vector weights.
🎛️
Hyperparameter
Set BEFORE training, by you (or a search process) — never learned by .fit() itself. Examples: alpha in Ridge, C and kernel in SVC, n_neighbors in KNN, n_estimators in gradient boosting.
🍳
The recipe vs. ingredients analogy
Think of "parameters" as the exact amount of each ingredient a chef ends up using after tasting and adjusting a dish during cooking — determined by the process itself. "Hyperparameters" are more like the oven temperature and cook time set BEFORE cooking even starts — decisions made in advance that shape how the whole process unfolds, but aren't discovered by the cooking process itself.

Every hyperparameter sweep this course has done manually so far — trying a few alpha values, a few K values, a few C values — is exactly what GridSearchCV and RandomizedSearchCV automate.

GridSearchCV — Exhaustive Search

GridSearchCV, from sklearn.model_selection, tries EVERY combination of hyperparameter values you specify in a param_grid, evaluating each combination with cross-validation (Lesson 4), and reports which combination scored best.

grid_search.py
PYTHON
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.svm import SVC

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Every key is a hyperparameter name, every value is a list of options to try
param_grid = {
    "C": [0.1, 1, 10, 100],
    "kernel": ["linear", "rbf"],
    "gamma": ["scale", "auto"]
}

grid_search = GridSearchCV(
    SVC(),
    param_grid,
    cv=5,             # 5-fold cross-validation for EVERY combination
    scoring="accuracy"
)
grid_search.fit(X_train, y_train)

print("Best hyperparameters:", grid_search.best_params_)
print("Best cross-validated score:", grid_search.best_score_)

# The final, refit model with the best hyperparameters, ready to use directly
best_model = grid_search.best_estimator_
print("Test accuracy of best model:", best_model.score(X_test, y_test))
⚠️
"Exhaustive" means the combinations multiply fast
The example above tries 4 × 2 × 2 = 16 combinations, EACH evaluated across 5 folds — 80 total model fits. Add a couple more hyperparameters with a few options each, and the count explodes combinatorially. This is exactly the problem RandomizedSearchCV exists to solve, in Section 3.
📝
GridSearchCV IS cross-validation, built in
Notice cv=5 is passed directly to GridSearchCV — every candidate combination gets evaluated with proper k-fold cross-validation (Lesson 4) automatically, not a single lucky/unlucky split. This is why grid_search.fit(X_train, y_train) is called on the TRAINING data only — the held-out X_test/y_test stays completely untouched until the very end, for one final honest check.

RandomizedSearchCV — For Large Search Spaces

Instead of trying every combination, RandomizedSearchCV samples a fixed NUMBER of random combinations from the specified ranges — controlled by n_iter. This trades exhaustiveness for speed.

randomized_search.py
PYTHON
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import GradientBoostingClassifier
from scipy.stats import randint, uniform

# A much larger space than we'd want to grid-search exhaustively
param_distributions = {
    "n_estimators": randint(50, 500),
    "max_depth": randint(2, 10),
    "learning_rate": uniform(0.01, 0.3),
    "subsample": uniform(0.6, 0.4)
}

random_search = RandomizedSearchCV(
    GradientBoostingClassifier(random_state=42),
    param_distributions,
    n_iter=30,        # only try 30 random combinations, not every possibility
    cv=5,
    scoring="accuracy",
    random_state=42
)
random_search.fit(X_train, y_train)

print("Best hyperparameters found:", random_search.best_params_)
print("Best cross-validated score:", random_search.best_score_)
Use GridSearchCV when...
The search space is small enough to check every combination in reasonable time — a handful of hyperparameters, each with just a few candidate values.
Use RandomizedSearchCV when...
The search space is large (many hyperparameters, wide continuous ranges) — exhaustive search would take too long, and a well-chosen random sample of combinations tends to find near-optimal settings much faster.
Randomized search isn't just "worse but faster"
A counterintuitive but well-established result: with a large search space, trying N random combinations often finds a better result than checking N sequential grid points along a coarse grid, because a random sample explores the space more broadly instead of being constrained to a fixed lattice. This is a real practical reason it's preferred, not just a shortcut when time is limited.

Reading best_params_ and best_score_

Both search classes expose the same result attributes after .fit() completes.

🏆
.best_params_
A dictionary of the hyperparameter values that scored best across the search — ready to plug directly into a fresh model, or already refit for you (see below).
📊
.best_score_
The mean cross-validated score achieved by the best combination — an estimate of performance, NOT the same as evaluating on the untouched test set.
🔧
.best_estimator_
By default, scikit-learn automatically refits a model using the best hyperparameters on the FULL training data — this attribute is that ready-to-use fitted model.
📋
.cv_results_
A detailed dictionary with the score of EVERY combination tried, useful for deeper analysis of which hyperparameters mattered most.

The illustrative output below shows the SHAPE of what a completed search reports — not a claim about any specific dataset's real accuracy:

output (illustrative)
OUTPUT
# Best hyperparameters: {'C': 10, 'gamma': 'scale', 'kernel': 'rbf'}
# Best cross-validated score: 0.891
# Test accuracy of best model: 0.87
# -> Test accuracy is close to but not identical to best_score_, which is
#    expected: best_score_ is a CV average on training data, the test
#    score is one final check on data the search never saw at all.
⚠️
Never tune hyperparameters against the test set
The whole point of holding out a test set (Lesson 4) is to get one honest, unbiased final estimate. If you tune hyperparameters by repeatedly checking test-set performance and adjusting, the test set stops being a fair, unseen check — it quietly becomes part of the training process, and its score becomes optimistic. Always run GridSearchCV/RandomizedSearchCV on the TRAINING data, and touch the test set only once, at the very end.

Lesson Summary — and Section 2 Almost Done

A hyperparameter is set before training; a parameter is learned during .fit().
GridSearchCV exhaustively tries every combination in a param_grid, each evaluated with cross-validation.
RandomizedSearchCV samples a fixed number (n_iter) of random combinations — better for large search spaces.
.best_params_, .best_score_, and .best_estimator_ report and hand you the winning configuration.
Tune on training data with cross-validation; touch the test set only once, at the very end.
🧩 Knowledge Check — Lesson 11
4 questions on hyperparameters, GridSearchCV, and RandomizedSearchCV.
1. Which of these is a hyperparameter, not a learned parameter?
2. What does GridSearchCV do with the values in param_grid?
3. When is RandomizedSearchCV generally preferred over GridSearchCV?
4. Why should you avoid tuning hyperparameters by repeatedly checking the test set?
💪
Try It Yourself — Lesson 11
Run real searches before the capstone project · Intermediate Level

These tasks prepare you directly for Lesson 12's project.

Task 1: Grid search a KNN 🔍

Using Section 2's pattern, run a GridSearchCV over KNeighborsClassifier with a param_grid of {"n_neighbors": [3, 5, 7, 9, 11], "weights": ["uniform", "distance"]}. Print .best_params_ and .best_score_. Remember to scale your features first (Lesson 8)!
Task 2: Randomized search a gradient booster ⚡

Using Section 3's pattern, run a RandomizedSearchCV with n_iter=20 over GradientBoostingClassifier's n_estimators, max_depth, and learning_rate. Compare .best_score_ to a default, untuned GradientBoostingClassifier()'s cross-validated score.
Task 3: Compare grid vs. random on the same budget ⚖️

For a small SVC search space (say, C in [0.1, 1, 10] and kernel in ["linear", "rbf"] — 6 combinations total), run BOTH GridSearchCV (all 6) and RandomizedSearchCV(n_iter=6) sampling from the same 6 options. Do they find the same best combination? Why might that make sense with such a small space?
💡 Show hints if you're stuck
  • Task 1: weights="distance" gives closer neighbors more voting power than farther ones — it often edges out "uniform" slightly, but not always.
  • Task 2: The tuned search's best_score_ should generally be at or above the untuned default's cross-validated score — if it's not, try a wider param_distributions range or a larger n_iter.
  • Task 3: With n_iter=6 covering all 6 possible combinations exactly, RandomizedSearchCV effectively becomes exhaustive too — they should find the identical best combination in this specific small-space case.
Finished this lesson?
Mark it complete to track your progress.
🎉

Lesson 11 Complete!

You now understand hyperparameters vs. parameters, GridSearchCV, RandomizedSearchCV, and how to read best_params_/best_score_ safely. One lesson left in Section 2: putting everything from Lessons 6–11 together into a full regression project.

Module 11 of 24 Section 2 — Supervised Learning Algorithms