Python Tutorial
Grid Search
Systematically search hyperparameter combinations to find the settings that generalize best.
Parameters vs Hyperparameters
A model learns its parameters (coefficients, split thresholds) from data. Its hyperparameters — like C, max_depth, or n_neighbors — are set before training and control how learning happens. Grid search tries every combination on a grid and keeps the best, judged by cross-validation.
Basic GridSearchCV
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
X, y = load_iris(return_X_y=True)
param_grid = {
"C": [0.01, 0.1, 1, 10, 100],
"solver": ["lbfgs", "liblinear"],
}
grid = GridSearchCV(
LogisticRegression(max_iter=1000),
param_grid,
cv=5,
scoring="accuracy",
n_jobs=-1) # use all CPU cores
grid.fit(X, y)
print("Best params:", grid.best_params_)
print("Best CV score:", grid.best_score_.round(3))After fitting, grid.best_estimator_ is the model retrained on the full data with the winning settings — ready to predict.
Searching Inside a Pipeline
Prefix parameter names with the step name (step__param) so preprocessing is re-fit inside each fold — the leak-free way to tune.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())])
param_grid = {
"svc__C": [0.1, 1, 10],
"svc__gamma": [0.01, 0.1, 1],
"svc__kernel": ["rbf"],
}
grid = GridSearchCV(pipe, param_grid, cv=5, n_jobs=-1)
grid.fit(X, y)Random Search for Large Spaces
Grid search cost explodes combinatorially. When the grid is huge, RandomizedSearchCV samples a fixed number of combinations and often finds near-optimal settings far faster.
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import loguniform
param_dist = {"C": loguniform(1e-3, 1e3), "gamma": loguniform(1e-4, 1e0)}
search = RandomizedSearchCV(
SVC(), param_dist, n_iter=25, cv=5, random_state=0, n_jobs=-1)
search.fit(X, y)Inspecting All Results
import pandas as pd
results = pd.DataFrame(grid.cv_results_)
print(results[["params", "mean_test_score", "std_test_score"]]
.sort_values("mean_test_score", ascending=False)
.head())Best Practices
- Hold out a final test set; grid search should only ever see the training data.
- Search hyperparameters on a log scale (0.01, 0.1, 1, 10, …).
- Use
RandomizedSearchCVor Bayesian optimization when the grid is large. - Set
n_jobs=-1to parallelize, and pick ascoringmetric that matches your goal (e.g.f1for imbalanced data).
Try It Yourself
Exercise 1: Grid-search C in [0.1, 1, 10] for logistic regression on iris and print the best value.
Show solution
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
X, y = load_iris(return_X_y=True)
g = GridSearchCV(LogisticRegression(max_iter=1000), {"C": [0.1, 1, 10]}, cv=5)
g.fit(X, y)
print(g.best_params_)Exercise 2: When the search grid is huge, what should you use instead of exhaustive grid search?
Show solution
RandomizedSearchCV (or Bayesian optimization) — it samples combinations and finds near-optimal settings far faster.
📘 Real-World Deep Dive
Knowing <strong>ML Grid Search (scikit-learn)</strong> well is what turns scikit-learn from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of ML Grid Search that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
from sklearn.datasets import load_iris
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV, train_test_split
X, y = load_iris(return_X_y=True)
Xtr, Xte, ytr, yte = train_test_split(X, y, random_state=0)
grid = GridSearchCV(SVC(), {"C":[0.1, 1, 10]}, cv=3).fit(Xtr, ytr)
print("best:", grid.best_params_, "score:", round(grid.score(Xte, yte), 3))Expected Output
(see source)Common mistakes
fitexpects numeric arrays;OneHotEncoder/LabelEncoderare easy to forget for categorical features.- Calling
predicton a model trained on unscaled data and then scaling inputs at inference time silently degrades accuracy. train_test_split(X, y)requires both arrays;train_test_split(X)for unsupervised learning slips past static checkers.- Treating ML Grid Search as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Wrap preprocessing + estimator in a
Pipelinesofit/predictstay reproducible. - Use
joblib/picklefor serialising models, not the entire Python state. - For > 100 k rows, switch to
HistGradientBoostingClassifierorcuML. - When working with scikit-learn, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.