- BrainTools - https://www.braintools.ru -
22 сентября 2025г. вышла версия 3.10 [1] XGBoost. Основной фишкой новой версии стал “категориальный ре-кодер(categorical re-coder [2])”. Он сохраняет категории в модели и так же может перекодировать данные на этапе инференса. И целью этой статьи является сравнить возможности новой версии XGBoost c лидером обработки категориальных данных, CatBoost.
Кто обучает на сырых данных?
Что такое этот категориальный ре-кодер?
Можно ли обучить модель полностью на сырых данных и получить приемлемый результат?
Стал ли XGBoost альтернативой CatBoost для работы с категориальными данными?
Так кто же обучает на грязных данных?
Банальный вопрос, банальный ответ. Все и никто одновременно. Это на самом деле деликатный вопрос. Каждый из нас не хочет думать о том, что когда-то написал “говнокод“, нарушил pep8 и наконец обучил модель не взглянув на данные.
Это плохо?
Безусловно.
Это экономит время?
В краткосрочной перспективе – безусловно. В долгосрочной – почти никогда
Надо от этого избавляться?
Конечно, иначе вы не поднимите свою F1-метрику на 0.0001, бизнес потеряет деньги, а вы работу.
Давайте смотреть правде в глаза: мир не идеален, а дедлайны горят. Стремление бросить сырые данные в модель и получить хоть какой-то базовый результат – это не признак лени, а часто прагматичная необходимость быстро оценить потенциал фичей или запустить MVP. Проблема в том, что до недавнего времени для данных с категориями этот путь в XGBoost был усыпан сложностями в кодировке. Вы не могли просто взять и скормить модели столбцы с городами или названиями товаров. Вам приходилось вручную заниматься кодированием – превращать текст в числа с помощью One-Hot Encoding или Ordinal Encoder. One-Hot раздувал размерность до космических масштабов, а Ordinal Encoder заставлял модель думать, что «Великий Новгород» > «Нижний Новгород» просто потому, что вы присвоили им числа 1 и 2, что бессмысленно для деревьев. Это был тот самый ручной труд. И самое главное – вы должны были тщательно следить, чтобы одна и та же схема кодирования применялась к обучающим и тестовым данным, иначе модель уверенно выдавала бы полную ерунду.
Новый категориальный ре-кодер в XGBoost – это и есть тот самый инструмент, который должен избавить нас от рутины обработки данных. Он не поощряет полное незнание своих данных, но он снимает с нас огромный пласт рутины и потенциальных ошибок, позволяя сосредоточиться на действительно важных вещах – feature engineering более высокого уровня и интерпретации результатов.
Итог нашего рассуждения описывает картинка ниже.
Категориальный ре-кодер в XGBoost – это механизм, который позволяет автоматически обрабатывать категориальные признаки в данных. В отличие от предыдущих версий, где XGBoost не сохранял информацию о кодировке категорий, ре-кодер теперь запоминает как закодированы данные. Он поддерживает категории в виде строк и требует строгой согласованности типов между обучающими и тестовыми данными. Архитектура ре-кодера состоит из двух основных компонентов: система хранения категорий(cat_container.cc [3], cat_container.cu [4], cat_container.cuh [5], cat_container.h [6]) и механизм перекодирования (encoder [7])
В R поддержка пока отсутствует, и кодировку нужно делать вручную. Более полную информацию по внедрению функционала работы с категориальными данными можете получить вот здесь [8]
Основная проблема в предыдущих версиях заключалась в несогласованности данных: пользователь должен был самостоятельно обеспечивать одинаковую предобработку (например, с помощью OrdinalEncoder из scikit-learn) для обучения [9] и инференса. Без этого модель могла неправильно интерпретировать категории. Ре-кодер решает эту проблему, делая XGBoost более удобным для работы с категориальными данными, особенно в больших пайплайнах машинного обучения. Он снижает время разработки пайплайна, риск ошибок, а также приближает библиотеку к лидеру по работе с категориальными данными CatBoost, где такая обработка встроена изначально.
XGBoost извлекает уникальные категории из входных данных и сохраняет их в контейнере. Во время инференса он автоматически перекодирует новые данные на основе сохранённой схемы, обеспечивая правильность кодирования без копирования данных.
На первый взгляд извлечение категорий может быть элементарным. Вызываем метод unique() и получаем уникальные значения нужной нам категории, но есть одно ‘НО’. В контексте машинного обучения этот процесс требует строгой детерминированности. Порядок возвращаемых уникальных значений может различаться в зависимости от встречаемости. Для XGBoost это не допустимо, поскольку алгоритм полагается на детерминированное кодирование на всех этапах(обучение, валидация, предсказание).
Детерминированные подхода XGBoost состоит из 4 шагов при обучении:
Извлечение уникальных значений из обучающей выборки c сохранением исходного порядка;
Создание отсортированных индексов с помощью алгоритма argsort(C++) – исходный порядок категорий не меняется, но создаются дополнительные индексы для эффективного бинарного поиска;
Фильтрация пропущенных значений и валидация;
Сохранение полного набора категорий внутри модели в специальном контейнере.
Такой подход обеспечивает полную воспроизводимость результатов и корректную работу модели на новых данных, даже если они приходят из разных источников или в измененном порядке.
В идеальном мире все категории в новых данных были бы знакомы модели. В реальности же ситуация, когда модель встречает категории, которые не видела при обучении, является нормой. В XGBoost используется строгая валидация категорий, чтобы избежать таких ситуаций. Она реализуется в виде следующих шагов:
Шаг 1: Бинарный поиск категории
Во время ре-кодинга для каждой категории из новых данных выполняется бинарный поиск в отсортированном списке тренировочных категорий.
Шаг 2: Ошибка [10] при неизвестных категориях
Если категория не найдена в тренировочном наборе, то мы получим исключение с сообщением об ошибке.
Думаю, вам уже могла прийти мысль: “Что, если мы хотим сохранить модель? Нам нужно будет сохранять помимо весов еще и список категориальных значений?”
Специальный контейнер CatContainer, который хранит всю информацию о категориальных признаках, является неотъемлемой частью модели, наряду с весами деревьев и параметрами бустинга. Когда вы вызываете стандартные методы save_model() или load_model(), XGBoost автоматически сохраняет или загружает этот контейнер вместе с остальными данными. Это больше не требует от вас никаких дополнительных действий – вы работаете с моделью как с единым целым, а внутри нее уже хранится вся схема кодирования. По сути, модель теперь стала умнее – она помнит не только закономерности в данных, но и то, как сами данные должны выглядеть.
Чтобы ответить на этот вопрос объективно, а не расскладывать карты таро, проведём сравнительный эксперимент по обучению моделей XGBoost и CatBoost на различных сырых данных и проанализируем полученные результаты.
Импортируем необходимые библиотеки и функции. В этой статье я не буду детально расписывать работу каждой функции, так как считаю их интуитивно понятными в контексте пайплайна.
# Базовые библиотеки
import pandas as pd
from tqdm.notebook import tqdm
# Функции обучения моделей
from models.catboost import test_catboost
from models.xgboost import test_xgb_manual_encoding, test_xgb_with_recoder
# Вспомогательные утилиты
from models.utils import (
calculation_metrics,
calculation_info_data,
load_data,
count_unique_data
)
Теперь определим параметры моделей для честного сравнения. Мы специально сфокусируемся на сравнении встроенных механизмов обработки категориальных признаков, чтобы выявить сильные стороны каждой реализации при работе с сырыми данными.
xgb_params = {
'n_estimators': 100, # Количество деревьев
'max_depth': 6, # Максимальная глубина деревьев
'learning_rate': 0.1, # Скорость обучения
'subsample': 0.8, # Доля данных для каждого дерева
'colsample_bytree': 0.8, # Доля признаков для каждого дерева
'reg_lambda': 1.0, # L2 регуляризация
'random_state': 42, # Seed для воспроизводимости
'n_jobs': -1, # Использование всех ядер
# Специфичные для категорий:
'tree_method': 'hist', # Метод для построения деревьев
'max_cat_to_onehot': 5 # Порог для one-hot кодирования
}
catboost_params = {
'iterations': 100, # Количество итераций (аналог n_estimators)
'depth': 6, # Максимальная глубина деревьев
'learning_rate': 0.1, # Скорость обучения
'subsample': 0.8, # Доля данных для каждого дерева
'colsample_bylevel': 0.8, # Доля признаков для каждого дерева
'l2_leaf_reg': 1.0, # L2 регуляризация
'random_state': 42, # Seed для воспроизводимости
'thread_count': -1, # Использование всех ядер
# Специфичные для категорий
'one_hot_max_size': 5, # Порог one-hot кодирования категорий
# Выкл отображения в консоли
'verbose': False # Отключение вывода обучения
}
Создадим конфигурационный список testing_models для систематического тестирования различных подходов к работе с категориальными данными:
testing_models = [
{
"name": "catBoost",
"model_func": test_catboost,
"params": catboost_params,
},
{
"name": "xgb_manual_encoder",
"model_func": test_xgb_manual_encoding,
"params": xgb_params
},
{
"name": "xgb_with_recoder",
"model_func": test_xgb_with_recoder,
"params": xgb_params
},
]
Обратите внимание [11]: мы создали два варианта XGBoost для сравнения разных стратегий работы с категориальными признаками.
xgb_manual_encoder: Традиционный подход с предобработкой через OneHotEncoder и OrdinalEncoder из sklearn
xgb_with_recoder: Подход с использованием встроенного категориального ре-кодера XGBoost
Такое разделение позволит нам оценить, насколько автоматизированное решение приближается по качеству к ручной настройке.
Возьмём 7 разнообразных датасетов с Kaggle, охватывающих как задачи классификации, так и регрессии. Это позволит оценить модели в различных условиях и на данных разного масштаба:
Список используемых датасетов:
Life Style Data [14]
All Computer Prices [15]
Каждый датасет описывается словарём с единой структурой:
dataset = {
"name": "my_dataset",
"url": "https://www.kaggle.com/competitions/my_dataset",
"file": "data\classification_bank_dataset.csv", # локальный путь
"task": "clf", # clf - задча классификации / reg - задача регрессии
"target": "target_name", # Целевая переменная
"trash_columns": ['id'] # Фичи, которые явно являются мусорными
}
Словари датасетов хранятся в списке info_datasets
Запускаем сравнительное тестирование всех моделей на всех датасетах:
results = []
pbar_datasets = tqdm(
info_datasets,
unit="dataset",
colour="#037503",
leave=True
)
for info_dataset in pbar_datasets:
pbar_datasets.set_description(f"Dataset '{info_dataset['name']}'")
X_train, X_test, y_train, y_test = load_data(info_dataset)
info_columns_dataset = calculation_info_data(X_train)
pbar_models = tqdm(
testing_models,
unit="model",
colour="#09ff00",
leave=False
)
for test_model in pbar_models:
pbar_models.set_description(f"Processing '{test_model['name']}'")
# Вызываем функцию модели
model, y_pred, train_time = test_model["model_func"](
X_train.copy(),
X_test.copy(),
y_train.copy(),
params=test_model["params"],
task_type=info_dataset["task"]
)
metrics = calculation_metrics(y_pred, y_test, info_dataset["task"])
results.append({
"model_name": test_model["name"],
"dataset_name": info_dataset["name"],
"train_time": train_time,
**info_columns_dataset,
**metrics
})
pbar_models.close()
pbar_datasets.close()
Вполне простая логика [19]:
Загружаем датасет – разделяем на train/test и автоматически кодируем целевую переменную для классификации
Получаем информацию – анализируем состав признаков (количественные/категориальные)
Обучаем модель – каждая модель получает идентичные данные и параметры для честного сравнения
Предсказываем – получаем предсказания на тестовой выборке
Рассчитываем метрики – accuracy/F1 для классификации, R²/RMSE для регрессии
Повторяем [20] цикл – пока не пройдём по каждому датасету каждой моделью
Такой подход гарантирует, что все модели сравниваются в абсолютно одинаковых условиях.
Результаты эксперимента представлены на таблицах ниже.
Задача регрессии
|
Датасет |
Модель |
Время (с) |
R² |
RMSE |
MAE |
|---|---|---|---|---|---|
|
Flight Delay(33 колонки, 5.7M строк)72.7% числовых |
CatBoost |
118.36 |
0.9784 |
4.34 |
0.96 |
|
|
XGBoost Manual |
57.05 |
0.9298 |
7.69 |
0.76 |
|
|
XGBoost Auto |
42.97 |
0.9144 |
8.42 |
0.98 |
|
Road Accident Risk(12 колонок, 414K строк)33.3% числовых |
CatBoost |
4.13 |
0.8680 |
0.06 |
0.04 |
|
|
XGBoost Manual |
2.88 |
0.8701 |
0.06 |
0.04 |
|
|
XGBoost Auto |
2.56 |
0.8701 |
0.06 |
0.04 |
|
All Computer Prices(32 колонки, 80K строк)59.4% числовых |
CatBoost |
4.12 |
0.8615 |
197.99 |
138.67 |
|
|
XGBoost Manual |
1.76 |
0.8531 |
205.03 |
143.60 |
|
|
XGBoost Auto |
34.42 |
0.8203 |
216.52 |
149.35 |
|
House Prices(79 колонок, 1.2K строк)45.6% числовых |
CatBoost |
2.64 |
0.8654 |
28658.26 |
17025.43 |
|
|
XGBoost Manual |
0.50 |
0.8984 |
25020.42 |
15807.90 |
|
|
XGBoost Auto |
0.49 |
0.8916 |
25440.36 |
15584.49 |
Задача классификации
|
Датасет |
Модель |
Время (с) |
Accuracy |
F1-score |
Recall |
Precision |
|---|---|---|---|---|---|---|
|
Bank Dataset(16 колонок, 600K строк)43.8% числовых |
CatBoost |
12.08 |
0.9275 |
0.6690 |
0.6040 |
0.7496 |
|
|
XGBoost Manual |
5.58 |
0.9318 |
0.6961 |
0.6434 |
0.7580 |
|
|
XGBoost Auto |
4.61 |
0.9326 |
0.7007 |
0.6502 |
0.7599 |
|
Mental Health(16 колонок, 209K строк)100% категориальных |
CatBoost |
6.08 |
0.9256 |
0.9596 |
0.9996 |
0.9227 |
|
|
XGBoost Manual |
2.75 |
0.9618 |
0.9789 |
0.9999 |
0.9586 |
|
|
XGBoost Auto |
3.10 |
0.9551 |
0.9753 |
0.9999 |
0.9518 |
|
Life Style Data(53 колонки, 16K строк)73.6% числовых |
CatBoost |
4.14 |
0.4868 |
0.4957 |
0.5148 |
0.4780 |
|
|
XGBoost Manual |
0.69 |
0.4985 |
0.4980 |
0.5077 |
0.4887 |
|
|
XGBoost Auto |
0.98 |
0.4963 |
0.4954 |
0.5046 |
0.4865 |
Результат полученный после обучения наших моделей мы разберём в заключительной главе статьи.
От общего к частному – после масштабного эксперимента с 7 датасетами погрузимся в разбор задачи прогнозирования арестов по данным о преступлениях. Давайте разберём датасет Crime & Consequence: A Metropolitan Dataset [21].
Посмотрев на количество уникальных значений для каждой фичи, даже опытный
data scientist невольно ахнет. Возникает закономерный вопрос: выживут ли
наши модели в этих условиях без предварительной обработки?
|
Фича |
Уникальных значений |
Тип данных |
|---|---|---|
|
Date |
3 022 335 |
object |
|
Location |
839 274 |
object |
|
Latitude |
838 172 |
float64 |
|
Longitude |
837 646 |
float64 |
Три миллиона уникальных дат – это уже не фича, это хронологический хаос.
Вы думаете, XGBoost справится с такой нагрузкой?
Немножко вам времени на размышление…
Ещё немного…
К сожалению вы правы, он не смог справиться. Наша модель застряла на этапе кодирования фич. На изображении можете увидеть этот процесс
И получился вот такой результат, в котором отсутсвует XGBoost с использование ре-кодера.
|
Датасет |
Модель |
Время (с) |
Accuracy |
F1-score |
Recall |
Precision |
|---|---|---|---|---|---|---|
|
Crime & Consequence(19 колонок, 6.7M строк)47.4% числовых |
CatBoost |
177.37 |
0.8924 |
0.7493 |
0.6367 |
0.9104 |
|
|
XGBoost Manual |
132.41 |
0.8910 |
0.7455 |
0.6321 |
0.9084 |
|
|
XGBoost Auto (НЕ обучилась) |
– |
– |
– |
– |
– |
Итог обучения показал, что на датасете Crime & Consequence: A Metropolitan Dataset с высоким количеством уникальных значений в категориальных признаках CatBoost демонстрирует не просто стабильность, а абсолютное превосходство. Пока XGBoost с автоматическим кодированием безнадёжно зависал, CatBoost
не только успешно обучился, но и показал лучшие метрики.
Проведя эксперимент, мы можем однозначно ответить на главный вопрос статьи: XGBoost действительно можно считать альтернативой CatBoost для работы с категориальными данными, НО с важными оговорками.
На датасетах малого и среднего размера XGBoost с автоматическим ре-кодером демонстрирует почти магическую способность он действительно обучается на “сырых” категориальных данных, избавляя нас от рутины ручного кодирования. Точность в 95-96% на Mental Health Dataset убедительно доказывает, что теперь можно быстро прототипировать, не погружаясь в тонкости One-Hot Encoding или Ordinal Encoder.
Однако на больших данных с огромным количеством уникальных значений, как в случае с Crime Dataset на 6.7 млн записей, магия исчезает. XGBoost с ре-кодером не смог обучиться за 60 минут, в то время как CatBoost стабильно выдал результат. Это критически важный момент. Автоматизация имеет свои пределы.
Таким образом, для MVP и быстрого прототипирования XGBoost с ре-кодером становится хорошим инструментом, тогда как для эксплуатации на сложных данных CatBoost сохраняет статус непобеждённого лидера.
Итоговый вердикт: да, теперь можно обучать XGBoost на “сырых” категориальных данных и получать приемлемые результаты, но волшебства не существует. XGBoost не перегнал CatBoost, но подарил нам ценную свободу выбора там, где раньше её не было, сделав работу с категориальными признаками более доступной без потери качества на типичных задачах.
Автор: sSindiKk
Источник [22]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/21791
URLs in this post:
[1] 3.10: https://github.com/dmlc/xgboost/releases/tag/v3.1.0
[2] categorical re-coder: https://xgboost.readthedocs.io/en/stable/changes/v3.1.0.html#categorical-re-coder
[3] cat_container.cc: https://github.com/dmlc/xgboost/blob/stable/src/data/cat_container.cc
[4] cat_container.cu: https://github.com/dmlc/xgboost/blob/stable/src/data/cat_container.cu
[5] cat_container.cuh: https://github.com/dmlc/xgboost/blob/stable/src/data/cat_container.cuh
[6] cat_container.h: https://github.com/dmlc/xgboost/blob/stable/src/data/cat_container.h
[7] encoder: https://github.com/dmlc/xgboost/blob/stable/src/encoder/ordinal.h
[8] здесь: https://github.com/dmlc/xgboost/issues/11088
[9] обучения: http://www.braintools.ru/article/5125
[10] Ошибка: http://www.braintools.ru/article/4192
[11] внимание: http://www.braintools.ru/article/7595
[12] House Prices – Advanced Regression Techniques: https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques
[13] Mental Health Dataset: https://www.kaggle.com/datasets/alamshihab075/mental-health-dataset
[14] Life Style Data: https://www.kaggle.com/datasets/jockeroika/life-style-data
[15] All Computer Prices: https://www.kaggle.com/datasets/paperxd/all-computer-prices
[16] Flight Delay Dataset — 2024: https://www.kaggle.com/datasets/hrishitpatil/flight-data-2024
[17] Predicting Road Accident Risk: https://www.kaggle.com/competitions/playground-series-s5e10
[18] Binary Classification with a Bank Dataset: https://www.kaggle.com/competitions/playground-series-s5e8
[19] логика: http://www.braintools.ru/article/7640
[20] Повторяем: http://www.braintools.ru/article/4012
[21] Crime & Consequence: A Metropolitan Dataset: https://www.kaggle.com/datasets/har5hdeep5harma/chicago-crime-incidents-2001-to-present
[22] Источник: https://habr.com/ru/articles/965382/?utm_campaign=965382&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.