2026 г.

Градиентный бустинг для табличных данных

Проект ЦИТадель

Градиентный бустинг по деревьям — один из основных методов обучения с учителем для табличных данных. Он умеет описывать нелинейные зависимости и взаимодействия признаков, не требует масштабирования числовых столбцов и доступен в быстрых промышленных реализациях. Поэтому XGBoost, LightGBM или CatBoost полезно включать в сравнение наряду с простым правилом, линейной моделью и случайным лесом.

Это сильный кандидат, а не заранее назначенный победитель. Результат зависит от определения цели, происхождения данных, схемы разбиения, метрики, вычислительных ограничений и качества настройки. Статья продолжает практическое введение в классическое машинное обучение: сначала разбирается математическая идея, затем различия библиотек, рабочий пример, интерпретация и ограничения метода.

1. Строительный блок: дерево решений

Дерево делит пространство признаков последовательностью условий. Для числового признака условие обычно имеет вид «значение меньше порога?». В каждом листе хранится число: прогноз регрессии, оценка класса или поправка к текущему прогнозу ансамбля. Обучение жадное: алгоритм выбирает разбиение, которое сильнее всего уменьшает заданный критерий на текущем шаге.

Пороговые разбиения не меняются при линейном масштабировании числового признака, поэтому стандартизация для деревьев обычно не нужна. Дерево может находить нелинейности и взаимодействия без их явного перечисления, но его прогноз остаётся кусочно-постоянным. Мелкое дерево способно недообучиться, глубокое — запомнить шум, а небольшое изменение выборки иногда заметно меняет структуру.

Поддержка пропусков и категориальных столбцов относится не к абстрактному дереву, а к конкретной реализации. Одни библиотеки учат направление для пропущенного числового значения, другие требуют предварительного заполнения. Категории могут быть закодированы one-hot-признаками, разбиты на группы или преобразованы в статистики. Поэтому фраза «деревья сами работают с любыми таблицами» слишком обща: контракт входных данных нужно читать в документации выбранной библиотеки.

2. Градиентный шаг в пространстве функций

Пусть Fm−1(x) — ансамбль после m−1 шагов, а ℓ(y, z) — функция потерь. Для каждого обучающего объекта вычисляется псевдоостаток — антиградиент потерь по текущему предсказанию:

rim = − ∂ℓ(yi, z) / ∂z   при   z = Fm−1(xi).

Новое дерево hm(x) приближает эти значения. Затем ансамбль обновляется:

Fm(x) = Fm−1(x) + ν · ρm hm(x),

где ν — скорость обучения, а ρm или значения в листьях выбирают величину шага. В конкретной реализации множитель ρm может быть поглощён значениями листьев.

Для квадратичной потери ℓ(y, z) = (y − z)2/2 псевдоостаток равен обычному остатку y − F(x). В бинарной классификации с логистической потерей F(x) обычно является необработанной оценкой, или логитом, а вероятность получается преобразованием

p(x) = 1 / (1 + e−F(x)).

В этом случае антиградиент связан с разностью между меткой и текущей вероятностью. Современные реализации могут использовать не только первые, но и вторые производные потерь для выбора разбиений и значений листьев. Общая схема остаётся аддитивной: каждое дерево вносит небольшую поправку к уже построенной функции.

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

Число деревьев нельзя рассматривать отдельно от скорости обучения. Меньший шаг обычно требует больше итераций, но произведение этих двух чисел не является инвариантом: при другой скорости меняются траектория обучения и обобщающая способность. Значение нужно подбирать по валидации, а не по универсальному диапазону.

Основные группы параметров таковы:

  • число итераций и скорость обучения задают длину и размер шагов аддитивного процесса;
  • глубина, число листьев и минимум объектов в листе ограничивают сложность отдельного дерева;
  • подвыборки строк и признаков добавляют случайность и иногда сокращают время обучения;
  • штрафы на значения листьев и минимальный выигрыш разбиения подавляют слишком слабые поправки;
  • функция потерь, метрика и ограничения должны соответствовать задаче, а не выбираться по привычке.

