Hyperparameter tuning на собеседовании Data Scientist
Карьерник — 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?», и кандидат не может ответить.
Grid search
Полный перебор по сетке значений:
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: использует уже посчитанные точки, чтобы предсказать, где минимум, и выбрать следующую точку для оценки.
Алгоритм (упрощённо):
- Тренируем модель в нескольких начальных точках (random)
- Подгоняем suarrogate model (обычно Gaussian Process) под
(hyperparams) → score - Оптимизируем acquisition function (например, Expected Improvement) — где ожидаем максимум improvement
- Тренируем модель в этой точке, добавляем в набор, повторяем
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+ гиперпараметров, бюджет ограничен.
Hyperband и Successive Halving
Идея: выделять разный compute на разных HP-кандидатах. Многим HP-комбинациям достаточно 1 эпохи, чтобы понять, что они плохие, без полной тренировки.
Successive Halving:
- Тренируем N моделей с маленьким бюджетом (1 эпоха)
- Берём верхнюю половину по метрике
- Удваиваем бюджет, тренируем оставшихся
- Повторяем до 1 модели
Hyperband — meta-алгоритм, который комбинирует несколько successive halving с разными параметрами.
В современных туллах Optuna имеет MedianPruner, реализующий пруннинг по медиане.
Когда применять: дорогая тренировка (нейросеть), много HP-кандидатов, метрика быстро становится понятна.
Practical tuning для популярных моделей
XGBoost / LightGBM / CatBoost — порядок:
n_estimators=500-1000,learning_rate=0.05-0.1, остальное дефолт — baselinemax_depth(3-10),min_child_weightилиmin_data_in_leafsubsample,colsample_bytree(0.7-1.0) — против переобученияreg_alpha,reg_lambda— регуляризация- После всего — снижение
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-512weight_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_) — иначе после рестарта начинаем заново.
Связанные темы
- Cross-validation простыми словами
- Регуляризация L1 и L2 на собесе DS
- Class imbalance на собесе DS
- Подготовка к собесу Data Scientist
- Что такое overfitting
FAQ
Сколько trials достаточно для random search?
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+ вопросами для собесов.