НОВОСТИ [Из песочницы] Общий обзор архитектуры сервиса для оценки внешности на основе нейронных сетей

Alvaros
Онлайн
Регистрация
14.05.16
Сообщения
21.452
Реакции
101
Репутация
204
DqT65V7luoGlYN20iKxUA2KzQzsHWn2qubRVsSGGUwe95GidKF2vP_e9KxFQzA6dQe_t52XwOWa4C8tPdjJKdQKpMRwdMo1afDkfr8Vjd1JByz6sfD38qdCiBVLuUVIKtM3PqMpP


Вступление



Привет!


В данной статье я поделюсь опытом построения микросервисной архитектуры для проекта, использующего нейронные сети.


Поговорим о требованиях к архитектуре, посмотрим на различные структурные диаграммы, разберем каждый из компонентов готовой архитектуры, а также оценим технические метрики решения.


Приятного чтения!


Пару слов о задаче и ее решении



Основная идея – на основе фото дать оценку привлекательности человека по десятибалльной шкале.


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


Сейчас же мы верхнеуровнево пройдемся по пайплайну оценки, а упор сделаем на взаимодействие микросервисов в контексте общей архитектуры проекта.


При работе над пайплайном оценки привлекательности, задача была декомпозирована на следующие составляющие:

  1. Выделение лиц на фото
  2. Оценка каждого из лиц
  3. Рендер результата


Первое решается силами предобученной . Для второго была обучена сверточная нейросеть на PyTorch, в качестве backbone был использован – из баланса «качество / скорость инференса на CPU»

187ep8Q11S6edVM0WKTkFN8bGDvm4DWFbr-TMNLiQDikAAixPZPnS81NYHv6AfFydL14pwlgZpBo_tGbpBWlVCxCIswxJvgqsLIQfiVTCuNoCldA6DUl5dSsLsg9x62uRhPa4pTl



Функциональная диаграмма пайплайна оценки

Анализ требований к архитектуре проекта



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

BnMeXcrpsWpLGez0N6bYl5NMHGSHyaqDAAx2yRRcqltU2-mQSeq8P_jwgp-eK0qMkj4lfrCYtFwN50gjo2poB4l4Afl54ne86yHbvdoe--PjOLBRTsT03KYxP3uUdyNDFpgxr8u8



Жизненный цикл ML проекта


Данный проект не исключение – было принято решение обернуть пайплайн оценки в онлайн-сервис, для этого требовалось погрузиться в архитектуру. Были обозначены следующие базовые требования:

  1. Единое хранилище логов – все сервисы должны писать логи в одно место, их должно быть удобно анализировать
  2. Возможность горизонтального масштабирования сервиса оценки — как наиболее вероятного Bottleneck
  3. На оценку каждого изображения должно быть выделено одинаковое кол-во ресурсов процессора — во избежание выбросов в распределении времени на инференс
  4. Быстрое (пере)развертывание как конкретных сервисов, так и стэка в целом
  5. Возможность, при необходимости, использовать в разных сервисах общие объекты

Архитектура



После анализа требований стало очевидно, что микросервисная архитектура вписывается практически идеально.


Для того, чтобы избавиться от лишней головной боли, в качестве фронтенда был выбран Telegram API.


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

YheopQgolRxjp4dG93Q3a7fcczmUDsJX059XQuqc4bvBzZPe-AAZ6dcuV6DsHU7g5HpXgnAkgA1TJdHRYstbfDG0-qIyRp5j6pB8XYTi1DjikjxMKu1DghL7QCfGR5lU28U9jsg8



Структурная диаграмма готовой архитектуры


Поговорим подробнее о каждом из компонентов диаграммы, обозначим их Single Responsibility в процессе оценки изображения.

Микросервис «attrai-telegram-bot»



Данный микросервис инкапсулирует все взаимодействия с Telegram API. Можно выделить 2 основных сценария – работа с пользовательским изображением и работа с результатом пайплайна оценки. Разберем оба сценария в общем виде.


При получении пользовательского сообщения с изображением:

  1. Производится фильтрация, состоящая из следующих проверок:
    • Наличия оптимального размера изображения
    • Количества изображений пользователя, уже находящихся в очереди
  2. При прохождении первичной фильтрации изображение сохраняется в docker volume
  3. В очередь “to_estimate” продьюсится таска, в которой, в том числе, фигурирует путь до изображения, лежащего в нашем volume
  4. Если вышеперечисленные этапы пройдены успешно – пользователь получит сообщение с примерным временем обработки изображения, которое рассчитывается на основе количества тасков в очереди. В случае ошибки пользователь будет явным образом об этом оповещен – путем отправки сообщения с информацией о том, что могло пойти не так.