Ранняя остановка следит за заранее выбранной метрикой на отдельной валидационной выборке и прекращает добавлять деревья после заданного числа итераций без улучшения. Библиотека сохраняет номер лучшей итерации, но это не превращает валидацию в тест: она участвует в выборе размера модели. Если на той же выборке перебрать много признаков и конфигураций, можно переобучиться уже на неё. Для устойчивого сравнения используют подходящую кросс-валидацию или дополнительное разбиение, а финальный тест оставляют закрытым.

Веса классов полезны, когда функция потерь должна по-разному учитывать ошибки, но они не «исправляют» несбалансированную проверочную выборку и могут изменить калибровку вероятностей. Сначала выбирают метрику и рабочий порог, затем проверяют, действительно ли взвешивание улучшает конечное решение.

4. XGBoost, LightGBM и CatBoost

Все три библиотеки строят градиентно усиленные деревья, поддерживают CPU и GPU и решают задачи классификации, регрессии и ранжирования. Они различаются алгоритмом построения дерева, обработкой категорий и пропусков, параметрами, форматами модели и эксплуатационными характеристиками.

XGBoost. Опубликованная в 2016 году система объединила регуляризованную целевую функцию, обработку разреженных данных и ряд инженерных оптимизаций. Это зрелая библиотека с несколькими алгоритмами построения дерева и стабильными интерфейсами. Актуальные версии умеют работать с категориальными столбцами: в интерфейсе scikit-learn для pandas их помечают типом category и включают enable_categorical. Для сохранения такой модели документация требует JSON или UBJSON, иначе сведения о категориях потеряются.

LightGBM. Система использует гистограммы признаков и растит дерево по лучшему листу, а не обязательно целыми уровнями. В исходной работе также предложены Gradient-based One-Side Sampling (GOSS) и Exclusive Feature Bundling (EFB) для ускорения на больших и разреженных данных. Рост по листьям может быстрее уменьшать ошибку при заданном числе листьев, но на малой выборке требует контроля num_leaves, min_data_in_leaf и при необходимости глубины. Категории передаются как pandas category или целочисленные коды с явным указанием категориальных признаков.

CatBoost. Ключевые идеи работы 2018 года — упорядоченные целевые статистики для категорий и ordered boosting. Для обучающего объекта целевая статистика считается по предшествующим объектам случайной перестановки, не используя его собственную метку. Это уменьшает специальный вид смещения, но случайная перестановка не заменяет временное разбиение данных. По умолчанию CatBoost использует симметричные деревья и принимает названия категориальных столбцов. Числовые NaN обрабатываются внутри алгоритма; пропуск в категории безопаснее заранее представить отдельной строковой категорией.

Нативная работа с категориями теперь есть у всех трёх библиотек, но семантика и требования различаются. CatBoost удобен для быстрого старта с множеством строковых категорий; LightGBM часто рассматривают при жёстких ограничениях на время и память; XGBoost ценят за зрелую экосистему и управляемость. Это ориентиры для эксперимента, а не правило выбора. Сравнение проводят на одинаковых разбиениях, с одинаковой метрикой и сопоставимым бюджетом настройки на целевом оборудовании.

5. Практикум: CatBoost с закрытым тестом

Продолжим пример прогноза оттока. Предположим, что основной метрикой выбора заранее назначен ROC-AUC; для редкого положительного класса дополнительно выводится average precision (AP). Временные или повторные записи одного клиента потребовали бы временного или группового разбиения вместо показанного стратифицированного.

# pip install catboost pandas scikit-learn
import pandas as pd
from catboost import CatBoostClassifier, Pool
from sklearn.metrics import average_precision_score, roc_auc_score
from sklearn.model_selection import train_test_split

df = pd.read_csv("churn.csv")
y = df["churned"]
X = df.drop(columns=["churned", "customer_id"]).copy()

# В примере всё нечисловое считаем категорией. В рабочей системе
# схему признаков и обработку дат следует задавать явно.
cat_features = X.select_dtypes(exclude="number").columns.tolist()
for column in cat_features:
    X[column] = (
        X[column]
        .astype("string")
        .fillna("__MISSING__")
        .astype("object")
    )

# Тест отделяем до настройки модели.
X_dev, X_test, y_dev, y_test = train_test_split(
    X,
    y,
    test_size=0.2,
    stratify=y,
    random_state=42,
)
X_train, X_valid, y_train, y_valid = train_test_split(
    X_dev,
    y_dev,
    test_size=0.2,
    stratify=y_dev,
    random_state=42,
)

