Hyperparameter tuning на собеседовании Data Scientist

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.

Параметры vs гиперпараметры

Параметры модели — что модель сама учит на данных: веса в нейросети, коэффициенты в линейной регрессии, разбиения в дереве.

Гиперпараметры — то, что задаёт человек до обучения: learning rate, max_depth, число скрытых слоёв, alpha регуляризации.

На собесе DS вопросы:

  • «Что такое hyperparameter? Чем отличается от параметра?»
  • «Как подбирать?»
  • «Чем grid search хуже random search?»

Главная боль без понимания HP tuning — кандидат запускает GridSearchCV на 6 параметрах по 5 значений = 15 625 комбинаций. Через две недели ноут не закончил, сроки сорваны. Тимлид спрашивает «зачем grid?», и кандидат не может ответить.

Полный перебор по сетке значений:

from sklearn.model_selection import GridSearchCV
param_grid = {
    'max_depth': [3, 5, 7, 10],
    'min_samples_split': [2, 5, 10],
    'n_estimators': [100, 200, 500],
}
gs = GridSearchCV(estimator, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)
gs.fit(X, y)
print(gs.best_params_, gs.best_score_)

Плюсы: простой, исчерпывающий по заданной сетке.

Минусы:

  • Экспоненциальный рост: 5 параметров × 5 значений = 3125 моделей
  • На большинстве параметров финальная модель нечувствительна — потратили compute впустую
  • Не масштабируется на 10+ параметров

Когда применять: 2–3 параметра, известные хорошие диапазоны, нужно 100% покрытие.

Random search и почему он лучше

Случайные точки в пространстве гиперпараметров.

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

param_dist = {
    'max_depth': randint(3, 15),
    'min_samples_split': randint(2, 20),
    'learning_rate': uniform(0.01, 0.3),
}
rs = RandomizedSearchCV(estimator, param_dist, n_iter=50, cv=5, scoring='roc_auc')
rs.fit(X, y)

Bergstra & Bengio (2012) доказали: random search обычно лучше grid за тот же бюджет.

Интуиция: если из 10 гиперпараметров только 2 «важны» — grid тратит 80% бюджета на «неважные» оси. Random не знает разницы и равномерно покрывает обе.

Grid 5x5 = 25 моделей, но только 5 уникальных значений по каждой оси
Random 25 моделей = 25 уникальных значений по каждой оси (с шумом)

Когда применять: дефолт для большинства задач. 50–200 итераций — обычная практика.

Bayesian optimization

Подход умнее random: использует уже посчитанные точки, чтобы предсказать, где минимум, и выбрать следующую точку для оценки.

Алгоритм (упрощённо):

  1. Тренируем модель в нескольких начальных точках (random)
  2. Подгоняем suarrogate model (обычно Gaussian Process) под (hyperparams) → score
  3. Оптимизируем acquisition function (например, Expected Improvement) — где ожидаем максимум improvement
  4. Тренируем модель в этой точке, добавляем в набор, повторяем

Optuna — стандарт для Bayesian-like tuning в DS:

import optuna

def objective(trial):
    max_depth = trial.suggest_int('max_depth', 3, 15)
    lr = trial.suggest_float('learning_rate', 1e-3, 0.5, log=True)
    n_est = trial.suggest_int('n_estimators', 100, 2000)
    model = XGBClassifier(max_depth=max_depth, learning_rate=lr, n_estimators=n_est)
    score = cross_val_score(model, X, y, cv=5, scoring='roc_auc').mean()
    return score

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

Плюсы: меньше итераций до хорошего результата.

Минусы: оверхед на surrogate model, на параллельных запусках хуже масштабируется, чем random.

Когда: дорогая оценка одной точки (большая нейросеть), 5+ гиперпараметров, бюджет ограничен.

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Hyperband и Successive Halving

Идея: выделять разный compute на разных HP-кандидатах. Многим HP-комбинациям достаточно 1 эпохи, чтобы понять, что они плохие, без полной тренировки.

