Сквозное научное путешествие по сбору данных, веб-скрейпингу, очистке данных, разработке функций, обучению модели и развертыванию в облаке с использованием Python и Flask

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

Помимо обычных подозрений в подмене данных, разработке функций, выборе функций, обучении моделей и проверке, мы также увидим, как выполнять парсинг веб-страниц (через BeautifulSoup) вместе с развертыванием модели машинного обучения на Heroku с помощью Flask. ». Я надеюсь провести вас через практическое путешествие по науке о данных, выделив некоторые из относительно несексуальных и трудоемких задач проекта машинного обучения.

Проблема предсказания

Наша цель в этом проекте будет заключаться в том, чтобы предсказать победителя матча по крикету One Day International (ODI) между любыми двумя странами, то есть проблема классификации.

Для тех, кто не знаком с крикетом, игра работает следующим образом:

  • Две команды по 11 игроков в каждой соревнуются в ODI.
  • Каждой команде дается максимум 50 оверов в каждом иннинге, чтобы отбить или выбить другую сторону.
  • Команда, выигравшая жеребьевку (путем подбрасывания монеты или подбрасывания битой), может решить сначала бить или подбросить.
  • Команда, отбивающая мяч первым, устанавливает целевой счет в одиночном иннинге. Иннинг длится до тех пор, пока сторона, отбивающая мяч, не выйдет полностью (т. Е. 10 из 11 игроков, отбивающих отбивающий, не выберут аут), или пока не будут выполнены все оверы, назначенные первой стороной.
  • Команда, которая бьет второй, пытается набрать больше, чем запланировано, чтобы выиграть игру. С точки зрения второй команды в боулинге, они пытаются выбить вторую команду или завершить отведенные им оверы до того, как отбивающая команда наберет целевое количество очков для победы.
  • Результатом игры может быть фактический результат (когда одна команда выигрывает, а другая проигрывает), ничья (когда забитые раны одинаковы для обеих команд в конце отведенных им оверов) или ничья / Нет результата / Прервано (в случае, если игра была невозможна или игра осталась незавершенной из-за погодных условий или других обстоятельств, не зависящих от игроков)

Получив обученную модель, мы:

  • Создайте интерфейс HTML с формой ввода для всех независимых переменных, требуемых нашей моделью - требуется знание HTML (в этом сообщении явно не рассматривается)
  • Создайте серверную часть приложения с помощью фреймворка Flask.
  • Разверните веб-приложение на Heroku, чтобы оно стало общедоступным и доступным через URL-адрес.

Сбор данных

Нашим основным источником данных будут четыре набора данных, связанных с мужскими ODI, публично доступные здесь. Эти данные были взяты из Cricinfo’s Statsguru и включали важную информацию обо всех ODI с первого ODI в январе 1971 года по март 2020 года. Четыре CSV содержат следующую необходимую информацию по каждой игре:

  • Соответствующая информация о матче, включая названия играющих стран, дату матча, поле для матча и результат.

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

Мы объединим эти фреймы данных вместе на этапе изменения данных. Несмотря на обширную информацию, доступную в этих файлах, в них по-прежнему отсутствует некоторая другая важная информация, а именно:

  • Выбросьте результат, который мы будем извлекать из системы показателей каждого матча, размещенной на Howstat.
  • Рейтинг игроков ODI Международного совета по крикету (ICC) - официальный международный рейтинг игроков по боулингу и боулингу согласно ICC. Мы будем извлекать эту информацию из Рейтинга ICC.

Веб-парсинг для подбрасывания информации

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

BeautifulSoup работает, беря веб-сайт, а затем анализируя его HTML-элементы в объект BeautifulSoup. Затем мы можем извлечь определенный текст в зависимости от его местоположения в объекте BeautifulSoup, используя HTML-теги и его древовидную структуру. Например, BeautifulSoup может помочь нам:

  • Найдите все ссылки
  • Найдите первую таблицу и дайте мне содержимое ее первой (или любой другой) ячейки
  • Найдите все ссылки определенного класса
  • Найдите конкретный текст в таблице, а затем дайте мне текст в следующей ячейке

Некоторые базовые знания HTML необходимы для понимания структуры веб-сайта, используемых HTML-тегов и того, как работать с HTML-тегами определенного веб-сайта с помощью BeautifulSoup.

