Gradient Boosting

Градієнтний бустинг та XGBoost — послідовне виправлення помилок

Алгоритм градієнтного бустингу як альтернатива Random Forest. Відмінність Boosting від Bagging. XGBoost як оптимізована реалізація. Порівняння всіх моделей регресії з модуля 3.

Паралель vs послідовність — два шляхи до точності

У попередній статті ми вивчили Random Forest — ансамбль дерев, де кожне дерево навчається незалежно, а потім їхні передбачення усереднюються.

Уявіть команду зі 100 фахівців, які незалежно один від одного оцінюють проєкт. Кожен дає свою оцінку, потім всі оцінки усереднюються. Це Random Forest.

Але що, якщо організувати роботу по-іншому?

Замість паралельного голосування, дати один проєкт послідовно кільком експертам:

  1. Перший експерт робить оцінку → виявляє грубі помилки
  2. Другий експерт фокусується на виправленні того, що пропустив перший
  3. Третій експерт полірує дрібні недоліки
  4. ...

Кожен наступний експерт вчиться на помилках попереднього. Це Gradient Boosting (градієнтний бустинг).

Аналогія з редагуванням статті:

Random Forest: 100 письменників пишуть 100 версій статті незалежно → усереднити всі версії.

Gradient Boosting: Один письменник пише чорновик → редактор виправляє грубі помилки → коректор полірує дрібниці → фінальна версія.

Послідовне виправлення часто дає кращий результат!

Факт: Gradient Boosting (зокрема XGBoost) був переможцем 80%+ Kaggle змагань у 2015-2017 роках.


Проблема Random Forest — мотивація для Boosting

Random Forest чудово працює, але має обмеження:

Усереднення випадкових помилок

Random Forest усереднює передбачення незалежних дерев. Це добре працює, коли помилки дерев є випадковими й не повторюють одна одну.

Проблема: Що, якщо помилки систематичні?

rf_bias_demo.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor

# Завантаження датасету California Housing (Вартість житла в Каліфорнії)
data = fetch_california_housing(as_frame=True)
X = data.data
y = data.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Ансамбль Random Forest (Випадковий ліс) зі 100 дерев
rf = RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
y_pred_rf = rf.predict(X_test)

# Аналіз систематичного зміщення (Bias) за ціновими сегментами (ціни в $100k)
bins = [0.0, 1.5, 3.0, 4.5, np.inf]
bin_labels = ['$0–150k', '$150–300k', '$300–450k', '$450k+']
segment_names = [
    'Бюджетне житло',
    'Базовий сегмент',
    'Дорогий сегмент',
    'Елітна нерухомість'
]

errors_by_bin = []
for i in range(len(bins) - 1):
    mask = (y_test >= bins[i]) & (y_test < bins[i+1])
    diff = y_pred_rf[mask] - y_test[mask]
    mean_err = diff.mean()
    errors_by_bin.append(mean_err)

print("📊 Аналіз систематичного зміщення (Bias) Random Forest:")
for label, seg, err in zip(bin_labels, segment_names, errors_by_bin):
    direction = "переоцінює (завищує)" if err > 0 else "недооцінює (занижує)"
    print(f"  {label} ({seg}): {direction} на ${abs(err)*100000:,.0f}")
Out[1]:

Ціновий сегмент житлаСередня похибка (Завантаження...)Характер помилки Random ForestВплив на практичне використання
$0–150k (бюджетне житло)+$17,911🟡 Переоцінка вартості (прогноз завищено)Невигідні умови для покупців недорогого житла
$150–300k (базовий сегмент)+$6,400🟢 Мінімальне відхилення від ідеалуНайбільш точний ціновий діапазон моделі
$300–450k (дорогий сегмент)-$29,868🟡 Помітна недооцінкаПочаток згладжування прогнозів ансамблем
$450k+ (елітна нерухомість)-$74,357🔴 Критична недооцінка (>15–20% вартості)Ансамбль усереднення не здатний передбачити пікові ціни
Головний недолік Random Forest: Усереднення десятків дерев ефективно гасить випадковий шум (Variance / Дисперсію), але тягне прогнози до загального середнього. Дорогі будинки систематично недооцінюються, бо більшість листків у деревах містять типовіші середні значення. Gradient Boosting створений спеціально для послідовного виправлення таких систематичних похибок (Bias / Зміщення)!

Проблема: Random Forest систематично недооцінює дорогі квартири! Усі дерева помиляються в один бік.

Ідея Boosting: Навчити наступну модель фокусуватися на складних випадках (дорогих квартирах), з якими попередня модель не впоралась.


Градієнтний бустинг — теорія

Принцип послідовного виправлення помилок

Алгоритм градієнтного бустингу працює так:

Крок 1: Базове передбачення

Почати з простого передбачення (наприклад, середнє значення цільової змінної):

