
Data Science Kitty так радостно мурлычет, потому что теперь она может предсказывать болезни сердца с помощью своей модели (https://dev.to/orthymarjan/beginners-journey-in-machine-learning-3ei9?fbclid=IwAR02yqZ8RVT13XW1-MP66RJiNKMnHvOyhw8Dvgu2AcWPuq1kzqnjByjEc0s). Теперь она хочет использовать модель для веб-сайта, где люди будут вносить свой вклад и после отправки данных получат прогноз. Это означает, что модель должна быть включена в интерактивную среду; hoomans будут вводить свои данные в форму, а модель будет давать прогноз на основе данных формы. Но как?
Китти вспоминает, что ее друг дож, инженер машинного обучения, умеет все трахать. Дож пообещал котенку: «Я научу тебя, как развернуть твою модель». Котенок по науке о данных сейчас в замешательстве. — Что такое развертывание, дож?

Дож нарисовал схему для объяснения основ:
Шаг 1: Установка виртуальной среды и Flask
Первое и главное, что нужно сделать для любого типа разработки Python, — это настроить виртуальную среду, в которой вы будете управлять всеми инструментами и зависимостями в выбранной вами версии Python. Python поставляется с модулем venv для управления виртуальными средами. Вы можете либо использовать это, либо выбрать виртуальную среду Python или pyenv, которые вы можете установить по ссылкам ниже —
- https://github.com/pyenv/pyenv
- https://github.com/pyenv/pyenv-virtualenv — этот плагин значительно упрощает управление несколькими виртуальными средами Python в Unix-подобных системах. Вы также можете использовать Conda для настройки своего виртуального окружения.
После настройки виртуальной среды вы можете установить Flask из официальной документации.
Шаг 2: Загрузите модель
У Китти есть модель для прогнозирования сердечных заболеваний в виде файла joblib. Вам необходимо убедиться, что версия Python вашего файла модели совпадает с версией Flask. Существует два подхода к обеспечению одинаковой версии Python:
1.
Если вы запустили весь свой проект машинного обучения в Colab, просто запустите приведенную ниже команду в ячейке Colab и установите эту конкретную версию виртуальной среды Python для проекта Flask:
!python — version
2.
Вы можете создать свою модель и извлечь файл joblib из блокнота Jupyter. Таким образом, ваш файл модели и проект Flask останутся в одной и той же версии Python.
Это решает проблему Китти, связанную с сопоставлением версий joblib для Python и системы, которую делает для нее doge. Внутри каталога проекта сначала создайте файл с именем app.py и напишите следующую строку кода, чтобы начать со скелета Flask:
from flask import Flask
app = Flask(__name__)
@app.route(“/”)
def hello_world():
return “<p>Hello, World!</p>”
if __name__ == “__main__”:
app.run()
Если вы выполните следующую команду, вы увидите на http://localhost:5000/, что есть веб-страница с текстом Hello, World!
python app.py
Для ясности doge поместил файл joblib в папку с именем «resource». Чтобы загрузить модель в наше приложение, мы импортируем функцию загрузки и сохраним модель в переменной с именем конвейер.
from joblib import load pipeline = load(“resource/diseaseprediction.joblib”)
Мы можем вернуть html-страницу с помощью нашей функции приложения. Все, что нам нужно сделать, это написать
def hello_world():
return render_template(‘home.html’)
Импортируйте функцию render_template с помощью Flask, и все готово.
Шаг 3: Напишите шаблон для ввода
Это самая захватывающая, я имею в виду самая скучная часть всего процесса. Мы написали render_template(‘home.html’), но html для рендеринга нет. В проекте Flask вам нужно создать каталог с именем templates, в котором будут все веб-страницы. Для этого проекта нам потребуется только домашняя страница. У нас есть W3Schools, чтобы сделать этот базовый макет.
Пользовательский ввод будет состоять из столбцов функций модели Китти, что означает, что в форме 13 полей. Я проверил тип значения каждого ввода и, исходя из этого, код для формы будет выглядеть следующим образом:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2 style="text-align:center">Do I have heart disease?</h2>
<p style="text-align:center">Fill out this form and check!</p>
<div>
<form>
<div>
<div>
<label for="age">Age</label>
</div>
<div>
<input type="text" id="age" name="age" placeholder="Your Age" required>
</div>
</div>
<div>
<div>
<label for="sex">Sex</label>
</div>
<div>
<input type="radio" id="sex" name="sex" value="1">
<label for="sex">Male</label><br>
</div>
<div>
<input type="radio" id="sex" name="sex" value="0" required>
<label for="sex">Female</label><br>
</div>
</div>
<div>
<div>
<label for="cp">Chest Pain Type</label>
</div>
<div>
<select id="cp" name="cp" required>
<option value="0">None</option>
<option value="1">Mild</option>
<option value="2">Medium</option>
<option value="3">Severe</option>
</select>
</div>
</div>
<div>
<div>
<label for="rbp">Resting Blood Pressure</label>
</div>
<div>
<input type="text" id="trestbps" name="trestbps" placeholder="Your bp in mm Hg" required>
</div>
</div>
<div>
<div>
<label for="chol">Cholesterol</label>
</div>
<div>
<input type="text" id="chol" name="chol" placeholder="Your serum cholestoral in mg/dl" required>
</div>
</div>
<div>
<div>
<label for="fbs">Fasting Blood Sugar</label>
</div>
<div>
<input type="radio" id="fbs" name="fbs" value="1" required>
<label for="fbs">Diabetic</label><br>
</div>
<div>
<input type="radio" id="fbs" name="fbs" value="0">
<label for="fbs">Non diabatic</label><br>
</div>
</div>
<div>
<div
<label for="restecg">Resting Electrocardiographic Results</label>
</div>
<div
<select id="restecg" name="restecg" required>
<option value="0">None</option>
<option value="1">Medium</option>
<option value="2">Severe</option>
</select>
</div>
</div>
<div>
<div>
<label for="thalach">Maximum Heart Rate Achieved</label>
</div>
<div
<input type="text" id="thalach" name="thalach" placeholder="Your maximum achieved heart rate" required>
</div>
</div>
<div
<div>
<label for="exang">Exercise Induced Angina</label>
</div>
<div>
<input type="radio" id="exang" name="exang" value="1">
<label for="exang">Yes</label><br>
</div>
<div>
<input type="radio" id="exang" name="exang" value="0" required>
<label for="exang">No</label><br>
</div>
</div>
<div>
<div>
<label for="oldpeak">OldPeak (floating point value)</label>
</div>
<div>
<input type="text" id="oldpeak" name="oldpeak"
placeholder="ST depression induced by exercise relative to rest" required>
</div>
</div>
<div>
<div>
<label for="slope">The Slope of the Peak Exercise ST Segment</label>
</div>
<div>
<select id="slope" name="slope" required>
<option value="0">None</option>
<option value="1">Medium</option>
<option value="2">Severe</option>
</select>
</div>
</div>
<div>
<div>
<label for="ca">Number of Major Vessels (0–3)</label>
</div>
<div>
<select id="ca" name="ca" required>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
</div>
<div>
<div>
<label for="thal">Thal: 3 = normal; 6 = fixed defect; 7 = reversable defect</label>
</div>
<div>
<select id="thal" name="thal" required>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
</div>
</div>
<div>
<button type="submit">Submit Data</button>
</div>
</form>
</div>
</body>
</html>
Вывод этого html-файла выглядит так:

Это выглядит слишком просто, потому что мы не делали стилизацию. Это тоже утомительно, я знаю. Doge хочет, чтобы шаблон оставался базовым, поэтому она искала шаблон формы в W3School. Без этого веб-сайта изучение html и CSS не составило бы труда для doge. Ссылка на шаблон формы — https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_contact_form. Поскольку наша цель — развернуть модель, нам нужно убедиться, что все 13 данных отправлены в эту форму, извлечены и использованы в качестве входных данных для модели, которую мы загрузили ранее. Выполните следующие две задачи, чтобы убедиться, что:
В теге форма укажите место назначения, куда будут отправлены данные. Поскольку это одностраничное веб-приложение, атрибут действия формы — это адрес того, где запущено это приложение, то есть http://localhost:5000/, а атрибут метода имеет тип post.
Входные данные к модели — фрейм данных; поэтому вы должны сопоставить все данные с назначенным им столбцом. Если вы установите идентификатор, имя и атрибут for на исходное имя столбца фрейма данных и установите атрибут значения (только теги, которым требуется атрибут значения, такой как тег выбора) на конечное предполагаемое значение столбца, форма будет способен генерировать объект типа immutablemultidict, который является объектом формы Flask. Мы обработаем этот объект в следующем разделе, а пока позвольте мне объяснить только один фрагмент кода.

Столбец «cp» в наборе данных может принимать значения 0, 1, 2 или 3, которые я сопоставил для текстовых значений; на веб-сайте мы можем видеть это, а не то, что содержат атрибуты значения. После включения всех классов CSS и исходников для оформления HTML-код выглядел следующим образом. Мы почти у цели, йемс!
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<style>
* {
box-sizing: border-box;
}
input[type=text], select, textarea {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
}
label {
padding: 12px 12px 12px 0;
display: inline-block;
}
input[type=submit]:hover {
background-color: #45a049;
}
.container {
border-radius: 5px;
background-color: #f2f2f2;
padding: 20px;
}
.col-25 {
float: left;
width: 25%;
margin-top: 6px;
}
.col-75 {
float: left;
width: 75%;
margin-top: 6px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
.center {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
/* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 600px) {
.col-25, .col-75, input[type=submit] {
width: 100%;
margin-top: 0;
}
}
</style>
</head>
<body>
<h2 style="text-align:center">Do I have heart disease?</h2>
<p style="text-align:center">Fill out this form and check!</p>
<div class="container">
<form action="http://localhost:5000/" method="post">
<div class="row">
<div class="col-25">
<label for="age">Age</label>
</div>
<div class="col-75">
<input type="text" id="age" name="age" placeholder="Your Age" required>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="sex">Sex</label>
</div>
<div class="col-25">
<input type="radio" id="sex" name="sex" value="1">
<label for="sex">Male</label><br>
</div>
<div class="col-25">
<input type="radio" id="sex" name="sex" value="0" required>
<label for="sex">Female</label><br>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="cp">Chest Pain Type</label>
</div>
<div class="col-75">
<select id="cp" name="cp" required>
<option value="0">None</option>
<option value="1">Mild</option>
<option value="2">Medium</option>
<option value="3">Severe</option>
</select>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="rbp">Resting Blood Pressure</label>
</div>
<div class="col-75">
<input type="text" id="trestbps" name="trestbps" placeholder="Your bp in mm Hg" required>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="chol">Cholesterol</label>
</div>
<div class="col-75">
<input type="text" id="chol" name="chol" placeholder="Your serum cholestoral in mg/dl" required>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="fbs">Fasting Blood Sugar</label>
</div>
<div class="col-25">
<input type="radio" id="fbs" name="fbs" value="1" required>
<label for="fbs">Diabetic</label><br>
</div>
<div class="col-25">
<input type="radio" id="fbs" name="fbs" value="0">
<label for="fbs">Non diabatic</label><br>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="restecg">Resting Electrocardiographic Results</label>
</div>
<div class="col-75">
<select id="restecg" name="restecg" required>
<option value="0">None</option>
<option value="1">Medium</option>
<option value="2">Severe</option>
</select>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="thalach">Maximum Heart Rate Achieved</label>
</div>
<div class="col-75">
<input type="text" id="thalach" name="thalach" placeholder="Your maximum achieved heart rate" required>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="exang">Exercise Induced Angina</label>
</div>
<div class="col-25">
<input type="radio" id="exang" name="exang" value="1">
<label for="exang">Yes</label><br>
</div>
<div class="col-25">
<input type="radio" id="exang" name="exang" value="0" required>
<label for="exang">No</label><br>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="oldpeak">OldPeak (floating point value)</label>
</div>
<div class="col-75">
<input type="text" id="oldpeak" name="oldpeak"
placeholder="ST depression induced by exercise relative to rest" required>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="slope">The Slope of the Peak Exercise ST Segment</label>
</div>
<div class="col-75">
<select id="slope" name="slope" required>
<option value="0">None</option>
<option value="1">Medium</option>
<option value="2">Severe</option>
</select>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="ca">Number of Major Vessels (0–3)</label>
</div>
<div class="col-75">
<select id="ca" name="ca" required>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
</div>
<div class="row">
<div class="col-25">
<label for="thal">Thal: 3 = normal; 6 = fixed defect; 7 = reversable defect</label>
</div>
<div class="col-75">
<select id="thal" name="thal" required>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
</div>
</div>
<div class="center">
<button class="w3-button w3-green" type="submit">Submit Data</button>
</div>
</form>
</div>
</body>
</html>
Сейчас это тоже выглядит немного причудливо.

Шаг 4: Обработайте ввод
Мы собрали данные формы. Мы должны ввести эту форму в модель, чтобы она могла предсказывать. Напишем для этого функцию. Сначала мы проверим тип объекта, который форма создает после того, как мы нажмем кнопку отправки. Совет: при программировании на python и javascript передайте объект через встроенную функцию «типа». Это даст тип объекта, с которым вы собираетесь иметь дело.
@app.route(‘/’, methods=[‘GET’, ‘POST’])
def inputForm():
print(“type”, type(request.form))
# type ImmutableMultiDict
Мы можем преобразовать этот объект в словарь и снова преобразовать его во фрейм данных pandas. Этот кадр данных является входом для модели. Мы сохраним вывод в переменной с именем «прогноз».
Вся функция будет выглядеть примерно так, как показано ниже.
import pandas as pd
@app.route('/', methods=['GET', 'POST'])
def inputForm():
print("type", type(request.form.to_dict(flat=False))) # changing to a dictionary
print("checking form", request.form.to_dict()) # dictionary to datframe
data = request.form.to_dict()
df = pd.DataFrame(data, index=[0])
print("prediction", pipeline.predict(df))
if pipeline.predict(df) == [1]:
prediction = "You are in risk of heart disease"
elif pipeline.predict(df) == [0]:
prediction = "You don't have risk of heart disease"
else:
prediction = "Can't predict anything"
return render_template('home.html', prediction=prediction, show_predictions_modal=True)
Шаг 5: Показать прогноз
«У нас есть прогноз, но как его показать сейчас?» — спросила Китти Дожа. Дож заверил ее, что она справилась. «В наш html мы включим модальное окно, которое появится после того, как мы нажмем кнопку отправки», — сказал Дож.
Сначала мы вытолкнем модальное окно. Доге становится не терпится, поэтому она снова направилась в W3schools (https://www.w3schools.com/w3css/tryit.asp?filename=tryw3css_modal2) и создала этот модал под названием See Result.
Действие кнопки этого модального окна состоит в том, чтобы показать переменную предсказания, которую мы возвращаем из файла app.py. Мы также устанавливаем флаг внутри функции render_template. Когда прогноз будет выполнен, мы будем условно отображать модальное окно. После нажатия кнопки отправки мы увидим модальное окно. Нажмите на нее, и вы увидите прогноз.
{% if show_predictions_modal %}
<div class="center">
<button onclick="document.getElementById('id01').style.display='block'" class="w3-button w3-black"
type="submit">See
Result
</button>
</div>
<div id="id01" class="w3-modal">
<div class="w3-modal-content">
<div class="w3-container">
<span onclick="document.getElementById('id01').style.display='none'"
class="w3-button w3-display-topright">×</span>
<div class="w3-container">
<p style="text-align:center">{{ prediction }}</p>
</div>
</div>
</div>
</div>
{% endif %}


Весь проект выложен в моем GitHub-репозитории в ветке Project_HeartDiseasPrediction, где можно увидеть весь html-код, т.к. встроенную версию я в итоге не написал — https://github.com/Afroza2/Production-Based- ML-портфолио/дерево/Project_HeartDiseasePrediction. Фрейм данных, который вы передали в качестве аргумента функции прогнозирования, теперь может быть создан из пользовательского ввода, а выходные данные будут отображаться в модальном режиме. Во второй части я планирую написать о том, как разместить весь этот проект в виде приложения Heroku, чтобы вы могли получить доступ к проекту онлайн. А пока, бонк!
Первоначально опубликовано на https://dev.to 18 ноября 2021 г.