Собесов

zadachi_ds: Как понять, что модель переобучилась

ML / Data ScienceДиагностикаЛёгкаяJunior

Условие

У модели train AUC = 0.99, test AUC = 0.72. Это переобучение? Какими способами диагностировать и какими бороться?

Решение

Диагностика — да, это сильное переобучение

Разрыв train vs val/test > 5-10 п.п. — обычно переобучение. AUC 0.27 разница — катастрофическая.

Дополнительные проверки

1. Learning curves

from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
import numpy as np
 
sizes, tr, va = learning_curve(model, X, y, cv=5, scoring='roc_auc',
                                train_sizes=np.linspace(0.1, 1.0, 8))
plt.plot(sizes, tr.mean(axis=1), label='train')
plt.plot(sizes, va.mean(axis=1), label='val')
plt.fill_between(sizes, va.mean(1)-va.std(1), va.mean(1)+va.std(1), alpha=0.2)
plt.legend(); plt.xlabel('Train size'); plt.ylabel('AUC')

Признаки переобучения:

  • train ≫ val при всех sizes.
  • val не растёт с n_train — модель не учится из доп. данных.

2. Validation curve по гиперпараметру

from sklearn.model_selection import validation_curve
depths = range(2, 15)
tr, va = validation_curve(model, X, y, 'max_depth', depths, cv=5, scoring='roc_auc')

U-образная val curve → есть «правильная» сложность.

3. CV stability

from sklearn.model_selection import cross_val_score
scores = cross_val_score(model, X, y, cv=10, scoring='roc_auc')
print(f"AUC: {scores.mean():.3f} ± {scores.std():.3f}")

Высокая std (>0.05) — нестабильно, видимо переобучение или leakage.

4. Leakage-чеклист

  • Фича вычислена с использованием target?
  • Train/test разделение учитывает группировку?
  • Препроцессинг (Scaler/Encoder) fit на full data?
  • Временной leakage (фича из будущего)?

Способы бороться

1. Регуляризация:

  • LightGBM / XGBoost: min_child_samples, reg_alpha, reg_lambda, feature_fraction, bagging_fraction.
  • Логрег: L1/L2.
  • NN: dropout, weight decay.

2. Уменьшить ёмкость модели:

  • max_depth: 6-8 для деревьев бустинга.
  • n_estimators: оставить только до early stopping.
  • Меньше слоёв / нейронов в NN.

3. Больше данных:

  • Собрать ещё.
  • Augmentation для картинок / текстов.

4. Feature selection:

  • 500 фич с одним полезным сигналом → переобучение. Убрать лишние.

5. Early stopping:

import lightgbm as lgb
model = lgb.LGBMClassifier(n_estimators=1000)
model.fit(X_tr, y_tr,
          eval_set=[(X_val, y_val)],
          callbacks=[lgb.early_stopping(50)])

Прекращает обучение, когда val перестаёт улучшаться.

6. Ensemble:

  • Bagging (Random Forest) уменьшает variance.
  • Stacking → ровнее ошибки.

7. CV для подбора гиперов:

  • Не подбирать на test (это уже не test!).
  • Использовать nested CV для honest estimate.

Specifically для AUC 0.99 vs 0.72

Подозрения:

  1. Leakage — самое вероятное. Что-то с target прокралось в фичи (например, target_lag1 или cohort_avg_y_other_users без OOF).
  2. Слишком сложная модель: depth=20, n_estimators=2000, без regularization.
  3. Малая выборка: на 1000 строк и 200 фичах любой XGBoost переобучится.
  4. Stratification сломана: классы перетекли в train.

Проверить в первую очередь — feature_importance и SHAP: какая фича доминирует с подозрительной силой?

imp = pd.Series(model.feature_importances_, index=X.columns).sort_values(ascending=False)
print(imp.head(10))
# если одна фича > 50% importance — почти всегда leakage

Sanity check: пермутация target

Если перемешать target и AUC всё равно 0.7 — это не leakage, а baseline-эффект от структуры данных:

np.random.shuffle(y_train)
model.fit(X_train, y_train)
# val AUC должен быть ≈ 0.5; если выше — leakage в split

Подводные камни

  1. Train высокий, test нормальный — не всегда плохо: deep models часто доводят train до 1.0, но это ок, если test = бизнес-target.
  2. Test AUC 0.72 мог бы быть нормальным для задачи — переобучение определяется по разнице, не по абсолюту.
  3. CV-AUC 0.99 — почти всегда leakage; реальные задачи редко имеют AUC > 0.95.
  4. early_stopping на test set = leakage. Только на отдельный val.
  5. Регуляризация без CV — гадание; всегда подбирать.
  6. «Бороться с переобучением» не нужно, если train ≈ val. Не «лечите здоровое».

Эталонный ответ

Да, переобучение (AUC 0.99 vs 0.72 — разрыв 27 п.п.).

Диагностика:

  • Learning curves: train ≫ val при всех sizes;
  • Validation curve по depth/n_estimators;
  • CV stability (std > 0.05 → проблема);
  • Leakage-чеклист (фича из будущего, target в фичах, общий fit);
  • Sanity-check через permuted y.

Борьба:

  • Регуляризация (reg_alpha, reg_lambda, min_child_samples);
  • Уменьшить depth / n_estimators (+ early_stopping);
  • Снять подозрительно «информативные» фичи (leakage candidates);
  • Больше данных / augmentation;
  • Bagging / ensemble.

Первая гипотеза при AUC 0.99 на train — leakage, а не «слишком сложная модель».

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти