Limitaciones de GridSearchCV
GridSearchCV evalúa todas las combinaciones posibles del espacio de hiperparámetros. Si defines 4 valores para C, 3 para gamma y 3 para kernel, el total de combinaciones es 4 × 3 × 3 = 36. Con validación cruzada de 5 pliegues, esto implica 180 entrenamientos.
El problema escala rápidamente: con 5 hiperparámetros y 5 valores cada uno son 5⁵ = 3125 combinaciones y 15 625 entrenamientos con cv=5. Esto puede ser computacionalmente prohibitivo.
RandomizedSearchCV resuelve esto muestreando aleatoriamente un número fijo de combinaciones del espacio de búsqueda, controlado por el parámetro n_iter. Bergstra y Bengio (2012) demostraron que la búsqueda aleatoria es igual o más eficiente que la búsqueda en rejilla con un número similar de evaluaciones.
Configuración básica de RandomizedSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from scipy.stats import randint, uniform
# Cargar datos
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.2, random_state=42)
# Espacio de búsqueda: mezcla de listas y distribuciones
param_dist = {
'n_estimators': randint(50, 300), # entero aleatorio entre 50 y 300
'max_depth': [None, 5, 10, 20], # lista de valores
'min_samples_split': randint(2, 15), # entero aleatorio
'min_samples_leaf': randint(1, 10),
'max_features': ['sqrt', 'log2', None],
'bootstrap': [True, False]
}
# Crear y ejecutar la búsqueda aleatoria
random_search = RandomizedSearchCV(
estimator=RandomForestClassifier(random_state=42),
param_distributions=param_dist,
n_iter=50, # evaluar 50 combinaciones aleatorias
cv=5,
scoring='accuracy',
n_jobs=-1,
random_state=42,
verbose=1
)
random_search.fit(X_train, y_train)
print(f"Mejor accuracy en CV: {random_search.best_score_:.4f}")
print(f"Mejores parámetros: {random_search.best_params_}")
print(f"Accuracy en test: {random_search.best_estimator_.score(X_test, y_test):.4f}")
Distribuciones de scipy para hiperparámetros continuos
Cuando el hiperparámetro es continuo (como C en SVM o alpha en Ridge), es más adecuado usar distribuciones de probabilidad en lugar de listas:
from scipy.stats import loguniform, uniform, randint
# loguniform: distribución uniforme en escala logarítmica
# Muy útil para C, alpha, learning_rate (varían órdenes de magnitud)
param_dist = {
'C': loguniform(1e-3, 1e3), # valores entre 0.001 y 1000
'gamma': loguniform(1e-4, 1e1), # valores entre 0.0001 y 10
'kernel': ['rbf', 'poly', 'linear']
}
from sklearn.svm import SVC
from sklearn.model_selection import RandomizedSearchCV
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
X, y = load_iris(return_X_y=True)
pipeline = Pipeline([
('scaler', StandardScaler()),
('svm', SVC(random_state=42))
])
# Prefijo del pipeline en los nombres de parámetros
param_dist_pipeline = {
'svm__C': loguniform(1e-2, 1e3),
'svm__gamma': loguniform(1e-4, 1e1),
'svm__kernel': ['rbf', 'poly', 'linear']
}
random_search = RandomizedSearchCV(
pipeline,
param_distributions=param_dist_pipeline,
n_iter=30,
cv=5,
scoring='accuracy',
random_state=42
)
random_search.fit(X, y)
print(f"Mejores parámetros: {random_search.best_params_}")
print(f"Mejor accuracy: {random_search.best_score_:.4f}")
Comparación: GridSearchCV vs RandomizedSearchCV
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from scipy.stats import randint, uniform
import time
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.2, random_state=42)
# Grid Search
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 7],
'learning_rate': [0.05, 0.1, 0.2]
}
t0 = time.time()
grid_search = GridSearchCV(GradientBoostingClassifier(random_state=42),
param_grid, cv=5, n_jobs=-1)
grid_search.fit(X_train, y_train)
t_grid = time.time() - t0
# Random Search con mismo número de combinaciones
param_dist = {
'n_estimators': randint(50, 200),
'max_depth': randint(3, 8),
'learning_rate': uniform(0.05, 0.2)
}
t0 = time.time()
random_search = RandomizedSearchCV(GradientBoostingClassifier(random_state=42),
param_dist, n_iter=27, cv=5, n_jobs=-1,
random_state=42)
random_search.fit(X_train, y_train)
t_random = time.time() - t0
print(f"GridSearchCV - Combinaciones: {len(grid_search.cv_results_['params'])}")
print(f"GridSearchCV - Mejor CV: {grid_search.best_score_:.4f} Test: {grid_search.score(X_test, y_test):.4f} Tiempo: {t_grid:.1f}s")
print(f"RandomSearchCV - Combinaciones: {len(random_search.cv_results_['params'])}")
print(f"RandomSearchCV - Mejor CV: {random_search.best_score_:.4f} Test: {random_search.score(X_test, y_test):.4f} Tiempo: {t_random:.1f}s")
Análisis de resultados
El atributo cv_results_ contiene los resultados de todas las combinaciones evaluadas. Permite analizar cuáles hiperparámetros tienen más impacto:
import pandas as pd
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from scipy.stats import randint
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.2, random_state=42)
param_dist = {
'n_estimators': randint(50, 300),
'max_depth': [None, 5, 10, 15],
'min_samples_split': randint(2, 20),
}
rs = RandomizedSearchCV(RandomForestClassifier(random_state=42),
param_dist, n_iter=20, cv=5, random_state=42)
rs.fit(X_train, y_train)
# Convertir resultados a DataFrame y ordenar por rendimiento
resultados = pd.DataFrame(rs.cv_results_)
cols = ['param_n_estimators', 'param_max_depth', 'param_min_samples_split',
'mean_test_score', 'std_test_score', 'rank_test_score']
print(resultados[cols].sort_values('rank_test_score').head(5).to_string())
HalvingRandomSearchCV: búsqueda sucesiva a mitades
Desde Scikit-learn 1.0, HalvingRandomSearchCV permite hacer búsquedas mucho más rápidas asignando más recursos a los candidatos más prometedores:
from sklearn.experimental import enable_halving_search_cv # necesario para activar
from sklearn.model_selection import HalvingRandomSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from scipy.stats import randint
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.2, random_state=42)
param_dist = {
'n_estimators': randint(10, 300),
'max_depth': [None, 5, 10, 20, 30],
'min_samples_split': randint(2, 20),
}
halving_search = HalvingRandomSearchCV(
RandomForestClassifier(random_state=42),
param_distributions=param_dist,
n_candidates='exhaust',
cv=5,
factor=3,
random_state=42,
n_jobs=-1
)
halving_search.fit(X_train, y_train)
print(f"Mejor score CV: {halving_search.best_score_:.4f}")
print(f"Accuracy test: {halving_search.score(X_test, y_test):.4f}")
print(f"Iteraciones realizadas: {halving_search.n_iterations_}")
Cuándo usar cada técnica
| Técnica | Cuándo usarla |
|---------|---------------|
| GridSearchCV | Espacio pequeño (< 100 combinaciones), hiperparámetros discretos |
| RandomizedSearchCV | Espacio grande o continuo, presupuesto de tiempo limitado |
| HalvingRandomSearchCV | Datasets grandes, muchos candidatos iniciales |
| Optuna / Hyperopt | Optimización bayesiana para búsquedas muy costosas (externas a Scikit-learn) |
La regla general es: si tienes muchos hiperparámetros o sus rangos son continuos, RandomizedSearchCV con distribuciones loguniform o uniform suele encontrar configuraciones comparables a GridSearch en mucho menos tiempo.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Scikit Learn es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Scikit Learn
Explora más contenido relacionado con Scikit Learn y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Entender cuándo RandomizedSearchCV es preferible a GridSearchCV. Configurar RandomizedSearchCV con distribuciones de scipy y listas de valores. Interpretar los resultados de la búsqueda aleatoria y extraer el mejor modelo. Comparar tiempos de ejecución y rendimiento entre búsqueda en rejilla y aleatoria. Aplicar HalvingRandomSearchCV de Scikit-learn 1.x para búsquedas aún más eficientes.