train_pool = Pool(X_train, y_train, cat_features=cat_features)
valid_pool = Pool(X_valid, y_valid, cat_features=cat_features)

common_params = {
    "learning_rate": 0.05,
    "depth": 6,
    "loss_function": "Logloss",
    "eval_metric": "AUC",
    "random_seed": 42,
    "allow_writing_files": False,
}
pilot = CatBoostClassifier(iterations=2000, **common_params)
pilot.fit(
    train_pool,
    eval_set=valid_pool,
    early_stopping_rounds=100,
    use_best_model=True,
    verbose=200,
)

# Число деревьев выбрано по validation. Теперь используем весь dev.
best_trees = pilot.tree_count_
fit_pool = Pool(X_dev, y_dev, cat_features=cat_features)
test_pool = Pool(X_test, y_test, cat_features=cat_features)

model = CatBoostClassifier(iterations=best_trees, **common_params)
model.fit(fit_pool, verbose=False)

# К закрытому тесту обращаемся только после выбора и обучения модели.
proba = model.predict_proba(test_pool)[:, 1]
print("trees:", best_trees)
print("test ROC-AUC:", round(roc_auc_score(y_test, proba), 3))
print("test AP:", round(average_precision_score(y_test, proba), 3))

# Встроенная важность -- средство диагностики, не причинный эффект.
importance = model.get_feature_importance(
    type="PredictionValuesChange"
)
for name, value in sorted(
    zip(X.columns, importance),
    key=lambda item: -item[1],
)[:10]:
    print(f"{value:6.2f}  {name}")

Первая модель нужна только для выбора числа деревьев. Благодаря use_best_model=True её tree_count_ соответствует лучшей итерации на валидации. Затем новая модель с этим числом деревьев обучается на всей рабочей части X_dev, включая прежнюю валидацию, и один раз оценивается на тесте.

Числовые пропуски CatBoost обрабатывает сам. Категориальные пропуски в примере превращаются в строку __MISSING__, поскольку значения категориального признака должны быть строками или целыми числами. Параметр allow_writing_files=False не позволяет учебному запуску создать служебный каталог catboost_info, а random_seed делает случайные этапы воспроизводимыми.

Этот листинг не доказывает превосходство CatBoost. Для такого вывода на тех же разбиениях нужно обучить базовое правило, логистическую регрессию, лес и другие реализации бустинга, предоставить им сопоставимый бюджет настройки и измерить не только качество, но и время, память, размер модели и задержку предсказания.

6. Важность признаков и SHAP

Название «важность признака» скрывает разные вопросы. Показанный в коде CatBoost PredictionValuesChange оценивает, насколько в среднем меняется значение формулы модели при разбиениях по признаку; значения нормируются до суммы 100. Такая величина не показывает направление эффекта и не говорит, насколько упадёт качество на новых данных.

Перестановочная важность отвечает на другой вопрос: как меняется метрика на контрольной выборке, если разрушить связь одного столбца с целью. При сильно коррелированных признаках один столбец может заменить другой, поэтому низкая перестановочная важность не всегда означает бесполезность. Оба метода описывают поведение обученной модели, а не устройство реального мира.

SHAP раскладывает отдельное значение модели на базовую величину и аддитивные вклады:

F(x) = φ0 + Σj φj(x).

Для бинарного CatBoost с Logloss это по умолчанию разложение не вероятности, а необработанной оценки F(x). Сумму сначала преобразуют сигмоидой; отдельный SHAP-вклад нельзя буквально назвать прибавкой к вероятности. Результат также зависит от определения отсутствующего признака и используемого фонового распределения. SHAP полезен для отладки, поиска подозрительных зависимостей и описания решения модели, но не устанавливает причинность и сам по себе не подтверждает корректность решения.

7. Ограничения и выбор метода

Экстраполяция. Сумма обычных деревьев остаётся кусочно-постоянной и не продолжает линейный или экспоненциальный тренд за диапазон обучающих значений. Для прогноза во времени полезно сравнить бустинг со структурной моделью тренда и проверить результат на будущих интервалах.

