RandomizedSearchCV para optimización de hiperparámetros

Avanzado
Scikit Learn
Scikit Learn
Actualizado: 18/04/2026

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 - Autor del tutorial

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.