Также, данный микросервис, как celery worker, слушает очередь «after_estimate», которая предназначается для тасков, прошедших через пайплайн оценки.


При получении новой таски из “after_estimate”:

  1. Если изображение обработано успешно – отправляем результат пользователю, если нет – оповещаем об ошибке
  2. Удаляем изображение, являющееся результатом пайплайна оценки

Микросервис оценки «attrai-estimator»



Данный микросервис является celery worker и инкапсулирует в себе всё, что связано с пайплайном оценки изображения. Алгоритм работы тут один – разберем его.


При получении новой таски из “to_estimate”:

  1. Прогоняем изображение через пайплайн оценки:
    1. Загружаем изображение в память
    2. Приводим изображение к нужному размеру
    3. Находим все лица (MTCNN)
    4. Оцениваем все лица (оборачиваем найденные в прошлом пункте лица в батч и инференсим ResNet34)
    5. Рендерим итоговое изображением
      1. Отрисоваем bounding boxes
      2. Отрисовываем оценки
  2. Удаляем пользовательское (исходное) изображение
  3. Сохраняем выход с пайплайна оценки
  4. Кладем таску в очередь “after_estimate”, которую слушает разобранный выше микросервис “attrai-telegram-bot”

Graylog (+ mongoDB + Elasticsearch)



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


Выбор пал именно на него, а не на привычный всем стэк, по причине удобства работы с ним из под Python. Все, что необходимо сделать для логирования в Graylog, это добавить GELFTCPHandler из пакета к остальным root logger handlers нашего python-микросервиса.


Я, как человек, который до этого работал только с ELK стэком, в целом, получил позитивный опыт во время работы с Graylog. Единственное, что удручает – превосходство по фичам Kibana над веб-интерфейсом Graylog.

RabbitMQ



— это брокер сообщений на основе протокола AMQP.


В данном проекте он использовался как брокер для Celery и работал в durable режиме.

Redis



— это NoSQL СУБД, работающая со структурами данных типа «ключ — значение»


Иногда возникает необходимость использовать в разных python-микросервисах общие объекты, реализующие какие-либо структуры данных.


Например, в Redis хранится hashmap вида «telegram_user_id => количество активных тасок в очереди», что позволяет ограничить количество запросов от одного пользователя определенным значением и, тем самым, предотвратить DoS-атаки.

Формализуем процесс успешной обработки изображения


  1. Пользователь отправляет изображение в Telegram бота
  2. «attrai-telegram-bot» получает сообщение от Telegram API и разбирает его
  3. Таск с изображением добавляется в асинхронную очередь «to_estimate»
  4. Пользователь получает сообщение с планируемым временем оценки
  5. «attrai-estimator» берет таск из очереди «to_estimate», прогоняет через пайплайн оценки и продьюсит таск в очередь «after_estimate»
  6. «attrai-telegram-bot», слушающий очередь «after_estimate», отправляет результат пользователю

DevOps



Наконец, после обзора архитектуры, можно перейти к не менее интересной части — DevOps

Docker Swarm





7p7hebq5SSsnEicY3c3yc0kWNAfqMaSAAhaGkCIrjBkYWEJrneNNQsIOs5qV_sWFufAoahReAJb7HM6bZEjxia2k3EJj5hIb3ZmEkKCzX_N2ltBovWINn8WsvF8RrbuaHyMxEOaa



- система кластеризации, функционал которой реализован внутри Docker Engine и доступен из коробки.


При помощи «роя», все ноды нашего кластера можно разделить на 2 типа – worker и manager. На машинах первого типа разворачиваются группы контейнеров (стэки), машины второго типа отвечают за скалирование, балансировку и . Менеджеры по умолчанию являются и воркерами.

Ud8wloKJw-ar6xsjY2ulbgQ1LGb8R9HXJuQySI-z8yws5q5XUsTYw8Z-0noIk6X_Te5fwxnPzLbFoXJTgIJe1DSF0Ye4i3nc2aYyD29uYjaZucM8vBvksfKSzTizbedwwuFo9-ux



Кластер с одним leader manager и тремя worker


