Hyperparameter tuning: методы подбора гиперпараметров

Что такое гиперпараметры

Parameters модели — значения, которые model учит из данных (весы в linear regression, splits в дереве).

Hyperparameters — настройки, которые задаёт пользователь ДО обучения:

  • n_estimators в Random Forest (число деревьев)
  • max_depth в tree
  • learning_rate в gradient boosting
  • alpha в Ridge/Lasso
  • C и kernel в SVM
  • hidden_layers в neural network

Выбор гиперпараметров сильно влияет на performance. Default значения — редко оптимальные.

Зачем подбирать

Разница performance между default и хорошо tuned моделью:

  • Logistic regression: 1-3% accuracy.
  • Random Forest: 3-5%.
  • Gradient Boosting: 5-10%.
  • Neural networks: 10-30%.

Для Kaggle соревнований и production systems — критически.

Для quick prototyping default часто достаточно, особенно если feature engineering still работа в процессе.

Grid Search

Перебор всех combinations из заданной сетки.

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [5, 10, 15, None],
    'min_samples_split': [2, 5, 10]
}

grid = GridSearchCV(
    RandomForestClassifier(),
    param_grid,
    cv=5,
    scoring='roc_auc',
    n_jobs=-1
)
grid.fit(X_train, y_train)

print(grid.best_params_)
print(grid.best_score_)

Плюсы. Систематический, reproducible.

Минусы. Exponential в количестве параметров (3 × 4 × 3 = 36 combinations × 5 folds = 180 fits). С 5+ параметрами impractical.

Random Search

Сэмплирует random комбинации из distribution.

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform

param_dist = {
    'n_estimators': randint(50, 500),
    'max_depth': randint(3, 30),
    'min_samples_split': randint(2, 20),
    'max_features': uniform(0.3, 0.7)
}

random_search = RandomizedSearchCV(
    RandomForestClassifier(),
    param_dist,
    n_iter=100,  # количество случайных combinations
    cv=5,
    scoring='roc_auc'
)
random_search.fit(X_train, y_train)

Плюсы. Covers больше ranges с тем же budget. Often лучше чем Grid. Bergstra и Bengio показали это в 2012.

Минусы. Чуть менее воспроизводимо (зависит от random seed). Не учитывает информацию из previous trials.

Bayesian Optimization

Умный подход. Строит surrogate модель performance как функцию hyperparameters. На основе этой модели выбирает next set hyperparameters для try.

Идея: explore unknown regions + exploit promising ones.

Gaussian Process. Classic Bayesian opt method.

Tree-structured Parzen Estimator (TPE). Используется в Hyperopt и Optuna. Fast, handles conditional parameters.

from hyperopt import hp, fmin, tpe, Trials
from sklearn.model_selection import cross_val_score

def objective(params):
    model = RandomForestClassifier(**params, random_state=42)
    score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()
    return -score  # минимизируем

space = {
    'n_estimators': hp.randint('n_estimators', 50, 500),
    'max_depth': hp.choice('max_depth', [5, 10, 20, None]),
    'min_samples_split': hp.randint('min_samples_split', 2, 20)
}

best = fmin(objective, space, algo=tpe.suggest, max_evals=100, trials=Trials())

Плюсы. Efficient — обычно находит good params с меньшим числом trials.

Минусы. Сложнее set up. Sequential (трудно parallelize perfectly).

Optuna

Modern библиотека для hyperparameter tuning. Simple API, powerful features.

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

def objective(trial):
    params = {
        'n_estimators': trial.suggest_int('n_estimators', 50, 500),
        'max_depth': trial.suggest_int('max_depth', 3, 30),
        'min_samples_split': trial.suggest_int('min_samples_split', 2, 20),
        'max_features': trial.suggest_float('max_features', 0.3, 1.0)
    }
    model = RandomForestClassifier(**params, random_state=42)
    return cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc').mean()

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)

print(study.best_params)
print(study.best_value)

Features Optuna:

  • Pruning: ранняя остановка failing trials.
  • Parallelization через databases.
  • Visualization (history, parameter importance).
  • Conditional hyperparameters.
  • Many sampler algorithms (TPE, CMA-ES, GP).

Optuna — стандарт современный в ML community.

Pruning — early stopping

Часть trials очевидно плохие ранний этапе. Pruning их убирает, экономя compute.

def objective(trial):
    params = {...}
    model = ...
    
    for step in range(100):
        # Progressive training (например, epoch у NN)
        model.partial_fit(...)
        score = validate(model)
        
        trial.report(score, step)
        if trial.should_prune():
            raise optuna.TrialPruned()
    
    return final_score

study = optuna.create_study(pruner=optuna.pruners.MedianPruner())

Typical savings: 2-5x speedup.