Каждый ODI на Howstat имеет уникальный код соответствия в URL этого соответствия. Мы будем использовать эти уникальные коды совпадений в for цикле, чтобы очистить следующую информацию для каждого когда-либо сыгранного ODI:

  • Названия команд
  • Команда, выигравшая жеребьевку
  • Дата матча
  • Название площадки, на которой проводился матч

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

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

  • Определите базовый URL-адрес для каждой карты показателей, который в нашем случае будет: http://www.howstat.com/cricket/Statistics/Matches/MatchScorecard_ODI.asp?MatchCode={code}&Print=Y
  • Разберите содержимое HTML и сохраните как объект BeautifulSoup.
  • Найдите все таблицы в проанализированном HTML. Мы не находим конкретную таблицу с информацией о отбрасывании, потому что исходный HTML-код не имеет каких-либо уникальных идентификаторов таблиц. Вы можете встретить определенные варианты использования, когда желаемая таблица определяется в HTML с уникальным классом. Если это так, то, не найдя всех таблиц, вы можете просто найти свою целевую таблицу с помощью параметра class_ метода find_all BeautifulSoup. Возвращаясь к нашему варианту использования, для каждого элемента таблиц в нашем объекте BeautifulSoup:
  1. Если текст элемента Match Date, найдите и сохраните следующий элемент. Следующим элементом будет фактическая дата матча.
  2. Если текст элемента Toss, найдите и сохраните следующий элемент. Следующим элементом будет название команды, выигравшей жеребьевку.
  3. Если текст элемента Venue, найдите и сохраните следующий элемент. Следующим элементом будет название площадки, на которой был сыгран матч.
  • добавить в словарь указанные выше три точки данных каждого совпадения
  • преобразовать словарь в фреймворк для эффективного управления данными

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

Код для шагов, выполненных до сих пор:

Перемещение данных и еще немного веб-скрейпинга

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

ODI_results Dataframe

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

  • Один с точки зрения Англии, показывающий, что Англия выиграла этот матч, и
  • Другой с точки зрения Австралии, когда Австралия проиграла матч.

Также обратите внимание, что во фрейме данных нет названия страны-оппозиции в виде отдельного столбца. Вместо этого он связан со столбцом Match следующим образом: Команда 1 v Группа 2. Мы выполним следующие шаги:

  • Создайте новую Opposition функцию, разделив столбец Match, удалив в результирующих столбцах конечные и ведущие пробелы и используя простой метод numpy.where
  • Отбросьте ненужные столбцы, чтобы оставить только следующее: Result, Home/Away, Ground, Match Date, Country, Opposition
  • Преобразуйте столбец Match Date в формат datetime

ODI_innings Dataframe

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

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

Некоторые проблемы, которые я заметил во время ручного EDA:

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

Задачи по очистке данных, аналогичные тем, что мы выполняли на ODI_results, включают:

  • Удалите повторяющиеся записи / строки на основе подмножества функций
  • Отбросьте ненужные столбцы, чтобы оставить только следующее: Players, Opposition, Ground, Match Date, Country. Мы вернемся к некоторым отброшенным столбцам на более позднем этапе разработки функций.
  • Очистите столбец Opposition (удалите v) и переименуйте определенные столбцы для согласованного наименования между двумя фреймами данных: ODI_results и ODI_innings
  • Преобразуйте столбец Match Date в формат datetime

