Как стать автором
Поиск
Написать публикацию
Обновить

Распознаем автомобильные номера на TorchServe

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров6.9K

Вокруг так много фреймворков для инференса нейронок, что глаза разбегаются. Продолжаем цикл о реализации сервинга одной задачи, но разными инструментами. В прошлый раз реализация была на Nvidia Triton Inference Serve (за анонсами прошу в мой телеграм канал. Код к статье находится в репозитории.

Задача

В качестве задачи взято распознавание российских автомобильных номеров. Модели были взяты из этого репозитория.

Пайплайн распознавания следующий:
1. Детекция номеров с помощью Yolov5;
2. Вырезанные номера прогоняются через Spatial transformer (STN) для выравнивания;
3. Текст номера распознается с LPR-net.

Фреймворк

Для инференса используется [TorchServe]. Данный фреймворк является частью экосистемы Pytorch. Он активно развивается.

В документации о нем говорится следующее:

TorchServe is a performant, flexible and easy to use tool for serving PyTorch eager mode and torschripted models.

Возможности:

Конвертируем модели

Как и Triton, TorchServe требует от пользователя перевести модели в свой формат. Для этого есть утилиты torch-model-archiver и torch-workflow-archiver для моделей и графов соответственно.

Для конвертации нам нужно:

  1. Модель в формате TorchServe/Onnx/др.;

  2. Скрипт, описывающий пайплайн работы модели.

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

Модели STN и LPR легко конверуются в TorchServe, поэтому в их хэндлерах не используются дополнительные библиотеки. Импорты выглядят так:

import json
import logging
from abc import ABC
import numpy as np
import torch
from ts.torch_handler.base_handler import BaseHandler

Yolo нельзя было просто перевести в TorchScript, так как часть логики для обработки запросов оставалась снаружи модели. Так как копаться с этим желания не было, а также ради более приближенного к жизни сценария, в хэндлере модели Yolo инициализируется из TorchHub. В импортах мы уже видим и сторонние модули:

from inference_torchserve.data_models import PlatePrediction
from nn.inference.predictor import prepare_detection_input, prepare_recognition_input
from nn.models.yolo import load_yolo
from nn.settings import settings

Чтобы это работало, необходимо в докерфайле установить в глобальный интерпретатор необходимые вам пакеты.

В TorchServe не нужно жестко задавать тип и размерность входов и выходов модели, поэтому никакие конфиги для моделей определять не нужно. С одной стороны это удобно, а с другой - порождает хаос, если не следовать какому-то одному формату.

Конвертированная модель представляет собой zip архив с расширением .mar, в котором лежат все артефакты (служебная информация, веса, скрипты и дополнительные файлы).

.
├── MAR-INF
│   └── MANIFEST.json
├── stn.pt
└── stn.py

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

Чтобы TorchServe загрузил модели, их нужно положить в одну папку - model storage и указать путь до нее в параметрах. Чтобы при запуске поднимались все модели, необходимо указать --models all.

Делаем пайплайн распознавания

Выбранный пайплайн распознавания номера автомобиля состоит из последовательного предсказания несколькими моделями. Для этого в TorchServe есть Workflow. Он позволяет задать как последовательный, так и параллельный граф обработки:

# последовательный 
dag:
  pre_processing : [m1]
  m1 : [m2]
  m2 : [postprocessing]
input -> function1 -> model1 -> model2 -> function2 -> output
# параллельный граф
dag:
  pre_processing: [model1, model2]
  model1: [aggregate_func]
  model2: [aggregate_func]
                          model1
                         /       \
input -> preprocessing ->         -> aggregate_func
                         \       /
                          model2

Для рассматриваемой задачи получился следующий последовательно-параллельный граф. Узел aggregate объединяет координаты номеров с распознанными текстами.

    ┌──────┐
    │ YOLO ├─────┐
    └──┬───┘     │
       │         v
       │      ┌─────┐
plate  │      │ STN │
coords │      └──┬──┘
       │         │
       │         v
       │      ┌──────┐
       │      │LPRNET│
       │      └──┬───┘
       v         │
   ┌─────────┐   │ plate
   │aggregate│<──┘ texts
   └─────────┘

Для удобства и простоты данные между моделями передаются в виде словарей. Сериализация таких данных в TorchServe весьма неэффективна (переводят в строку и добавляют переносов строк), поэтому старайтесь передавать их как тензоры или байты.

Учтите, что workflow нельзя стартовать автоматически при запуске сервера - необходимо явно послать запрос на это. Если очень хочется делать при поднятии сервера, то можно так.

curl -X POST http://localhost:8081/workflows?url=plate_recognition

Использование моделей

Модели определены. Сервер запущен.

Чтобы выполнить определенную ранее модель или workflow нужно послать в TorchServe запрос на использование plate_recognition (я пользовался REST, но есть еще и GRPC). Для моделей используется эндпоинт predictions, а для workflow wfpredict.

response = requests.post(
    "http://localhost:8080/predictions/yolo", data=image.open("rb").read()
)

response = requests.post(
    "http://localhost:8080/wfpredict/plate_recognition", 
    data=image.open("rb").read()
)

Заключение

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

В этом туториале были раскрыты не все возможности TorchServe, поэтому советую посмотреть в документации про:

Подписывайтесь на мой канал - там я рассказываю про нейронки с упором в сервинг.

Теги:
Хабы:
Всего голосов 4: ↑3 и ↓1+3
Комментарии0

Публикации

Ближайшие события