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

На каком таргете учатся деревья в случае алгоритма boosting в задаче бинарной классификации

1.0 Junior🔥 191 комментариев
#Машинное обучение

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

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

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

На каком таргете учатся деревья в случае алгоритма boosting в задаче бинарной классификации

Основная идея Boosting

В отличие от других ensemble методов (например, Random Forest, где все деревья независимы), boosting работает последовательно. Каждое новое дерево обучается на остатках (residuals) или ошибках предыдущих деревьев, а не на оригинальном таргете.

Это фундаментальное отличие — каждое дерево пытается исправить ошибки ансамбля предыдущих деревьев.

Случай 1: GradientBoosting для регрессии (как понять, как работает для классификации)

Для регрессии это просто:

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor

# Данные
X = np.array([[1], [2], [3], [4], [5]])
y_true = np.array([2, 4, 6, 8, 10])  # y = 2*x

# Обучение
gb_reg = GradientBoostingRegressor(n_estimators=3, learning_rate=0.1, random_state=42)
gb_reg.fit(X, y_true)

print("Процесс обучения GradientBoosting для регрессии:")
print(f"Истинный таргет: {y_true}")

# Шаг 1: Первое дерево учится на y_true
first_tree = DecisionTreeRegressor(max_depth=1)
first_tree.fit(X, y_true)
y_pred_1 = first_tree.predict(X)
print(f"\nПредсказание 1-го дерева: {y_pred_1}")
print(f"Ошибка (остаток): {y_true - y_pred_1}")

# Шаг 2: Второе дерево учится на остатках
residuals_1 = y_true - y_pred_1
second_tree = DecisionTreeRegressor(max_depth=1)
second_tree.fit(X, residuals_1)  # <-- ТАРГЕТ = ОСТАТКИ!
y_pred_2 = second_tree.predict(X)
print(f"\nПредсказание 2-го дерева (на остатках): {y_pred_2}")

# Обновляем предсказания
y_ensemble = y_pred_1 + 0.1 * y_pred_2  # learning_rate = 0.1
print(f"Ансамбль после 2-го дерева: {y_ensemble}")
print(f"Новые остатки: {y_true - y_ensemble}")

Случай 2: GradientBoosting для БИНАРНОЙ классификации

Для классификации все сложнее. Есть несколько подходов:

Подход 1: Вероятности (AdaBoost, Gradient Boosting с log loss)

Деревья учатся предсказывать вероятность класса 1 или логарифм шансов (log-odds).

from sklearn.ensemble import GradientBoostingClassifier, AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import log_loss

# Бинарная классификация
X = np.array([[1], [2], [3], [4], [5]])
y_true = np.array([0, 0, 1, 1, 1])  # Бинарный таргет

# GradientBoosting для классификации
gb_clf = GradientBoostingClassifier(
    n_estimators=3,
    learning_rate=0.1,
    max_depth=1,
    random_state=42
)

gb_clf.fit(X, y_true)

print("Процесс обучения GradientBoosting для классификации:")
print(f"Истинный таргет (классы): {y_true}")

# Получить вероятности
y_proba = gb_clf.predict_proba(X)[:, 1]  # Вероятность класса 1
print(f"\nПредсказанные вероятности: {y_proba}")

# На что учатся деревья?
print(f"\nДеревья учатся на: логарифм шансов (log-odds)")
log_odds_target = np.log(y_true / (1 - y_true + 1e-10))  # Для уникальных точек
print(f"Log-odds таргет: {log_odds_target}")

Подход 2: AdaBoost для классификации (более явный)

AdaBoost изменяет веса примеров, заставляя следующее дерево больше внимания уделять ошибочным примерам.

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier

X = np.array([[1], [2], [3], [4], [5], [6], [7], [8]])
y_true = np.array([0, 0, 0, 1, 1, 1, 1, 0])

adaboost = AdaBoostClassifier(
    estimator=DecisionTreeClassifier(max_depth=1),
    n_estimators=3,
    random_state=42
)

adaboost.fit(X, y_true)

print("AdaBoost процесс:")
print(f"Истинные классы: {y_true}\n")

# Шаг 1: Первое дерево
first_tree = DecisionTreeClassifier(max_depth=1)
first_tree.fit(X, y_true)
y_pred_1 = first_tree.predict(X)
errors_1 = (y_pred_1 != y_true).astype(int)
error_rate_1 = errors_1.sum() / len(y_true)

print(f"Предсказания 1-го дерева: {y_pred_1}")
print(f"Ошибки:                   {errors_1}")
print(f"Общая ошибка: {error_rate_1:.4f}")

