Ajuste de Hiperparâmetros

O que é Ajuste de Hiperparâmetros (Hyperparameter Tuning)?

Ajuste de hiperparâmetros é o processo de selecionar valores de configuração que governam como um modelo de aprendizado de máquina (machine learning) é treinado, com o objetivo de maximizar o desempenho em dados não vistos. Ao contrário dos parâmetros do modelo (model parameters) (pesos (weights) aprendidos a partir dos dados), os hiperparâmetros (hyperparameters) são definidos antes do treinamento e, em geral, controlam:

  • Capacidade do modelo (por exemplo, profundidade de uma árvore, número de camadas em uma rede)
  • Comportamento da otimização (por exemplo, taxa de aprendizado (learning rate), tamanho do lote (batch size))
  • Força da regularização (regularization) (por exemplo, penalidade L2, taxa de dropout (dropout))
  • Escolhas de processamento de dados/atributos (features) (por exemplo, n-gramas (n-grams), opções de normalização (normalization))

O ajuste de hiperparâmetros faz parte da seleção de modelos (model selection) e deve ser feito com práticas cuidadosas de avaliação (geralmente Validação Cruzada (Cross-Validation)) para evitar resultados excessivamente otimistas.

Por que o ajuste importa (e por que é difícil)

Hiperparâmetros influenciam fortemente o trade-off viés–variância (bias–variance tradeoff) (veja Trade-off Viés–Variância). Por exemplo:

  • Uma árvore de decisão muito profunda pode se ajustar bem aos dados de treino, mas sofrer sobreajuste (overfit) (alta variância).
  • Regularização demais pode causar subajuste (underfit) (alto viés).
  • Uma taxa de aprendizado muito alta pode impedir a convergência; muito baixa pode tornar o treinamento proibitivamente lento.

O ajuste é difícil porque:

  • O espaço de busca (search space) pode ser de alta dimensionalidade e misto (contínuo, inteiro, categórico).
  • O treinamento costuma ser estocástico (stochastic) (inicialização aleatória, minilotes (minibatching)), tornando a avaliação ruidosa.
  • Cada avaliação pode ser cara (treinar um modelo grande).
  • Os “melhores” hiperparâmetros dependem do conjunto de dados, da métrica e do orçamento computacional (compute budget).

Do ponto de vista matemático, o ajuste costuma ser tratado como otimização de caixa-preta (black-box optimization):

[ \text{maximize } f(\lambda) \quad \text{where } \lambda \in \Lambda ]

  • (\lambda): vetor de hiperparâmetros
  • (\Lambda): espaço de busca
  • (f(\lambda)): desempenho de validação (estimado via holdout (holdout) ou validação cruzada)

Como (f) é caro e não tem gradiente analítico, dependemos de estratégias de busca como busca em grade (grid search), busca aleatória (random search) e otimização bayesiana (Bayesian optimization).

Fluxo de trabalho de ajuste de hiperparâmetros (base prática)

Um fluxo de trabalho robusto de ajuste normalmente se parece com isto:

  1. Defina o objetivo e a métrica (metric)

    • Classificação: acurácia, F1, ROC-AUC, perda logarítmica (log loss)
    • Regressão: RMSE, MAE, R²
    • Ranqueamento/recomendações: NDCG, MAP Escolha uma métrica alinhada a objetivos de negócio ou científicos.
  2. Divida os dados corretamente

    • Use um conjunto de teste (test set) apenas uma vez no final.
    • Use validação ou validação cruzada para o ajuste.
    • Para séries temporais, use divisões sensíveis ao tempo (sem vazamento de dados (data leakage)).
  3. Construa uma pipeline (pipeline) segura contra vazamento

    • O pré-processamento deve ser ajustado dentro de cada dobra (fold) de treinamento.
    • No scikit-learn, isso significa usar Pipeline.
  4. Escolha um espaço de busca

    • Intervalos razoáveis, escalas corretas (frequentemente escala logarítmica para taxas).
    • Inclua dependências condicionais (conditional dependencies) (por exemplo, penalty depende do solver).
  5. Escolha uma estratégia de busca

    • Comece barato e amplo; depois refine.
    • Considere métodos de multifidelidade (multi-fidelity) se o treinamento for caro.
  6. Considere o ruído

    • Repita a avaliação ou use validação cruzada robusta.
    • Controle a aleatoriedade (sementes (seeds)) quando apropriado.
  7. Finalize

    • Reajuste (refit) usando todos os dados de treinamento com os melhores hiperparâmetros.
    • Avalie uma única vez no conjunto de teste separado.

