Randomized cross-validated search over hyperparameter distributions.
This module provides RandomSearchCV, which draws a fixed budget of n_iter configurations from the distributions you declare instead of enumerating a grid. Because the budget is decoupled from the dimensionality of the space, it is the practical default when some parameters are continuous, when you do not know in advance which parameters matter, or when you simply want to cap the number of model fits.
The classic argument (Bergstra & Bengio, 2012) is that when only a few of the tuned parameters actually influence the score, a random draw of n points explores n distinct values of each important parameter, while a grid re-tests the same handful of values over and over.
Search spaces are expressed with ParameterDistribution, which accepts explicit value lists as well as (low, high), (low, high, 'log') and (low, high, 'int') range tuples.
Classes
Randomized cross-validated search over hyperparameter distributions.
n_iter independent configurations from the declared distributions, cross-validates each, and keeps the best. Unlike GridSearchCV, the cost is fixed by n_iter rather than by the size of the space, so continuous parameters and high-dimensional spaces are affordable.__init__( self, estimator, param_distributions: Dict, n_iter: int = 10, scoring: Union[str, Callable] = 'accuracy', cv: int = 5, refit: bool = True, verbose: int = 0, n_jobs: int = 1, random_seed: Optional[int] = None, progress_callback: Optional[Callable] = None, **kwargs, )
Overview
-
Seed a random generator from
random_seed(or the global TuiML seed). -
Repeat
n_itertimes: sample one value per parameter from its
- Log the mean score, its standard deviation, and the mean per-fold fit
cv_results_, and track the running best.
-
Rank all samples in
cv_results_['rank_test_score'](rank 1 is best). -
If
refit=True, refit a fresh copy of the estimator on the full
best_params_ and store it as best_estimator_.Search space
param_distributions maps a parameter name to one of the forms accepted by ParameterDistribution:
{'criterion': ['gini', 'entropy'], # list -> uniform choice
'max_depth': (2, 20, 'int'), # 3-tuple 'int' -> uniform integer, high INCLUSIVE
'C': (0.001, 100, 'log'), # 3-tuple 'log' -> log-uniform (low > 0 required)
'max_features': (0.1, 1.0), # 2-tuple numeric -> uniform continuous, high exclusive
'alpha': lambda: 10 ** -3} # callable -> called with no arguments
Two subtleties follow from how a tuple is classified. A 2-tuple of numbers is always read as a continuous range, so use a list when you mean "choose one of these two numbers". A tuple of non-numbers such as ('linear', 'rbf') is not a range and is treated as a choice. Sampled choices come back as NumPy scalars (np.str_('gini'), np.int64(5)) because the draw goes through numpy.random.RandomState.choice.
Cost
refit=True — independent of how many parameters are tuned. That is the whole point: with n_iter=20, cv=5 you pay 101 fits whether the space has two dimensions or twenty, whereas GridSearchCV would need the full cartesian product.Parameters
estimator
fit(X, y) and predict(X) and accept the sampled parameter names as writable attributes. The instance is never modified; every evaluation works on a deep copy.
param_distributions
ParameterDistribution.
n_iter
scoring
'accuracy', 'neg_mse' and 'r2'; a callable must have the signature scorer(y_true, y_pred) -> float and follow the higher-is-better convention. An unrecognized string falls back to 'accuracy'.
cv
refit
best_params_ after the search. Required for predict/score.
verbose
0 is silent; any value above 0 prints one line per sample plus a final summary.
n_jobs
joblib. 1 runs sequentially; other values fall back to sequential execution with a warning when joblib is not installed.
random_seed
None, the global TuiML seed is used, falling back to 42. The legacy keyword random_state is still accepted as an alias and is stored on the instance as self.random_state.
progress_callback
'type', 'iteration', 'total', 'params', 'mean_score', 'std_score' and 'best_score'.
Attributes
param_distributions
n_iter
best_params_
None before fit is called.
best_score_
best_params_.
best_estimator_
estimator refitted on the full training data with best_params_. Only set when refit=True.
cv_results_
Per-sample log with parallel lists of length n_iter:
- •
'params': list of dict, the sampled configuration. - •
'mean_test_score': list of float, mean score across folds. - •
'std_test_score': list of float, standard deviation of the
fold scores.
- •
'mean_fit_time': list of float, mean seconds per fold. - •
'rank_test_score': list of int, 1 for the best sample.
total_time_
Notes
Complexity. O(\text{n\_iter} \cdot \text{cv} \cdot C) where C is the cost of a single estimator fit; memory is O(\text{n\_iter}) for the result log.
When to use. The pragmatic default: any continuous parameter, more than a couple of tuned parameters, or a hard budget on training time. Prefer GridSearchCV when the space is tiny and you want guaranteed coverage, and BayesianSearchCV when each fit is expensive enough to justify modelling the score surface.
References
See Also
Sample four decision-tree configurations from a mixed space:
>>> from tuiml.evaluation.tuning import RandomSearchCV
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> from tuiml.datasets import load_iris
>>> data = load_iris()
>>> X, y = data.X, data.y
>>> search = RandomSearchCV(
... estimator=DecisionTreeClassifier(),
... param_distributions={
... 'max_depth': (2, 8, 'int'),
... 'criterion': ['gini', 'entropy'],
... },
... n_iter=4,
... cv=3,
... scoring='accuracy',
... random_seed=0,
... )
>>> search = search.fit(X, y)
>>> sorted(search.best_params_)
['criterion', 'max_depth']
>>> str(search.best_params_['criterion'])
'entropy'
>>> round(float(search.best_score_), 2)
0.94
The budget, not the size of the space, fixes the amount of work:
>>> len(search.cv_results_['params'])
4
>>> min(search.cv_results_['rank_test_score'])
1
>>> search.predict(X).shape
(150,)
Methods
fit
(self, X: np.ndarray, y: np.ndarray) -> 'RandomSearchCV'
fit
(self, X: np.ndarray, y: np.ndarray) -> 'RandomSearchCV'
Sample n_iter configurations and keep the best-scoring one.
Parameters
X
y
Returns
self
best_params_, best_score_, cv_results_ and total_time_ populated (and best_estimator_ when refit=True).
get_results
(self) -> TuningResult
get_results
(self) -> TuningResult
Bundle the fitted search state into a TuningResult.
Returns
result
best_params, best_score, best_estimator, cv_results, n_iterations (equal to n_iter), and the total search time in seconds.
Raises
AttributeError
fit, because total_time_ does not exist yet.