Bayesian vs Random — когда что

Random Search wins:

  • Много hyperparameters (>10).
  • Nondifferentiable или discontinuous performance landscape.
  • Budget limits (few dozens of trials).
  • Parallelization критически важна.

Bayesian wins:

  • Moderate dimensionality (2-10 params).
  • Expensive evaluations (deep learning с days training).
  • Smooth performance landscape.

На практике для most tabular ML tasks: Random Search с 100-500 trials часто как good как Bayesian. Для deep learning Bayesian обычно значительно лучше.

Nested Cross-Validation

Problem: tuning на всей training set → hyperparameters overfit на val. Final test score biased.

Решение: nested CV.

Outer CV (e.g., 5-fold) for performance estimation:
    Inner CV (e.g., 5-fold) for hyperparameter tuning:
        Try different hyperparameters
        Select best on inner val
    Report performance on outer test fold

Gives unbiased performance estimate.

from sklearn.model_selection import cross_val_score, KFold

outer_cv = KFold(5, shuffle=True)
outer_scores = cross_val_score(grid, X, y, cv=outer_cv, scoring='roc_auc')
# grid sам делает inner CV для tuning

Computational cost: outer × inner × trials × folds.

Automl tools

Tools, которые автоматизируют весь ML pipeline (feature selection + model + hyperparameters):

AutoSklearn. Bayesian optimization над scikit-learn pipeline.

TPOT. Genetic programming.

H2O AutoML. Enterprise tool, multiple algorithms.

Google Vertex AI, Azure AutoML, SageMaker Autopilot. Cloud solutions.

FLAML (Microsoft). Fast AutoML.

Для quick baselines — AutoML полезен. Для production понимание модели лучше ручной tuning.

Подбор гиперпараметров — ключевой ML skill. В тренажёре Карьерник есть задачи по ML, моделям и метрикам оценки.

Tuning в Deep Learning

Neural networks имеют unique гиперпараметры:

  • Architecture (layers, width, type)
  • Learning rate (самый важный!)
  • Batch size
  • Regularization (dropout, weight decay)
  • Optimizer (Adam, SGD, etc.)

Best practices:

Learning rate first. Используйте learning rate finder (Cyclical Learning Rates). Обычно 1e-3 to 1e-4 для Adam.

Batch size. Bigger — faster но needs scaled LR. Typical 32-512.

Architecture. Start with literature-proven (ResNet, Transformer). Custom scarce оправдано.

Tools specific:

  • Ray Tune. Scalable tuning для deep learning.
  • Weights & Biases Sweeps. Interactive UI.

Budget management

Fixed compute budget? How distribute?

Random Search: evenly distribute (budget = trials × cv_folds × training_time).

Successive Halving: выделяйте больше compute promising trials, меньше losing.

from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import HalvingGridSearchCV

halving = HalvingGridSearchCV(
    estimator=RandomForestClassifier(),
    param_grid=param_grid,
    factor=3,  # keep 1/3 best
    resource='n_samples'  # или 'n_estimators'
)

Trials эвалюируются на малом subset, лучшие — на большем. Эффективно при large data.

Hyperband. Как successive halving, но с многими стартовыми budgets. Explores-exploit trade-off.

Типичные ошибки

Tuning на test set. Test должен быть untouched. Tuning на val (часть train) via CV.

No search. Используют default hyperparameters даже для серьёзных проектов. Легко достижимый uplift игнорируют.

Over-tuning. Потратить 10x больше compute на tuning чем на feature engineering. Features обычно важнее.

Ignoring feature quality. «Tune harder пока accuracy не вырастет». Если features плохие, tuning не спасёт.

Manual selection без CV. «Попробовал depth=5, 10, 20, choose 10». Should be cross-validated.

Read the defaults

Во-первых, понимать что default делает:

sklearn RandomForestClassifier defaults (v1.3):

  • n_estimators=100, max_depth=None (grow full), max_features='sqrt'.

Эти defaults — stability > max accuracy. Tuning обычно improves.

XGBoost defaults:

  • learning_rate=0.3, max_depth=6, n_estimators=100.

Aggressive LR. Usually lower LR + more trees работает лучше.

Знание defaults — starting point для understanding tuning direction.

Читайте также

FAQ

Сколько trials для random search?

50-200 для most applications. 500+ если budget позволяет или много params.

Optuna vs Hyperopt?

Optuna newer, better API, более active development. Hyperopt старее, но proven.

Нужен ли hyperparameter tuning для linear models?

Для Ridge/Lasso — alpha важен. Для Logistic — C. Simple tuning 5-10 значений через CV достаточно.

Tuning на laptop — реалистично?

Для medium datasets (до millions rows) — да. Для deep learning или huge datasets — нужно GPU / cloud.