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

Почему градиентный бустинг называется градиентным?

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

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

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

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

Почему градиентный бустинг называется градиентным?

Короткий ответ

Потому что каждое новое дерево обучается на градиентах ошибок предыдущих деревьев. Градиент — это производная функции потерь, которая показывает направление минимизации ошибки.

Что такое градиент?

Градиент функции — это вектор частных производных, показывающий направление и скорость наибольшего возрастания функции.

# Для функции потерь L(y, y_pred) = (y - y_pred)^2
# Градиент: dL/dy_pred = -2*(y - y_pred)

# Если y=100, y_pred=90, то:
# dL/dy_pred = -2*(100-90) = -20
# Это значит: увеличивайте y_pred на 20, чтобы уменьшить ошибку

Как работает обучение

Итерация 1: первое дерево

from sklearn.tree import DecisionTreeRegressor

y_pred_1 = tree_1.predict(X)
errors_1 = y - y_pred_1  # ошибки

Итерация 2: считаем градиент и учим новое дерево

# Градиент показывает, на сколько нужно увеличить y_pred
gradient = -dL/dy_pred = -(-2*errors_1) = 2*errors_1

# ИЛИ можно просто использовать остатки (для MSE это одно и то же)
gradient = errors_1

# Теперь обучаем второе дерево НА ГРАДИЕНТАХ
tree_2.fit(X, gradient)  # не на (X, y), а на (X, gradient)

y_pred_2_from_tree = tree_2.predict(X)
y_pred_combined = y_pred_1 + learning_rate * y_pred_2_from_tree

Итерация 3: повторяем

errors_2 = y - y_pred_combined
gradient_2 = errors_2  # новые градиенты

tree_3.fit(X, gradient_2)
y_pred_combined += learning_rate * tree_3.predict(X)

Почему именно градиент, а не просто остатки?

Универсальность — вот ключевое слово.

# Для регрессии (MSE):
L = (y - y_pred)^2
gradient = -dL/dy_pred = -2*(y - y_pred)  # просто остаток × (-2)

# Для классификации (LogLoss):
L = -[y*log(y_pred) + (1-y)*log(1-y_pred)]
gradient = -dL/dy_pred = y_pred - y  # совсем другое

# Для кастомной функции потерь (например, квантильная регрессия):
# gradient = something_else

# Но ЛЮБАЯ дифференцируемая функция потерь имеет градиент
# Вот почему это универсально

Пример кода

import numpy as np
from sklearn.tree import DecisionTreeRegressor

def gradient_boosting_example(X, y, n_trees=5, lr=0.1):
    """Упрощённая реализация градиентного бустинга"""
    
    predictions = np.zeros(len(y))
    
    for iteration in range(n_trees):
        # Шаг 1: считаем ошибки
        residuals = y - predictions
        
        # Шаг 2: считаем градиент потерь L = (y - y_pred)^2
        # dL/dy_pred = -2 * (y - y_pred) = -2 * residuals
        # ИЛИ просто используем остатки (результат похож)
        gradients = residuals  # целевые значения для дерева
        
        # Шаг 3: обучаем дерево предсказывать градиенты
        tree = DecisionTreeRegressor(max_depth=3)
        tree.fit(X, gradients)
        
        # Шаг 4: обновляем предсказания
        tree_predictions = tree.predict(X)
        predictions += lr * tree_predictions  # с learning rate
        
        # Шаг 5: считаем MSE
        mse = np.mean((y - predictions) ** 2)
        print(f"Итерация {iteration+1}: MSE = {mse:.4f}")
    
    return predictions

Сравнение: AdaBoost vs Gradient Boosting

AdaBoost (взвешивание ошибок):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Дерево 1: L1 ошибок
  ↓
Дерево 2: переобучаем на ОШИБОЧНЫХ объектах (весим их выше)
  ↓
Дерево 3: переобучаем на неправильных объектах Дерева 2

✗ Работает только для определённых функций потерь
✗ Не универсален


Gradient Boosting:
━━━━━━━━━━━━━━━
Дерево 1: обучаемся на (X, y)
  ↓
Считаем gradient = -dL/dy_pred  ← производная функции потерь
  ↓
Дерево 2: обучаемся на (X, gradient)
  ↓
Повторяем

✓ Работает для ЛЮБОЙ дифференцируемой функции потерь
✓ Универсален
✓ Более мощный

Реальные реализации

# XGBoost использует градиент И гессиан (вторую производную)
from xgboost import XGBRegressor

model = XGBRegressor()
model.fit(X_train, y_train)
# Внутри XGBoost использует:
# gradient = -dL/dy_pred
# hessian = d²L/dy_pred²
# Это позволяет оптимизировать ещё эффективнее

# LightGBM делает почти то же
from lightgbm import LGBMRegressor

model = LGBMRegressor()
model.fit(X_train, y_train)

Математическое обоснование

Мы минимизируем: L(y, y_pred) = Σ(y_i - y_pred_i)²

Оптимизация методом градиентного спуска:
y_pred_new = y_pred_old - learning_rate * ∇L

Где ∇L = dL/dy_pred (градиент потерь)

В бустинге:
y_pred_new = y_pred_old + learning_rate * f(X)

Где f(X) — новое дерево, обученное на градиентах
Это эквивалентно шагу градиентного спуска!

Заключение

Градиентный бустинг зовется "градиентным", потому что:

  1. Использует градиент (производную) функции потерь
  2. Каждое новое дерево учится приблизительно вычислять градиент
  3. Это обновление эквивалентно шагу градиентного спуска
  4. Работает с ЛЮБОЙ дифференцируемой функцией потерь

Это мощнее и универсальнее, чем просто переобучение на ошибках (AdaBoost).