Предоставление моделей scikit-learn с помощью FastAPI и Docker
Введение
Специалисты по данным тратят огромные усилия на сбор бизнес-требований, выполнение исследовательского анализа данных, предварительную обработку данных, разработку функций, настройку гиперпараметров и оценку моделей только для того, чтобы их модели застряли в локальных средах ноутбуков. Чтобы раскрыть всю ценность обученных моделей, они должны быть доступны для последующих приложений. В этой статье мы рассмотрим шаги по предоставлению моделей машинного обучения scikit-learn нижестоящим приложениям с использованием Docker и FastAPI. По сути, мы будем обучать модель, обертывать модель в API и контейнеризовать приложение.
Что такое Docker?
Docker – это открытая платформа для разработки, доставки и запуска приложений. Он предоставляет возможность упаковывать и запускать приложение в слабо изолированной среде, называемой контейнером. Контейнеры легкие и содержат все необходимое для запуска приложения, поэтому вам не нужно полагаться на то, что в данный момент установлено на хосте [1].
Что такое FastAPI?
FastAPI – это современная, быстрая (высокопроизводительная) веб-инфраструктура для создания API с помощью Python 3.7+ на основе стандартных подсказок типов Python [2].
Содержание этой статьи раскрывается в следующей последовательности:
- Настраивать
- Данные
- Каталог проекта
- Модель поезда
- Создать API с помощью FastAPI
- Тестировать API локально
- Создать образ докера
- Тестовый API в докер-контейнере
Настраивать
Вот установка, используемая для пошагового руководства.
- Код Visual Studio
- Докер Рабочий стол
- Пакеты
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
numpy==1.23.3
scikit-learn==0.23.2
joblib==1.1.0
pandas==1.4.4
Данные
В этом примере мы будем использовать набор данных о сердечных заболеваниях [3]. Задача состоит в том, чтобы предсказать, есть ли у пациента заболевание сердца, учитывая различные характеристики пациента. condition
— целевая переменная, где 1
указывает на наличие болезни сердца, а 0
указывает на обратное.
Каталог проектов
Структура каталогов проекта выглядит следующим образом:
G:.
│ requirements.txt
│ Dockerfile
│ app.py
|
├───data
│ heart-disease.csv
│
└───model
│ train.py
│ heart-disease-v1.joblib
[app.py](<http://app.py>)
: содержит логику API/data
: данные тренировки (heart-disease.csv
) сохраняются в этом каталоге./model
: в этом каталоге сохраняется обученная модель и обучающий скрипт.[train.py](<http://train.py>)
: модель scikit-learn обучается этому сценарию.Dockerfile
: Dockerfile для контейнеризации APIrequirements.txt
: содержит требования к пакетам Python.
Модель поезда
Имя файла: train.py
В целях демонстрации FasAPI и Docker мы сделали обучение простым и не включали предварительную обработку или разработку функций. Данные считываются из каталога /data
для обучения классификатора случайного леса с 10 деревьями. Затем обученная модель сохраняется в виде файла .joblib
в каталоге /model
.
#train.py
from os import PathLike from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from joblib import dump import pandas as pd import pathlib
df = pd.read_csv(pathlib.Path('data/heart-disease.csv')) y = df.pop('condition') X = df
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.2)
print ('Training model.. ') clf = RandomForestClassifier(n_estimators = 10, max_depth=2, random_state=0) clf.fit(X_train, y_train)
print ('Saving model..') dump(clf, pathlib.Path('model/heart-disease-v1.joblib'))
Создать API
Имя файла: app.py
Именно здесь происходит большая часть действия.
Импортировать библиотеки
from fastapi import FastAPI
from pydantic import BaseModel
import numpy as np
from joblib import load
import pathlib
Создайте экземпляр FastAPI
app = FastAPI(title = 'Heart Disease Prediction')
Загрузить обученную модель
model = load(pathlib.Path('model/heart-disease-v1.joblib'))
Определить модель запроса
Мы определяем входные данные, требуемые API. Формат состоит из имени поля, типа данных и необязательного значения по умолчанию.
class InputData(BaseModel):
age:int=64
sex:int=1
cp:int=3
trestbps:int=120
chol:int=267
fbs:int=0
restecg:int=0
thalach:int=99
exang:int=1
oldpeak:float=1.8
slope:int=1
ca:int=2
thal:int=2
Определить модель ответа
Мы можем определить данные ответа аналогичным образом.
class OutputData(BaseModel):
score:float=0.80318881046519
Определить запрос на публикацию
@app.post('/score', response_model = OutputData)
def score(data:InputData):
model_input = np.array([v for k,v in data.dict().items()]).reshape(1,-1)
result = model.predict_proba(model_input)[:,-1]
return {'score':result}
В приведенном выше фрагменте кода происходит много вещей, давайте разберем его.
Сначала рассмотрим @app.post('/score', response_model = OutputData)
.
/score
— это имя маршрута. Это последняя часть URL-адреса, например. «http://my-url.com/score».@app.post
указывает, что маршрут/score
принимает почтовый запрос.@app.post('/score')
сообщает FastAPI, что указанная ниже функция (т. е.def score
) отвечает за обработку запроса, идущего по маршруту/score
.response_model
относится к данным формата, которые API возвращает вызывающей стороне.
Под @app.post
у нас есть функция score
.
- Для функции score требуется аргумент
data
, который принимает форматInputData
, который мы определили ранее. - Затем функция оценки анализирует
data
и преобразует его в массив numpy. - Массив передается методу
.predict_proba
, который возвращает оценку от 0 до 1.
Запустить рабочий сервер
Мы можем запустить API локально следующим образом. В терминале в корневом каталоге проекта запустите:
uvicorn app:app --reload
Результат успешного развертывания выглядит следующим образом:
INFO: Will watch for changes in these directories: ['path/to/project']
INFO: Uvicorn running on <http://127.0.0.1:8000> (Press CTRL+C to quit)
INFO: Started reloader process [24040] using StatReload
INFO: Started server process [26612]
INFO: Waiting for application startup.
INFO: Application startup complete.
Вторая строка в приведенном выше блоке кода показывает URL-адрес, по которому обслуживается приложение.
Документация по автоматизированному API
На основе определений API FastAPI создает автоматизированную документацию в пользовательском интерфейсе Swagger и Redoc. Получите доступ к документации Swagger API, используя http://127.0.0.1:8000/docs
.
и документацию API Redoc с использованием http://127.0.0.1:8000/redoc
.
Тестировать API локально
На данный момент API можно протестировать либо через пользовательский интерфейс swagger, терминал, либо с помощью python.
Интерфейс Swagger
Терминал
Мы можем вызвать API через терминал, используя curl
. Аргумент data
, требуемый функцией score
, передается через аргумент -d
в curl
.
curl -X 'POST' \\
'<http://127.0.0.1:8000/score>' \\
-H 'accept: application/json' \\
-H 'Content-Type: application/json' \\
-d '{
"age": 64,
"sex": 1,
"cp": 3,
"trestbps": 120,
"chol": 267,
"fbs": 0,
"restecg": 0,
"thalach": 99,
"exang": 1,
"oldpeak": 1.8,
"slope": 1,
"ca": 2,
"thal": 2
}'
Питон
API можно вызвать с помощью пакета Python requests
. Аргумент data
, требуемый функцией score
, передается через аргумент json
в request.post
.
import requests
body = { "age": 64, "sex": 1, "cp": 3, "trestbps": 120, "chol": 267, "fbs": 0, "restecg": 0, "thalach": 99, "exang": 1, "oldpeak": 1.8, "slope": 1, "ca": 2, "thal": 2 }
response = requests.post(url = '<http://127.0.0.1:8000/score>', json = body)
print (response.json()) # output: {'score': 0.866490130600765}
Создать образ Docker
Большой! API работает локально. Часто нам может потребоваться развернуть API в облачной среде, такой как AWS, Azure или GCP. Один из способов сделать это — контейнеризировать ваше приложение.
Определить требования Python
Давайте определим требования в requirements.txt
# requirements.txt
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
numpy==1.23.3
scikit-learn==0.23.2
joblib==1.1.0
Определить Dockerfile
#Dockerfile FROM python:3.8
WORKDIR /app
COPY ./requirements.txt ./requirements.txt
RUN pip install --no-cache-dir --upgrade -r ./requirements.txt
COPY ./app.py . COPY ./model ./model
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
Dockerfile делает следующее:
- Получите образ python 3.8 из концентратора Docker
- Создайте рабочий каталог с именем
/app
- Скопируйте
requirements.txt
в рабочий каталог - Установите пакеты, определенные в
requirements.txt
- Скопируйте код и модель в рабочий каталог
CMD
запускает сервер на порту 80 при запуске контейнера
Создать образ Docker
# at the project root folder
docker build -t fastapiml .
Тест в контейнере Docker
Запустить контейнер Docker
docker run -d --name heart-disease-serving -p 80:80 fastapiml
Запущенный контейнер будет показан в Docker Desktop.
Документация по автоматизированному API
Как и раньше, мы можем получить доступ к документации API, используя:
- Чванство:
http://127.0.0.1/docs
- Редок:
http://127.0.0.1/redoc
API, работающий в док-контейнере, можно протестировать так же, как и раньше. Либо с помощью пользовательского интерфейса Swagger, команды curl
в терминале, либо пакета Python requests
.
Заключение
В этой статье мы обсудили важность обеспечения доступности моделей машинного обучения для последующих приложений. Мы можем раскрыть ценность моделей машинного обучения, предоставляя эти модели через API с помощью FastAPI. Одним из преимуществ использования FastAPI является автоматическая документация в Swagger, которая также позволяет проводить быстрые тесты. Мы также продемонстрировали, как контейнеризировать приложения FastAPI, что упрощает развертывание в облаке.
Присоединяйтесь к Medium, чтобы читать больше подобных историй!
Ссылка
[1] Обзор докера | Документация по докеру
[3] Набор данных о сердечных заболеваниях из Хранилища машинного обучения UCI. Лицензия CC BY 4.0.