Grid search in scikit-learn compares a fixed set of hyperparameter combinations by fitting the estimator repeatedly through cross-validation. It suits small search spaces where every candidate should be scored instead of sampled, such as choosing a few SVC values before training a final classifier.
GridSearchCV wraps the estimator and behaves like the selected estimator after fitting. Parameter names follow the estimator path, so a pipeline step named svc accepts grid keys such as svc__C and svc__gamma.
A compact breast cancer classification run keeps the grid intentionally small and holds back a separate test split. Cross-validation chooses the best parameter combination on the training rows, and the held-out score checks that the refitted estimator still predicts unseen rows after the search.
import sklearn from sklearn.datasets import load_breast_cancer from sklearn.metrics import accuracy_score from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC X, y = load_breast_cancer(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, stratify=y, random_state=42, ) model = make_pipeline( StandardScaler(), SVC(), ) param_grid = { "svc__C": [0.1, 1, 10], "svc__gamma": ["scale", 0.01], "svc__kernel": ["rbf"], } cv = StratifiedKFold( n_splits=5, shuffle=True, random_state=42, ) search = GridSearchCV( model, param_grid=param_grid, scoring="accuracy", cv=cv, refit=True, ) search.fit(X_train, y_train) candidate_count = len(search.cv_results_["params"]) test_predictions = search.predict(X_test) held_out_accuracy = accuracy_score(y_test, test_predictions) print(f"scikit-learn {sklearn.__version__}") print(f"scoring: {search.scoring}") print(f"cross-validation folds: {search.n_splits_}") print(f"evaluated candidates: {candidate_count}") print(f"total fits: {candidate_count * search.n_splits_}") print(f"best parameters: {search.best_params_}") print(f"best mean CV accuracy: {search.best_score_:.3f}") print(f"held-out accuracy: {held_out_accuracy:.3f}") print("top candidates:") ranked_candidates = sorted( zip( search.cv_results_["rank_test_score"], search.cv_results_["mean_test_score"], search.cv_results_["params"], ), key=lambda item: item[0], ) for rank, mean_score, params in ranked_candidates[:3]: print(f" rank {rank}: mean={mean_score:.3f}, params={params}")
The parameter grid has three C values, two gamma values, and one kernel value. GridSearchCV evaluates their cross-product, so this run has six candidate settings before cross-validation expands them across folds.
$ python run_grid_search.py
scikit-learn 1.9.0
scoring: accuracy
cross-validation folds: 5
evaluated candidates: 6
total fits: 30
best parameters: {'svc__C': 10, 'svc__gamma': 0.01, 'svc__kernel': 'rbf'}
best mean CV accuracy: 0.972
held-out accuracy: 0.979
top candidates:
rank 1: mean=0.972, params={'svc__C': 10, 'svc__gamma': 0.01, 'svc__kernel': 'rbf'}
rank 2: mean=0.970, params={'svc__C': 10, 'svc__gamma': 'scale', 'svc__kernel': 'rbf'}
rank 3: mean=0.967, params={'svc__C': 1, 'svc__gamma': 'scale', 'svc__kernel': 'rbf'}
The output shows six candidates and thirty total fits because each candidate is evaluated across five cross-validation folds.
The output selects svc__C=10, svc__gamma=0.01, and svc__kernel=rbf for the configured accuracy scorer. The top-candidate rows show how close the next two settings were.
refit=True retrains the best candidate on the full training split, so search.predict(X_test) uses the selected pipeline. The held-out accuracy line checks that the refitted estimator can score rows outside the grid-search folds.
param_grid = { "svc__C": [0.5, 1, 2], "svc__gamma": ["scale", 0.05], "svc__kernel": ["rbf"], }
Keep the grid bounded. Each extra candidate is evaluated once per fold, so small-looking grid additions can multiply the fit count quickly.
$ rm run_grid_search.py