FastAPI versus BentoML: что лучше для MLOps и почему

FastAPI BentoML MLOps Machine Learning примеры курсы обучение, обучение MLOps, BentoML MLops инженер, BentoML MLOps, MLOps обучение, машинное обучение Python примеры курсы, Школа Больших Данных Учебный Центр Коммерсант

Что общего у FastAPI с BentoML, чем они отличаются и почему только один из них является полноценным MLOps-инструментом. Смотрим на примере операций разработки и развертывания API сервисов машинного обучения.

Что общего у FastAPI с BentoML и при чем здесь MLOps

С точки зрения промышленной эксплуатации, в проектах машинного обучения следует фокусироваться не только и не столько на точности самих алгоритмов Machine Learning. Огромного внимания требует автоматизация рутинных процессов развертывания и сопровождения ML-моделей, что превратилось в целую концепцию под названием MLOps. Поскольку это направление инженерной науки о данных еще довольно молодое, в нем постоянно появляются новые идеи и инструменты. В частности, одним из самых популярных MLOps-средств считается FastAPI – высокопроизводительный веб-фреймворк для создания API с помощью стандартной аннотации типов Python 3.6+. ML-инженеры часто используют его для развертывания своих моделей в качестве сервисов API.

Впрочем, FastAPI используется не только в ML-проектах. Этот фреймворк отлично подходит для быстрого создания любых REST-приложений на Python. Его дополнительным преимуществом является автогенерация документации API в формате Swagger (открытый стандарт OpenAPI 3) и JSON Schema. Пример создания такого REST API с тестированием методов в Swagger UI мы описывали в этой статье на блоге нашей Школы прикладного бизнес-анализа.

Возвращаясь к тему MLOps, отметим, что способность FastAPI генерировать документацию поддерживает идеи MLOps, а потому этот фреймворк можно отнести к набору инструментальных средств MLOps-Инженера. Однако, это далеко не единственный инструмент этой категории. Сюда же относится BentoML – открытая библиотека для быстрого создания MVP систем машинного обучения. Она упрощает создание API методов для доступа к обученной ML-модели и совместима со всеми крупными фреймворками: Tensorflow, Keras, PyTorch, XGBoost, scikit-learn и fastai.

BentoML поддерживает адаптивную микропакетную обработку данных, а также предоставляет функции управления моделью и ее развертывания. BentoML отлично реализует идеи MLOps, позволяя разработчикам ML-моделей использовать лучшие практики DevOps. Специалисты по Data Science и инженеры Machine Learning используют BentoML для ускорения и стандартизации процесса запуска моделей в производство, создавая масштабируемые и высокопроизводительные сервисы машинного обучения. Библиотека поддерживает непрерывное развертывание, мониторинг и сопровождение сервисов в производственной среде. Пример практического использования BentoML мы рассматривали в этом материале.

Отметим общие функции FastAPI и BentoML с точки зрения MLOps:

  • они оба основаны на Starlette – легковесной ASGI-платформе для создания асинхронных веб-сервисов на Python;
  • автоматическая документация в Swagger UI согласно открытому стандарту OpenAPI 3;
  • асинхронные запросы, что позволяет обрабатывать несколько запросов одновременно, а не выполнять их линейно.

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

Применение в разработке и развертывании ML-систем

Создание API в машинном обучении обычно начинается в Jupyter-блокноте или Google Colab. Формат сохраненной модели напрямую влияет на то, как она передается на сервер API. В FastAPI используется сериализованный Python-объект pickle, в котором хранится код ML-модели. Но этот формат очень ограничивает возможности разработчика манипулировать им. Помимо вопросов безопасности и производительности, самой большой MLOps-проблемой Pickle-формата является полное отсутствие контроля версий модели и управления зависимостями. Почему лучше не использовать Pickle-формат для сохранения ML-моделей, мы подробно писали в этой статье.

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

В BentoML есть стандартная процедура сохранения и загрузки моделей на основе среды, в которой они были обучены. А если модели не являются отдельными файлами и имеют зависимости, которые также следует сохранить, в BentoML можно использовать параметры custom_objects и метаданные для сохранения дополнительных объектов и информации о модели:

import bentoml
bentoml.keras.save_model(
    "cnn16",
    model_cnn,
    metadata={"desc": "CNN architecture with 16 starting filters", "owner": "Bex"},
    custom_objects={"model_history": history_object["history"]},
)

