A randomized hyperparameter search trades exhaustive coverage for a fixed number of sampled candidates. That budget is especially useful for continuous SVC parameters such as C and gamma, where a small hand-written grid can overemphasize a few arbitrary values.
The estimator is a StandardScaler-to-SVC Pipeline, which keeps scaling inside each cross-validation training fold. Log-uniform distributions give each order of magnitude equal sampling weight, while nested parameter names target the classifier without separating it from preprocessing.
A stratified split reserves rows from every search decision. Five shuffled folds rank eight training-set candidates by mean accuracy; only after that ranking is inspected does a separate verifier score the refitted winner against the held-out rows.
from joblib import dump from scipy.stats import loguniform from sklearn.datasets import load_iris from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold, train_test_split from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC X, y = load_iris(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, ) pipeline = Pipeline( [ ("scaler", StandardScaler()), ("svc", SVC()), ] )
The test split stays outside RandomizedSearchCV so it cannot influence candidate selection. Scaling remains inside the pipeline and is fitted separately for each training fold.
param_distributions = { "svc__C": loguniform(1e-2, 1e2), "svc__gamma": loguniform(1e-4, 1e0), "svc__kernel": ["rbf"], }
The svc__ prefix addresses the named classifier step. Each log-uniform range samples across several orders of magnitude without enumerating a fixed grid.
cv = StratifiedKFold( n_splits=5, shuffle=True, random_state=42, ) search = RandomizedSearchCV( estimator=pipeline, param_distributions=param_distributions, n_iter=8, scoring="accuracy", cv=cv, random_state=42, refit=True, )
n_iter=8 bounds the search at eight sampled settings. refit=True fits the selected pipeline again on all training rows after cross-validation.
search.fit(X_train, y_train)
results = search.cv_results_ ranked_indices = sorted( range(len(results["params"])), key=lambda index: results["rank_test_score"][index], ) print("rank mean CV accuracy C gamma") for index in ranked_indices: params = results["params"][index] print( f"{results['rank_test_score'][index]:>4} " f"{results['mean_test_score'][index]:>16.3f} " f"{params['svc__C']:>8.4f} " f"{params['svc__gamma']:>8.4f}" )
rank_test_score orders candidates by the configured accuracy scorer. Equal mean scores can share the same rank.
dump(search.best_estimator_, "randomized_search_winner.joblib") dump((X_test, y_test), "randomized_search_holdout.joblib")
$ python3 run_randomized_search.py rank mean CV accuracy C gamma 1 0.965 0.3149 0.6351 1 0.965 8.4718 0.0248 1 0.965 2.5378 0.0680 4 0.911 21.3683 0.0007 5 0.588 0.0121 0.7579 6 0.579 0.0171 0.2915 7 0.570 0.0421 0.0004 7 0.570 0.0534 0.0005
from joblib import load from sklearn.metrics import accuracy_score from sklearn.utils.validation import check_is_fitted winner = load("randomized_search_winner.joblib") X_test, y_test = load("randomized_search_holdout.joblib") check_is_fitted(winner) predictions = winner.predict(X_test) held_out_accuracy = accuracy_score(y_test, predictions) required_accuracy = 0.85 if held_out_accuracy < required_accuracy: raise SystemExit( f"held-out accuracy {held_out_accuracy:.3f} " f"is below the required {required_accuracy:.3f}" ) print(f"held-out accuracy: {held_out_accuracy:.3f}") print(f"required accuracy: {required_accuracy:.3f}")
The verifier loads the already-refitted pipeline and predicts rows that were excluded from every candidate fit. The nonzero exit below the declared accuracy floor makes this check fail-capable.
$ python3 verify_randomized_winner.py held-out accuracy: 0.921 required accuracy: 0.850