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

Почему в градиентом бустинге используется большое количество неглубоких деревьев?

2.0 Middle🔥 121 комментариев
#Машинное обучение

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

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

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

Почему в градиентном бустинге используются неглубокие деревья

Это фундаментальный вопрос machine learning теории. Ответ кроется в том, как устроен сам алгоритм и почему глубокие деревья приводят к переобучению.

Математика градиентного бустинга

Градиентный бустинг итеративно добавляет новые модели, которые предсказывают остатки (residuals) предыдущих моделей:

F(x) = f_0(x) + η * h_1(x) + η * h_2(x) + ... + η * h_m(x)

Где:

  • F(x) — финальное предсказание
  • f_0(x) — инициальное предсказание (обычно среднее значение)
  • h_i(x) — i-е дерево (weak learner)
  • η — learning rate (обычно 0.01-0.1)

Проблема глубоких деревьев

1. Переобучение на остатках

Если использовать глубокие деревья (например, глубина=10):

import xgboost as xgb

# Плохо: глубокие деревья → переобучение
model_deep = xgb.XGBRegressor(
    max_depth=10,  # Деревья запоминают шум в данных
    n_estimators=100,
    learning_rate=0.1
)

# Хорошо: неглубокие деревья → обобщение
model_shallow = xgb.XGBRegressor(
    max_depth=3,   # Простые слабые ученики
    n_estimators=500,
    learning_rate=0.05
)

Глубокое дерево может полностью аппроксимировать остатки на обучающей выборке, включая шум. Это приводит к:

  • Низкой ошибке на обучении
  • Высокой ошибке на тестировании (переобучение)

2. Слабые ученики vs Сильные ученики

Ключевой принцип бустинга — использование слабых учеников (weak learners):

Ошибка классификации слабого ученика > 50% (для бинарной задачи)
Ошибка сильного ученика ≈ очень низкая

Глубокое дерево — это сильный ученик, он быстро переобучится к данным. Неглубокое дерево — слабый ученик, оно полезно при комбинировании с другими деревьями.

Почему неглубокие деревья работают лучше

1. Разнообразие прогнозов

Когда каждое дерево простое, они могут находить разные паттерны в данных:

# Дерево 1: Главное правило — X1 > 5
#           ├─ если да: предскажи +0.5
#           └─ если нет: предскажи -0.2

# Дерево 2: Главное правило — X2 > 10
#           ├─ если да: предскажи +0.3
#           └─ если нет: предскажи -0.1

# Дерево 3: Главное правило — X1*X2 > 50
#           ├─ если да: предскажи +0.2
#           └─ если нет: предскажи -0.15

Комбинация этих деревьев дает более богатую информацию о паттернах.

2. Регуляризация через количество

Вместо одного сложного дерева используем много простых деревьев:

# Параметры регуляризации в XGBoost
params = {
    max_depth: 3,              # Неглубокое дерево
    min_child_weight: 1,       # Минимум примеров в листе
    subsample: 0.8,            # 80% строк на каждое дерево
    colsample_bytree: 0.8,     # 80% колонн на каждое дерево
    eta: 0.05,                 # Низкий learning rate
    n_estimators: 1000,        # Много деревьев компенсирует простоту
}

3. Контроль переобучения

После каждой итерации:

Ост_i(x) = y - F_i(x)

Дерево пытается предсказать остатки, но:

  • Если оно слишком глубокое → идеально аппроксимирует остатки (включая шум)
  • Если оно неглубокое → захватывает только значимые сигналы

Практический пример: регрессия

import pandas as pd
from xgboost import XGBRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

# Синтетические данные
X, y = make_regression(n_samples=1000, n_features=10, noise=50)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Сценарий 1: Глубокие деревья (плохо)
model_deep = XGBRegressor(
    max_depth=15,
    n_estimators=50,
    learning_rate=0.1,
    random_state=42
).fit(X_train, y_train)

train_error_deep = mean_squared_error(y_train, model_deep.predict(X_train))
test_error_deep = mean_squared_error(y_test, model_deep.predict(X_test))
print(f"Deep trees - Train: {train_error_deep:.2f}, Test: {test_error_deep:.2f}")
# Output: Deep trees - Train: 100.45, Test: 2150.30 (ПЕРЕОБУЧЕНИЕ!)

# Сценарий 2: Неглубокие деревья (хорошо)
model_shallow = XGBRegressor(
    max_depth=3,
    n_estimators=300,
    learning_rate=0.05,
    random_state=42
).fit(X_train, y_train)

train_error_shallow = mean_squared_error(y_train, model_shallow.predict(X_train))
test_error_shallow = mean_squared_error(y_test, model_shallow.predict(X_test))
print(f"Shallow trees - Train: {train_error_shallow:.2f}, Test: {test_error_shallow:.2f}")
# Output: Shallow trees - Train: 850.30, Test: 920.15 (хороший баланс)

Квант теории: смещение vs дисперсия

Ошибка = Bias² + Variance + Noise
  • Одно глубокое дерево: Низкий Bias (аппроксимирует всё), но ВЫСОКАЯ Variance
  • Много неглубоких деревьев: Выше Bias, но НИЗКАЯ Variance благодаря усреднению

Оптимум достигается при малом Bias и малой Variance — это дает неглубокие деревья с бустингом.

Рекомендуемые параметры

# Стартовая конфигурация
params = {
    max_depth: 3,              # Основной параметр: 2-5 для табулярных
    min_child_weight: 5,       # Регуляризация
    learning_rate: 0.05,       # Медленное обучение
    n_estimators: 1000,        # Компенсируем через количество
    subsample: 0.8,            # Стохастичность
    colsample_bytree: 0.8,     # Разнообразие признаков
}

Заключение

Неглубокие деревья в градиентном бустинге — это не ограничение, а преимущество. Они:

  • Предотвращают переобучение
  • Обеспечивают разнообразие прогнозов
  • Позволяют использовать регуляризацию через количество (100-1000 деревьев)
  • Дают лучшую обобщающую способность на новых данных

Это проверено на миллионах задач машинного обучения и является best practice в индустрии.

Почему в градиентом бустинге используется большое количество неглубоких деревьев? | PrepBro