Hyperparameter Tuning — GridSearch & RandomSearch
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.
.fit(). Examples: linear regression's .coef_ and .intercept_ (Lesson 6), an SVM's support vector weights..fit() itself. Examples: alpha in Ridge, C and kernel in SVC, n_neighbors in KNN, n_estimators in gradient boosting.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.
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))
RandomizedSearchCV exists to solve, in Section 3.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.
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_)
Reading best_params_ and best_score_
Both search classes expose the same result attributes after .fit() completes.
The illustrative output below shows the SHAPE of what a completed search reports — not a claim about any specific dataset's real accuracy:
# 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.
GridSearchCV/RandomizedSearchCV on the TRAINING data, and touch the test set only once, at the very end.Lesson Summary — and Section 2 Almost Done
.fit().param_grid, each evaluated with cross-validation.n_iter) of random combinations — better for large search spaces.These tasks prepare you directly for Lesson 12's project.
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)!
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.
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.