Grid Search para Ajuste de Hiperparâmetros em Python
Aprenda a usar GridSearchCV e RandomizedSearchCV em Python para ajustar hiperparâmetros de modelos de machine learning com exemplos scikit-learn.
O ajuste de hiperparâmetros é o processo de encontrar os valores de configuração de um modelo de machine learning que não são aprendidos a partir dos dados — coisas como a profundidade de uma árvore de decisão, a força de regularização de uma regressão logística ou o número de neurônios em uma rede neural. O grid search é a abordagem mais direta: defina um conjunto discreto de valores para cada hiperparâmetro, experimente todas as combinações e mantenha a melhor.
Esta página aborda:
- O que são hiperparâmetros e por que eles importam
- Como o
GridSearchCVpesquisa exaustivamente uma grade de parâmetros - Como ler o
cv_results_e entender o que foi testado - Uso de
n_jobs=-1para paralelizar a busca RandomizedSearchCVcomo alternativa mais rápida para grades grandes- Combinando grid search com um
Pipelinepara evitar vazamento de dados - Quando usar grid search versus alternativas mais rápidas
Todos os exemplos usam conjuntos de dados integrados do scikit-learn, portanto você pode executá-los imediatamente.
O Que São Hiperparâmetros?
Todo modelo de machine learning possui dois tipos de parâmetros:
- Parâmetros do modelo são aprendidos automaticamente durante o treinamento (ex.: os pesos em uma rede neural, os limiares de divisão em uma árvore de decisão).
- Hiperparâmetros são definidos por você antes do início do treinamento. Eles controlam o próprio processo de aprendizagem.
Escolher hiperparâmetros errados pode deixar um modelo capaz com desempenho muito abaixo do esperado. Por exemplo, uma árvore de decisão sem limite de profundidade irá sobreajustar os dados de treinamento; uma com limite de profundidade igual a 1 irá subajustar. O limite correto depende dos seus dados, e o grid search o encontra sistematicamente em vez de por tentativa e erro.
Como o GridSearchCV Funciona
O GridSearchCV do scikit-learn combina duas ideias:
- Enumeração de grade — ele gera todas as combinações de valores de hiperparâmetros que você especifica.
- Validação cruzada — para cada combinação, ele realiza a validação cruzada k-fold (veja Validação Cruzada em Python) e registra a pontuação média.
Após a busca, o GridSearchCV armazena a melhor combinação e reajusta automaticamente um modelo com essas configurações no conjunto de treinamento completo.
O número de ajustes é (combinações) × (folds de cv). Uma grade com 3 × 3 × 3 = 27 combinações e cv=5 executa 135 ajustes — gerenciável para modelos rápidos, caro para os lentos.
Exemplo Básico com GridSearchCV
O exemplo abaixo ajusta um Classificador de Árvore de Decisão no conjunto de dados 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_))O que cada argumento faz:
| Argumento | Finalidade |
|---|---|
estimator | O modelo a ser ajustado. Qualquer estimador do scikit-learn funciona. |
param_grid | Dicionário que mapeia nomes de parâmetros a listas de valores candidatos. |
cv | Número de folds na validação cruzada (5 é um padrão comum). |
scoring | Métrica a otimizar. Padrão: o método .score() do estimador. |
n_jobs | Número de jobs paralelos. -1 usa todos os núcleos de CPU disponíveis. |
Saída típica:
Best parameters: {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV accuracy: 0.967Lendo cv_results_
Após o ajuste, grid_search.cv_results_ é um dicionário de arrays — uma entrada por combinação testada. As chaves mais úteis são:
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))Colunas principais:
mean_test_score— a pontuação de CV média em todos os folds para aquela combinação.std_test_score— desvio padrão; um valor alto significa que a pontuação é instável entre os folds.rank_test_score— classificação 1 é o vencedor.
Opções de Pontuação
Por padrão, o GridSearchCV otimiza a métrica padrão do estimador. Você pode especificar qualquer pontuador integrado ou um 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,
)Usando um Pipeline para Evitar Vazamento de Dados
Quando seu pré-processamento depende dos dados de treinamento (normalização, imputação, seleção de features), você deve ajustar o pré-processador somente nos folds de treinamento — nunca no conjunto completo de dados antes da divisão. Um Pipeline cuida disso automaticamente e funciona perfeitamente com o 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_))A sintaxe com dois underscores (svc__C) é a chave: ela diz ao scikit-learn para passar C para o passo svc dentro do pipeline. Sem um pipeline, normalizar o conjunto de dados completo antes da validação cruzada vazaria informações do fold de teste para o normalizador, gerando uma pontuação excessivamente otimista.
RandomizedSearchCV: Mais Rápido para Grades Grandes
O grid search exaustivo torna-se impraticável quando cada hiperparâmetro possui muitos valores candidatos. O RandomizedSearchCV amostra um número fixo de combinações aleatórias em vez de testar todas elas:
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:
GridSearchCV | RandomizedSearchCV | |
|---|---|---|
| Estratégia de busca | Exaustiva (todas as combinações) | Amostragem aleatória |
| Reprodutibilidade | Totalmente determinístico | Defina random_state |
| Melhor para | Grades pequenas e bem definidas | Espaços de busca grandes |
| Distribuições contínuas | Não suportado | Suportado via scipy.stats |
| Garante encontrar o melhor | Sim (dentro da grade) | Não, mas frequentemente próximo |
Para grades grandes, o RandomizedSearchCV com n_iter=50–100 frequentemente encontra uma solução próxima do ótimo em uma fração do tempo de computação.
Fazendo Previsões com o Melhor Modelo
Após o ajuste, o GridSearchCV age como um estimador regular. Seu atributo best_estimator_ contém o 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: sempre mantenha um conjunto de teste separado que nunca seja visto durante o grid search. A pontuação de validação cruzada dentro do GridSearchCV estima a generalização, mas a avaliação final verdadeira deve ser feita em dados que a busca nunca tocou.
Quando Usar Grid Search
O grid search é uma escolha padrão sólida quando:
- Você tem um modelo pequeno ou médio que treina rapidamente (segundos a poucos minutos por ajuste).
- Você sabe aproximadamente quais hiperparâmetros são mais importantes e tem um conjunto sensato de valores candidatos.
- Reprodutibilidade e exhaustividade são importantes.
Considere alternativas quando:
- A grade é grande (muitos parâmetros com muitos valores) — use
RandomizedSearchCVprimeiro para estreitar o espaço, depois refine comGridSearchCV. - O treinamento é caro (deep learning, grandes ensembles) — bibliotecas de otimização bayesiana como
scikit-optimizeouOptunafazem escolhas mais inteligentes do que a amostragem aleatória. - Você quer parada automática — estratégias de halvening (
HalvingGridSearchCV) eliminam candidatos de baixo desempenho cedo e requerem menos ajustes no total.
Dicas Práticas
- Comece de forma grosseira. Use uma grade pequena com valores distribuídos em ordens de magnitude (ex.:
C: [0.01, 0.1, 1, 10, 100]). Após encontrar uma região promissora, refine com uma grade mais fina. - Observe os desvios padrão. Se
std_test_scorefor grande, o modelo é sensível à divisão específica dos dados. Considere aumentarcvou coletar mais dados. - Defina
n_jobs=-1para usar todos os núcleos de CPU — não custa nada e frequentemente oferece uma aceleração de 4–8× em uma máquina moderna. - Use com um Pipeline. Sempre envolva o pré-processamento e o modelo juntos em um
Pipelineantes de passar para oGridSearchCV. Esta é a prática mais importante para obter pontuações confiáveis. - Use folds estratificados para classificação. O
GridSearchCVusaStratifiedKFoldautomaticamente para classificadores, o que preserva as proporções de classes entre os folds.
Tópicos Relacionados
- Validação Cruzada em Python — entenda a avaliação k-fold que alimenta o
GridSearchCV - Classificador de Árvore de Decisão — um modelo comum para ajustar com grid search
- Regressão Logística — outro modelo rápido bem adequado para grid search
- K-Nearest Neighbors —
n_neighborsemetricsão alvos clássicos de grid search - Regressão Linear — ajuste de hiperparâmetros para variantes de regressão regularizada