Слияние ODI_results и ODI_innings

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

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

  • Сначала объедините ODI_results_clean и ODI_innings_clean на Match Date, Ground и Country, чтобы игроки страны-участницы присоединились к ODI_results_clean в длинной форме. Длинная форма в этом контексте означает наличие одного столбца для хранения имен игроков, и в результате каждое совпадение будет охватывать несколько строк - аналогично ODI_innings_clean. Обратите внимание на использование здесь left join вместо inner join, поскольку мы хотим сохранить все данные из ODI_results_clean, даже если информация об игроках соответствующей команды недоступна в ODI_innings_clean
  • Затем выполните аналогичную merge операцию, только на этот раз используя столбец Opposition вместо Country (чтобы получить имена игроков команды соперника) вместе с Match Date и Ground
  • Мы хотим объединить два новых полноформатных фрейма данных со всеми подробностями и игроками каждой команды в отдельных столбцах. Однако мы не можем сделать это в их нынешнем виде без дублирования имен игроков в процессе слияния. Мы хотим иметь «плоскую» структуру фрейма данных, в которой данные о матчах каждой страны находятся в одной строке, то есть все игроки находятся в столбцах, прежде чем применять операцию слияния. Мы добьемся этого с помощью специальной функции, в которой используются методы groupby и unstack.
  • Когда у нас есть два «плоских» фрейма данных с точки зрения как играющих, так и противостоящих команд, мы выполним простое внутреннее слияние, чтобы объединить их вместе. Мы использовали внутреннее соединение, потому что мы хотим, чтобы были объединены только общие строки, поскольку я ожидаю, что ODI_innings_clean не будет иметь информацию об игроках конкретной команды с учетом официального статуса ODI команды или иным образом.
  • Замените некоторые варианты написания имен игроков, чтобы обеспечить согласованное написание между нашими данными и рейтингами ODI, которые мы будем исключать. Я нашел это вручную методом проб и ошибок, что обычно требуется в определенной степени, если у вас есть данные из нескольких источников.

Код для операций изменения данных, выполненных на данный момент:

Веб-парсинг для рангов ICC ODI

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

  • Декомпозируйте фрейм данных ODI_matches обратно в «длинный» формат, чтобы был только один столбец Players, а отдельные совпадения охватывали несколько строк. Это поможет нам объединить рейтинги, прошедшие очистку.

Для каждой уникальной даты, когда проводился ODI:

  • Очистите 100 лучших игроков как для игры в боулинг, так и для боулинга и сохраните их в отдельных фреймах данных.
  • Получите имена игроков, которые играли в этот день, и сохраните их в третьем фрейме данных.
  • Объедините эти три фрейма данных
  • Разложите объединенные фреймы данных один за другим по именам игроков, рангам в бейке и боулингу, чтобы получить эти три набора данных в трех фреймах данных с игроками и рангами в столбцах.
  • Объедините три фрейма данных без сложения вместе по столбцам.
  • Добавить полученный DF в список

В конце цикла for у нас будет список из нескольких фреймов данных с именами игроков, боулингом и рейтингами игроков на каждую дату, когда играли ODI. Последним шагом будет простая конкатенация этого списка, в результате чего будет получен исчерпывающий окончательный фрейм данных (final_result), содержащий следующую информацию в 7 738 строках и 78 столбцах:

  • Имя страны
  • Название оппозиции
  • Дома / В гостях - была ли игра домашней или выездной с точки зрения страны.
  • Земля
  • Дата матча
  • Результат - наша целевая переменная
  • Имена 24 игроков, по 12 от страны и оппозиции.
  • ICC ODI Bowling Ранги каждого из 24 игроков (np.nan в случае, если игрок не входил в топ-100 рейтинга)
  • ICC ODI Batting Ранги каждого из 24 игроков (np.nan в случае, если игрок не входил в топ-100 рейтинга)

toss_df Dataframe

Далее нужно очистить наш toss_df фрейм данных и объединить его с ODI_matches фреймом данных. Некоторые задачи высокого уровня включают в себя:

  • Извлеките названия играющей и оппозиционной команд из столбца Match.
  • Извлеките точное название земли из столбца Ground
  • В результате веб-сканирования некоторые записи в столбце Toss Won by включают Result:. Это более вероятно для матчей, которые были прерваны, не завершены или иным образом, когда результат был невозможен. Мы сбросим такие рекорды
  • Обновите некоторые Ground имена, чтобы они соответствовали фрейму данных ODI_matches
  • Слияние с final_result фреймом данных в final_data фрейм данных

Код для следующего бита задач по изменению данных следующий:

Функциональная инженерия

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

Месяц матча

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

Соотношение выигрышей / проигрышей

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

Средние показатели бэттинга / боулинга и количество пробежек за овер (RPO)

Затем мы рассчитаем следующие четыре новых функции для каждой команды на основе информации об уровне подач, которая у нас есть в нашем исходном ODI_innings фрейме данных:

  • Средний результат: количество забитых пробежек на количество потерянных калиток
  • Средний показатель по боулингу: количество пропущенных забегов на количество выполненных калиток
  • RPO ватта: количество забитых раундов на количество оверфейсов
  • RPO для боулинга: количество пропущенных забегов на количество забитых мячей