После вызова функции save_model(), BentoML сохраняет их в локальном реестре моделей, который находится по адресу ~/bentoml/models. Команда list выведет содержимое этого каталога:

$ bentoml models list
Tag                     Module         Size        Creation Time        Path
cnn16:2uo5fkgxj27exuqj  bentoml.keras  5.81 KiB    2022-12-19 08:36:52  ~/bentoml/models/cnn16/2uo5fkgxj27exuqj
cnn16:nb5vrfgwfgtjruqj  bentoml.keras  5.80 KiB    2022-12-19 21:36:27  ~/bentoml/models/cnn16/nb5vrfgwfgtjruqj

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

$ bentoml models export cnn:version_tag .

Аналогично можно импортировать любую ML-модель в архив .bentomodel со всеми ее пользовательскими объектами и метаданными, добавив ее в свой собственный реестр моделей. Впрочем, BentoML не только упрощает и автоматизирует управление реестром моделей, но и облегчает работу с входными и выходными данными API.

REST API обычно работают с данными в формате JSON, который не очень подходит для упаковки векторов машинного обучения. Предположим, ML-модель обучена на датасете с четырьмя фичами. Ее форматы ввода/вывода в FastAPI будут выглядеть так:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import numpy as np
import pickle

app = FastAPI()

class Input(BaseModel):
   feature_1: float
   feature_2: float
   feature_3: float
   feature_4: float

class Output(BaseModel):
   prediction: float

FastAPI использует Pydantic для проверки данных. Это означает, что фреймворк выдаст ошибку, если форма ввода и типы данных не соответствуют входному классу. Но когда ML-модель имеет сотню фичей для обучения, определить структуру таких больших массивов NumPy или DataFrames в классах Python почти невозможно.

В BentoML проверка входных данных массива NumPy выполняется следующим образом:

import bentoml
from bentoml.io import NumpyNdarray


runner = bentoml.sklearn.get("model_name:latest").to_runner()

svc = bentoml.Service("classifier", runners=[runner])

# The important part 
@svc.api(input=NumpyNdarray(), output=NumpyNdarray())
def classify(input_series: np.ndarray) -> np.ndarray:

   result = runner.predict.run(input_series)
   return result

Класс NumpyNdarray проверяет входные данные на соответствие массивам NumPy любой формы в одной строке кода. Чтобы применить к входным данным определенную форму, достаточно указать всего один параметр:

@svc.api(input=NumpyNdarray(shape=(-1,15), enforce_shape=True), output=NumpyNdarray())

Предоставление (-1, 15) параметру формы подтвердит, что входные данные имеют как можно больше строк, но всегда 15 фичей. В FastAPI и Pydantic это выглядит сложнее:

class Input(BaseModel):
   data: List[conlist(item_type=float, min_items=15, max_items=15)]

В реальности ML-модели работают не только с массивами NumPy: данные, отправляемые в API, могут быть любыми, от датафреймов Pandas до двоичных файлов, таких как изображения или аудио. FastAPI и Pydantic проверяют только стандартные типы данных. А BentoML содержит классы проверки для типов данных, характерных для машинного обучения. Есть даже специальный валидатор MultiPart, который позволяет передавать в модель несколько типов данных. Этот класс особенно полезен в задачах компьютерного зрения, когда для модели Machine Learning надо предоставить изображения и табличные метаданные во время логического вывода.

Когда скрипт сервиса готов, его необходимо упаковать для развертывания. Проще всего сделать это с помощью Docker, упаковав API в образ, чтобы разместить его в различных операционных системах и облачных средах. Чтобы сделать это в FastAPI, необходимо знать, как создать Dockerfile:

# Get the Fast API image with Python version 3.7
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

# Create the directory for the container
WORKDIR /app
COPY requirements.txt ./requirements.txt

# Install the dependencies
RUN pip install --no-cache-dir -r requirements.txt

COPY ./app.py ./

# Copy the serialized model
COPY ./models/cnn_model.pkl ./models/cnn_model.pkl

# Run by specifying the host and port
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]

В BentoML все гораздо проще – в корневом каталоге создается YAML-файл bentofile.yaml со следующим шаблоном:

service: "service.py:service_name"
include:
- "*.py"
python:
 packages:
  - scikit_learn
  - numpy
  - tensorflow

В поле include следует указать все файлы, необходимые скрипту сервиса для запуска. В разделе пакеты перечисляются зависимости проекта. Далее следует вызвать bentoml build на терминале:

$ bentoml build

