Разработка приложения для прогнозирования вероятности отскока с использованием ML и Flask

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

Прежде всего, позвольте мне показать, как в конечном итоге выглядит приложение:

Как показано на изображении выше, пользователи смогут получить вероятность подбора для каждого игрока/стороны, что полезно для баскетбольных операторов, когда им нужно организовать тактику подбора. И некоторые выводы из GIF выше:

  1. Нам нужен интерфейс во внешнем интерфейсе, который позволит пользователям размещать игроков на площадке, перетаскивая элементы;
  2. нам нужно ядро ​​в бэкенде, которое может предсказать любую индивидуальную вероятность того, что игрок поймает отскок (и суммарную вероятность для команды) на основе их местоположения на площадке;
  3. нам нужен сервер, на котором размещается веб-сайт 24/7.

В оставшейся части статьи я собираюсь описать свои идеи именно в таком порядке.

Внешний дизайн

В процессе разработки нам необходимо решить две основные трудности:

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

Для сложности №1 я, к счастью, нашел эту полезную ссылку, которая позволяет перетаскивать любые элементы на веб-странице. Что я обновил, так это то, что я ограничил элементы div, представляющие игроков на панели:

Для трудности № 2 мой обходной путь заключается в том, чтобы вставить невидимую ‹текстовую область› в ‹форму›‹/форму›, а затем все данные, необходимые для машинного обучения, должны быть назначены здесь.

До сих пор мы могли позволить пользователям размещать игрока там, где они хотят, и машина также может распознавать местоположения.

Обучение модели машинного обучения

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

См. также:  Что происходит, когда вы запускаете «gcc main.c»?

Давайте посмотрим на мои исходные данные:

 

Примечание:

  1. Местоположение представляет собой положение игрока в момент выстрела и представлено координатами x-y.
  2. Координата X измеряется в футах и ​​представляет собой расстояние от центра площадки по длине. -47 представляет собой базовую линию конца атакующей команды. 47 представляет собой исходную линию энда обороняющейся команды.
  3. Координата Y измеряется в футах и ​​представляет собой расстояние от корзины по ширине. -25 представляет правую сторону площадки, 25 представляет левую сторону площадки (для тех, кто стоит лицом к корзине нападения).
  4. В выводе есть пустые значения, которые означают, что выполнены соответствующие броски или штрафные броски. Поскольку нас интересуют только сценарии, в которых есть подборы, нам нужно обработать это позже.

Очистка данных

Для начала удалим нежелательные значения строк:

# remove the rows where the shots or free throws were made
train = train[train['f.oreb'].isna()==False]

Затем сопоставьте идентификатор отскочившего игрока с его позицией в своей команде и состоянием команды (нападающая/защищающаяся):

# target columns is a list containing the input columns' names
target_columns = []
for event in ['off', 'def']:
    for i in range(1, 6):
        target_columns.append('playerid_' + event + '_player_' + str(i))
reb_player_id_df = train[target_columns].eq(train['reb_player_id'], axis = 0)
reb_player_position_df = reb_player_id_df.idxmax(1).where(reb_player_id_df.any(1)).dropna()
# encode all players on court
# 1~5 means a player is an offending one while 6~10 means a defending one
position_code = {
'playerid_off_player_1': 1,
'playerid_off_player_2': 2,
'playerid_off_player_3': 3,
'playerid_off_player_4': 4,
'playerid_off_player_5': 5,
'playerid_def_player_1': 6,
'playerid_def_player_2': 7,
'playerid_def_player_3': 8,
'playerid_def_player_4': 9,
'playerid_def_player_5': 10
}
output = reb_player_position_df.apply(lambda x: position_code[x])
# reset the index
output = output.reset_index(drop=True)

Теперь во многих случаях нормализованные данные обычно лучше работают в машинном обучении, потому что они уменьшают влияние выбросов и избегают попадания в локальные оптимальные точки. Таким образом, поскольку у нас есть определенный диапазон для координат x и y, мы можем попробовать нормализатор Min-Max:

train[[col for col in location_columns if '_y_' in col]] = (25 - train[[col for col in location_columns if '_y_' in col]]) / (25 - (-25))
train[[col for col in location_columns if '_x_' in col]] = (47 - train[[col for col in location_columns if '_x_' in col]]) / (47 - (-47))

Теперь данные готовы к работе!

См. также:  Шаблон фабрики приложений WSGI и время импорта созданных объектов уровня модуля

Выбор модели

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

