Какие методы борьбы с переобучением вы знаете?

«Какие методы борьбы с переобучением вы знаете?» — вопрос из категории Классическое ML, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В своих проектах я применяю комбинацию методов для борьбы с переобучением, начиная с простых и переходя к более сложным:

1. Регуляризация — добавляю штраф за сложность модели:

# L1 (Lasso) и L2 (Ridge) регуляризация
from sklearn.linear_model import LogisticRegression

# L1 регуляризация - для отбора признаков
l1_model = LogisticRegression(
    penalty='l1',
    C=0.1,  # обратная сила регуляризации
    solver='liblinear'
)

# L2 регуляризация - для уменьшения весов
l2_model = LogisticRegression(
    penalty='l2',
    C=0.1,
    solver='lbfgs',
    max_iter=1000
)

# ElasticNet - комбинация L1 и L2
from sklearn.linear_model import ElasticNet
elastic_model = ElasticNet(
    alpha=0.1,
    l1_ratio=0.5  # 0.5 = 50% L1, 50% L2
)

2. Ранняя остановка (Early Stopping) — для итеративных алгоритмов:

# Для Gradient Boosting
import xgboost as xgb

xgb_model = xgb.XGBClassifier(
    n_estimators=1000,  # Ставим большое число
    early_stopping_rounds=50,
    eval_metric='logloss',
    eval_set=[(X_val, y_val)]  # Валидационный набор
)

# Для нейросетей в TensorFlow/Keras
import tensorflow as tf

early_stopping = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss',
    patience=10,
    restore_best_weights=True  # Важно: восстанавливаем лучшие веса
)

model.fit(X_train, y_train,
          validation_data=(X_val, y_val),
          epochs=100,
          callbacks=[early_stopping],
          verbose=0)

3. Dropout — для нейронных сетей:

model = tf.keras.Sequential([
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dropout(0.5),  # 50% нейронов отключаются случайно
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dropout(0.3),  # 30% отключаются
    tf.keras.layers.Dense(10, activation='softmax')
])

4. Аугментация данных — для изображений и текстов:

# Для изображений
from tensorflow.keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

5. Упрощение модели:

# Для деревьев
from sklearn.tree import DecisionTreeClassifier

simple_tree = DecisionTreeClassifier(
    max_depth=5,           # Ограничиваем глубину
    min_samples_split=20,  # Минимум samples для разделения
    min_samples_leaf=10    # Минимум samples в листе
)

# Для нейросетей
simple_nn = tf.keras.Sequential([
    tf.keras.layers.Dense(32, activation='relu'),  # Меньше нейронов
    tf.keras.layers.Dense(16, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

6. Кросс-валидация — для надежной оценки:

from sklearn.model_selection import cross_val_score

scores = cross_val_score(
    estimator=model,
    X=X_train,
    y=y_train,
    cv=5,           # 5-fold кросс-валидация
    scoring='accuracy',
    n_jobs=-1
)
print(f"Средняя accuracy: {scores.mean():.3f} (±{scores.std():.3f})")

7. Batch Normalization — стабилизация обучения нейросетей:

model = tf.keras.Sequential([
    tf.keras.layers.Dense(128),
    tf.keras.layers.BatchNormalization(),  # Нормализуем активации
    tf.keras.layers.Activation('relu'),
    tf.keras.layers.Dropout(0.5)
])

Моя стратегия:

  1. Начинаю с кросс-валидации и мониторинга разницы train/val ошибок
  2. Для линейных моделей → регуляризация (L1 если нужно отобрать признаки, L2 если нет)
  3. Для деревьев → ограничение глубины и минимального размера узлов
  4. Для бустинга → ранняя остановка + небольшая скорость обучения
  5. Для нейросетей → dropout + batch normalization + early stopping
  6. Для изображений → аугментация + dropout

Важнее всего понимать, почему модель переобучается: мало данных, слишком сложная модель или шумные признаки. Диагностика определяет лечение.