Busca em Grade

Ideia

A busca em grade enumera um conjunto fixo de valores candidatos para cada hiperparâmetro e testa todas as combinações.

Se você tem:

  • 5 valores para max_depth
  • 4 valores para min_samples_split
  • 3 valores para min_samples_leaf

Isso dá (5 \times 4 \times 3 = 60) treinamentos de modelo (vezes o número de dobras de validação cruzada).

Quando funciona bem

  • Espaços de busca de baixa dimensionalidade (1–3 hiperparâmetros)
  • Quando você tem boas hipóteses prévias sobre intervalos adequados
  • Quando treinar é barato e você quer cobertura sistemática

Limitações

  • Explosão combinatória (combinatorial explosion): o custo cresce exponencialmente com o número de hiperparâmetros.
  • Desperdiça tentativas em dimensões irrelevantes se apenas alguns hiperparâmetros forem os mais importantes.
  • Muitas vezes é rígida demais: você não consegue se adaptar facilmente com base nos resultados.

Exemplo: busca em grade com scikit-learn

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

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("svc", SVC())
])

param_grid = {
    "svc__C": [0.1, 1, 10, 100],
    "svc__gamma": [1e-3, 1e-2, 1e-1],
    "svc__kernel": ["rbf"]
}

search = GridSearchCV(
    pipe,
    param_grid=param_grid,
    cv=5,
    scoring="f1",
    n_jobs=-1
)

search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)

Busca Aleatória

Ideia

A busca aleatória amostra combinações de hiperparâmetros a partir de distribuições especificadas (uniforme (uniform), log-uniforme (log-uniform), categórica, etc.) por um orçamento fixo de tentativas.

Isso costuma ser surpreendentemente forte. Uma intuição-chave (popularizada por Bergstra & Bengio, 2012) é que, em muitos problemas, apenas um pequeno subconjunto de hiperparâmetros é realmente determinante. A busca aleatória explora mais valores distintos de cada hiperparâmetro do que a busca em grade para o mesmo número de tentativas, especialmente em dimensões mais altas.

Quando funciona bem

  • Espaços moderados/altamente dimensionais
  • Você ainda não conhece bons intervalos
  • Você tem um orçamento fixo (por exemplo, “tentar 100 execuções”)

Escolhendo distribuições (importante)

Muitos hiperparâmetros operam em escalas multiplicativas; amostrá-los de forma uniforme é ineficiente.

Boas práticas comuns:

  • Log-uniforme para:
    • taxa de aprendizado (por exemplo, (10^{-5}) a (10^{-1}))
    • força de regularização (L2, weight decay)
    • C de SVM, parâmetros de kernel
  • Uniforme para parâmetros limitados:
    • taxa de dropout em ([0, 0.5])
  • Intervalos inteiros para contagens:
    • profundidade de árvore, número de estimadores, unidades ocultas

Exemplo: RandomizedSearchCV

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV

param_dist = {
    "n_estimators": np.arange(200, 1200, 100),
    "max_depth": [None] + list(np.arange(3, 25)),
    "min_samples_split": np.arange(2, 50),
    "max_features": ["sqrt", "log2", None],
}

rf = RandomForestClassifier(random_state=0)

search = RandomizedSearchCV(
    rf,
    param_distributions=param_dist,
    n_iter=60,          # budget
    cv=5,
    scoring="roc_auc",
    n_jobs=-1,
    random_state=0
)

search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)

Ideia

A otimização bayesiana (Bayesian optimization, BO) escolhe hiperparâmetros de forma sequencial ao:

  1. Ajustar um modelo substituto (surrogate model) que prevê desempenho dado um conjunto de hiperparâmetros.
  2. Usar uma função de aquisição (acquisition function) para decidir quais hiperparâmetros tentar a seguir, equilibrando:
    • Aproveitamento (exploitation) (tentar o que parece melhor)
    • Exploração (exploration) (tentar regiões incertas)

Isso torna a BO eficiente em amostras (sample-efficient): ela frequentemente encontra boas configurações em menos tentativas do que a busca aleatória, especialmente quando cada tentativa é cara.

A otimização bayesiana está intimamente relacionada (e frequentemente é implementada como) métodos de Otimização Bayesiana como:

  • Substitutos baseados em processo gaussiano (Gaussian process, GP) (BO clássico)
  • Estimador de Parzen Estruturado em Árvore (Tree-structured Parzen Estimator, TPE) (popular em Optuna/Hyperopt)
  • Substitutos de floresta aleatória (random forest) (SMAC)

Modelos substitutos (visão geral)

  • Processos Gaussianos: ótimos para espaços contínuos de baixa dimensionalidade; estimativas de incerteza surgem naturalmente.
  • TPE: lida bem com espaços categóricos/condicionais; escala melhor em dimensões mais altas.
  • Floresta aleatória/árvores com boosting: robustas, frequentemente usadas para espaços mistos.

Funções de aquisição (intuição)

  • Melhoria Esperada (Expected Improvement, EI): “Quanto esperamos superar o melhor atual?”
  • Limite Superior de Confiança (Upper Confidence Bound, UCB): “Tente lugares que parecem bons ou incertos.”
  • Probabilidade de Melhoria (Probability of Improvement, PI): “Quão provável é melhorar?”

Quando a BO funciona bem

  • O treinamento é caro (redes profundas (deep nets), grandes conjuntos de dados, engenharia de atributos (feature engineering) pesada)
  • Você pode arcar com experimentação sequencial ou em lotes sequenciais
  • Você espera que o objetivo tenha “estrutura” (não seja puro ruído)

Limitações

  • Mais complexa de implementar e de raciocinar do que a busca aleatória
  • Pode ter dificuldades com objetivos muito ruidosos, a menos que seja bem ajustada
  • A BO “pura” é tipicamente sequencial (embora muitos frameworks suportem sugestões em paralelo/em lote)

Exemplo: busca Bayesiana/TPE com Optuna

import optuna
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import GradientBoostingClassifier

def objective(trial):
    params = {
        "learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.2, log=True),
        "n_estimators": trial.suggest_int("n_estimators", 50, 600),
        "max_depth": trial.suggest_int("max_depth", 1, 5),
        "subsample": trial.suggest_float("subsample", 0.5, 1.0),
    }
    model = GradientBoostingClassifier(random_state=0, **params)
    score = cross_val_score(model, X_train, y_train, cv=5, scoring="roc_auc").mean()
    return score

study = optuna.create_study(direction="maximize")  # default sampler is TPE
study.optimize(objective, n_trials=50)

print(study.best_params, study.best_value)

Ajuste eficiente: como obter mais sinal por unidade de computação

Ajuste eficiente de hiperparâmetros é, em grande parte, sobre gastar computação onde importa e evitar avaliações enganosas.

1) Comece com uma linha de base (baseline) forte e padrões sensatos

Antes de ajustar, estabeleça uma linha de base com:

  • Um modelo simples (por exemplo, regressão logística, um ensemble de árvores pequeno (tree ensemble))
  • Hiperparâmetros padrão do modelo escolhido Isso ajuda a julgar se o ajuste está realmente melhorando algo de forma significativa.

Além disso, ajuste primeiro a classe de modelo correta; ajuste de hiperparâmetros não vai salvar uma escolha ruim de modelo.

2) Ajuste um pequeno conjunto de hiperparâmetros com maior impacto

A maioria dos modelos tem algumas poucas “alavancas grandes”:

  • Ensembles de árvores (XGBoost/LightGBM/RandomForest)
    • taxa de aprendizado, número de árvores, profundidade máxima / número de folhas
    • peso mínimo do filho / mínimo de amostras por folha
    • subamostragem e amostragem de colunas
  • Modelos lineares
    • tipo/força de regularização (L1/L2/elastic net)
  • Redes neurais (neural networks)
    • taxa de aprendizado, tamanho do lote, decaimento de pesos (weight decay), tamanho da arquitetura
    • dropout, escolha do otimizador (optimizer) (veja Descida do Gradiente para fundamentos de otimização)

Se você ajustar muitos controles de baixo impacto ao mesmo tempo, desperdiça tentativas.

3) Use a escala e as restrições corretas no espaço de busca

