Деплоим модель с Seldon MLServer

Полное руководство по интеграции и использованию YOLOv8 с Seldon MLServer для задач компьютерного зрения

 · 5 min read

Введение

В данной статье мы рассмотрим процесс проектирования и развертывания модели машинного обучения с использованием Seldon MLServer, особенно акцентируя внимание на использовании модели YOLOv8 от Ultralytics для задач компьютерного зрения. Seldon MLServer — это мощная среда для обслуживания моделей машинного обучения, основанная на Python, которая обеспечивает удобное взаимодействие с моделями в различных средах.

Что такое Seldon MLServer?

Seldon MLServer

Seldon MLServer — это специализированный сервер для обслуживания моделей, который поддерживает множество стандартов и фреймворков машинного обучения. Он позволяет легко интегрировать, масштабировать и управлять жизненным циклом моделей ML. MLServer поддерживает такие фреймворки, как Scikit-learn, XGBoost, MLflow и многие другие, обеспечивая при этом совместимость с стандартом Open Model Interface (OMI).

Интеграция YOLOv8 с Seldon MLServer

YOLO (You Only Look Once) - это популярный алгоритм объектного обнаружения, который был разработан для выполнения обнаружения объектов в реальном времени. Версия YOLOv8 представляет собой последнее улучшение в этой серии, обеспечивающее еще большую точность и скорость.

Шаги для интеграции:

Установка зависимостей:

Установите необходимые пакеты для работы YOLOv8 и Seldon MLServer.

pip install mlserver  ultralytics

Создание обертки для модели YOLOv8

Для интеграции с MLServer модель YOLOv8 должна быть обернута в специальный класс, который наследует MLModel

from mlserver.types import InferenceRequest, InferenceResponse, ResponseOutput
from ultralytics import YOLO  # Импорт класса YOLO для объектного обнаружения
import base64  # Импорт библиотеки для работы с base64 кодировкой
from PIL import Image  # Импорт класса Image для работы с изображениями
import numpy as np  # Импорт библиотеки NumPy для работы с массивами
from io import BytesIO  # Импорт класса BytesIO для работы с бинарными потоками
import json  # Импорт библиотеки для работы с JSON
import mlserver  # Импорт mlserver для серверной работы с ML моделями

# Определение класса модели YOLOv8 на основе MLModel из mlserver
class YOLOv8Model(mlserver.MLModel):
    async def load(self) -> bool:
        self._model = YOLO('yolov8n.pt')  # Загрузка официальной модели YOLOv8n
        return await super().load()  # Вызов метода загрузки базового класса

    async def predict(self, payload: InferenceRequest) -> InferenceResponse:
        img = self.base64_to_img(payload.inputs[0].data[0])  # Преобразование входной строки base64 в изображение
        results = self._model(img)  # Выполнение обнаружения объектов на изображении
        response_outputs = []  # Список для хранения результатов

        # Обход результатов обнаружения объектов
        for result in results:
            # Добавление информации о прямоугольниках объектов
            response_outputs.append(ResponseOutput(
                name="boxes",
                shape=result.boxes.xywh.shape,
                datatype="FP32",
                data=self.serialize_numpy(result.boxes.xywh)
            ))
            # Добавление информации о вероятностях обнаружения
            response_outputs.append(ResponseOutput(
                name="probs",
                shape=result.boxes.conf.shape,
                datatype="FP32",
                data=self.serialize_numpy(result.boxes.conf)
            ))
            # Добавление информации о классах объектов
            response_outputs.append(ResponseOutput(
                name="cls",
                shape=result.boxes.cls.shape,
                datatype="FP32",
                data=self.serialize_numpy(result.boxes.cls)
            ))
        return InferenceResponse(
                model_name="yolov8n",
                model_version="v1",
                outputs=response_outputs
            )

    def base64_to_img(self, b64str):
        """Преобразование строки base64 в изображение."""
        image_data = base64.b64decode(b64str)  # Декодирование строки base64
        image = Image.open(BytesIO(image_data))  # Создание изображения из бинарных данных
        image = image.convert('RGB')  # Преобразование изображения в формат RGB
        return np.array(image)  # Возврат изображения в виде массива numpy

    def serialize_numpy(self, np_array):
        """Сериализация массива numpy в строку JSON."""
        return json.dumps(np.array(np_array).tolist())  # Преобразование и сериализация массива numpy в JSON

Запуск сервера:

Запустите MLServer с указанной конфигурацией.

mlserver start .

Теперь напишем код для тестирования модели

import requests  # Импортируем библиотеку для отправки HTTP-запросов
import json  # Импортируем библиотеку для работы с JSON
import base64  # Импортируем библиотеку для кодирования и декодирования в Base64
import matplotlib.pyplot as plt  # Импортируем модуль pyplot из библиотеки matplotlib для визуализации данных
import matplotlib.patches as patches  # Импортируем модуль patches для создания фигур
from PIL import Image  # Импортируем класс Image из библиотеки PIL для работы с изображениями