Завантаження...

Крок 2: Обчислити залишки (residuals)

Порахувати, наскільки ми помилились:

Завантаження...

Крок 3: Навчити дерево на залишках

Побудувати дерево Завантаження..., яке передбачає залишки Завантаження....

Крок 4: Оновити передбачення

Завантаження...

Де Завантаження... (learning rate) — темп навчання, тобто крок оновлення (зазвичай 0.01–0.1), який не дає окремому дереву робити занадто різкі зміни.

Крок 5: Повторити

Обчислити нові залишки Завантаження..., навчити Завантаження..., оновити Завантаження..., і так далі.

Фінальна формула ансамблю

Після M ітерацій:

Завантаження...

Аналогія з написанням есе:

  1. Перша версія (дерево 1) — грубий чорновик з багатьма помилками
  2. Редагування (дерево 2) — виправлення найгрубіших помилок
  3. Полірування (дерево 3) — виправлення дрібних недоліків

Кожна ітерація фокусується на тому, що попередні пропустили.

Чому "градієнтний"?

Математично, залишки Завантаження... — це негативний градієнт функції втрат MSE:

Завантаження...

Тобто кожне нове дерево рухається в напрямку негативного градієнта — як у градієнтному спуску!

Детальна математика виходить за межі курсу, але концепція важлива: Gradient Boosting = градієнтний спуск у просторі функцій.

На наступній анімації наочно показано, як ансамбль послідовно навчає слабкі дерева (праворуч угорі) виправляти залишки помилок Завантаження..., і як сумарна крива прогнозу Завантаження... (ліворуч) крок за кроком наближається до справжнього сигналу даних:

Зверніть увагу на ключові інженерні моменти в динаміці навчання:

  1. Базовий прогноз (Завантаження...): Модель починає з простої константи — середнього арифметичного Завантаження.... Похибки Завантаження... величезні та повністю повторюють форму нелінійної функції.
  2. Перші слабкі дерева (Завантаження...): Перші неглибокі дерева підхоплюють найгрубіші піки та западини. Залишки зменшуються вдвічі.
  3. Поступове згладжування (Завантаження...): Оскільки кожне дерево масштабується темпом навчання Завантаження..., модель не робить різких перекосів. Залишки стискаються у вузьку смугу навколо нуля, перетворюючись на звичайний шум.

Алгоритм Gradient Boosting покроково (на прикладі)

Розглянемо конкретний приклад: передбачення ціни 3 квартир.

КвартираПлоща (м²)Реальна ціна (y), $
A5040 000
B8065 000
C12095 000

Ітерація 0: Baseline

Почнемо з найпростішого передбачення — середнього значення:

Завантаження...

Помилки (залишки):

  • Завантаження...
  • Завантаження...
  • Завантаження...

Ітерація 1: Перше дерево

Навчимо дерево Завантаження... передбачати залишки [−26667, −1667, +28333].

Припустимо, дерево навчилось і передбачає:

  • Завантаження...
  • Завантаження...
  • Завантаження...

Оновлюємо передбачення з learning rate Завантаження...:

Завантаження...
  • Завантаження...
  • Завантаження...
  • Завантаження...

Нові помилки:

  • Завантаження... (покращилось з −26667!)
  • Завантаження... (покращилось з −1667!)
  • Завантаження... (покращилось з +28333!)

Ітерація 2: Друге дерево

Навчаємо Завантаження... на нових залишках [−24167, −1467, +25633], оновлюємо Завантаження..., і так далі.

gradient_boosting_manual.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
import numpy as np

# Дані: площа та ціна трьох квартир
area = np.array([50, 80, 120])
price = np.array([40000, 65000, 95000], dtype=float)

# Симуляція 5 ітерацій Gradient Boosting (Градієнтний бустинг)
learning_rate = 0.1
predictions = np.full(len(price), price.mean())  # F_0 = mean ($66,667)

print("Ітерація 0 (Baseline / Базове середнє):")
print(f"  Передбачення (Predictions): {np.round(predictions).astype(int)}")
print(f"  Залишки (Residuals):        {np.round(price - predictions).astype(int)}\n")

for iteration in range(1, 6):
    residuals = price - predictions
    
    # Симулюємо слабке дерево (Weak learner): наближає залишки з точністю 95%
    tree_pred = residuals * 0.95
    
    # Оновлення ансамблю: F_m = F_{m-1} + eta * h_m
    predictions += learning_rate * tree_pred
    
    mae = np.abs(price - predictions).mean()
    print(f"Ітерація {iteration}:")
    print(f"  Передбачення (Predictions): {np.round(predictions).astype(int)}")
    print(f"  Залишки (Residuals):        {np.round(price - predictions).astype(int)}")
    print(f"  MAE (Середня абсолютна помилка): {mae:.0f}\n")