Эти четыре характеристики будут рассчитаны для каждой страны на основе ее совокупной статистики до этой даты, но не включая ее. Соответствующие столбцы, необходимые для их расчета:

У нас есть только пять из шести функций, которые нам нужны для расчета четырех новых функций, поскольку команда в игре упускает Innings Wickets Taken. Но это легко подсчитать, поскольку у нас есть Innings Wickets Taken оппонентов для каждого матча. Калитки, потерянные командой, эквивалентны калиткам, взятым противником.

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

Затем мы отбросим все столбцы, кроме последних четырех, и объединим их с фреймом данных final_data.

Бросок выиграл

Мы также можем обновить столбец Toss Won By на что-то вроде Toss Won?, чтобы указать, выиграла ли играющая команда жребий или нет. Это преобразует столбец в двоичный формат и устранит потенциальный разреженный фрейм данных с помощью фиктивного / горячего кодирования.

Рейтинг игроков

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

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

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

Но перед тем как это сделать, следует вменить недостающие ранги. Недостающие ранги связаны с игроками, которые не входят в топ-100 рейтинга ICC ODI на дату матча, т. Е. Их рейтинг выше 100. Я буду использовать постоянное 101, чтобы заменить недостающие ранги. Вы можете попробовать некоторые другие константы, но я считаю, что это наиболее подходящий вариант, так как более высокая константа может привести к неправильной интерпретации из-за того, что она в какой-то мере выбрасывается.

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

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

Весь код для действий по разработке функций следующий:

Выбор функции

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

Выбор категориальных признаков - критерий хи-квадрат

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

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

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

Выбор числовых характеристик - F-статистика

Выполнение теста ANOVA F-статистики для числовых признаков приводит к следующим выводам:

  • Match Month не является статистически значимым для прогнозов (снова удивительно)
  • Все остальные характеристики сами по себе статистически значимы.
  • F-статистика четырех характеристик среднего ранга существенно ниже по сравнению с общим и средним рангами: мы не будем рассматривать их для обучения модели.
  • F-статистика общего и среднего рангов одинакова, поэтому любой из них можно использовать, если мы хотим
  • В качестве приблизительной эвристики, если мы сравним F-статистику Country Total Batting Rank функции с суммой F-статистики каждого Country_Batting Rank_x столбца, последняя будет больше первой.

Поэтому я отброшу все 12 дополнительных функций, которые описывают ранги игроков и были созданы в ходе последней разработки функций (вместе с Match Month), и продолжу моделирование с рангами отдельных игроков.

Код для выбора функции следующий:

Обучение и проверка модели

Наконец, мы подошли к самому интересному моменту в машинном обучении - обучению модели. Мы будем использовать простой алгоритм логистической регрессии, чтобы делать наши прогнозы.

Я также пробовал определенные другие алгоритмы (например, XGBoost, Random Forest, Ada Boost, Gradient Boosting), но, кроме Random Forest, три других не привели к существенному улучшению точности прогнозов.

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

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

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

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

Используя J-статистику Юдена, наш идеальный порог вероятности составляет 48,27%, то есть этот порог дает максимальную дискриминационную способность различать ожидаемые выигрыши и проигрыши.

Нашим последним шагом будет просто выбрать модель, чтобы можно было делать прогнозы через наше веб-приложение (Final_LR_Model.pkl).

Весь код, связанный с моделированием и валидацией:

Создайте интерфейс HTML для сбора данных

Итак, как нам теперь делать прогнозы, используя нашу модель? Есть несколько способов сделать это: мы можем передать функции модели и вызвать метод predict или predict_proba, или мы можем поделиться маринованным файлом с другими, чтобы предсказать себя (при условии, что они разбираются в Python) .

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

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

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

Код HTML сообщает браузеру, что отображать на экране, а файл CSS сообщает ему, как отображать содержимое HTML на экране. CSS - это эффективный способ управления макетом веб-страницы путем указания цвета фона, размера и цвета шрифта, полей и т. Д.

