CatBoost в Python: Прогнозирование футбольных матчей

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

1. Введение в CatBoost и подготовка датасета футбольной статистики

Введение в CatBoost и подготовка датасета футбольной статистики

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

Почему CatBoost?

CatBoost (сокращение от Categorical Boosting) — это библиотека градиентного бустинга на решающих деревьях, разработанная компанией Яндекс. Она стала невероятно популярной в соревнованиях по анализу данных (Kaggle) и в индустрии благодаря двум ключевым особенностям:

  • Работа с категориальными признаками: Большинство алгоритмов требуют перевода текста в цифры (например, One-Hot Encoding). CatBoost умеет работать с категориями (например, названиями команд «Спартак», «Зенит») напрямую, используя умные алгоритмы кодирования.
  • SOTA (State of the Art) точность: Алгоритм показывает одни из лучших результатов на табличных данных «из коробки», то есть с минимальной настройкой гиперпараметров.
  • В контексте футбола это критически важно. Названия команд, лиги, имена судей — это всё категориальные данные. Использование CatBoost позволяет нам скармливать модели эти данные практически в сыром виде, сохраняя важную информацию о силе команд.

    !Схема работы градиентного бустинга: последовательное исправление ошибок.

    Основы градиентного бустинга

    Прежде чем писать код, важно понять, как модель «думает». Градиентный бустинг — это ансамблевый метод. Представьте, что у вас есть совет из тысячи слабых экспертов. Первый эксперт делает грубый прогноз. Второй смотрит на ошибки первого и пытается их исправить. Третий исправляет ошибки, оставшиеся после второго, и так далее.

    Математически это можно записать следующим образом:

    Где: * — это предсказание ансамбля на шаге (текущая модель). * — это предсказание ансамбля на предыдущем шаге (предыдущая модель). * — это скорость обучения (learning rate), коэффициент, определяющий, насколько сильно мы доверяем новому эксперту. * — это новый «слабый» алгоритм (обычно решающее дерево), который обучается предсказывать ошибку (антиградиент) предыдущей модели.

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

    Специфика футбольных данных

    Футбол — это сложная стохастическая (случайная) среда. В отличие от физики, здесь нет жестких законов. Однако есть тренды. Для обучения модели нам понадобятся исторические данные.

    Какие данные нам нужны?

    Для построения качественной модели нам необходим датасет, где каждая строка — это один сыгранный матч. Минимальный набор колонок:

    * Date (Дата): Чтобы соблюдать хронологию (мы не можем предсказывать прошлое, зная будущее). * HomeTeam (Хозяева): Название принимающей команды. * AwayTeam (Гости): Название гостевой команды. * FTHG (Full Time Home Goals): Голы хозяев. * FTAG (Full Time Away Goals): Голы гостей. * FTR (Full Time Result): Результат матча (H — победа хозяев, D — ничья, A — победа гостей). Это будет нашей целевой переменной (таргетом).

    Однако на «сырых» данных модель многому не научится. Нам нужно создать фичи (признаки), которые описывают текущую форму команд.

    > Данные — это новая нефть, но в сыром виде она бесполезна. Её нужно переработать в топливо для модели.

    Подготовка окружения и данных в Python

    Перейдем к практике. Для начала установим необходимые библиотеки. Нам понадобятся catboost для моделирования и pandas для работы с данными.

    Теперь создадим структуру нашего датасета. В реальном проекте вы будете загружать данные из CSV-файлов (например, с ресурса football-data.co.uk), но для понимания структуры мы создадим небольшой пример вручную.

    Feature Engineering: Создание признаков

    Самая важная часть работы дата-сайентиста в футболе — это создание признаков, отражающих силу команд до начала матча. Мы не можем использовать HomeGoals и AwayGoals для предсказания, так как эти данные становятся известны только после матча. Но мы можем использовать среднее количество голов в прошлых матчах.

    Давайте создадим простую фичу: Среднее количество голов забитых командой в последних 3 матчах.

    Для этого нам нужно:

  • Развернуть датасет так, чтобы для каждой команды была своя хронология.
  • Посчитать скользящее среднее.
  • Вернуть данные обратно в структуру матчей.
  • Это сложный процесс, поэтому для старта мы упростим задачу и добавим статические рейтинги (в реальном проекте они должны быть динамическими).

    Предположим, мы посчитали некий «Рейтинг Атаки» (AttackStrength) на основе прошлых игр:

    !Процесс превращения сырых данных матчей в признаки для обучения.

    Подготовка таргета (Целевой переменной)

    CatBoostClassifier ожидает, что классы будут числами или строками. Для удобства переведем результаты в числовой формат: * 0: Победа хозяев (Home) * 1: Ничья (Draw) * 2: Победа гостей (Away)

    Работа с категориальными фичами в CatBoost

    Вот здесь начинается магия CatBoost. Нам нужно явно указать модели, какие колонки являются категориальными. В нашем случае это названия команд.

    Обычно в машинном обучении нам пришлось бы делать pd.get_dummies() для команд, раздувая датасет до сотен колонок. В CatBoost мы просто передаем список имен колонок cat_features.

    Разделение на Train и Test

    В футболе нельзя использовать обычный train_test_split со случайным перемешиванием (shuffle=True). Почему? Потому что мы заглянем в будущее. Если мы обучаемся на матче из декабря, чтобы предсказать матч из августа, модель выучит, что команда стала чемпионом, и будет использовать это знание нечестно.

    Поэтому разделение всегда идет по времени:

    Класс Pool

    CatBoost имеет свой собственный формат данных, оптимизированный по памяти и скорости — Pool. Рекомендуется оборачивать данные в него перед обучением.

    Использование Pool позволяет CatBoost заранее обработать категориальные признаки и квантовать числовые данные, что значительно ускоряет процесс обучения.

    Заключение

    Мы подготовили фундамент для нашей модели. Мы узнали:

  • CatBoost идеально подходит для футбола благодаря нативной работе с названиями команд.
  • Градиентный бустинг — это последовательное исправление ошибок.
  • Данные должны быть строго отсортированы по времени.
  • Необходимо создавать признаки, отражающие силу команд до матча.
  • В следующей статье мы перейдем непосредственно к инициализации модели, разбору гиперпараметров (learning rate, depth, iterations) и запустим процесс обучения, чтобы получить наши первые прогнозы.

    Готовьте свои датасеты, игра начинается!

    2. Инжиниринг признаков: работа с CatFeatures и создание футбольных метрик

    Инжиниринг признаков: работа с CatFeatures и создание футбольных метрик

    В предыдущей статье мы подготовили базовую структуру данных и познакомились с философией CatBoost. Теперь настало время превратить «сырые» данные в мощное топливо для нашей модели. Этот процесс называется Feature Engineering (инжиниринг признаков).

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

    Магия CatFeatures: Как это работает?

    Одной из главных проблем классического машинного обучения является обработка категориальных данных — признаков, которые представляют собой строки или метки, а не числа (например, название команды: «Ливерпуль», «Реал Мадрид»).

    Традиционный подход — это One-Hot Encoding. Он создает новую колонку для каждой уникальной категории. Если у нас 1000 команд в истории, мы получим 1000 новых колонок, состоящих из нулей и единиц. Это раздувает датасет и замедляет обучение.

    CatBoost решает эту проблему иначе. Он использует метод, называемый Ordered Target Statistics (Упорядоченные статистики таргета). Грубо говоря, алгоритм заменяет название категории на число, которое отражает вероятность целевого события для этой категории, вычисленную на основе прошлых данных.

    !Сравнение классического кодирования и подхода, используемого в градиентном бустинге.

    Математика кодирования

    Упрощенная формула того, как CatBoost превращает категорию в число, выглядит так:

    Где: * — это вычисленное значение (Click-Through Rate) для -го объекта. * — количество раз, когда категория встречалась в прошлом (до текущей строки ). * — целевое значение (таргет) для прошлых объектов (например, 1 — победа, 0 — поражение). * — параметр сглаживания (приор), чтобы редкие категории не получали экстремальных значений. * — общая средняя вероятность целевого события по всему датасету (априорная вероятность).

    Почему это гениально? Мы сохраняем информацию о том, насколько сильна команда (как часто она выигрывала в прошлом), прямо внутри признака, не создавая тысячи лишних колонок. И всё это CatBoost делает автоматически, если мы передадим ему параметр cat_features.

    Создание футбольных метрик (Feature Engineering)

    Теперь перейдем к созданию собственных признаков. Самая большая ошибка новичков — использовать статистику текущего матча. Мы не можем знать, сколько угловых подаст команда, до начала игры. Мы должны опираться только на историю.

    Нам нужно создать Lag Features (лаговые признаки) — данные, сдвинутые во времени.

    Подготовка данных: Переход к Team-Centric формату

    В исходном датасете одна строка — это один матч (HomeTeam vs AwayTeam). Чтобы посчитать среднее количество голов за последние 5 игр для «Арсенала», нам нужно собрать все матчи «Арсенала» (и домашние, и гостевые) в одну хронологическую цепочку.

    Давайте напишем функцию для преобразования данных:

    Скользящие средние (Rolling Windows)

    Теперь, когда у нас есть история каждой команды по порядку, мы можем посчитать «форму» команды. Классический подход — взять среднее за последние матчей.

    Мы будем считать:

  • Среднее забитых голов (Атакующая мощь).
  • Среднее пропущенных голов (Слабость защиты).
  • Важно: Мы должны использовать .shift(). Статистика для матча, который состоится сегодня, должна рассчитываться на основе матчей, сыгранных до сегодня. Если мы не сделаем сдвиг, модель увидит голы текущего матча и переобучится (Data Leakage).

    Очки формы (Form Points)

    Еще одна полезная метрика — количество очков, набранных в последних играх. В футболе: Победа = 3, Ничья = 1, Поражение = 0.

    Нам нужно сначала перевести результат ('H', 'D', 'A') в очки для конкретной команды.

    Объединение признаков обратно в матчи

    После того как мы посчитали метрики для каждой команды на каждую дату, нам нужно вернуть их в исходную таблицу матчей. У нас теперь есть богатый контекст: когда «Спартак» играет с «Динамо», мы знаем, сколько «Спартак» забивал в среднем за последние 5 игр, и сколько «Динамо» пропускало.

    Процесс объединения (Merge):

  • Берем исходный df (матчи).
  • Присоединяем к нему данные team_stats по ключам ['Date', 'HomeTeam'] — получаем фичи хозяев.
  • Присоединяем к нему данные team_stats по ключам ['Date', 'AwayTeam'] — получаем фичи гостей.
  • В итоге наш датасет расширяется:

    | Date | HomeTeam | AwayTeam | Home_AvgGoals_5 | Home_Form_5 | Away_AvgGoals_5 | Away_Form_5 | Target | |---|---|---|---|---|---|---|---| | 01.09 | Team A | Team B | 1.2 | 2.0 | 0.8 | 1.0 | H |

    !Процесс генерации признаков: от сырых матчей к обогащенному датасету.

    Взаимодействие признаков (Feature Interaction)

    CatBoost умеет сам находить комбинации признаков, но мы можем ему помочь. В футболе важна разница в классе.

    Полезно создать дифференциальные признаки: * Diff_Form: Home_Form_5 - Away_Form_5. Если число положительное, хозяева в лучшей форме. * Diff_Attack_Defense: Home_AvgGoalsScored - Away_AvgGoalsConceded. Насколько атака хозяев сильнее защиты гостей.

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

    Работа с датой и временем

    Помимо игровой статистики, время проведения матча тоже несет информацию. CatBoost не понимает дату как объект datetime напрямую в обучении, но мы можем извлечь из неё числа:

    * День недели: Некоторые команды хуже играют после еврокубков (обычно в воскресенье или понедельник). * Месяц: В начале сезона (август) результаты более непредсказуемы, чем в конце (май).

    Эти признаки лучше тоже пометить как категориальные (cat_features), так как день недели 6 (воскресенье) не «больше» дня недели 0 (понедельник) в математическом смысле, это просто разные категории дней.

    Заключение

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

  • Историческую силу команд (через CatBoost encoding).
  • Текущую форму (через скользящие средние).
  • Контекст противостояния (через разницу метрик).
  • Качество вашей модели на 80% зависит от качества этих признаков и только на 20% от настройки гиперпараметров. В следующей статье мы загрузим этот обогащенный датасет в CatBoost, настроим валидацию и наконец-то обучим нашу первую серьезную модель.