Градиентный бустинг — это способ собрать одну сильную модель из множества слабых деревьев решений, где каждое следующее дерево учится на ошибках предыдущих. Логика простая: первое дерево даёт грубый прогноз, второе смотрит, где первое ошиблось, и правит именно эти ошибки, третье добивает то, что осталось, и так по кругу. В сумме получается модель, которая на табличных данных — заказы, платежи, события пользователей — почти всегда обыгрывает и логистическую регрессию, и случайный лес. Дальше разберу, как это устроено внутри, чем отличается от леса и как не выстрелить себе в ногу переобучением.
Что такое градиентный бустинг простыми словами?
Возьмём задачу: предсказать, уйдёт ли пользователь в отток. Одно короткое дерево глубиной 3-4 уровня — это очень слабый прогнозист. Оно поймает пару грубых закономерностей вроде «давно не заходил и мало заказов — скорее уйдёт», но в деталях будет ошибаться на половине людей.
Бустинг превращает толпу таких слабаков в одного эксперта. Работает это так. Первое дерево делает прогноз. Мы считаем ошибку — насколько предсказание разошлось с реальностью. Дальше обучаем второе дерево не на исходном таргете, а на этой ошибке (в реальности — на градиенте функции потерь, отсюда и «градиентный»). Второе дерево, по сути, отвечает на вопрос «в какую сторону и насколько подвинуть прогноз первого, чтобы стало лучше». Прибавляем его вклад, снова считаем остаточную ошибку, обучаем третье дерево — и так сотни раз.
Ключевая деталь — вклад каждого дерева домножается на маленький коэффициент, learning_rate (обычно 0.01-0.1). Он не даёт модели за один шаг перепрыгнуть в переобучение и заставляет её приближаться к ответу мелкими аккуратными шажками. Слово «градиент» тут не для красоты: мы буквально спускаемся по функции ошибки в сторону её уменьшения, и каждое новое дерево — один шаг этого спуска.
Чем бустинг отличается от случайного леса?
Оба — ансамбли деревьев, но собирают их принципиально по-разному, и это главное, что стоит понимать.
Случайный лес строит деревья параллельно и независимо. Каждое дерево обучается на своей случайной подвыборке строк и признаков, растёт глубоким, а финальный прогноз — это усреднение (или голосование) всех деревьев. Деревья ничего не знают друг о друге, их можно считать хоть на разных ядрах одновременно. Лес борется с разбросом (variance): по отдельности деревья шумные и переобученные, но их среднее — устойчивое.
Бустинг строит деревья последовательно и зависимо. Дерево номер 200 нельзя обучить, пока не готовы первые 199 — оно правит именно их суммарную ошибку. Деревья тут короткие и слабые, а сила рождается из их сложения. Бустинг в первую очередь снижает смещение (bias): постепенно убирает систематические ошибки, до которых лес не докапывается.
Отсюда практические следствия. Лес прощает почти любые настройки — поставил 300 деревьев и получил приличный результат из коробки. Бустинг требовательнее к гиперпараметрам, легче переобучается, но при аккуратной настройке выжимает больше точности. Лес я беру, когда нужен быстрый крепкий бейзлайн без возни, бустинг — когда борюсь за каждую сотую метрики.
Почему бустинг выигрывает на табличных данных?
Табличка — это разношёрстные признаки: числа вроде среднего чека соседствуют с категориями (город, тип устройства), пропусками и величинами в разных масштабах. Деревьям на всё это плевать в хорошем смысле. Им не нужна нормализация — дереву без разницы, чек в рублях или в копейках, оно всё равно ищет точку разбиения. Они ловят нелинейности и взаимодействия признаков: например, что низкий чек опасен только у новых пользователей, а у старых — норма. Логрегрессии такое взаимодействие нужно прописывать руками, дерево находит его само.
Нейросети сильны там, где есть структура и много данных — картинки, текст, звук. На типичной аналитической таблице в десятки тысяч строк они проигрывают бустингу: им нужно больше данных, аккуратный препроцессинг и куда больше времени на подбор архитектуры. Поэтому для задач отток, LTV, вероятность конверсии, антифрод, скоринг заявок бустинг — дефолтный выбор, а не экзотика.
Если хочется почувствовать, откуда берутся признаки для такой модели, попробуйте собрать их SQL-агрегатами — это половина работы аналитика. Потренироваться можно в SQL-тренажёре, а логику витрин под ML удобно отрабатывать на реальных кейсах.
Как обучить бустинг на Python: минимальный пример
Начну с фичей. Допустим, есть таблицы users, orders, payments, а нам нужна одна строка на пользователя с агрегатами за последний месяц.
-- фичи для модели оттока: собираем поведение за 30 дней на пользователя
SELECT
u.user_id,
COUNT(o.order_id) AS orders_30d,
COALESCE(AVG(o.amount), 0) AS avg_check,
MIN(julianday('now') - julianday(o.created_at)) AS days_since_last_order,
SUM(CASE WHEN o.discount > 0 THEN 1 ELSE 0 END) AS discount_orders
FROM users u
LEFT JOIN orders o
ON o.user_id = u.user_id
AND o.created_at >= date('now', '-30 day')
GROUP BY u.user_id;
Дальше — сама модель. Возьму XGBoost, но код почти идентичен для LightGBM и CatBoost.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from xgboost import XGBClassifier
# df — одна строка на пользователя, таргет churned (1 — ушёл, 0 — остался)
features = ['orders_30d', 'avg_check', 'days_since_last_order',
'sessions_7d', 'support_tickets', 'discount_share']
X = df[features]
y = df['churned']
X_train, X_val, y_train, y_val = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
model = XGBClassifier(
n_estimators=600,
learning_rate=0.03,
max_depth=4,
subsample=0.8,
colsample_bytree=0.8,
eval_metric='auc',
early_stopping_rounds=50,
)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
pred = model.predict_proba(X_val)[:, 1]
print('ROC-AUC:', round(roc_auc_score(y_val, pred), 4))
Здесь всё, что делает бустинг бустингом: много слабых деревьев (n_estimators=600), мелкий шаг (learning_rate=0.03), неглубокие деревья (max_depth=4) и подвыборки строк и колонок (subsample, colsample_bytree), чтобы деревья получались разнообразнее и модель меньше липла к обучающим данным. Прогоняйте такие сниппеты вживую в Python-тренажёре — на своих данных всё запоминается быстрее, чем с листа.
Какие гиперпараметры реально влияют на результат?
Их у бустинга десятки, но реально двигают метрику четыре-пять.
learning_rate и n_estimators — связанная пара. Чем меньше шаг, тем больше деревьев нужно, чтобы дойти до хорошего решения, но и тем оно аккуратнее. Рабочая тактика — поставить небольшой learning rate (0.01-0.05), задать заведомо большое число деревьев и обрубить обучение по early stopping, когда метрика на валидации перестаёт расти. Тогда число деревьев подбирается само.
max_depth — глубина каждого дерева, то есть сложность одного «шага». Для бустинга держат её маленькой: 3-6. Глубокое дерево внутри бустинга — почти гарантированное переобучение, потому что каждое такое дерево слишком жадно запоминает обучающую выборку.
subsample и colsample_bytree — доля строк и признаков, которые видит очередное дерево (0.7-0.9 — хороший диапазон). Это встроенная регуляризация: деревья становятся разнообразнее и модель обобщает лучше.
min_child_weight (и родственные reg_lambda, reg_alpha) отвечают за то, насколько модель готова делать разбиения на маленьких группах. Поднимаете их — модель осторожнее, меньше переобучения, но рискуете недоучиться.
Совет из практики: не кидайтесь сразу в перебор всех параметров сеткой. Сначала зафиксируйте небольшой learning rate с early stopping, потом подберите глубину, потом subsample-параметры. Такой порядок экономит часы. Вопросы про этот тюнинг любят задавать на собеседованиях — часть из них разобрана в подборке вопросов.
Как понять, что модель переобучилась?
Симптом один и очевидный: на обучающей выборке метрика отличная, на валидации и в проде — заметно хуже, и разрыв растёт. Модель выучила шум конкретных данных вместо закономерностей.
У бустинга это подкрадывается тихо, потому что добавление деревьев почти всегда улучшает метрику на трейне. Поэтому смотреть только на трейн бесполезно — нужна отложенная валидация. Самый надёжный инструмент — early stopping: модель на каждой итерации проверяет качество на валидационной выборке и останавливается, когда оно N шагов подряд не улучшается. В примере выше это early_stopping_rounds=50.
Полезно распечатать кривую обучения — как метрика на трейне и валидации меняется с ростом числа деревьев. Пока обе линии идут вверх — учимся. Как только валидация вышла на плато или поползла вниз, а трейн всё лезет вверх — дальше только переобучение. Если разрыв большой с самого начала, лечится уменьшением глубины, подъёмом регуляризации и subsample-параметров.
Что показывает важность признаков и можно ли ей верить?
Feature importance — любимый выхлоп бустинга для аналитика, потому что бизнес всегда спрашивает «а что влияет на отток». Достать её легко:
imp = pd.Series(model.feature_importances_, index=features)
print(imp.sort_values(ascending=False))
Но с этими числами есть нюанс, о котором молчат туториалы. Встроенная важность (по gain — суммарному вкладу признака в улучшение разбиений) смещена в сторону признаков с большим числом уникальных значений. Числовой признак вроде days_since_last_order почти всегда обгонит бинарный флаг просто потому, что по нему больше вариантов разбиения, а не потому, что он важнее по сути. Плюс, если два признака сильно скоррелированы, модель раскидает важность между ними случайно, и один может выглядеть бесполезным, хотя несёт тот же сигнал.
Поэтому для выводов бизнесу я не доверяю только встроенной важности. Беру permutation importance (перемешиваем значения одного признака и смотрим, насколько просела метрика — честно и модель-агностично) или SHAP, если нужно объяснить прогноз для конкретного пользователя. И всегда держу в голове: важность показывает, на что опирается модель, а не причинно-следственную связь. Признак может быть важным, потому что он следствие оттока, а не его причина. Разбор того, где заканчивается корреляция и начинается каузальность, — отдельная большая тема, её удобно смотреть на фоне метрик вроде DAU и удержания.
Какую реализацию выбрать: XGBoost, LightGBM или CatBoost?
Все три — градиентный бустинг, разница в деталях реализации и удобстве.
XGBoost — ветеран и самый предсказуемый вариант. Отлично документирован, стабилен, куча параметров под тонкую настройку. Если сомневаетесь, с чего начать, — начинайте с него, ошибиться сложно.
LightGBM — про скорость. Строит деревья по-другому (растит по листьям, а не по уровням) и заметно быстрее на больших данных. Расплата — при неаккуратных настройках легче переобучается, за глубиной и числом листьев надо следить внимательнее. Мой выбор, когда строк много и время обучения начинает кусаться.
CatBoost — заточен под категориальные признаки. Умеет обрабатывать категории сам, без ручного кодирования, использует специальный приём против переобучения (упорядоченный бустинг) и в среднем даёт крепкий результат прямо на дефолтных настройках. Если в данных много категорий — город, канал привлечения, тип тарифа — часто это самый короткий путь к хорошей модели.
Есть и HistGradientBoostingClassifier из sklearn — быстрый, без внешних зависимостей, хорош для прототипа. На практике разница в метрике между всеми ними обычно меньше, чем разница от качества фичей. Так что не залипайте в выборе библиотеки — залипайте в данных.
С чего начать аналитику
Понимание бустинга на собеседовании и в работе стоит дорого, но само чтение не научит — учит практика. Соберите фичи SQL-запросами, обучите первую модель оттока на своих или учебных данных, покрутите глубину и learning rate, посмотрите на кривую обучения и важность признаков. Начать удобно с Python-тренажёра и подборки задач, а базовые запросы для витрин — в SQL-справочнике и курсе по SQL.
Если хотите пройти путь системно — от чистого SQL до ML-задач с проверкой в браузере, разобранными кейсами и мок-собеседованиями — это всё открывается в Pro. Подписка снимает лимиты на задачи, кейсы и AI-интервью, чтобы готовиться не урывками, а до автоматизма.