Обратитесь к коду HTML и CSS для нашего веб-приложения и обратите внимание на следующие особенности:

  • Код HTML (вместе со всеми связанными изображениями) должен находиться в папке «шаблоны». Папка шаблонов должна находиться на том же уровне, что и приложение Flask, о котором мы поговорим позже.
  • Исходный код должен находиться в папке с именем «static». Статическая папка должна находиться на том же уровне, что и приложение Flask. Обратите внимание, что имя этой папки может быть любым, если оно правильно указано в HTML-коде.
  • У нас будет два HTML-кода внутри папки шаблонов: один для захвата пользовательского ввода (home.html) и другой для отображения выходных данных модели (result.html)
  • Файл home.html будет содержать несколько раскрывающихся списков и текстовых полей для ввода пользовательского ввода. Раскрывающиеся списки предпочтительнее для категориальных функций, чтобы ограничить пользователя от ввода значения, неизвестного нашей модели. Например, функции Country, Opposition и Home/Away

Пример кода для определения раскрывающегося списка:

<select class="element select large" id="element_2" name="element_2" required="required">     
<option value="" selected="selected"></option>    <option>Afghanistan</option>
<option>Australia</option>    
<option>Bangladesh</option>    
<option>England</option>    
<option>India</option>    
<option>Ireland</option>    
<option>New Zealand</option>    
<option>Pakistan</option>    
<option>Scotland</option>    
<option>South Africa</option>    
<option>Sri Lanka</option>    
<option>West Indies</option>    
<option>Zimbabwe</option>  
</select>

Пример кода для определения текстового поля для целых чисел (например, ранг игрока в боулинге):

<input id="element_99" name="element_99" class="element text large" type="number" step=".01" required="required" value=""/>

Пример кода для определения текстового поля для поплавков (например, средний уровень):

<input id="element_3" name="element_3" class="element text large" type="number" step=".01" required="required" value=""/>

Давайте рассмотрим некоторые основные элементы этих тегов:

  1. <select>…</select> указывает раскрывающийся список
  2. <option>…</option> определяет каждую опцию раскрывающегося списка
  3. id - уникальный идентификатор поля
  4. name - уникальное имя поля, которое может использоваться для ссылки на конкретное поле нашим приложением Flask.
  5. class - это ссылка на конкретное руководство по стилю, как определено в нашем коде CSS.
  6. number указывает, что в качестве входных данных могут приниматься только целые числа.
  7. step позволяет вводить числа с плавающей запятой
  8. required следит за тем, чтобы пользователь не мог оставить поле пустым
  9. value - значение по умолчанию, которое видит пользователь при загрузке формы.
  • Код CSS упоминается в тегах <head>…</head> файла HTML как что-то вроде этого (где 'static' - это имя папки, в которой хранится наш CSS-код 'style.css', а url_for - это встроенный метод Flask для создания динамических URL-адресов) :
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='./style.css') }}">
  • Главный элемент в result.html - это ссылка на переменную pred как {{pred}}, которая отображает ее значение, хранящееся в приложении Flask.

Разработка серверной части веб-приложения с помощью Flask

Мы будем разрабатывать серверную часть нашего веб-приложения с использованием фреймворка Flask. Думайте о Flask как о библиотеке, которую вы можете импортировать, как любую другую библиотеку в Python. Таким образом, наше приложение будет простым файлом Python (с расширением .py, а не записной книжкой с расширением .ipynb), который использует Flask в дополнение ко всем другим функциям Python.

Код нашего внутреннего приложения выглядит следующим образом:

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

  • Импортируйте все необходимые библиотеки
  • Создайте экземпляр приложения Flask с именем app
  • Загрузите нашу маринованную модель (Final_LR_Model.pkl), которая находится в той же папке, что и app.py.
  • Создайте два списка: cols с именами входных столбцов, то есть с функциями, используемыми моделью до применения фиктивной кодировки, и col_list с именами фиктивных закодированных функций.
  • Отображение home.html веб-страницы с помощью метода render_template Flask
  • Сохраните все данные, введенные пользователем, в список int_features, а название страны, для которой требуется прогноз, в переменной country.
  • Преобразуйте список ввода в массив NumPy, а затем в фрейм данных с теми же столбцами, что и в нашей модели, прежде чем применять фиктивную кодировку (cols)
  • Создайте фиктивные переменные категориальных функций и повторно проиндексируйте результирующий фрейм данных, чтобы убедиться, что в нем есть все фиктивные закодированные столбцы в соответствии с требованиями нашей модели (через pd.reindex с параметром labels = col_list)
  • Предсказать вероятность выигрыша в игре с помощью predic_proba, преобразовать ее в число с плавающей запятой и определить, выиграет или проиграет страна, с помощью простого if утверждения.
  • Отобразите от result.html веб-страницу до render_template со строкой по вашему выбору, назначенной переменной pred
  • Запустите приложение

Это оно!

Но давайте сначала протестируем наше приложение локально, прежде чем развертывать его в облаке. Запустите Anaconda Prompt (или любой другой терминал по вашему выбору), перейдите в папку, в которой есть app.py, Final_LR_Model.pkl, а также шаблоны и статические папки, и запустите его, используя:

python app.py

Не обращайте внимания на версию с конфликтом версий (возможно, вы ее не видите), откройте URL-адрес в своем веб-браузере. Вы должны увидеть свой симпатичный home.html и уметь вводить тестовые значения и делать прогнозы.

Поздравляю! вы создали свое первое приложение машинного обучения. Затем мы перенесем это приложение с вашего локального компьютера в облако, чтобы другие люди могли использовать его с URL-адресом.

Развертывание в облаке на Heroku

Heroku предлагает платформу как услугу (PaaS) для облачных развертываний. Есть несколько способов завершить развертывание Heroku, но самый простой, вероятно, через репозиторий GitHub.

Помимо двух файлов и двух папок, которые мы использовали при локальном тестировании нашего приложения, нам нужны еще два файла в нашем репозитории GitHub перед развертыванием Heroku:

  • requirements.txt - это текстовый файл, содержащий имена пакетов Python, необходимых для выполнения приложения. Это необходимые пакеты, которые необходимо установить в среде для успешного выполнения приложения.

Вы должны быть хорошо знакомы со всеми приведенными выше требованиями. Думайте о Gunicorn как о промежуточном программном обеспечении, которое позволяет вашему приложению Flask взаимодействовать с сервером, на котором оно размещено. С технической точки зрения, это HTTP-сервер интерфейса шлюза веб-сервера Python (WSGI).

  • Procfile (без расширения файла) - это список типов процессов в приложении, то есть список инструкций для веб-сервера, указывающий, какой файл должен быть выполнен первым, когда кто-то входит в приложение. Наша единственная строка кода на изображении ниже сообщает серверу открыть веб-приложение с помощью Gunicorn WSGI и запустить приложение с именем app (это имя приложения, которое мы определили внутри app.py) в файле с именем app.py

На этом этапе ваш репозиторий GitHub должен выглядеть примерно так:

Выполните следующие шаги, чтобы завершить развертывание Heroku:

  • Войдите в свою учетную запись Heroku (зарегистрироваться для проекта такого размера можно бесплатно) и нажмите «Создать новое приложение» на панели управления.

  • Введите желаемое название приложения и предпочтительный регион и нажмите «Создать приложение».

  • Выберите GitHub в разделе «Метод развертывания» и подключитесь к своей учетной записи GitHub.

  • GitHub может попросить вас пройти аутентификацию, прежде чем разрешить доступ к вашей учетной записи GitHub через Heroku.
  • Как только ваша учетная запись GitHub будет связана с Heroku, найдите целевой репозиторий GitHub и подключитесь к нему.

  • После подключения к репозиторию разверните требуемую ветку в разделе «Развертывание вручную».

  • Подождите некоторое время, пока Heroku извлечет весь необходимый код из репозитория, установит все необходимые библиотеки на свои серверы и завершит развертывание. После завершения Heroku подтвердит ваше успешное развертывание (или иначе) и предоставит вам возможность просмотреть ваше приложение по определенному URL-адресу.

Частичный взгляд на наше приложение, доступное через Интернет:

Вот и все - полная рабочая модель машинного обучения, разработанная на Python и развернутая в облаке, доступная для всех в любое время суток!

Вывод

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

Вы также можете найти полную записную книжку здесь.

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

До следующего раза, зажигай!