Out[1]:
Ітерація бустингу (Завантаження...)Прогноз Квартира 1 ($40k)Прогноз Квартира 2 ($65k)Прогноз Квартира 3 ($95k)Залишок Завантаження...Залишок Завантаження...Залишок Завантаження...Динаміка похибки (MAE)
0 (Baseline Завантаження...)$66,667$66,667$66,667-$26,667-$1,667+$28,333Базовий прогноз: MAE = $18,889
Ітерація 1$64,133$66,508$69,358-$24,133-$1,508+$25,642Перше дерево коригує залишок: MAE = $17,094
Ітерація 2$61,841$66,365$71,794-$21,841-$1,365+$23,206Наближення до індивідуальних цін: MAE = $15,470
Ітерація 3$59,766$66,235$73,999-$19,766-$1,235+$21,001Помилка квартири 3 впала на 26%: MAE = $14,001
Ітерація 4$57,888$66,118$75,994-$17,888-$1,118+$19,006Послідовне зменшення помилки: MAE = $12,671
Ітерація 5$56,189$66,012$77,800-$16,189-$1,012+$17,200Сумарна помилка скоротилася на 39.3%: MAE = $11,467
Принцип залишків (Residuals) у дії: Кожне наступне дерево навчається не на самих цільових значеннях, а на залишках (помилках прогнозу) попередньої моделі, помножених на темп навчання Завантаження.... У результаті сумарна модель крок за кроком «підтягується» до істинних значень.

Висновок: З кожною ітерацією помилки зменшуються — модель поступово навчається!


Boosting vs Bagging — порівняльна таблиця

ХарактеристикаBagging (Random Forest)Boosting (Gradient Boosting)
Побудова моделейПаралельно, незалежноПослідовно, кожна залежить від попередньої
Навчальні даніBootstrap вибірка (з поверненням)Зважені або залишки попередніх моделей
МетаЗменшення дисперсії (variance)Зменшення зміщення (bias)
Ризик перенавчанняНизький (усереднення згладжує)Високий (фокус на складних випадках)
Швидкість навчанняШвидше (можна паралелізувати)Повільніше (послідовність)
Зрозумілість логікиНизька (багато дерев)Низька (багато дерев)
Якість на практиціВисока, стабільнаДуже висока, але потребує тюнінгу
Глибина деревГлибокі дерева (max_depth=None або 10-20)Неглибокі дерева (max_depth=3-6, "weak learners")
Коли застосовуватиBaseline, малі датасети, стабільністьKaggle, максимальна якість, є час на тюнінг
Правило великого пальця: Якщо потрібна швидка baseline модель — Random Forest. Якщо потрібна максимальна якість — Gradient Boosting (XGBoost).

GradientBoostingRegressor у scikit-learn

Тепер побудуємо градієнтний бустинг на California Housing:

gradient_boosting_sklearn.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
2.5s
import time
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error

# Завантаження датасету California Housing (Вартість житла в Каліфорнії)
data = fetch_california_housing(as_frame=True)
X = data.data
y = data.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Навчання Gradient Boosting (Градієнтний бустинг зі sklearn)
start = time.time()
gb = GradientBoostingRegressor(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=3,
    random_state=42
)
gb.fit(X_train, y_train)
gb_time = time.time() - start

y_pred_gb = gb.predict(X_test)
r2_gb_train = gb.score(X_train, y_train)
r2_gb_test = r2_score(y_test, y_pred_gb)
mae_gb_test = mean_absolute_error(y_test, y_pred_gb)
rmse_gb_test = np.sqrt(mean_squared_error(y_test, y_pred_gb))

print("🌳 Gradient Boosting (Градієнтний бустинг зі sklearn):")
print(f"  R² train (Навчальна вибірка): {r2_gb_train:.4f}")
print(f"  R² test (Тестова вибірка):    {r2_gb_test:.4f}")
print(f"  MAE test (Середня абсолютна помилка):  {mae_gb_test:.4f}")
print(f"  RMSE test (Корінь із середньоквадратичної): {rmse_gb_test:.4f}")
print(f"  Час навчання: {gb_time:.2f}s")

# Порівняємо з Random Forest (Випадковий ліс, 100 дерев)
start = time.time()
rf = RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
rf_time = time.time() - start

y_pred_rf = rf.predict(X_test)
r2_rf_train = rf.score(X_train, y_train)
r2_rf_test = r2_score(y_test, y_pred_rf)
mae_rf_test = mean_absolute_error(y_test, y_pred_rf)
rmse_rf_test = np.sqrt(mean_squared_error(y_test, y_pred_rf))

print("\n🌲 Random Forest (Випадковий ліс для порівняння):")
print(f"  R² train (Навчальна вибірка): {r2_rf_train:.4f}")
print(f"  R² test (Тестова вибірка):    {r2_rf_test:.4f}")
print(f"  MAE test (Середня абсолютна помилка):  {mae_rf_test:.4f}")
print(f"  RMSE test (Корінь із середньоквадратичної): {rmse_rf_test:.4f}")
print(f"  Час навчання: {rf_time:.2f}s")

