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

В серии предстоящих публикаций я хотел бы поделиться своими впечатлениями за последние недели — надеюсь, кто-то найдет для себя что-то ценное из этой работы.

Управляющее резюме. С чем мы имеем дело.

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

Каков рыночный стандарт?

Это был первый ключевой вопрос, на который мне пришлось ответить в ходе исследования. Чтобы иметь возможность на него ответить, приходится говорить об одной и той же метрике — в данном случае это «Точность», которая определяет, сколько раз в n случаях модель была права. В то же время стоит также отметить, что эффективность может варьироваться в зависимости от хода разных соревнований, поэтому для простоты мы сначала сосредоточимся на английской премьер-лиге. И что это за рыночные стандарты?

Ниже приведены некоторые ссылки на источники:

  1. Точность букмекерских алгоритмов колеблется в районе 54% — http://andrew.carterlunn.co.uk/programming/2018/02/20/beating-the-bookmakers-with-tensorflow.html
  2. Точность модели премьер-лиги Octosport составляет 54% — https://www.octosport.io/model-performance;
  3. Точность базовой модели логистической регрессии ниже 50%

В следующей статье Николас Утикал цитирует статьи о лучших моделях машинного обучения, достигающих точности 60–65%. Его собственный подход близок к этому показателю — https://medium.com/@nicholasutikal/predicting-football-results-using-archetype-analysis-and-xgboost-1344027eae28. Я также понимаю, что на Kaggle есть конкуренция, где они достигают почти 100% точности, используя некоторые функции проектирования и сети LSTM. Но я не беру это в расчет, так как у каждой проблемы в мире есть 100% точное решение на Kaggle :)

Вес каждого процента успеха.

С точки зрения эффективности каждый процент имеет значение. Нам достаточно рассмотреть приведенную ниже таблицу для английской премьер-лиги сезона 21/22.

Если мы сложим произведение матчей и назначенный им ход, а затем разделим на количество игр в сезоне (380), мы узнаем, какова была бы окупаемость инвестиций, в том случае, если бы все матчи мы ставки оказались успешными. Здесь я принимаю одиночную ставку на результат (дома/в гостях/ничья). Сумма произведений составляет 715,26, что составляет ~ 1,8822 процента прибыли.

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

Беглый взгляд заставляет нас понять, что «отраслевой стандарт» едва ли приносит прибыль. И то, что только приближение к 60% или выше делает алгоритм чем-то, что можно считать интересным с инвестиционной точки зрения.

Цель

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

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

Сбор и обработка данных.

Мне так повезло, что я некоторое время назад поступил на курсы футбольных тренеров УЕФА, и полжизни этот вид спорта был мне близок. Поэтому я решил, что, используя имеющиеся у меня знания, я попытаюсь построить набор данных, который потенциально может быть аналитически релевантным.