Erros comuns:

  • Amostrar taxa de aprendizado uniformemente em ([0, 1]) (ruim)
  • Amostrar regularização uniformemente em ([0, 1000]) (ruim)

Melhor:

  • escala logarítmica (log-scale) para hiperparâmetros positivos de “taxa/força”
  • impor combinações válidas (espaços de busca condicionais (conditional search spaces)), por exemplo:
    • Se solver="liblinear", então penalty não pode ser "none" em algumas configurações

4) Prefira busca aleatória em vez de busca em grade em dimensões mais altas

Regra prática:

  • Se você tem mais do que ~2–3 hiperparâmetros importantes, a busca aleatória geralmente é uma primeira tentativa melhor do que a busca em grade para o mesmo orçamento.

Uma receita prática:

  • Fase 1: busca aleatória com intervalos amplos (por exemplo, 50–200 tentativas)
  • Fase 2: estreitar os intervalos ao redor dos melhores candidatos
  • Fase 3: otimização bayesiana opcional para refinar

5) Use métodos de multifidelidade: parada antecipada e redução sucessiva

Quando treinar é caro, não treine completamente todos os candidatos.

Abordagens populares:

  • Parada antecipada (early stopping): interromper o treino quando o score de validação para de melhorar (comum em boosting (boosting) e em aprendizado profundo (deep learning); veja Parada Antecipada).
  • Redução Sucessiva (successive halving) / Hyperband: avaliar muitas configurações de forma barata (poucas épocas / menos dados / menos árvores), manter a melhor fração e alocar mais orçamento a elas.

No scikit-learn, você pode usar redução sucessiva:

from sklearn.experimental import enable_halving_search_cv  # noqa
from sklearn.model_selection import HalvingRandomSearchCV
from sklearn.ensemble import HistGradientBoostingClassifier
import numpy as np

model = HistGradientBoostingClassifier(random_state=0)

param_dist = {
    "learning_rate": np.logspace(-3, -0.5, 50),
    "max_depth": [None, 2, 3, 4, 5],
    "max_leaf_nodes": np.arange(15, 255),
}

search = HalvingRandomSearchCV(
    model,
    param_dist,
    factor=3,          # how aggressively to prune
    scoring="roc_auc",
    cv=5,
    n_jobs=-1,
    random_state=0
)

search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)

Multifidelidade é um dos maiores “multiplicadores de eficiência” em fluxos de trabalho modernos de ajuste.

6) Acerte a avaliação: validação cruzada aninhada e prevenção de vazamento

Se você ajusta hiperparâmetros e reporta o score de validação cruzada no mesmo processo de validação cruzada, pode obter estimativas otimistas porque você efetivamente “buscou” entre muitas configurações.

Duas boas práticas comuns:

  • Conjunto de validação + conjunto de teste: ajustar na validação, avaliar uma vez no teste.
  • Validação cruzada aninhada (nested cross-validation): uma validação cruzada interna ajusta hiperparâmetros, e uma validação cruzada externa estima o desempenho de generalização.

Validação cruzada aninhada é mais cara, mas estatisticamente mais honesta, especialmente em pesquisa ou cenários com poucos dados.

7) Paralelize com eficiência (mas não quebre o método)

  • Busca em grade/aleatória paraleliza de forma trivial: avalie tentativas de forma independente em CPUs/GPUs.
  • Otimização bayesiana é mais sequencial, mas pode rodar no modo em lotes (batched) (pedir múltiplas sugestões de uma vez) com alguma perda de eficiência em amostras.

Dicas práticas:

  • Mantenha o treinamento determinístico quando viável (random_state, operações determinísticas).
  • Registre tudo: parâmetros, versão do código, hash do conjunto de dados (dataset hash), métrica, semente.

8) Lide com ruído e variância

Seu score medido inclui ruído de:

  • divisões dos dados
  • inicialização aleatória
  • kernels de GPU não determinísticos
  • treinamento estocástico

Mitigações:

  • Use validação cruzada repetida ou execuções repetidas para as melhores configurações
  • Compare configurações com intervalos de confiança quando as diferenças forem pequenas
  • Prefira modelos mais simples se o desempenho estiver dentro da tolerância ao ruído (robustez importa)

9) Não exagere no ajuste: pare quando os retornos diminuírem