print("\n📊 Порівняння базових моделей:")
print(f"  Різниця R² (RF vs GB): {(r2_rf_test - r2_gb_test) * 100:+.2f}%")
print(f"  GB повільніший за паралельний RF у: {gb_time / rf_time:.1f}× разів")
Out[1]:
Модель ансамблюНавчальна якість (Train R²)Тестова якість (Test R²)Середня помилка (Test MAE)Квадратична помилка (Test RMSE)Час тренуванняОсобливості навчання
Gradient Boosting (sklearn, 100 дерев, depth=3)0.80490.77560.37160.5422~1.80 сПослідовна побудова неглибоких дерев
Random Forest (sklearn, 100 дерев, без обмежень)0.97350.80440.32780.5063~0.65 сПаралельні незалежні глибокі дерева
Чому базовий Gradient Boosting у scikit-learn поступається Random Forest? За замовчуванням GradientBoostingRegressor у scikit-learn використовує консервативні обмеження (max_depth=3), виконується суто в однопотоковому режимі Python і не підтримує паралелізацію побудови дерева або випадковий відбір ознак (колонок). Щоб усунути ці обмеження та розкрити справжню міць бустингу, було створено спеціалізовану оптимізовану бібліотеку — XGBoost!

Висновок: Базовий Gradient Boosting у scikit-learn працює надійно, але повільно й консервативно. Потрібен інструмент промислового рівня!


XGBoost — eXtreme Gradient Boosting

XGBoost (розробка 2014 року, Tianqi Chen) — це не новий алгоритм, а оптимізована бібліотека для градієнтного бустингу.

Чим XGBoost кращий за sklearn GradientBoostingRegressor?

✅ Регуляризація дерев

sklearn: Лише обмеження глибини (max_depth).

XGBoost: L1 (alpha) та L2 (lambda) регуляризація на ваги листків:

Завантаження...

→ Менше перенавчання.

✅ Паралелізація

sklearn: Послідовна побудова дерев.

XGBoost: Паралелізація на рівні побудови одного дерева (пошук розбиттів).

→ Швидше навчання в 5-10 разів.

✅ Обробка пропусків

sklearn: Вимагає заповнення пропусків (SimpleImputer).

XGBoost: Автоматично "вчиться" напрямку для пропусків під час побудови дерева.

→ Не потрібен preprocessing.

✅ Вбудована крос-валідація

Можливість моніторингу якості на валідаційному наборі під час тренування + early stopping.

→ Автоматична зупинка, коли якість перестала покращуватись.

Встановлення XGBoost

pip install xgboost

Використання XGBoost

xgboost_basic.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
0.2s
import time
from xgboost import XGBRegressor
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error

# Навчання XGBoost (eXtreme Gradient Boosting)
start = time.time()
xgb = XGBRegressor(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=5,
    subsample=0.8,           # випадкова підвибірка 80% рядків (Subsample)
    colsample_bytree=0.8,    # випадкова підвибірка 80% колонок (Colsample by tree)
    random_state=42,
    n_jobs=-1
)
xgb.fit(X_train, y_train)
xgb_time = time.time() - start

y_pred_xgb = xgb.predict(X_test)
r2_xgb_train = xgb.score(X_train, y_train)
r2_xgb_test = r2_score(y_test, y_pred_xgb)
mae_xgb_test = mean_absolute_error(y_test, y_pred_xgb)
rmse_xgb_test = np.sqrt(mean_squared_error(y_test, y_pred_xgb))

print("🚀 XGBoost (eXtreme Gradient Boosting):")
print(f"  R² train (Навчальна вибірка): {r2_xgb_train:.4f}")
print(f"  R² test (Тестова вибірка):    {r2_xgb_test:.4f}")
print(f"  MAE test (Середня абсолютна помилка):  {mae_xgb_test:.4f}")
print(f"  RMSE test (Корінь із середньоквадратичної): {rmse_xgb_test:.4f}")
print(f"  Час навчання: {xgb_time:.2f}s")

print(f"\n📊 Порівняння з іншими ансамблями:")
print(f"  XGBoost швидший за sklearn GB у: {gb_time / xgb_time:.1f}× разів")
print(f"  Приріст R² тестового порівняно з sklearn GB: +{(r2_xgb_test - r2_gb_test) * 100:.2f}%")
print(f"  Приріст R² тестового порівняно з Random Forest: +{(r2_xgb_test - r2_rf_test) * 100:.2f}%")
Out[1]:
Бібліотека / Реалізація ансамблюНавчальна якість (Train R²)Тестова якість (Test R²)Середня помилка (Test MAE)Квадратична помилка (Test RMSE)Час навчанняПрискоренняВідносна якість
🚀 XGBoost (XGBRegressor, 100 дерев)0.86390.81660.33090.4902~0.17 с⚡ ~10× швидше GB🏆 Перевершує і GB, і RF
🌲 sklearn RandomForestRegressor0.97350.80440.32780.5063~0.65 с~4× повільніше XGBСтійкий паралельний бейзлайн
🌳 sklearn GradientBoostingRegressor0.80490.77560.37160.5422~1.80 сБазова швидкістьОднопотокова базова реалізація
Технологічна перевага XGBoost: Завдяки оптимізованій системі розпаралелювання побудови дерев на C++ та випадковому відбору рядків і колонок (subsample=0.8, colsample_bytree=0.8), базовий XGBoost досяг тестового Завантаження..., обійшовши і Random Forest (Завантаження...), і sklearn Gradient Boosting (Завантаження...), виконавши навчання всього за ~0.17 секунди!

Результат: XGBoost водночас у 10 разів швидший і суттєво точніший за стандартну реалізацію бустингу!


Гіперпараметри Gradient Boosting / XGBoost

n_estimators (кількість дерев)

Більше дерев → краща якість, але повільніше навчання та ризик перенавчання.

  • Невеликі датасети: 100-500
  • Великі датасети: 1000+
Важливо комбінувати з learning_rate і early_stopping.

learning_rate (η, темп навчання)

Контролює внесок кожного дерева: Завантаження....

  • Малий η (0.01-0.05) → повільна збіжність, потрібно більше дерев, але краще узагальнення
  • Великий η (0.3-0.5) → швидка збіжність, менше дерев, але ризик перенавчання
Золоте правило:Завантаження...Наприклад:
  • η = 0.1, n_estimators = 100 ≈ η = 0.01, n_estimators = 1000

Подивіться на порівняння швидкості сходження та якості трьох темпів навчання (Завантаження..., Завантаження..., Завантаження...):

Аналіз поведінки моделей на анімації:

  1. Занадто малий темп (Завантаження..., помаранчева лінія): За перші 10–20 дерев модель ледь зрушила з мертвої точки (висока похибка недонавчання). Їй знадобиться 200–500 дерев, аби вийти на якісний результат.
  2. Оптимальний баланс (Завантаження..., зелена лінія): Модель впевнено знаходить форму справжнього сигналу і демонструє найнижчу похибку на тестових даних (Завантаження...).
  3. Занадто агресивний темп (Завантаження..., червона лінія): Швидко стартує, але миттєво починає перенавчатися (перескакує оптимум, вигинається навколо окремих шумів у даних, через що помилка на тесті стрімко зростає).

max_depth (глибина дерев)

GB зазвичай використовує неглибокі дерева (3-6 рівнів) — "weak learners".

  • Відмінність від RF: Random Forest використовує глибокі дерева (10-20+)
  • Глибокі дерева в GB → ризик перенавчання

subsample (підвибірка рядків)

Випадкова частка рядків для навчання кожного дерева (наприклад, 0.8 = 80%).

  • Додає випадковість → зменшує перенавчання
  • Схоже на bootstrap у RF, але без повернення

colsample_bytree (підвибірка колонок)

Випадкова частка ознак для навчання кожного дерева.

XGBoost має також:

  • colsample_bylevel — на кожному рівні дерева
  • colsample_bynode — на кожному вузлі

reg_alpha, reg_lambda (регуляризація, тільки XGBoost)

L1 (alpha) та L2 (lambda) регуляризація на ваги листків.

Працює як Lasso/Ridge для дерев → зменшує перенавчання.


Практика: тюнінг гіперпараметрів

xgboost_tuning.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
35s
from sklearn.model_selection import GridSearchCV
from xgboost import XGBRegressor

# Підбір гіперпараметрів через GridSearchCV (Пошук по сітці)
param_grid = {
    'n_estimators': [100, 200, 300],
    'learning_rate': [0.05, 0.1],
    'max_depth': [3, 5, 7],
    'subsample': [0.8, 1.0]
}

grid_search = GridSearchCV(
    XGBRegressor(random_state=42, n_jobs=-1),
    param_grid,
    cv=3,
    scoring='r2',
    n_jobs=-1,
    verbose=1
)

grid_search.fit(X_train, y_train)

print("🔍 Результати GridSearchCV:")
print(f"  Найкращі параметри: {grid_search.best_params_}")
print(f"  R² CV (Крос-валідаційний бал): {grid_search.best_score_:.4f}")

# Оцінка на тестовій вибірці (Test)
best_xgb = grid_search.best_estimator_
r2_test = best_xgb.score(X_test, y_test)
print(f"  R² test (Фінальний тестовий бал): {r2_test:.4f}")
Out[1]:
Параметр підбору GridSearchCVЗначенняОпис та роль у налаштуванні
Кількість фолдів крос-валідації (cv)33-Fold Cross Validation для об'єктивної оцінки
Кількість досліджених комбінацій363 (дерева) × 2 (темп) × 3 (глибина) × 2 (підвибірка)
Загальна кількість навчених моделей10836 кандидатів × 3 фолди
Найкращий learning_rate (Завантаження...)0.1Оптимальний темп кроку градієнтного спуску
Найкраща глибина max_depth7Оптимальна глибина невеликих дерев (Weak learners) для опису складної залежності
Кількість дерев n_estimators300Компенсує плавне послідовне усунення залишків
Підвибірка спостережень subsample0.880% випадкових рядків на кожне дерево (захист від перенавчання)
Крос-валідаційний бал (Завантаження...)0.8405Середній результат на валідаційних фолдах
Фінальний тестовий бал (Завантаження...)0.8466Відмінна точність узагальнення (MAE = 0.2986)
Підсумок налаштування: Поєднання темпу навчання Завантаження... із 300 деревами глибиною 7 та випадковим відбором 80% рядків забезпечило відчутне зростання якості моделі, піднявши тестовий коефіцієнт детермінації до Завантаження... (похибка MAE впала нижче $29,860).

Early Stopping — автоматична зупинка

early_stopping.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
1.2s
# Early stopping (Рання зупинка): зупинка, якщо якість не покращується
xgb_early = XGBRegressor(
    n_estimators=1000,         # великий початковий ліміт дерев
    learning_rate=0.05,        # плавний низький темп навчання
    max_depth=5,
    early_stopping_rounds=10,  # зупинка після 10 ітерацій без покращення
    random_state=42
)

xgb_early.fit(
    X_train, y_train,
    eval_set=[(X_test, y_test)],  # моніторинг помилки на test
    verbose=False
)

print("🛑 Early Stopping (Рання зупинка):")
print(f"  Зупинилось на ітерації (best_iteration): {xgb_early.best_iteration}")
print(f"  R² test (Тестова якість): {xgb_early.score(X_test, y_test):.4f}")
print(f"\n💡 Замість 1000 дерев алгоритм навчив лише {xgb_early.best_iteration} → економія понад 25% часу!")
Out[1]:
Показник процедури Early StoppingЗначенняПрактичний ефект
Заданий ліміт дерев (n_estimators)1000Максимальний бюджет обчислень
Фактично навчено дерев (зупинка)743Алгоритм виявив плато валідаційної помилки
Критерій зупинки (early_stopping_rounds)1010 ітерацій поспіль без покращення валідаційної якості
Результуючий тестовий коефіцієнт (Завантаження...)0.8448Найвищий досягнутий показник моделі
Збережений ресурс та час25.7%Уникнуто тренування 257 непотрібних дерев
Автоматичний захист від перенавчання: Замість ручного підбору кількості дерев Early Stopping моніторить метрику на валідаційному наборі в реальному часі й перериває тренування, коли подальше додавання дерев лише збільшує перенавчання. Це заощадило понад 25% процесорного часу!

Наочна анімація демонструє роботу механізму Early Stopping: коли Train Loss продовжує падати, а Validation Loss розвертається вгору, алгоритм запускає зворотний відлік терпіння (patience) та вчасно фіксує модель у найкращій точці:

Ключові етапи роботи Early Stopping:

  1. Зниження похибок (Завантаження...): Модель активно навчається, Train Loss та Validation Loss синхронно падають, досягаючи глобального валідаційного мінімуму (зірочка на графіку).
  2. Зона ризику та лічильник Patience (Завантаження...): Train Loss продовжує нескінченно падати до нуля (модель завчає шум), але Validation Loss починає зростати (U-подібна крива). Алгоритм відраховує 10 раундів терпіння без покращень.
  3. Зупинка та відкат до чекпоінта (Завантаження...): Спрацьовує Early Stopping. Процес навчання припиняється, а фінальні ваги автоматично повертаються до стану найкращої ітерації Завантаження... (зелена лінія), запобігаючи спотворенню форми (червоний пунктир).

Фінальна порівняльна таблиця — ВСІ моделі модуля 3

Зведемо результати всіх моделей, які ми навчили на датасеті California Housing протягом модуля 3:

Алгоритм / МодельНавчальна якість (Train R²)Тестова якість (Test R²)Тестова похибка (Test MAE)Час навчанняЗрозумілість логікиСхильність до перенавчання
LinearRegression (бейзлайн)0.610.580.53~0.05 с✅ Висока (ваги коефіцієнтів)❌ Мінімальна
Ridge / Lasso (з поліномами d=2)0.650.640.48~0.30 с⚠️ Посередня⚠️ Помірна
DecisionTree (глибина d=None, перенавчене)1.000.610.46~0.15 с❌ Відсутня (перенавчене)🔴 Критична (Gap 0.39)
DecisionTree (глибина d=5, базове)0.640.610.51~0.05 с✅ Висока (візуальне дерево)❌ Мінімальна
DecisionTree (глибина d=9, оптимальне)0.780.680.44~0.08 с⚠️ Середня (512 листків)⚠️ Помірна (Gap 0.10)
RandomForest (100 дерев, за замовчуванням)0.970.800.33~0.65 с⚠️ Складна (100 дерев)🟢 Низька (усереднення)
GradientBoosting (sklearn, 100 дерев, d=3)0.800.780.37~1.80 с❌ Низька⚠️ Помірна
XGBoost (базовий, 100 дерев, subsample=0.8)0.860.820.33~0.17 с❌ Низька🟢 Низька
XGBoost (оптимізований / Tuned)0.970.850.30~0.60 с❌ Низька⚠️ Контрольована

🏆 Найкраща якість

XGBoost (Tuned) — Test R² = 0.85, MAE = 0.30 (найвища точність на нових даних).

⚡ Найшвидша

LinearRegression, DecisionTree — 0.05 с. Серед потужних ансамблів лідер швидкості — XGBoost (0.17 с).

👁️ Найпростіша та найзрозуміліша

LinearRegression, DecisionTree (d=5) — точні числові коефіцієнти або проста наочна схема правил.

🛡️ Найстабільніша

RandomForest — дає відмінний результат «з коробки» без тривалого тюнінгу гіперпараметрів.

Візуалізація важливості ознак у XGBoost

feature_importance_xgboost.ipynb
Python 3.11 (ipykernel)
Code
In [1]:
0.2s
import pandas as pd
from xgboost import XGBRegressor
from sklearn.ensemble import RandomForestRegressor

# Отримання важливостей ознак для Random Forest та XGBoost
importances_rf = rf.feature_importances_
importances_xgb = xgb.feature_importances_

# Двомовний словник найменувань ознак датасету California Housing
feature_names_en = list(X.columns)
feature_names_ua = [
    'MedInc (Медіанний дохід жителів)',
    'HouseAge (Середній вік будинків)',
    'AveRooms (Середня кількість кімнат)',
    'AveBedrms (Середня кількість спалень)',
    'Population (Чисельність населення)',
    'AveOccup (Середня заселеність житла)',
    'Latitude (Географічна широта)',
    'Longitude (Географічна довгота)'
]

comparison_df = pd.DataFrame({
    'Feature': feature_names_en,
    'Bilingual_Label': feature_names_ua,
    'RF_Importance': importances_rf,
    'XGB_Importance': importances_xgb
}).sort_values('XGB_Importance', ascending=False)

print("📊 Порівняння важливості ознак (Random Forest vs XGBoost):\n")
for _, row in comparison_df.iterrows():
    print(f"  {row['Bilingual_Label']:42s} | RF: {row['RF_Importance']*100:4.1f}% | XGB: {row['XGB_Importance']*100:4.1f}%")
Out[1]:

Ознака датасетуАнглійська назваУкраїнський перекладВажливість RF (Gini Impurity)Важливість XGBoost (Gain)Характер впливу на прогноз
MedIncMedian IncomeМедіанний дохід жителів району ($10k)0.5250 (52.5%)0.5750 (57.5%)Головний фактор вартості (>50% загального впливу)
AveOccupAverage OccupancyСередня заселеність житла (мешканців/дім)0.1384 (13.8%)0.1229 (12.3%)Другий за силою фактор: висока щільність маркує дешевші райони
LongitudeLongitudeГеографічна довгота району0.0886 (8.9%)0.0844 (8.4%)Географічна координата наближення до узбережжя Тихого океану
LatitudeLatitudeГеографічна широта району0.0889 (8.9%)0.0724 (7.2%)Просторовий поділ на дорожчу Південну та Північну Каліфорнію
HouseAgeHouse AgeСередній вік будинків (роки)0.0547 (5.5%)0.0617 (6.2%)Помірний додатковий фактор історичного розвитку кварталу
AveRoomsAverage RoomsСередня кількість кімнат0.0442 (4.4%)0.0468 (4.7%)Помірна важливість через кореляцію із доходом жителів
AveBedrmsAverage BedroomsСередня кількість спалень0.0297 (3.0%)0.0197 (2.0%)Вторинна характеристика планування оселі
PopulationPopulationЧисельність населення району0.0306 (3.1%)0.0170 (1.7%)Локальний показник розміру кварталу, найменш важливий
Чому оцінки важливості узгоджені? Обидва ансамблі базуються на деревах рішень, проте Random Forest оцінює важливість на випадкових підвибірках ознак (через що вага розподіляється рівномірніше), тоді як XGBoost через градієнтний приріст (Gain) сильніше концентрує вагу на найважливішому факторі — MedInc (Медіанний дохід жителів).

Висновок: Обидві моделі чітко підтверджують, що MedInc (Медіанний дохід жителів району) найсильніше визначає вартість нерухомості.


Коли використовувати Gradient Boosting vs Random Forest?

✅ Використовуйте Gradient Boosting (Градієнтний бустинг / XGBoost):

  • Змагання Data Science (Kaggle) — де критично вибороти максимальну точність прогнозу
  • Табличні дані середнього розміру (10k–1M рядків)
  • Є обчислювальний час на підбір гіперпараметрів (GridSearchCV / Optuna)
  • Можливість ретельного контролю перенавчання (наявність окремого валідаційного набору)

✅ Використовуйте Random Forest (Випадковий ліс):

  • Потрібна надійна базова модель (Baseline) без тривалого налаштування
  • Обмежений час на швидкі експерименти та перевірку робочих гіпотез
  • Малий або середній датасет (< 10k рядків), де простий ліс менш схильний до перенавчання
  • Потрібне швидке навчання на всіх ядрах процесора «з коробки» (n_jobs=-1)

✅ Використовуйте лінійні моделі (Ridge / Lasso):

  • Потрібна абсолютна прозорість правил (необхідно чітко пояснити вплив кожного коефіцієнта клієнтам чи регулятору)
  • Дані демонструють наближено лінійні зв'язки
  • Високі вимоги до швидкості отримання відповідей (інференсу): десятки тисяч прогнозів за секунду
  • Жорсткі обмеження пам'яті мікросервісу (лінійна модель потребує лише кілька ваг)

Альтернативи XGBoost — LightGBM та CatBoost

LightGBM (Light Gradient Boosting Machine, Microsoft)

⚡ Екстремальна швидкість

LightGBM суттєво швидший за класичний XGBoost на великих датасетах (1M+ рядків) завдяки гістограмному підходу (Histogram-based algorithm).

🌳 Leaf-wise ріст дерева

Використовує Leaf-wise (розгалуження листка з найбільшою помилкою) замість Level-wise (пошаровий ріст рядок за рядком), що швидше зменшує похибку.

💾 Економія оперативної пам'яті

Групування значень у компактні числові кошики (гістограми) суттєво заощаджує оперативну пам'ять.
from lightgbm import LGBMRegressor

lgbm = LGBMRegressor(n_estimators=100, learning_rate=0.1, max_depth=5, random_state=42)
lgbm.fit(X_train, y_train)

CatBoost (Categorical Boosting, Yandex)

🏷️ Категоріальні ознаки без One-Hot

Автоматичне кодування категоріальних колонок (Target Encoding) прямо під час тренування без роздування пам'яті.

🛡️ Захист від Target Leakage

Вбудована техніка Ordered Boosting запобігає витоку цільової змінної у валідаційні оцінки.

📚 Мінімальний тюнінг

Має чудові параметри за замовчуванням і рідко потребує агресивного підбору гіперпараметрів.
from catboost import CatBoostRegressor

catboost = CatBoostRegressor(iterations=100, learning_rate=0.1, depth=5, verbose=False, random_seed=42)
catboost.fit(X_train, y_train)
У реальних проектах розробники зазвичай порівнюють трьох головних лідерів: XGBoost, LightGBM, CatBoost. Їхні прогнози нерідко об'єднують у фінальний стекінг (Stacking), щоб отримати ще вищу точність.

Резюме

🔗 Gradient Boosting (Градієнтний бустинг)

Ансамблевий метод послідовного навчання, де кожне наступне дерево виправляє помилки (залишки) попередніх дерев.

📉 Residuals (Залишки / помилки)

Помилка передбачення Завантаження.... Кожне дерево є кроком у напрямку негативного градієнта функції втрат.

⚡ Boosting vs Bagging

Bagging (Random Forest): незалежні паралельні глибокі дерева → зниження дисперсії (Variance).
Boosting (XGBoost): взаємозалежні послідовні неглибокі дерева → усунення систематичного зміщення (Bias).

🚀 XGBoost (eXtreme Gradient Boosting)

Швидка C++ бібліотека з регуляризацією ваг листків (L1/L2), випадковим відбором рядків і колонок та ранньою зупинкою (Early Stopping).

🎚️ Learning Rate / η (Темп навчання)

Параметр, який обмежує внесок кожного окремого дерева. Менший темп вимагає більше дерев, але дає плавне навчання та захищає від перенавчання.

🌲 Weak Learners (Слабкі дерева)

Неглибокі дерева рішень (max_depth=3–7), які окремо працюють посередньо, але разом дають дуже точний результат.

Практичні завдання


Наступний модуль: Логістична регресія та задачі класифікації — перехід від прогнозу неперервних величин до ймовірностей та бінарної класифікації.

Copyright © 2026