Hyperparameter Tuning
Definition
Hyperparameter tuning systematically searches over combinations of model settings (not learned from data, but set beforehand) to find the combination that performs best under cross-validation.
GridSearchCV β Exhaustive Search
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
param_grid = {
"n_estimators": [100, 200, 300],
"max_depth": [None, 10, 20, 30],
"min_samples_split": [2, 5, 10]
}
grid = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid,
cv=5, # 5-fold cross-validation for each combination
scoring="f1_weighted",
n_jobs=-1, # parallelize across all CPU cores
verbose=1
)
grid.fit(X_train, y_train)
grid.best_params_ # the winning combination
grid.best_score_ # its CV score
grid.best_estimator_ # a fitted model with the best params, ready to use
grid.predict(X_test) # GridSearchCV itself can predict directly (uses best_estimator_)Grid search scales combinatorially
3 params Γ 4 values Γ 3 values Γ 5-fold CV = 3Γ4Γ3Γ5 = 180 model fits. Grid search gets expensive fast with many hyperparameters β consider
RandomizedSearchCVfor larger search spaces.
RandomizedSearchCV β Sample a Fixed Number of Combinations
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform
param_dist = {
"n_estimators": randint(50, 500),
"max_depth": randint(3, 50),
"min_samples_split": randint(2, 20),
"max_features": uniform(0.1, 0.9)
}
random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_distributions=param_dist,
n_iter=50, # only try 50 random combinations, not all possible
cv=5,
scoring="f1_weighted",
random_state=42,
n_jobs=-1
)
random_search.fit(X_train, y_train)
RandomizedSearchCVis usually more efficientWith many hyperparameters, random search often finds near-optimal combinations faster than exhaustive grid search, because it doesnβt waste evaluations on unpromising regions of low-impact parameters.
Tuning a Full Pipeline
param_grid = {
"model__C": [0.1, 1, 10], # "stepname__paramname" syntax
"preprocessor__num__imputer__strategy": ["mean", "median"]
}
grid = GridSearchCV(full_pipeline, param_grid, cv=5)
grid.fit(X_train, y_train)See 04-Pipelines-ColumnTransformer for building the pipeline these parameter names refer to.
Inspecting All Results
import pandas as pd
results = pd.DataFrame(grid.cv_results_)
results[["params", "mean_test_score", "std_test_score"]].sort_values("mean_test_score", ascending=False)Learning Curves β Diagnose Bias vs Variance
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
train_sizes, train_scores, val_scores = learning_curve(
model, X, y, cv=5, train_sizes=np.linspace(0.1, 1.0, 10)
)
plt.plot(train_sizes, train_scores.mean(axis=1), label="Training score")
plt.plot(train_sizes, val_scores.mean(axis=1), label="Validation score")
plt.xlabel("Training set size"); plt.ylabel("Score"); plt.legend()Reading a learning curve
- Both curves low, converged β underfitting (high bias) β try a more complex model or more features
- Large gap between curves β overfitting (high variance) β try regularization, more data, or a simpler model
- Both curves high, converged β good fit
Validation Curves β Tune a Single Hyperparameter
from sklearn.model_selection import validation_curve
param_range = [1, 5, 10, 20, 50, 100]
train_scores, val_scores = validation_curve(
RandomForestClassifier(random_state=42), X, y,
param_name="max_depth", param_range=param_range, cv=5
)Bayesian / Advanced Search (External Libraries)
# pip install scikit-optimize
from skopt import BayesSearchCV
opt = BayesSearchCV(model, search_spaces, n_iter=32, cv=5)
BayesSearchCVuses prior results to intelligently choose the next hyperparameter combination to try, rather than sampling randomly or exhaustively β often finds good results with fewer total fits thanRandomizedSearchCV.