И команда сборки упакует весь проект в автономный архив внутри локального хранилища Bento. Чтобы преобразовать архив в Docker-образ, достаточно выполнить всего 1 команду:

$ bentoml containerize model_name:latest

Теперь можно развернуть полученный Docker-образ в любой среде. Работа FastAPI на этом заканчивается – фреймворк помогает создать API, но не развернуть его. В BentoML есть специальная вспомогательная библиотека под названием bentoctl, которая позволяет развертывать контейнерные API на любой облачной платформе (AWS, GCP, Azure, Heroku). Например, для развертывания ML-модели из реестра в AWS SageMaker потребуется всего несколько команд:

$ pip install bentoctl terraform
$ bentoctl operator install aws-sagemaker
$ export AWS_ACCESS_KEY_ID=REPLACE_WITH_YOUR_ACCESS_KEY
$ export AWS_SECRET_ACCESS_KEY=REPLACE_WITH_YOUR_SECRET_KEY
$ bentoctl init
$ bentoctl build -b model_name:latest -f deployment_config.yaml
$ terraform init
$ terraform apply -var-file=bentoctl.tfvars -auto-approve

Как мы недавно отмечали, большинство современных ML-моделей использует графические процессоры для рабочих нагрузок. Особенно это характерно для проектов глубокого обучения. Поэтому рекомендуется создавать Docker-образ, поддерживающий ускорение GPU. Но, чтобы запустить логический вывод на графическом процессоре, необходима библиотека CUDA от NVIDIA, установить которую не так-то просто из-за наличия множества версий под разные ML-фреймворки (TensorFlow, PyTorch или XGBoost).

В FastAPI для установки CUDA на Docker-образ нужен большой Dockerfile:

FROM nvidia/cuda:11.2.0-runtime-ubuntu20.04

# install utilities
RUN apt-get update && \
   apt-get install --no-install-recommends -y curl

ENV CONDA_AUTO_UPDATE_CONDA=false \
   PATH=/opt/miniconda/bin:$PATH
RUN curl -sLo ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py38_4.9.2-Linux-x86_64.sh \
   && chmod +x ~/miniconda.sh \
   && ~/miniconda.sh -b -p /opt/miniconda \
   && rm ~/miniconda.sh \
   && sed -i "$ a PATH=/opt/miniconda/bin:\$PATH" /etc/environment

# Installing python dependencies
RUN python3 -m pip --no-cache-dir install --upgrade pip && \
   python3 --version && \
   pip3 --version

RUN pip3 --timeout=300 --no-cache-dir install torch==1.10.0+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html

COPY ./requirements.txt .
RUN pip3 --timeout=300 --no-cache-dir install -r requirements.txt

# Copy model files
COPY ./model /model

# Copy app files
COPY ./app /app
WORKDIR /app/
ENV PYTHONPATH=/app
RUN ls -lah /app/*

COPY ./start.sh /start.sh
RUN chmod +x /start.sh

EXPOSE 80
CMD ["/start.sh"]

В BentoML установить CUDA так же просто, как добавить одно поле в bentofile.yaml:

service: "service.py:service_name"
include:
- "*.py"
python:
packages:
- torch
- torchvision
- torchaudio
docker:
distro: ubuntu
python_version: "3.8.12"
cuda_version: "11.6.2"

Затем следует снова запустить команду containerize:

$ bentoml containerize model_name:latest

Фреймворки PyTorch и TensorFlow позволяют группировать входные данные, используя векторизацию для одновременного выполнения операций с несколькими входами благодаря специальной аппаратной структуре современных графических процессоров. Чтобы наилучшим образом использовать GPU после успешной установки CUDA, инфраструктура API должна разрешать автоматическую группировку входных данных, чего не делает FastAPI. А в BentoML эту функцию можно включить с помощью одного параметра при сохранении моделей, поддерживающих пакетную обработку:

bentoml.pytorch.save_model(
   name="my-model",
   model=model,
   signatures={
       "__call__": {
           "batchable": True,
           "batch_dim": (0, 0),
       },
   },
)

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

Как применять эти и другие средства MLOps в проектах аналитики больших данных и машинного обучения, вы узнаете на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Источники

  1. https://pub.towardsai.net/bentoml-vs-fastapi-the-best-ml-model-deployment-framework-and-why-its-bentoml-f0ed26cae88d
  2. https://www.bentoml.com/
  3. https://fastapi.tiangolo.com/
Поиск по сайту