Successive Halving:

  1. Тренируем N моделей с маленьким бюджетом (1 эпоха)
  2. Берём верхнюю половину по метрике
  3. Удваиваем бюджет, тренируем оставшихся
  4. Повторяем до 1 модели

Hyperband — meta-алгоритм, который комбинирует несколько successive halving с разными параметрами.

В современных туллах Optuna имеет MedianPruner, реализующий пруннинг по медиане.

Когда применять: дорогая тренировка (нейросеть), много HP-кандидатов, метрика быстро становится понятна.

Practical tuning для популярных моделей

XGBoost / LightGBM / CatBoost — порядок:

  1. n_estimators=500-1000, learning_rate=0.05-0.1, остальное дефолт — baseline
  2. max_depth (3-10), min_child_weight или min_data_in_leaf
  3. subsample, colsample_bytree (0.7-1.0) — против переобучения
  4. reg_alpha, reg_lambda — регуляризация
  5. После всего — снижение learning_rate до 0.01-0.03 + увеличение n_estimators, лучшая модель

Random Forest:

  • n_estimators — 100-500, чем больше тем стабильнее (без риска переобучения)
  • max_depth, min_samples_split, min_samples_leaf — против переобучения
  • max_features — 'sqrt' (классификация), 'log2' или 0.3-0.7 от total

Linear/Logistic Regression:

  • C (обратный alpha) или alpha — регуляризация
  • penalty='l1'/'l2'/'elasticnet'
  • class_weight='balanced' для дисбаланса

Neural networks:

  • learning_rate — самое важное (lr finder)
  • batch_size — 32-512
  • weight_decay — 1e-4 typically
  • Архитектура (число слоёв, размер скрытых) — отдельная эпопея

Частые ошибки

Тюнить на тесте. Гиперпараметры подбираются на validation (или CV) — на тесте только финальная оценка. Иначе тест становится валидацией, оверфит на тест.

Не использовать stratified CV для классификации. Особенно при дисбалансе. StratifiedKFold сохраняет пропорции классов.

Тюнить всё одновременно. Когда HP много, начать с самых важных (lr, depth для деревьев), фиксируя остальные. Потом сдвигать остальные.

Ставить grid 5×5×5 для бустингов. 125 моделей × 5 cv folds × 30 минут = 1.5 суток. Использовать random search или optuna с 30-50 trials.

Не использовать early stopping в бустингах. XGBoost/LGBM имеют early_stopping_rounds — экономит compute, не позволяет оверфиту.

Игнорировать reproducibility. Без random_state HP search дают разные результаты. Зафиксировать seed везде (модель, фолды, sampler).

Не сохранять результаты trials. Optuna RDB storage, sklearn joblib.dump(grid_search.cv_results_) — иначе после рестарта начинаем заново.

Связанные темы

FAQ

50–200 для большинства задач. Если каждое обучение дешёвое (< 1 мин) — больше. Если дорогое (> 10 мин) — меньше, переходить на bayesian.

GridSearch или RandomSearch для XGBoost?

Random или Optuna почти всегда лучше grid для бустингов: 5+ важных параметров, экспоненциальный grid невозможен.

Сколько learning rate тюнить — на log scale?

Да, всегда log-uniform или log-grid. От 1e-5 до 1e-1 — типичный диапазон. Линейный grid [0.001, 0.01, 0.1] не покроет область между.

Tune model на тех же данных, что и финальная оценка?

Нет. Train (обучение модели) → val (HP tuning) → test (финальная оценка). На малых данных — nested CV.

Optuna или scikit-optimize?

Optuna современнее, имеет prune, удобнее API, активно развивается. Scikit-optimize старее, проще, но менее популярен. По умолчанию — Optuna.

Это официальная информация?

Нет. Статья основана на работах Bergstra-Bengio (2012) для random search, Snoek et al. (2012) для bayesian, Li et al. (2017) для hyperband, документации Optuna и sklearn.


Тренируйте Data Science — откройте тренажёр с 1500+ вопросами для собесов.