W3docs

Búsqueda en Cuadrícula para Ajuste de Hiperparámetros en Python

Aprende a usar GridSearchCV y RandomizedSearchCV en Python para ajustar hiperparámetros de modelos de machine learning con ejemplos de scikit-learn.

El ajuste de hiperparámetros es el proceso de encontrar los valores de configuración de un modelo de machine learning que no se aprenden a partir de los datos — cosas como la profundidad de un árbol de decisión, la intensidad de regularización de una regresión logística, o el número de neuronas en una red neuronal. La búsqueda en cuadrícula es el enfoque más sencillo: define un conjunto discreto de valores para cada hiperparámetro, prueba cada combinación y quédate con la mejor.

Esta página cubre:

  • Qué son los hiperparámetros y por qué importan
  • Cómo GridSearchCV realiza una búsqueda exhaustiva en una cuadrícula de parámetros
  • Cómo leer cv_results_ y entender qué se probó
  • Usar n_jobs=-1 para paralelizar la búsqueda
  • RandomizedSearchCV como alternativa más rápida para cuadrículas grandes
  • Combinar la búsqueda en cuadrícula con un Pipeline para evitar la fuga de datos
  • Cuándo usar la búsqueda en cuadrícula frente a alternativas más rápidas

Todos los ejemplos utilizan los conjuntos de datos integrados de scikit-learn, por lo que puedes ejecutarlos de inmediato.

¿Qué Son los Hiperparámetros?

Cada modelo de machine learning tiene dos tipos de parámetros:

  • Los parámetros del modelo se aprenden automáticamente durante el entrenamiento (por ejemplo, los pesos en una red neuronal, los umbrales de división en un árbol de decisión).
  • Los hiperparámetros los configuras tú antes de que comience el entrenamiento. Controlan el proceso de aprendizaje en sí.

Elegir los hiperparámetros incorrectos puede dejar a un modelo capaz con un rendimiento muy por debajo de su potencial. Por ejemplo, un árbol de decisión sin límite de profundidad sobreajustará los datos de entrenamiento; uno con un límite de profundidad de 1 tendrá un ajuste insuficiente. El límite correcto depende de tus datos, y la búsqueda en cuadrícula lo encuentra de forma sistemática en lugar de por intuición.

Cómo Funciona GridSearchCV

GridSearchCV de scikit-learn combina dos ideas:

  1. Enumeración de la cuadrícula — genera cada combinación de los valores de hiperparámetros que especificas.
  2. Validación cruzada — para cada combinación realiza una validación cruzada k-fold (consulta Validación Cruzada en Python) y registra la puntuación media.

Tras la búsqueda, GridSearchCV almacena la mejor combinación y ajusta automáticamente un modelo con esa configuración sobre el conjunto de entrenamiento completo.

El número de ajustes es (combinaciones) × (pliegues de cv). Una cuadrícula con 3 × 3 × 3 = 27 combinaciones y cv=5 ejecuta 135 ajustes — manejable para modelos rápidos, costoso para los lentos.

Ejemplo Básico de GridSearchCV

El siguiente ejemplo ajusta un Clasificador de Árbol de Decisión sobre el conjunto de datos Iris.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

# Model with no hyperparameters set yet
model = DecisionTreeClassifier(random_state=42)

# Define the grid of values to try
param_grid = {
    'max_depth': [2, 3, 5, None],
    'min_samples_split': [2, 5, 10],
    'criterion': ['gini', 'entropy'],
}

# cv=5 means 5-fold cross-validation for each combination
grid_search = GridSearchCV(
    estimator=model,
    param_grid=param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1,       # use all CPU cores
    verbose=0,
)

grid_search.fit(X, y)

print("Best parameters:", grid_search.best_params_)
print("Best CV accuracy: {:.3f}".format(grid_search.best_score_))

Qué hace cada argumento:

