← Назад к вопросам

Как регуляризуется бустинг?

2.3 Middle🔥 122 комментариев
#Машинное обучение#Статистика и A/B тестирование

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как регуляризуется бустинг?

Бустинг (Boosting) — один из самых мощных методов машинного обучения, но он легко переобучается. Регуляризация в бустинге критична для хорошего обобщения. Давайте разберёмся в основных техниках.

1. Теория: почему бустинг нуждается в регуляризации?

print("=== Проблемы переобучения в бустинге ===")

print("\n1. ПОСЛЕДОВАТЕЛЬНОЕ УЛУЧШЕНИЕ ОШИБОК")
print("   Каждый следующий learner фокусируется на предыдущих ошибках")
print("   Может привести к переподстройке под шум")

print("\n2. СЛОЖНОСТЬ АНСАМБЛЯ")
print("   Добавляем всё больше моделей")
print("   Без регуляризации = переобучение")

print("\n3. АДАПТАЦИЯ К ШУМУ")
print("   На поздних итерациях модель может адаптироваться только к шуму")
print("   Качество на новых данных падает")

2. Основной механизм регуляризации: Learning Rate

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_auc_score
import matplotlib.pyplot as plt

X, y = make_classification(n_samples=5000, n_features=20, n_informative=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Learning rate (также называется shrinkage или eta) — самая важная регуляризация
print("=== Learning Rate (Shrinkage) ===")
print("\nФормула обновления:")
print("y_pred_new = y_pred_old + learning_rate * tree_contribution")
print("\nДействие:")
print("- learning_rate = 1.0: используем полный вклад каждого дерева")
print("- learning_rate = 0.1: используем 10% вклада каждого дерева")
print("- learning_rate = 0.01: используем 1% вклада каждого дерева")

print("\nЭффект:")
print("- Большая learning_rate -> быстрое обучение, высокий риск переобучения")
print("- Малая learning_rate -> медленное обучение, лучшее обобщение")
print("- Нужно больше итераций при малой learning_rate")

# Практический пример
learning_rates = [0.001, 0.01, 0.05, 0.1, 0.2, 0.5, 1.0]
train_scores = []
test_scores = []

for lr in learning_rates:
    model = GradientBoostingClassifier(
        n_estimators=500,          # много итераций
        learning_rate=lr,          # регуляризация
        max_depth=5,
        random_state=42
    )
    model.fit(X_train, y_train)
    
    train_auc = roc_auc_score(y_train, model.predict_proba(X_train)[:, 1])
    test_auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
    
    train_scores.append(train_auc)
    test_scores.append(test_auc)
    
    print(f"LR={lr:5.3f}: Train AUC={train_auc:.4f}, Test AUC={test_auc:.4f}, Gap={train_auc-test_auc:.4f}")

# Визуализируем
plt.figure(figsize=(10, 6))
plt.semilogx(learning_rates, train_scores, 'o-', linewidth=2, label='Train AUC')
plt.semilogx(learning_rates, test_scores, 's-', linewidth=2, label='Test AUC')
plt.xlabel('Learning Rate')
plt.ylabel('AUC')
plt.title('Effect of Learning Rate on Boosting')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

3. Остановка по ошибке валидации (Early Stopping)

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split

print("=== Early Stopping ===")
print("\nИдея:")
print("1. Разделяем train на train и validation")
print("2. После каждой итерации проверяем ошибку на validation")
print("3. Если ошибка растёт -> СТОП, не учимся дальше")
print("4. Берём лучшую модель")

# Разделяем train на train и validation
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

# Обучаем с early stopping
model = GradientBoostingClassifier(
    n_estimators=1000,
    learning_rate=0.05,
    max_depth=5,
    random_state=42,
    validation_fraction=0.2,  # 20% для валидации
    n_iter_no_change=50,      # стоп если 50 итераций без улучшения
    tol=1e-4                  # минимальное улучшение
)

model.fit(X_train, y_train)

print(f"\nЛучшее количество итераций: {model.n_estimators_}")
print(f"Мы бы обучали 1000, но остановились на {model.n_estimators_}")

# Метрики
train_auc = roc_auc_score(y_train, model.predict_proba(X_train)[:, 1])
test_auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
print(f"\nTrain AUC: {train_auc:.4f}")
print(f"Test AUC: {test_auc:.4f}")
print(f"Overfitting gap: {train_auc - test_auc:.4f}")

4. Глубина деревьев (Tree Depth)

print("=== Ограничение глубины деревьев ===")
print("\nВеак learners vs Strong learners:")
print("- Weak learners: max_depth = 1-3 (пни и малые деревья)")
print("- Strong learners: max_depth = 5-10 (более сложные деревья)")

print("\nДействие max_depth:")
print("- Меньше глубина -> меньше каждого дерева способно переобучиться")
print("- Нужно больше деревьев для хорошего результата")
print("- Но ансамбль в целом более стабилен")

# Пример
max_depths = [1, 2, 3, 5, 8, 10, 15]
for max_d in max_depths:
    model = GradientBoostingClassifier(
        n_estimators=200,
        learning_rate=0.1,
        max_depth=max_d,
        random_state=42
    )
    model.fit(X_train, y_train)
    
    train_auc = roc_auc_score(y_train, model.predict_proba(X_train)[:, 1])
    test_auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
    
    print(f"max_depth={max_d:2d}: Train={train_auc:.4f}, Test={test_auc:.4f}, Gap={train_auc-test_auc:.4f}")

5. Минимум примеров в листе (min_samples_leaf)

print("=== min_samples_leaf и min_samples_split ===")
print("\nСмысл:")
print("min_samples_leaf: каждый лист содержит минимум N примеров")
print("min_samples_split: разбиение возможно только если >= N примеров")

print("\nРегуляризация:")
print("- Больше min_samples -> проще деревья")
print("- Проще деревья -> меньше дисперсия")
print("- Выше смещение, но лучше обобщение")

model_good = GradientBoostingClassifier(
    n_estimators=200,
    learning_rate=0.05,
    max_depth=5,
    min_samples_leaf=10,      # каждый лист >= 10 примеров
    min_samples_split=20,     # разбиение при >= 20 примеров
    subsample=0.8,            # (см ниже)
    random_state=42
)
model_good.fit(X_train, y_train)

6. Subsampling (выборка строк)

print("=== Subsample (Stochastic Gradient Boosting) ===")
print("\nИдея:")
print("Вместо того чтобы обучать каждое дерево на ВСЕх примерах,")
print("обучаем на случайной подвыборке (например, 80%)")

print("\nДействие:")
print("- subsample=1.0: используем все примеры (обычный GBM)")
print("- subsample=0.8: используем 80% случайных примеров")
print("- subsample=0.5: используем 50% случайных примеров")

print("\nЭффект регуляризации:")
print("1. Добавляет случайность -> разные деревья")
print("2. Меньше примеров -> невозможно запомнить детали")
print("3. Похоже на Bagging -> уменьшает дисперсию")

subsamples = [0.5, 0.7, 0.8, 0.9, 1.0]
for subsamp in subsamples:
    model = GradientBoostingClassifier(
        n_estimators=200,
        learning_rate=0.1,
        max_depth=5,
        subsample=subsamp,
        random_state=42
    )
    model.fit(X_train, y_train)
    
    train_auc = roc_auc_score(y_train, model.predict_proba(X_train)[:, 1])
    test_auc = roc_auc_score(y_test, model.predict_proba(X_test)[:, 1])
    
    print(f"subsample={subsamp:.1f}: Train={train_auc:.4f}, Test={test_auc:.4f}")

7. Feature Subsampling (выборка признаков)

print("=== Feature Subsampling (max_features) ===")
print("\nПараметры:")
print("max_features='sqrt': используем sqrt(n) случайных признаков")
print("max_features='log2': используем log2(n) случайных признаков")
print("max_features=0.8: используем 80% случайных признаков")
print("max_features=None: используем ВСЕ признаки")

print("\nДействие регуляризации:")
print("1. Меньше признаков -> проще деревья")
print("2. Добавляет случайность -> разнообразие")
print("3. Каждое дерево знает меньше -> не может запомнить")

model = GradientBoostingClassifier(
    n_estimators=200,
    learning_rate=0.1,
    max_depth=5,
    max_features='sqrt',  # регуляризация по признакам
    random_state=42
)
model.fit(X_train, y_train)

8. Регуляризация в XGBoost: параметры

from xgboost import XGBClassifier

print("=== Регуляризация в XGBoost ===")

# XGBoost имеет встроенные параметры регуляризации
model_xgb = XGBClassifier(
    # Похоже на GradientBoosting
    n_estimators=200,
    learning_rate=0.05,          # eta (learning rate)
    max_depth=5,
    subsample=0.8,               # выборка строк
    colsample_bytree=0.8,        # выборка признаков для каждого дерева
    colsample_bylevel=0.8,       # выборка признаков для каждого уровня
    colsample_bynode=0.8,        # выборка признаков для каждого узла
    
    # Специфично для XGBoost
    min_child_weight=1,          # минимальная сумма весов в листе
    gamma=0,                     # минимальное улучшение для разделения
    reg_alpha=0,                 # L1 регуляризация
    reg_lambda=1,                # L2 регуляризация
    
    random_state=42
)

model_xgb.fit(X_train, y_train)

print("\nКлючевые параметры XGBoost:")
print("- learning_rate: 0.01-0.1 (меньше = консервативнее)")
print("- max_depth: 3-8 (меньше = проще)")
print("- subsample: 0.5-1.0 (меньше = больше регуляризация)")
print("- colsample_bytree: 0.5-1.0 (меньше = больше регуляризация)")
print("- gamma: 0-5 (больше = меньше разделений)")
print("- reg_alpha (L1), reg_lambda (L2): 0-10 (больше = сильнее штраф)")

9. Комплексная регуляризация

print("=== Оптимальная конфигурация GBM ===")

# Для среднего датасета (1000-10000 примеров)
model_optimal = GradientBoostingClassifier(
    # Основные параметры
    n_estimators=500,              # много итераций
    learning_rate=0.05,            # консервативный learning rate
    
    # Регуляризация структуры деревьев
    max_depth=5,                   # глубина
    min_samples_split=20,          # минимум для разбиения
    min_samples_leaf=10,           # минимум в листе
    max_features='sqrt',           # выборка признаков
    
    # Регуляризация через выборку
    subsample=0.8,                 # выборка строк
    
    # Early stopping
    validation_fraction=0.2,
    n_iter_no_change=50,
    tol=1e-4,
    
    random_state=42
)

model_optimal.fit(X_train, y_train)

train_auc = roc_auc_score(y_train, model_optimal.predict_proba(X_train)[:, 1])
test_auc = roc_auc_score(y_test, model_optimal.predict_proba(X_test)[:, 1])

print(f"\nOptimal GBM:")
print(f"Train AUC: {train_auc:.4f}")
print(f"Test AUC: {test_auc:.4f}")
print(f"Overfitting gap: {train_auc - test_auc:.4f}")

10. Рекомендации

print("=== Стратегия регуляризации бустинга ===")

print("\nШаг 1: Learning Rate")
print("  - Выбери low learning_rate (0.01-0.1)")
print("  - Это позволит обучаться медленнее и стабильнее")
print("  - n_estimators должен быть большим (200-1000)")

print("\nШаг 2: Tree Depth")
print("  - Начни с max_depth=3-5")
print("  - Это weak learners")
print("  - Они не переобучаются сильно")

print("\nШаг 3: Early Stopping")
print("  - Установи validation_fraction и n_iter_no_change")
print("  - Это предотвратит обучение слишком долго")

print("\nШаг 4: Subsampling")
print("  - subsample=0.7-0.8")
print("  - colsample_bytree=0.7-0.8")
print("  - Это добавляет случайность")

print("\nШаг 5: Cross-validation & Tuning")
print("  - Используй GridSearch или RandomSearch")
print("  - Оптимизируй по CV метрике")
print("  - Не на train, не на test")

Ключевые выводы

  1. Learning Rate — самый важный параметр регуляризации
  2. Early Stopping — прекращаем обучение когда качество падает
  3. Tree Depth — ограничиваем сложность каждого дерева
  4. Subsampling (строк и признаков) добавляет разнообразие
  5. min_samples_leaf & min_samples_split делают деревья проще
  6. XGBoost имеет встроенные L1/L2 параметры
  7. Комбинация техник — используй несколько одновременно
  8. Cross-validation — проверяй всегда на независимых данных
Как регуляризуется бустинг? | PrepBro