Неструктурированный вход. Пиксели, звук и длинный сырой текст обычно требуют представления, которое выучивает нейросеть. Бустинг может работать поверх извлечённых признаков или эмбеддингов, но качество такой связки зависит от способа получения представления и отсутствия утечки (см. главу об эмбеддингах).

Размер и задержка. Тысячи деревьев могут занимать больше памяти и работать медленнее линейной модели. Скорость обучения и предсказания зависит от числа строк и столбцов, разреженности, глубины, оборудования и формата экспорта; её нужно измерять в целевой среде, а не переносить из чужого benchmark.

Вероятности и сдвиг данных. Высокий ROC-AUC не гарантирует калиброванные вероятности. После взвешивания классов, смены доли событий или переезда в другую популяцию калибровку и рабочий порог проверяют заново. Научившись направлять пропуски в подходящие листья, модель может скрыть сбой источника данных, поэтому долю пропусков всё равно следует мониторить.

Утечка и причинность. Высокая выразительность помогает бустингу использовать даже слабый след будущего события. Корректное разбиение, временной срез и происхождение признаков важнее выбора библиотеки. Ни важность, ни SHAP не превращают предсказательную зависимость в причинный эффект.

8. Воспроизводимый рабочий порядок

  1. Определить прикладное решение, целевую переменную, горизонт и основную метрику.
  2. Зафиксировать временное, групповое или стратифицированное разбиение и закрыть тестовую выборку.
  3. Обучить простое правило, линейную модель и лес как базовые варианты.
  4. Задать схему типов, правила пропусков и неизвестных категорий до сравнения библиотек.
  5. Для каждого кандидата выполнить раннюю остановку внутри одной и той же схемы валидации; не подбирать параметры по тесту.
  6. Сравнить качество и разброс, время обучения, пиковую память, размер артефакта и задержку на целевом оборудовании.
  7. Проверить калибровку и выбрать порог на валидационных прогнозах, затем один раз оценить готовый процесс на тесте.
  8. Сохранить версии библиотеки, кода признаков, схемы категорий, параметров и формата сериализации вместе с моделью.

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

9. Практические задания

  1. Для четырёх чисел y = {1, 2, 4, 5} возьмите начальный постоянный прогноз F0 = 3 и квадратичную потерю. Вычислите псевдоостатки и новый прогноз после дерева, которое идеально их воспроизвело, при ν = 0,1.
  2. Запустите листинг на бинарном наборе данных и сравните CatBoost с логистической регрессией и случайным лесом на одних разбиениях. Запишите ROC-AUC, AP, время обучения и размер сохранённой модели.
  3. Для скоростей 0,2; 0,05 и 0,01 сохраните лучшую итерацию и метрику. Объясните, почему одинаковое произведение скорости на число деревьев не гарантирует одинаковую модель.
  4. Добавьте признак, вычисленный после целевого события, и измерьте рост метрики. Затем удалите его и сформулируйте проверку времени доступности для каждого оставшегося признака.
  5. Сравните PredictionValuesChange, перестановочную важность и SHAP на данных с двумя коррелированными признаками. Для одного объекта проверьте, что базовое значение и SHAP-вклады складываются в raw-прогноз, а не непосредственно в вероятность.

Литература и документация

  1. J. H. Friedman. Greedy Function Approximation: A Gradient Boosting Machine, 2001.
  2. T. Chen, C. Guestrin. XGBoost: A Scalable Tree Boosting System, 2016.
  3. G. Ke et al. LightGBM: A Highly Efficient Gradient Boosting Decision Tree, 2017.
  4. L. Prokhorenkova et al. CatBoost: Unbiased Boosting with Categorical Features, 2018.
  5. Categorical Data — актуальная документация XGBoost.
  6. LightGBM Features и Advanced Topics — построение деревьев, пропуски и категории.
  7. Transforming Categorical Features to Numerical Features, Missing Values Processing и CatBoostClassifier — официальная документация CatBoost.
  8. Feature Importance и ShapValues в CatBoost.
  9. S. M. Lundberg, S.-I. Lee. A Unified Approach to Interpreting Model Predictions, 2017.
404 Not Found

404 Not Found


nginx/1.24.0 (Ubuntu)

Связь с редакцией