# Словарь меток классов (можно расширить в соответствии с классами вашей модели)
class_labels = {
    15: 'Cat',  # ID 15 соответствует категории "Кошка"
    16: 'Dog'   # ID 16 соответствует категории "Собака"
}

def encode_image_to_base64(image_path):
    """
    Кодирует изображение в строку Base64.

    :param image_path: Путь к файлу изображения.
    :return: Строка, закодированная в Base64.
    """
    with open(image_path, "rb") as image_file:  # Открываем файл в бинарном режиме
        base64_encoded = base64.b64encode(image_file.read()).decode('utf-8')  # Читаем, кодируем в Base64 и декодируем в строку
    return base64_encoded  # Возвращаем закодированную строку

def send_image_to_mlserver(image_path, url):
    """
    Отправляет POST запрос на MLServer с изображением в формате Base64.

    :param image_path: Путь к файлу изображения.
    :param url: URL MLServer для обработки запросов.
    :return: Ответ сервера.
    """
    encoded_image = encode_image_to_base64(image_path)  # Кодируем изображение
    request_data = {
        "inputs": [
            {
                "name": "image",  # Имя входного параметра
                "shape": [1],  # Форма данных
                "datatype": "BYTES",  # Тип данных
                "data": [encoded_image]  # Данные
            }
        ]
    }  # Формируем тело запроса в формате JSON
    response = requests.post(url, json=request_data)  # Отправляем POST-запрос
    return response.json()  # Возвращаем ответ сервера в виде JSON

image_path = './image-2.jpeg'  # Путь к изображению
mlserver_url = 'http://localhost:8080/v2/models/yolov8-model/infer'  # URL вашего MLServer
response = send_image_to_mlserver(image_path, mlserver_url)  # Отправляем изображение на сервер и получаем ответ

boxes = json.loads(response['outputs'][0]['data'])  # Разбираем JSON ответ для получения координат рамок
probs = json.loads(response['outputs'][1]['data'])  # Разбираем JSON ответ для получения вероятностей
classes = json.loads(response['outputs'][2]['data'])  # Разбираем JSON ответ для получения классов объектов

img = Image.open(image_path)  # Загружаем изображение
fig, ax = plt.subplots()  # Создаем фигуру и оси для рисования

ax.imshow(img)  # Отображаем изображение

for box, prob, cls in zip(boxes, probs, classes):  # Для каждого объекта
    x, y, w, h = box  # Координаты и размеры рамки
    rect = patches.Rectangle((x-w/2, y-h/2), w, h, linewidth=2, edgecolor='r', facecolor='none')  # Создаем прямоугольник
    ax.add_patch(rect)  # Добавляем прямоугольник на график
    label = f"{class_labels[int(cls)]}: {prob:.2f}"  # Текст метки
    plt.text(x-w/2, y-h/2, label, color='white', fontsize=12, bbox=dict(facecolor='red', alpha=0.5))  # Добавляем текст метки

plt.show()  # Отображаем результат

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

Добавляем кастомные метрики

Теперь нам необходимо понимать какие запросы отправляет пользователь, для этого можно использовать экспорт касмтоных метрик

С помощью mlserver.register можно создать метрику для экпорта в модуль Prometheus. как пример мы добавим экпосрт меитрики в количесте обнаруженных объектов. Модифицируем метод load

    async def load(self) -> bool:
        self._model = YOLO('yolov8n.pt')  # Загрузка официальной модели YOLOv8n
        # Регистрация функции логирования для обнаружения объектов
        mlserver.register("count_obj_detection", "This is a count objects detection")
        return await super().load()  # Вызов метода загрузки базового класса

C помощью команды mlserver.register регистрируем кастомную метрику и теперь заполняем ее в методе predict

   async def predict(self, payload: InferenceRequest) -> InferenceResponse:
        ...остальной код...
        # Логирование количества обнаруженных объектов
        mlserver.log(count_obj_detection=len(response_outputs))
        return InferenceResponse(
                model_name="yolov8n",
                model_version="v1",
                outputs=response_outputs
            )

Метрики по умолчанию доступны http://0.0.0.0:8082/metrics

Сборка орбраза

Соберем все используемые библиоетеки в файлы requirements.txt

Запустем сборку образа черещ mlserver

mlserver build ./ -t 'yolov8_ml_service'

Полный репозиторий с кодом можете найти в репозитории https://github.com/MLSystem-Design/mlserver-yolov8

Заключение

MLServer от Seldon предоставляет мощные и гибкие возможности для развертывания моделей машинного обучения в производственной среде. Используя интеграцию с YOLOv8, разработчики могут эффективно обрабатывать задачи компьютерного зрения, улучшая при этом мониторинг и анализ производительности моделей с помощью кастомных метрик.

Подписывайтесь на телеграмм канал там больше материла про ML https://t.me/ml_pops


No comments yet.

Добавить комментарий
Ctrl+Enter, чтобы добавить комментарий