Предоставление моделей scikit-learn с помощью FastAPI и Docker

Введение

Специалисты по данным тратят огромные усилия на сбор бизнес-требований, выполнение исследовательского анализа данных, предварительную обработку данных, разработку функций, настройку гиперпараметров и оценку моделей только для того, чтобы их модели застряли в локальных средах ноутбуков. Чтобы раскрыть всю ценность обученных моделей, они должны быть доступны для последующих приложений. В этой статье мы рассмотрим шаги по предоставлению моделей машинного обучения scikit-learn нижестоящим приложениям с использованием Docker и FastAPI. По сути, мы будем обучать модель, обертывать модель в API и контейнеризовать приложение.

Что такое Docker?

Docker – это открытая платформа для разработки, доставки и запуска приложений. Он предоставляет возможность упаковывать и запускать приложение в слабо изолированной среде, называемой контейнером. Контейнеры легкие и содержат все необходимое для запуска приложения, поэтому вам не нужно полагаться на то, что в данный момент установлено на хосте [1].

Что такое FastAPI?

FastAPI – это современная, быстрая (высокопроизводительная) веб-инфраструктура для создания API с помощью Python 3.7+ на основе стандартных подсказок типов Python [2].

Содержание этой статьи раскрывается в следующей последовательности:

  1. Настраивать
  2. Данные
  3. Каталог проекта
  4. Модель поезда
  5. Создать API с помощью FastAPI
  6. Тестировать API локально
  7. Создать образ докера
  8. Тестовый API в докер-контейнере

Настраивать

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

  1. Код Visual Studio
  2. Докер Рабочий стол
  3. Пакеты
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 для контейнеризации API
  • requirements.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 делает следующее:

  1. Получите образ python 3.8 из концентратора Docker
  2. Создайте рабочий каталог с именем /app
  3. Скопируйте requirements.txt в рабочий каталог
  4. Установите пакеты, определенные в requirements.txt
  5. Скопируйте код и модель в рабочий каталог
  6. 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, используя:

  1. Чванство: http://127.0.0.1/docs
  2. Редок: http://127.0.0.1/redoc

API, работающий в док-контейнере, можно протестировать так же, как и раньше. Либо с помощью пользовательского интерфейса Swagger, команды curl в терминале, либо пакета Python requests.

Заключение

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

Присоединяйтесь к Medium, чтобы читать больше подобных историй!

Ссылка

[1] Обзор докера | Документация по докеру

[2] FastAPI (tiangolo.com)

[3] Набор данных о сердечных заболеваниях из Хранилища машинного обучения UCI. Лицензия CC BY 4.0.