Минимально возможный размер кластера – 1 нода, единственная машина будет одновременно выступать как leader manager и worker. Исходя из размера проекта и минимальных требований к отказоустойчивости, было принято решение использовать именно этот подход.


Забегая вперед, скажу, что с момента первой production-поставки, которая была в середине июня, проблем, связанных с данной организацией кластера, не было (но это не значит, что подобная организация хоть сколько-нибудь допустима в любых средне-крупных проектах, на которые накладываются требования по отказоустойчивости).

Docker Stack



В режиме «роя» за развертывание стэков (наборов docker services) отвечает


Он поддерживает docker-compose конфиги, позволяя дополнительно использовать deploy параметры.


Например, при помощи данных параметров были ограничены ресурсы на каждый из инстансов микросервиса оценки (выделяем на N инстансов N ядер, в самом микросервисе ограничиваем кол-во ядер, используемое PyTorch`ем, одним)


attrai_estimator:
image: 'erqups/attrai_estimator:1.2'
deploy:
replicas: 4
resources:
limits:
cpus: '4'
restart_policy:
condition: on-failure



Важно отметить, что Redis, RabbitMQ и Graylog — stateful сервисы и масштабировать их так же просто, как «attrai-estimator», не получится

Предвещая вопрос — почему не Kubernetes?



Кажется, что использование Kubernetes в проектах маленького и среднего размера – оверхед, весь необходимый функционал можно получить от Docker Swarm, который довольно user friendly для оркестратора контейнеров, а также имеет низкий порог вхождения.

Инфраструктура



Развертывалось это все на VDS со следующими характеристиками:

  • CPU: 4 ядра Intel® Xeon® Gold 5120 CPU @ 2.20GHz
  • RAM: 8 GB
  • SSD: 160 GB


После локального нагрузочного тестирования, казалось, что при серьезном наплыве пользователей, данной машинки будет хватать впритык.


Но, сразу после деплоя, я запостил ссылку на одну из самых популярных в СНГ имиджборд (ага, ту самую), после чего люди заинтересовались и за несколько часов сервис успешно обработал десятки тысяч изображений. При этом в пиковые моменты ресурсы CPU и RAM не были использованы даже наполовину.

hd7dGG80--PqMSIya_-nN9cvFAgGFYktyDZFO6K63KA1OhvcuXTnNmX4D1bnxNxC_gdiGeI8lf90zfiA733zFudargEnKgi8pFFsBXHWdbJOtgR-doz-Ms0tC7EjhCu0MHqNYtos


N0o3aWQG2PVvk7F3B9zCHFvjcbiAWlC8WfXH4LbZnpF22D60bKBqR6jcsi84-1-qZ67kOAGJ0PB4G7quz7J2T1LXPbiV0HEEnZQqd187XaISoKJwbjxlH8YslfPYqan1TJjKdWVp


Еще немного графики



Количество уникальных пользователей и запросов на оценку, с момента деплоя, в зависимости от дня

VhaBbu31SrbiOfu38DmJMNUo3pXj5KpxYtyLa3GBUb7gNCkKUvuPPcFH1pRODWijb0rpM_mUH_Y9jQ5fEflMlk6siduK6OFzXRJKBSHkW8ewkBGc6Qdsro9j9Mm82ShJT7045HOM



Распределение времени инференса пайплайна оценки

VZ_BCjkTF_wv3ilhV5iM0swRXLnEUw4vGyOH2hDYBWiRp58bRAolBi6XMi7oTSbv_W_DgLgH8lTpgOlyAbAdDvcDGn6ekjjGz2-AC93mi0fpi_s3mXS-xuGpoyJk9xIFsD8x5XGu


Выводы



Резюмируя, могу сказать, что архитектура и подход к оркестрации контейнеров полностью себя оправдали — даже в пиковые моменты не было падений и проседаний по времени обработки.


Думаю, проекты маленького и среднего размеров, использующие в своем процессе реалтайм инференс нейронных сетей на CPU, успешно могут перенять практики, описанные в данной статье.


Добавлю, что изначально статья была больше, но, дабы не постить лонгрид, решил некоторые моменты в данной статье опустить — вернемся к ним в следующих публикациях.


Потыкать бота можно в Telegram — @AttraiBot, работать будет, как минимум, до конца осени 2020 года. Напомню — никакие пользовательские данные не хранятся — ни исходные изображения, ни результаты пайплайна оценки — все сносится после обработки.
 
Сверху Снизу