# define models
models = [LogisticRegression(n_jobs=-1), LinearDiscriminantAnalysis(), QuadraticDiscriminantAnalysis(), GaussianNB(), MultinomialNB()]

Перекрестная проверка:

names, values = [], []
# evaluate each model one by one
# and store their names and log loss values
for model in models:
# get a name for the model
    name = type(model).__name__[:15]
    scores = evaluate_model(train, LabelEncoder().fit_transform(output), model)
    # output the results
    print('>%s %.3f (+/- %.3f)' % (name, np.mean(scores), np.std(scores)))
    names.append(name)
    values.append(scores)

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

# save the model
dump(LDA, 'LDA.joblib')

Бэкенд-дизайн

Имея в руках модель, пришло время синтезировать ее в модель, которая ее использует. На этом этапе моими основными инструментами являются Python и Flask. Flask — это облегченная среда веб-разработки, написанная на Python, которая обычно применяется в продуктах машинного обучения. Он прост, гибок и позволяет пользователям решать, что реализовывать и как управлять своими приложениями.

По сути, нам нужна веб-страница (домашняя страница), которая показывает панель и другие, когда места назначены. Доступ к домашней странице осуществляется методами «GET», когда сеанс начинается, а страница результатов — «POST», потому что она не будет отображаться, пока пользователь не нажмет кнопку «Отправить».

Вау! Теперь мы сделали это, и наша разработка завершена! Пока вы выполняете следующие команды в рабочем каталоге, вы можете увидеть приложение по адресу 127.0.0.1:5000 в своем браузере.

> set FLASK_APP=app.py (for windows; for linux, it should be "export FLASK_APP=app.py")
> flask run

Развертывание сервера

Существует множество вариантов размещения веб-приложения. Теперь мой выбор — Heroku за его простоту. Обычно я хотел бы развертывать свои приложения через GitHub, и это не исключение. Итак, первый шаг — создать новый репозиторий для вашей программы, включая вашу основную функцию (app.py), каталог static и шаблоны каталог. Вот как теперь должна выглядеть ваша папка:

├── README.md  
├── app.py    
├── LDA.joblib   
├── templates   
│   ├── index.html    
│   ├── output.html
│   └── output2.html     
├── static  
    ├── bar.png    
    ├── basketball.jpg
    └── court.png

Когда вы собираете эти вещи, вы также должны включить еще две вещи:

  1. requirements.txt, список необходимых зависимостей, вы можете быстро собрать их все:
pip freeze > requirements.txt

Обратите внимание, что после заморозки обязательно добавьте gunicorn в requirements.txt.

Вот что необходимо на протяжении всей разработки:

Flask==1.1.2 
joblib 
matplotlib 
pandas 
scikit-learn 
gunicorn

2. Procfile, записывает команду, которая сообщает серверу Heroku, что делать, чтобы активировать приложение. Поскольку наше приложение разработано с использованием Flask, наша команда должна быть такой:

web: gunicorn app:app --log-level debug

Все готово! Вы можете загрузить эту папку во вновь созданный репозиторий прямо сейчас:

git init
git clone your_github_repo_link
git add .
git commit -m "Initial Commit"
git push

Когда мы закончим решать проблемы на GitHub, пришло время взглянуть на Heroku. Убедитесь, что вы уже зарегистрировались на Heroku и у вас еще есть свободные слоты.

См. также:  Code With Me - никогда больше не покидайте свой чердак

Если да, создайте новое приложение в личном кабинете:

Когда это будет сделано, он обычно перенаправляет на домашнюю страницу приложения. Итак, вы нажимаете на вкладку «Развернуть» и выбираете развертывание приложения через GitHub:

И выберите репозиторий, к которому вы хотите подключиться:

Наконец, осталось сделать еще один шаг: нажать «Развернуть ветку».

Ура! вы сделали это, и теперь вы можете увидеть, что вы создали, по ссылке, которую предоставляет слот!

Выводы

Это мой первый раз, когда я делаю что-то с традиционными инструментами внешнего интерфейса (например, HTML, JS и CSS), поэтому я бы рассматривал это как одно из моих испытаний, чтобы бросить вызов пределу, поскольку я обычно остаюсь в зоне комфорта, используя более удобные инструменты, такие как Python. и стримлит. Таким образом, мой вывод из этого проекта заключается в том, что никогда не позволяйте вашим инструментам ограничивать вас, вместо этого позвольте им служить вашим потребностям!

И вам нужен весь исходный код этого проекта, вот ссылка:

https://github.com/MemphisMeng/rebound-app

Понравилась статья? Поделиться с друзьями:
IT Шеф
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: