model_selection: cross-validation and hyperparameter search
GridSearchCV and RandomizedSearchCV are meta-estimators: they never touch the internals of the wrapped estimator, only its get_params/set_params/clone contract. BaseSearchCV.fit() clones the estimator once as a template, then re-clones and re-configures it via set_params for every (candidate, fold) pair so fits never share mutable state, before finally cloning and re-fitting on the full data with the winning parameters. This module traces that clone-per-fit discipline end to end and shows why it lets a single generic algorithm search over literally any estimator.
Every (candidate, split) pair gets its own clone
sklearn/model_selection/_search.py · lines 1083–1101clone(base_estimator) is called once per delayed task — every candidate/fold combination gets its own fresh, unfitted instance instead of reusing or mutating a shared one, which is what makes running these fits in parallel safe.
parameters is one candidate's hyperparameter dict (e.g. {'estimator__C': 10}); it rides along to _fit_and_score, which is where it actually gets applied to the clone.
candidate_split_pairs is the cross product of every candidate parameter dict with every CV split, so this comprehension is literally n_candidates * n_splits independent fit-and-score jobs.
out = parallel( delayed(_fit_and_score)( clone(base_estimator), X, y, train=train, test=test, parameters=parameters, split_progress=(split_idx, n_splits), candidate_progress=(cand_idx, n_candidates), **fit_and_score_kwargs, caller=self, callback_ctx=evaluation_ctx, ) for ( ((cand_idx, parameters), (split_idx, (train, test))), evaluation_ctx, ) in zip(candidate_split_pairs, evaluation_contexts) )
if parameters is not None: # here we clone the parameters, since sometimes the parameters # themselves might be estimators, e.g. when we search over different # estimators in a pipeline. # ref: https://github.com/scikit-learn/scikit-learn/pull/26786 estimator = estimator.set_params(**clone(parameters, safe=False))
sklearn/model_selection/_validation.py · lines 827–832if self.refit: # here we clone the estimator as well as the parameters, since # sometimes the parameters themselves might be estimators, e.g. # when we search over different estimators in a pipeline. # ref: https://github.com/scikit-learn/scikit-learn/pull/26786 self.best_estimator_ = clone(base_estimator).set_params( **clone(self.best_params_, safe=False) )
sklearn/model_selection/_search.py · lines 1164–1171Check your understanding
Answered in place — nothing is graded, everything is explained. 0 / 3 passed
Why does BaseSearchCV.fit() call clone(base_estimator) separately for every (candidate, split) pair instead of reusing one shared estimator instance across all of them?
Inside _fit_and_score, which method is used to apply a candidate's hyperparameter dict onto the cloned estimator before fitting?
When refit=True, what data does best_estimator_ end up being fit on?