Что общего у 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 в Москве:
Источники