ArgumentoPropósito
estimatorEl modelo a ajustar. Funciona con cualquier estimador de scikit-learn.
param_gridDiccionario que mapea nombres de parámetros a listas de valores candidatos.
cvNúmero de pliegues en la validación cruzada (5 es el valor predeterminado habitual).
scoringMétrica a optimizar. Por defecto usa el método .score() del estimador.
n_jobsNúmero de trabajos en paralelo. -1 usa todos los núcleos de CPU disponibles.

Salida típica:

Best parameters: {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV accuracy: 0.967

Lectura de cv_results_

Después del ajuste, grid_search.cv_results_ es un diccionario de arrays — una entrada por cada combinación probada. Las claves más útiles son:

import pandas as pd

results = pd.DataFrame(grid_search.cv_results_)

# Show top 5 combinations by mean test score
cols = ['param_max_depth', 'param_min_samples_split', 'param_criterion',
        'mean_test_score', 'std_test_score', 'rank_test_score']
print(results[cols].sort_values('rank_test_score').head(5).to_string(index=False))

Columnas clave:

  • mean_test_score — la puntuación de CV media en todos los pliegues para esa combinación.
  • std_test_score — desviación estándar; un valor alto significa que la puntuación es inestable entre pliegues.
  • rank_test_score — el rango 1 es el ganador.

Opciones de Puntuación

Por defecto, GridSearchCV optimiza la métrica predeterminada del estimador. Puedes especificar cualquier puntuador integrado o uno personalizado:

# Common scoring strings
scoring_options = [
    'accuracy',       # classification
    'f1_weighted',    # F1 for multi-class
    'roc_auc',        # binary classification
    'neg_mean_squared_error',  # regression (note: negative so higher = better)
    'r2',             # regression
]

# Evaluate multiple metrics at once (refit on the one you care most about)
grid_search = GridSearchCV(
    estimator=DecisionTreeClassifier(random_state=42),
    param_grid={'max_depth': [2, 3, 5]},
    cv=5,
    scoring={'acc': 'accuracy', 'f1': 'f1_weighted'},
    refit='acc',       # use accuracy to pick the best model
    n_jobs=-1,
)

Usar un Pipeline para Prevenir la Fuga de Datos

Cuando tu preprocesamiento depende de los datos de entrenamiento (escalado, imputación, selección de características), debes ajustar el preprocesador únicamente en los pliegues de entrenamiento — nunca en el conjunto de datos completo antes de dividirlo. Un Pipeline gestiona esto automáticamente y funciona sin problemas con GridSearchCV.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import GridSearchCV

X, y = load_breast_cancer(return_X_y=True)

# Build a pipeline: scale then classify
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('svc', SVC()),
])

# Reference pipeline steps with double-underscore: step__param
param_grid = {
    'svc__C': [0.1, 1, 10],
    'svc__kernel': ['linear', 'rbf'],
    'svc__gamma': ['scale', 'auto'],
}

grid_search = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X, y)

print("Best params:", grid_search.best_params_)
print("Best CV accuracy: {:.3f}".format(grid_search.best_score_))

La sintaxis de doble guión bajo (svc__C) es la clave: le indica a scikit-learn que pase C al paso svc dentro del pipeline. Sin un pipeline, escalar el conjunto de datos completo antes de la validación cruzada filtraría información del pliegue de prueba al escalador, generando una puntuación demasiado optimista.

RandomizedSearchCV: Más Rápido para Cuadrículas Grandes

La búsqueda en cuadrícula exhaustiva se vuelve impracticable cuando cada hiperparámetro tiene muchos valores candidatos. RandomizedSearchCV muestrea un número fijo de combinaciones aleatorias en lugar de probarlas todas:

from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from scipy.stats import randint

X, y = load_iris(return_X_y=True)

# Use distributions instead of discrete lists
param_dist = {
    'n_estimators': randint(50, 500),   # random integer in [50, 500)
    'max_depth': [3, 5, 10, None],
    'min_samples_split': randint(2, 20),
    'max_features': ['sqrt', 'log2'],
}

rand_search = RandomizedSearchCV(
    estimator=RandomForestClassifier(random_state=42),
    param_distributions=param_dist,
    n_iter=30,        # try 30 random combinations instead of all
    cv=5,
    scoring='accuracy',
    n_jobs=-1,
    random_state=42,
)