O ajuste pode virar um “loop infinito”. Critérios práticos para parar:

  • Você atingiu um orçamento de computação/tempo
  • As melhorias são menores do que seu limiar prático
  • As melhores configurações são estatisticamente indistinguíveis

Em muitas aplicações reais, a qualidade dos atributos e a quantidade de dados dominam pequenos ganhos de ajuste.

Grade vs Aleatória vs Bayesiana: escolhendo a ferramenta certa

Guia rápido de decisão

  • Busca em grade

    • Melhor para: espaços minúsculos, valores “bons” conhecidos, checagens rápidas de sanidade
    • Evite quando: houver mais do que poucos hiperparâmetros realmente relevantes
  • Busca aleatória

    • Melhor para: método-base forte, espaços de alta dimensionalidade, orçamentos fixos de tentativas
    • Muitas vezes é a melhor abordagem “padrão”
  • Otimização bayesiana

    • Melhor para: treinamento caro, necessidade de eficiência em amostras, experimentação iterativa
    • Funciona especialmente bem depois que você definiu um espaço de busca sensato

Uma estratégia híbrida pragmática

Um padrão eficiente comum:

  1. Busca aleatória ampla (encontrar regiões razoáveis)
  2. Otimização bayesiana para refinar localmente
  3. Confirmar as 3–10 melhores configurações com avaliação repetida
  4. Reajustar a melhor configuração e avaliar uma vez no teste

Exemplos práticos do que ajustar (por família de modelo)

Árvores com boosting de gradiente (gradient-boosted trees) (XGBoost/LightGBM/CatBoost)

Controles de alto impacto:

  • learning_rate (escala logarítmica)
  • n_estimators com parada antecipada
  • max_depth / num_leaves
  • min_child_weight / min_data_in_leaf
  • subsample, colsample_bytree

Dica de eficiência: use parada antecipada com um conjunto de validação; isso efetivamente ajusta n_estimators automaticamente.

Redes neurais

Controles de alto impacto:

  • taxa de aprendizado e agendamento (veja Agendamentos da Taxa de Aprendizado)
  • tamanho do lote
  • decaimento de pesos (L2)
  • tamanho da arquitetura (largura/profundidade), camadas de normalização
  • dropout

Dica de eficiência: use multifidelidade (poucas épocas primeiro) e, depois, treine por mais tempo os melhores candidatos.

Modelos lineares regularizados (regularized linear models)

Controles de alto impacto:

  • força de regularização (C ou alpha) (escala logarítmica)
  • tipo de penalidade (L1/L2/elastic net)
  • pré-processamento de atributos (padronização, interações)

Dica de eficiência: ajuste o pré-processamento como parte da pipeline; ele pode importar tanto quanto o modelo.

Armadilhas comuns

  • Vazamento de dados: escalonamento/seleção de atributos realizada antes das divisões de validação cruzada.
  • Ajustar no conjunto de teste: invalida o teste como estimativa não enviesada.
  • Métrica errada: otimizar acurácia em dados desbalanceados em vez de F1/ROC-AUC.
  • Espaço de busca ruim: intervalos estreitos demais (perde o ótimo) ou amplos demais (desperdiça tentativas).
  • Ignorar variância: escolher a configuração “melhor” a partir de medições ruidosas.
  • Sobreajuste à validação cruzada: especialmente com orçamentos de busca enormes e conjuntos de dados pequenos.

Ajuste de hiperparâmetros no panorama maior de seleção de modelos

Ajuste de hiperparâmetros é um componente da seleção de modelos junto com:

Um único modelo bem ajustado pode ser forte, mas criar ensembles ou melhorar a qualidade dos dados pode trazer ganhos maiores do que tentar passar do percentil 99,0 para 99,1 via ajuste.

Principais conclusões

  • Busca em grade é sistemática, mas escala mal; use-a para espaços pequenos.
  • Busca aleatória é um ótimo padrão; escala bem e frequentemente supera grades com o mesmo orçamento.
  • Otimização bayesiana geralmente é melhor quando as tentativas são caras e você quer eficiência em amostras.
  • Ajuste eficiente depende mais de correção na avaliação, desenho do espaço de busca e alocação de recursos (parada antecipada, redução sucessiva) do que de qualquer otimizador isolado.
  • Sempre se proteja contra vazamento e sobreajuste à validação/validação cruzada — caso contrário, o ajuste pode produzir números impressionantes que não generalizam.