Back to Blog
Python

Python Sklearn GridSearchCV and RandomizedSearchCV Compared

python sklearn gridsearchcv and randomizedsearchcv: Compare GridSearchCV and RandomizedSearchCV in scikit-learn: how each searches parameter space, runtime tradeoffs,...

scikit-learnhyperparameter tuningGridSearchCVRandomizedSearchCVcross-validationmodel selection
A visual comparison of an exhaustive grid search and a random sampling search over a two-dimensional hyperparameter space in scikit-learn.

When you need to tune hyperparameters in scikit-learn, python sklearn gridsearchcv and randomizedsearchcv are the two standard tools. GridSearchCV exhaustively evaluates every combination in a defined parameter grid, while RandomizedSearchCV samples a fixed number of combinations from parameter distributions. The choice between them affects runtime, coverage, and how you define the search space.

The Core Difference Between Exhaustive and Sampled Search

GridSearchCV builds a Cartesian product of all parameter values you provide and evaluates every combination using cross-validation. If you specify 3 values for n_estimators, 4 values for max_depth, and 2 values for min_samples_split, it evaluates 3 × 4 × 2 = 24 combinations, each with the configured number of folds.

RandomizedSearchCV instead samples n_iter combinations from distributions or lists you provide. It does not evaluate the full grid. With n_iter=50, it evaluates exactly 50 combinations regardless of how large the underlying parameter space is.

This is the fundamental distinction: exhaustive enumeration versus random sampling with a fixed budget.

How GridSearchCV Evaluates the Parameter Grid

from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier param_grid = { "n_estimators": [50, 100, 200], "max_depth": [None, 10, 20], "min_samples_split": [2, 5], } grid_search = GridSearchCV( estimator=RandomForestClassifier(random_state=42), param_grid=param_grid, cv=5, scoring="f1_macro", n_jobs=-1, ) grid_search.fit(X_train, y_train) print(grid_search.best_params_)

GridSearchCV takes a dict where each key maps to a list of candidate values. It constructs the full Cartesian product internally. The number of fits is the product of list lengths multiplied by the number of folds. In this example: 3 × 3 × 2 = 18 combinations, times 5 folds, which is 90 model fits.

The fitted object exposes best_params_, best_score_, and cv_results_, so you can inspect the full evaluation table after the search completes.

How RandomizedSearchCV Samples Parameter Space

from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint, uniform param_distributions = { "n_estimators": randint(50, 300), "max_depth": [None, 10, 20, 30], "min_samples_split": randint(2, 10), } random_search = RandomizedSearchCV( estimator=RandomForestClassifier(random_state=42), param_distributions=param_distributions, n_iter=40, cv=5, scoring="f1_macro", n_jobs=-1, random_state=42, ) random_search.fit(X_train, y_train) print(random_search.best_params_)

The parameter specification differs from GridSearchCV. GridSearchCV expects param_grid with lists. RandomizedSearchCV accepts param_distributions, where each key can be a list or a SciPy distribution such as randint or uniform. When you pass a list, RandomizedSearchCV samples from it with replacement. When you pass a distribution, it draws random values from that distribution.

The number of fits is exactly n_iter × cv. With n_iter=40 and cv=5, that is 200 fits, independent of how many distinct values the distributions could produce.

GridSearchCV vs RandomizedSearchCV: Key Tradeoffs

AspectGridSearchCVRandomizedSearchCV
Search strategyExhaustive Cartesian productRandom sampling with fixed budget
Parameter specparam_grid with listsparam_distributions with lists or distributions
Number of fitsproduct of list lengths × cvn_iter × cv
CoverageComplete within defined gridPartial, probabilistic
Best forSmall grids, known good rangesLarge or continuous spaces
Runtime controlGrows with grid sizeFixed by n_iter

Choosing Between GridSearchCV and RandomizedSearchCV

Use GridSearchCV when the parameter space is small and you already know reasonable ranges. Exhaustive evaluation gives you a complete picture of how each combination performs, which is useful when you want to compare all candidates or when the grid is small enough that runtime is not a concern.

Use RandomizedSearchCV when the parameter space is large, includes continuous values, or when you have a fixed compute budget. With a fixed n_iter, runtime is predictable. Random sampling also tends to explore more distinct values per parameter when distributions are used, because each draw can produce a new value rather than reusing a small set of discrete options.

A practical rule: if the total number of grid combinations times the number of folds runs in acceptable time, GridSearchCV is the simpler choice. Otherwise, RandomizedSearchCV with a reasonable n_iter gives you more coverage per unit of compute.

Computational Cost and Runtime Behavior

The dominant cost in both approaches is model fitting during cross-validation. GridSearchCV's cost grows multiplicatively with the size of each parameter list. Adding one more value to a single parameter multiplies the total number of fits by that value's count.

RandomizedSearchCV decouples runtime from the size of the parameter space. n_iter controls the number of combinations evaluated, so doubling n_iter doubles the number of fits. This makes it the preferred option when you have a hard time or compute budget.

Both classes accept n_jobs and verbose. Setting n_jobs=-1 parallelizes fits across available CPU cores, which helps but does not change the total work. With large models or large datasets, the per-fit time dominates, so reducing the number of fits matters more than parallelization.

Common Pitfalls and Edge Cases

Pitfall 1: Using a distribution where a list is expected, or vice versa. GridSearchCV requires lists; passing a SciPy distribution raises an error. RandomizedSearchCV accepts both, but a list means sampling with replacement, which can evaluate the same value multiple times.

Pitfall 2: Forgetting to set random_state on RandomizedSearchCV. Without it, results are not reproducible. Set both the estimator's random_state and the search's random_state.

Pitfall 3: Ignoring the scoring metric. Both classes default to the estimator's default scoring. For classification, accuracy may not be appropriate for imbalanced data. Pass an explicit scoring argument such as "f1_macro" or a custom scorer.

Pitfall 4: Using the same data for tuning and final evaluation. The best_params_ found by either search are selected based on cross-validation scores. Evaluating the final model on the same data leaks information. Hold out a test set before tuning.

Pitfall 5: For GridSearchCV, the grid size explodes quickly. With 5 parameters each having 5 values, you get 3125 combinations. At 5 folds, that is 15,625 fits. Estimate the total fit count before launching a long-running search.

Combining Both Approaches in a Tuning Pipeline

A common production pattern is to use RandomizedSearchCV first to explore a wide space, then use GridSearchCV around the promising region to refine. This combines the exploration efficiency of random sampling with the exhaustive precision of grid search on a narrowed range.

# Stage 1: broad exploration random_search.fit(X_train, y_train) best = random_search.best_params_ # Stage 2: refine around the best values for numeric parameters refined_grid = { "n_estimators": [ max(10, best["n_estimators"] - 20), best["n_estimators"], best["n_estimators"] + 20, ], "min_samples_split": [ max(2, best["min_samples_split"] - 1), best["min_samples_split"], best["min_samples_split"] + 1, ], } grid_refine = GridSearchCV( estimator=RandomForestClassifier(random_state=42), param_grid=refined_grid, cv=5, scoring="f1_macro", n_jobs=-1, ) grid_refine.fit(X_train, y_train) print(grid_refine.best_params_)

This two-stage approach keeps the total fit count manageable while still performing a dense search near the most promising region. It is especially useful when the initial parameter space is large and the best region is unknown. Note that non-numeric parameters such as max_depth=None need separate handling when constructing the refined grid, since arithmetic on None is not valid.

python sklearn gridsearchcv and randomizedsearchcv: Practica | RYUSLOG DEV