rand_search.fit(X, y)

print("Best params:", rand_search.best_params_)
print("Best CV accuracy: {:.3f}".format(rand_search.best_score_))

GridSearchCV vs RandomizedSearchCV:

GridSearchCVRandomizedSearchCV
Estrategia de búsquedaExhaustiva (todas las combinaciones)Muestreo aleatorio
ReproducibilidadTotalmente deterministaEstablecer random_state
Mejor paraCuadrículas pequeñas y bien definidasEspacios de búsqueda grandes
Distribuciones continuasNo compatibleCompatible mediante scipy.stats
Garantiza encontrar el mejorSí (dentro de la cuadrícula)No, pero suele estar cerca

Para cuadrículas grandes, RandomizedSearchCV con n_iter=50–100 frecuentemente encuentra una solución casi óptima en una fracción del tiempo de cómputo.

Hacer Predicciones con el Mejor Modelo

Después del ajuste, GridSearchCV actúa como un estimador normal. Su atributo best_estimator_ contiene el modelo reajustado:

from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris

X, y = load_iris(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_grid = {'max_depth': [2, 3, 5], 'criterion': ['gini', 'entropy']}
grid_search = GridSearchCV(
    DecisionTreeClassifier(random_state=42),
    param_grid, cv=5, n_jobs=-1
)
grid_search.fit(X_train, y_train)

# Evaluate on the held-out test set
test_score = grid_search.score(X_test, y_test)
print("Test accuracy: {:.3f}".format(test_score))

# Access the best model directly
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test[:5])
print("Predictions for first 5 test samples:", predictions)

Importante: mantén siempre un conjunto de prueba separado que nunca se vea durante la búsqueda en cuadrícula. La puntuación de validación cruzada dentro de GridSearchCV estima la generalización, pero la evaluación final verdadera debe realizarse sobre datos que la búsqueda nunca tocó.

Cuándo Usar la Búsqueda en Cuadrícula

La búsqueda en cuadrícula es una opción sólida por defecto cuando:

  • Tienes un modelo pequeño o mediano que entrena rápidamente (segundos a pocos minutos por ajuste).
  • Sabes aproximadamente qué hiperparámetros son los más importantes y tienes un conjunto sensato de valores candidatos.
  • La reproducibilidad y la exhaustividad son importantes.

Considera alternativas cuando:

  • La cuadrícula es grande (muchos parámetros con muchos valores) — usa RandomizedSearchCV primero para reducir el espacio y luego refina con GridSearchCV.
  • El entrenamiento es costoso (aprendizaje profundo, ensambles grandes) — las bibliotecas de optimización bayesiana como scikit-optimize o Optuna toman decisiones más inteligentes que el muestreo aleatorio.
  • Quieres parada automática — las estrategias de reducción progresiva (HalvingGridSearchCV) eliminan a los candidatos con bajo rendimiento de forma temprana y requieren menos ajustes en total.

Consejos Prácticos

  • Empieza con una cuadrícula gruesa. Usa una cuadrícula pequeña con valores distribuidos en órdenes de magnitud (por ejemplo, C: [0.01, 0.1, 1, 10, 100]). Una vez que encuentres una región prometedora, refina con una cuadrícula más fina.
  • Observa las desviaciones estándar. Si std_test_score es grande, el modelo es sensible a la partición de datos concreta. Considera aumentar cv o recopilar más datos.
  • Establece n_jobs=-1 para usar todos los núcleos de CPU — no tiene ningún coste y a menudo proporciona una aceleración de 4 a 8 veces en una máquina moderna.
  • Combínalo con un Pipeline. Envuelve siempre el preprocesamiento y el modelo juntos en un Pipeline antes de pasarlo a GridSearchCV. Esta es la práctica más importante para obtener puntuaciones fiables.
  • Usa pliegues estratificados para clasificación. GridSearchCV utiliza StratifiedKFold automáticamente para clasificadores, lo que preserva las proporciones de clases en todos los pliegues.

Temas Relacionados

Was this page helpful?