На помощь пришла библиотека soccerdata (https://soccerdata.readthedocs.io/), представляющая собой парсер данных из нескольких разных источников. Подробное описание того, что и как из этой библиотеки использовалось, вы можете найти в моем блокноте, ссылка на который прикреплена ниже.

https://github.com/SquareGraph/FootballPredictionsModel/blob/main/DataGathering_from_Soccerdata.ipynb

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

Футбольные данные Fbref API

Это мой основной API, в который я объединяю данные из других источников и разрабатываю новые функции, которые будут использоваться для алгоритма:

  1. fbref.read_schedule() — расписание лиги. Метод возвращает DataFrame всех игр в данном сезоне. Мне пришлось немного почистить его, так как в строках отложенных игр было написано «NaN». Приведенный ниже код является примером того, что возвращает моя функция, использующая библиотеку soccerdata. Важным столбцом является «game_id», который позволит вам связать разные данные вместе.
  2. fbref.read_player_match_stats() — по этому методу у нас получается получать очень точные данные по разным видам статистики по каждой игре, накопленной на игрока. Статистики доступно много, но для этой модели мы будем использовать только передачу (еще и потому, что когда я запустил одну функцию, которая собиралась построить DataFrame из всей статистики по всем встречам, после 5 часов работы блокнота, когда объекты были готовы, они оказались настолько большими, что любые операции над ними, например сохранение в csv, приводили к сбоям в работе блокнота в сервисе Google Colab).
  3. fbref.read_shot_events() — группирует все события бросков за игру. Информация является как категориальной, так и количественной, поэтому категории были переведены в числовые значения, а информация о расстоянии выстрелов сгруппирована в 10-метровые ячейки.

Здесь две важные аннотации для людей, которые захотят попробовать использовать эти данные самостоятельно. Первый носит программный характер. Ну, названия команд различаются между API. Вот почему вам часто приходится работать с картоподобными словарями, потому что один из API хочет «Манчестер Юнайтед», а другой — «Манчестер Юнайтед».

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

В приведенном выше фрагменте кода показана функция, которую я создал для создания DataFrame для каждой команды с их домашними и выездными выступлениями соответственно. Я предположил, что окно размером с последние пять игр. Важным параметром здесь является close = «left», по которому мы можем быть уверены, что учитывается предыдущая, а не текущая встреча. В то же время при расчете такого среднего первой строкой будет NaN, поэтому мы заполняем ее scipy.stats.mode.

API футбольных данных ELO

Еще один элемент, который показался мне интересным, — это рейтинг ELO. Этот параметр добавлен для каждой команды, участвующей в матче, и рассчитывается в день матча. Я создал функцию и позже использовал ее через метод pd.DataFrame.apply(), добавив в DataFrame два столбца — «home_rank» и «away_rank».

API футбольных данных MatchHistory

Этот API оказался очень важным, потому что он содержал коэффициенты ставок на каждый матч и некоторые дополнительные данные, такие как столбец FTR (Full Time Result), который будет не только дополнительной меткой, но и позволит рассчитать форму команд. , аналогично подсчету передач или бросков с использованием скользящего среднего. Здесь также имена команд были другими, чем в FBref API, поэтому, прежде чем я смог объединить эти данные вместе, мне пришлось подготовить другой словарь, а затем выполнить pd.DataFrame.merge(on=[“home_team”, “away_team” ].Аннотация — MatchHistory API имеет неверные даты матчей, так что имейте в виду.

Параметры игрока — игровые данные ФИФА

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

Откуда берутся эти данные? Soccerdata также предоставляет доступ к парсеру SoFIFA, однако данные почему-то неполные. С другой стороны, Kaggle приходит с помощью и отличной работой, которую люди делают годами (@StefanoLeone, спасибо), работая над следующим набором данных:

https://www.kaggle.com/datasets/stefanoleone992/fifa-22-complete-player-dataset?rvi= 1

Благодаря этому я избежал много времени, посвященного написанию скраппера. Итоговая таблица с данными FIFA состоит из 43 столбцов, заполненных значениями с плавающей запятой.

Подготовка данных для машинного обучения.

Данные, подготовленные на предыдущих шагах, выглядят следующим образом.

Параметр shape (380 141) говорит нам, что каждая строка в DataFrame соответствует одной игре в английской премьер-лиге. И почти все столбцы станут фичами для предлагаемых моделей. Почти потому что:

  1. main.FTR будет y_labels. FTR расшифровывается как Full Time Result. С помощью pd.factorize() исходным категориальным значениям (H — победа хозяев, A — победа гостей, D — ничья) предварительно были присвоены соответствующие числовые значения.
  2. Мы исторически получили производные от двух разных столбцов DataFrame (MatchHistory и FBref) с именами «date_x» и «date_y» — пока мы их никак не будем рассматривать, поэтому опустим.
  3. То же самое касается параметра game_id, который мы ранее использовали для объединения двух таблиц.
  4. Есть еще несколько особенностей, которые появились при линковке (и которые мне нужно исправить в следующей итерации собирающего блокнота) — это такие параметры как «home_point», «away_points», «draw», «D_HT» которые были вспомогательными параметрами для подсчета командных форм; или копия индекса «Безымянный: 0».

Кроме того, у нас есть несколько случаев, когда столбцы почти полностью заполнены значениями NaN. В данном случае я проверил ответ API с реальными совпадениями, и решил заполнить их 0. Часть этих столбцов является следствием отсутствия данных на стороне API, а часть того, что при расчете скользящей средней от значения 0 в первых строках (до игры 1 данных не было), следующие заполнялись NaN.

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

Обученные модели и метрики.

В теме метрик, помимо вышеупомянутой «Точности», я буду использовать и одну свою, специфичную для задачи и ниши. А именно, речь идет о проверке того, сколько действительно стоили типы по алгоритму, т.е. рентабельности. Поскольку размер проверочной выборки составил 76 совпадений, это число также является минимальным значением, которого должна достичь модель, чтобы считать ее безубыточной (все время помнить об упрощении, предполагающем наивное управление портфелем). , где все совпадения представляют собой постоянный денежный банк, разделенный поровну на каждую ставку).

Я назвал эту пользовательскую метрику «счет», и она рассчитывается следующим образом:

Выбранные модели

Для всех приведенных ниже моделей гиперпараметры были выбраны изначально (с некоторым базовым пониманием модели), но без тонкой настройки. Тонкая настройка еще впереди, в следующих статьях. Сначала я решил проанализировать четыре модели. всем известный RandomForestClassifier из библиотеки Sickit Learn и XGBoost; оба не требуют представления. В случае с табличными данными они считаются рыночным стандартом и одним из самых эффективных решений.

Два других — это соответствующий TabNet (https://arxiv.org/abs/1908.07442) в реализации https://github.com/dreamquark-ai/tabnet и упрощенная версия DeepInsight (https ://www.nature.com/articles/s41598-019-47765-6), алгоритм, который переводит линейные табличные данные в двумерные группы объектов для последующего использования в сети CNN.

Ссылка на блокнот, в котором пошагово описан весь процесс, прикреплена ниже.

https://github.com/SquareGraph/FootballPredictionsModel/blob/main/BaselineModels_Football_Predictions_55_60_version_to_publish.ipynb

Краткое содержание

Самое главное, однако, это таблица со сравнением всех моделей по отношению к цели 55% точности.

Что касается DeepInsight, я запускал его несколько раз, и результаты были очень случайными. Я пока не отказываюсь от этой концепции, но я обязательно проверю сети LSTM, а не буду пытаться усовершенствовать эту модель или данные, которые должны быть доставлены в нее.

RandomForest и XGB показали потенциал, но каждый из параметров оценки ставит их ниже квалификационного порога, но достаточно близко, чтобы я очень настоятельно рассматривал GridSearchCV или другой метод оптимизации гиперпараметров для дальнейших исследований.

С другой стороны, TabNet в базовой версии оказался значительно выше предположений и более того, вплотную приблизился к топовым рыночным решениям. Экстраполируя на весь сезон, при наивном мани-менеджменте алгоритм потенциально дал бы шанс возврата в 11%.

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

Следующие шаги? Тонкая настройка данных и тонкая настройка алгоритмов :)!

Некоторые статьи, которые я упомянул *

  1. https://medium.com/analytics-vidhya/beating-soccer-odds-using-machine-learning-project-walkthrough-a1c3445b285a Артура Калдаса
  2. https://medium.com/geekculture/building-a-simple-football-prediction-model-using-machine-learning-f061e607bec5 от octosport.io
  3. https://medium.com/@nicholasutikal/predicting-football-results-using-archetype-analysis-and-xgboost-1344027eae28 Николас Утикал