# Шаг 2: Вычисляем веса ошибочных примеров
sample_weight = np.ones(len(y_true))  # Начальные веса = 1
alpha = 0.5 * np.log((1 - error_rate_1) / (error_rate_1 + 1e-10))
sample_weight[errors_1 == 1] *= np.exp(alpha)  # Увеличиваем вес ошибок
sample_weight /= sample_weight.sum()  # Нормализуем

print(f"\nВеса примеров для 2-го дерева:")
for i, w in enumerate(sample_weight):
    print(f"  Пример {i}: {w:.4f} (была ошибка: {errors_1[i]})")

# Шаг 3: Второе дерево учится на данных с переназначенными весами
second_tree = DecisionTreeClassifier(max_depth=1)
second_tree.fit(X, y_true, sample_weight=sample_weight)
y_pred_2 = second_tree.predict(X)
print(f"\nПредсказания 2-го дерева: {y_pred_2}")
print(f"(обучено на переназначенных весах, больше внимания ошибкам)")

Три ключевых понимания

1. Таргет для каждого дерева

АлгоритмТаргет для дерева
GradientBoosting (регрессия)Остатки (residuals): y_true - y_pred_ensemble
GradientBoosting (классификация)Псевдо-остатки (градиенты): d(loss)/d(pred)
AdaBoostОригинальные классы, но с переназначенными весами
XGBoostГрадиенты и гессианы loss функции

2. GradientBoosting для классификации подробнее

Для бинарной классификации с log loss:

from sklearn.ensemble import GradientBoostingClassifier
import numpy as np

X_train = np.random.randn(100, 5)
y_train = np.random.randint(0, 2, 100)

# GradientBoosting
gb = GradientBoostingClassifier(n_estimators=5, learning_rate=0.1)

# Инициализация: начальное предсказание
init_prediction = np.log(y_train.sum() / (len(y_train) - y_train.sum()))
print(f"Начальное log-odds: {init_prediction:.4f}")

# Каждое дерево предсказывает корректировку к log-odds
# Итоговое предсказание = sum(learning_rate * tree_prediction)

gb.fit(X_train, y_train)
y_proba = gb.predict_proba(X_train)[:, 1]
y_pred = gb.predict(X_train)

print(f"Вероятности класса 1: {y_proba[:5]}")
print(f"Предсказанные классы: {y_pred[:5]}")

3. Визуализация процесса

import matplotlib.pyplot as plt

# Простой пример
np.random.seed(42)
X = np.linspace(0, 10, 50).reshape(-1, 1)
y_true = np.array([1 if (x[0] > 5) else 0 for x in X]).astype(int)

# Обучим GradientBoosting
gb = GradientBoostingClassifier(
    n_estimators=5,
    learning_rate=0.5,
    max_depth=1,
    random_state=42
)
gb.fit(X, y_true)

# Получить вероятности
y_proba_ensemble = gb.predict_proba(X)[:, 1]

# Визуализировать
plt.figure(figsize=(12, 6))
plt.scatter(X, y_true, label='True labels', alpha=0.5)
plt.plot(X, y_proba_ensemble, 'r-', linewidth=2, label='Ensemble probability')
plt.xlabel('X')
plt.ylabel('Probability of class 1')
plt.legend()
plt.title('GradientBoosting для классификации')
plt.show()

print(f"Деревья учились корректировать вероятности")
print(f"На каждом шаге: новый таргет = остаток от предыдущего ансамбля")

Практический пример с XGBoost

import xgboost as xgb

X_train = np.random.randn(200, 10)
y_train = np.random.randint(0, 2, 200)

# XGBoost для бинарной классификации
model = xgb.XGBClassifier(
    n_estimators=10,
    learning_rate=0.1,
    max_depth=3,
    objective='binary:logistic',  # Объективная функция
    random_state=42
)

model.fit(X_train, y_train)

print("XGBoost для бинарной классификации:")
print(f"Objective: binary:logistic")
print(f"Каждое дерево предсказывает поправку к log-odds")
print(f"\nВероятности: {model.predict_proba(X_train[:5])[:,1]}")
print(f"Классы: {model.predict(X_train[:5])}")

РЕЗЮМЕ

В boosting для бинарной классификации деревья учатся на:

  1. AdaBoost: на оригинальных классах (0/1), но с переназначенными весами примеров
  2. GradientBoosting/XGBoost: на псевдо-остатках — градиентах loss функции (log loss, hinge loss, etc.)
  3. Фактически: каждое дерево предсказывает поправку к логарифму шансов (log-odds), которая затем преобразуется в вероятность через сигмоиду

Основная идея остается одна: каждое новое дерево исправляет ошибки ансамбля, но таргет для каждого дерева — это не оригинальные классы 0/1, а остатки или градиенты предыдущих предсказаний.