Из этого руководства вы узнаете, как быстро развернуть модели машинного обучения с помощью FastAPI, Redis и Docker.
Если вы хотите перемотать вперед, репозиторий сопутствующего кода предоставит вам модель классификации изображений за считанные минуты.
Обзор
Существует ряд отличных руководств по завершению модели машинного обучения с помощью Flask. Однако, когда я увидел эту потрясающую серию сообщений Адриана Роузброка, я подумал, что его подход был немного более готовым к производству и хорошо подходил для докеризации. Докеризация этой настройки не только значительно упрощает настройку и работу с Docker Compose, но и становится более легко масштабируемой для производства.
По окончании обучения вы сможете:
1. Создайте веб-сервер с помощью FastAPI (с Uvicorn) для обслуживания наших конечных точек машинного обучения.
2. Создайте сервер модели машинного обучения, который обслуживает модель классификации изображений Keras (ResNet50 обучен на ImageNet).
3. Используйте Redis в качестве очереди сообщений для передачи запросов и ответов между веб-сервером и модельным сервером.
4. Используйте Docker Compose, чтобы раскрутить их всех!
Архитектура
Мы будем использовать ту же архитектуру из вышеупомянутых сообщений Адриана Роузброка, но заменим фреймворки веб-сервера (FastAPI + Uvicorn на Flask + Apache) и, что более важно, поместим всю установку в контейнер для простоты использования. Мы также будем использовать большую часть кода Адриана, поскольку он проделал великолепную работу с обработкой, сериализацией и борьбой с несколькими ошибками NumPy.
Основная функция веб-сервера - обслуживать /predict
конечную точку, через которую другие приложения будут вызывать нашу модель машинного обучения. Когда вызывается конечная точка, веб-сервер направляет запрос в Redis, который действует как очередь сообщений в памяти для многих одновременных запросов. Модельный сервер просто опрашивает очередь сообщений Redis на предмет наличия пакета изображений, классифицирует пакет изображений, а затем возвращает результаты в Redis. Веб-сервер получает результаты и возвращает их.
Репозиторий кода
Вы можете найти весь код, использованный в этом руководстве, здесь:
Создание веб-сервера
Я решил использовать tiangolo/uvicorn-gunicorn-fastapi
для веб-сервера. Этот образ Docker предоставляет аккуратный стек ASGI (Uvicorn, управляемый Gunicorn с платформой FastAPI), который обещает значительные улучшения производительности по сравнению с более распространенным flask-uwsgi-nginx на основе WSGI.
Это решение было в значительной степени вызвано желанием опробовать стек ASGI, а высококачественные образы докеров, такие как tiangolo, значительно упростили эксперименты. Кроме того, как вы увидите в коде позже, написание простых конечных точек HTTP в FastAPI не слишком отличается от того, как мы это делали во Flask.
webserver/Dockerfile
довольно прост. Он берет вышеупомянутый образ, устанавливает необходимые требования Python и копирует код в контейнер:
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 COPY requirements.txt /app/ RUN pip install -r /app/requirements.txt COPY . /app
Файл webserver/main.py
запускает сервер FastAPI, открывая конечную точку/predict
, которая принимает загруженное изображение, сериализует его, отправляет в Redis и опрашивает полученные прогнозы.
Код в основном сохраняется как есть, с некоторыми дополнительными функциями для среды Dockerized, а именно с разделением вспомогательных функций и параметров для веб-сервера и сервера модели. Кроме того, параметры передаются в контейнер Docker через переменные среды (подробнее об этом позже).
Построение сервера модели
modelserver/Dockerfile
тоже довольно прост:
FROM python:3.7-slim-buster COPY requirements.txt /app/ RUN pip install -r /app/requirements.txt # Download ResNet50 model and cache in image RUN python -c "from keras.applications import ResNet50; ResNet50(weights='imagenet')" COPY . /app CMD ["python", "/app/main.py"]
Здесь я использовал изображение python:3.7-slim-buster
. Вариант slim
уменьшает общий размер изображения примерно на 700 МБ. Вариант alpine
не работает с тензорным потоком, поэтому я решил не использовать его.
Я также решил загрузить модель машинного обучения в Dockerfile, чтобы она была кэширована в образе Docker. В противном случае модель будет загружена в момент запуска сервера модели. Это не проблема, кроме добавления задержки в несколько минут к процессу репликации (поскольку каждый запускающийся рабочий процесс должен сначала загрузить модель).
И снова Dockerfile устанавливает требования, а затем запускает файл main.py
.
Сервер модели опрашивает Redis для получения пакета изображений для прогнозирования. Пакетный вывод особенно эффективен для моделей глубокого обучения, особенно при работе на графическом процессоре. Параметр BATCH_SIZE
можно настроить так, чтобы обеспечить минимальную задержку.
Мы также должны использовать pipeline
в redis-py (это неправильное название, поскольку он по умолчанию является транзакционным в redis-py) для реализации атомарного выталкивания нескольких элементов слева (см. Строки 45–48). Это становится важным для предотвращения состояний гонки, когда мы реплицируем серверы модели.
Собираем все вместе с Docker Compose
Мы создаем 3 сервиса - Redis, сервер модели и веб-сервер - все они находятся в одной сети Docker.
«Глобальные» параметры находятся в файле app.env
, а параметры, специфичные для службы (такие как SERVER_SLEEP
и BATCH_SIZE
), передаются в контейнеры как переменные среды.
Параметры deploy
используются только для Docker Swarm (подробнее об этом в следующем посте) и будут игнорироваться Docker Compose.
Мы можем раскрутить все с помощью docker-compose up
, который будет создавать образы и запускать различные службы. Вот и все!
Тестирование конечных точек
Теперь протестируйте сервис, свернув конечные точки:
$ curl http://localhost "Hello World!" $ curl -X POST -F [email protected] http://localhost/predict {"success":true,"predictions":[{"label":"dingo","probability":0.6836559772491455},{"label":"Pembroke","probability":0.17909787595272064},{"label":"basenji","probability":0.07694739103317261},{"label":"Eskimo_dog","probability":0.01792934536933899},{"label":"Chihuahua","probability":0.005690475460141897}]}
Успех! Вероятно, в ImageNet нет класса «сиба-ину», так что «динго» пока подойдет. Достаточно близко.
Нагрузочное тестирование с Locust
Locust - это инструмент нагрузочного тестирования, предназначенный для нагрузочного тестирования веб-сайтов. Он предназначен для веб-сайтов с нагрузочным тестированием, но также отлично подходит для простых конечных точек HTTP, таких как наша.
Его легко запустить и запустить. Сначала установите его с помощью pip install locustio
, затем запустите, запустив в каталоге проекта:
locust --host=http://localhost
При этом используется предоставленный locustfile
для проверки конечной точки /predict
. Обратите внимание, что мы указываем хосту на localhost
- мы проверяем время отклика нашей службы машинного обучения без реальной задержки в сети.
Теперь укажите в браузере http://localhost:8089
, чтобы получить доступ к веб-интерфейсу locust.
Мы смоделируем 50 пользователей (все они вылуплены в начале).
Время отклика p95 около 5000 мс означает, что 95% запросов должны выполняться в течение 5 секунд. В зависимости от вашего варианта использования и ожидаемой нагрузки это может быть слишком медленно.
Заключение
В этом посте мы увидели, как создать службу машинного обучения Dockerized с помощью Keras, FastAPI и Redis. Мы также провели нагрузочный тест и увидели, что производительность может быть менее чем адекватной.
В этом посте я проиллюстрирую использование Docker Swarm для простого масштабирования нашего модельного сервера для повышения производительности: