Создание интеллектуальных решений, основанных на данных и использующих модели машинного обучения, является одной из наиболее интересных новых тем. Но любой, кто пытался создавать такие решения, знает, что в проектировании архитектуры есть некоторые важные моменты, когда все становится очень сложным и очень быстрым. В этом посте мы рассмотрим проблемы, с которыми вы можете столкнуться при «обслуживании» моделей в конечной точке, и то, как вы можете преодолеть эти проблемы, не создавая сложную архитектуру вокруг вашего приложения.

Введение

Традиционно запуск моделей машинного обучения в производственной среде включал в себя организацию сложных настроек с использованием таких компонентов, как очереди сообщений (например, Redis или RabbitMQ) и рабочие платформы, такие как Celery. Эти системы были объединены для решения таких задач, как балансировка нагрузки, управление рабочими процессами и поддержание связи между различными частями приложения. Несмотря на свою эффективность, этот подход часто требовал значительных инженерных усилий для обеспечения отказоустойчивости, эффективного масштабирования и реагирования с малой задержкой.

Здесь Ray Serve — это фреймворк, который упрощает это путешествие. Благодаря Ray Serve процесс развертывания моделей машинного обучения упрощается, позволяя избежать большей части сложной настройки. Ray Serve элегантно сочетает в себе мощь очередей сообщений и рабочих процессов, предоставляя единое комплексное решение для развертывания, управления и масштабирования моделей. Такой подход устраняет необходимость отдельного управления несколькими компонентами и значительно снижает сложность, связанную с традиционными установками. Интуитивно понятный API Ray Serve позволяет разработчикам сосредоточиться на основных функциях своих моделей, в то время как платформа заботится о распределении нагрузки, отказоустойчивости и динамическом масштабировании. Этот сдвиг не только ускоряет процесс развертывания, но также повышает надежность и оперативность обслуживаемых моделей.

Выполнение

Одна из важнейших практик, которую я лично считаю бесценной при работе с обслуживанием моделей, — это стратегическое разделение обслуживаемой модели машинного обучения в отдельный контейнер Docker. Изолируя модель внутри собственного контейнера, она получает независимость от сложностей остальной части приложения. Это не только способствует модульности и удобству сопровождения, но также значительно упрощает процесс масштабирования и включения новых моделей в систему. Каждый контейнер модели становится автономным блоком, невосприимчивым к изменениям или обновлениям в других компонентах, что обеспечивает плавное масштабирование, не затрагивая все приложение. Этот модульный подход идеально соответствует гибкому характеру современной разработки программного обеспечения и исключительно хорошо гармонирует с такими средами, как Ray Serve, где независимыми контейнерами можно динамически управлять и масштабировать в соответствии с требованиями. Когда мы исследуем нюансы этой практики, ее роль в упрощении конвейеров развертывания и ускорении жизненного цикла разработки становится совершенно очевидной.

Для этого разработаем следующую файловую структуру:

- Dockerfile
- model_deployment.py
- requirements.txt

Эта простая файловая структура позволяет нам запускать модель, просто запустив model_deployment.py.

import json
from typing import Dict

import torch
from starlette.requests import Request

import ray
from ray import serve
from ray.serve.drivers import DAGDriver

from sentence_transformers import SentenceTransformer


# Asynchronous function to resolve incoming JSON requests
async def json_resolver(request: Request) -> dict:
    """
    Resolve incoming JSON requests asynchronously.

    Args:
        request: The incoming HTTP request containing JSON data.

    Returns:
        A dictionary representing the parsed JSON data.
    """
    return await request.json()


# Step 1: Wrap the pretrained sentiment analysis model in a Serve deployment.
@serve.deployment
class ModelDeployment:
    def __init__(self):
        """
        Initialize the ModelDeployment class.

        This constructor initializes the class and loads the pretrained sentiment analysis model.
        """
        self._model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')

    def __call__(self, data: dict) -> Dict:
        """
        Embed texts using sentence transformers.

        Args:
            data: The input data containing a list of texts to embed.

        Returns:
            A dictionary containing embeddings of input texts, each represented as a list of floats.
        """
        input_texts = json.loads(data)['input']
        embeddings = [torch.from_numpy(self._model.encode(text, convert_to_numpy=True)).tolist() for text in input_texts]
        response = {'data': embeddings}
        return response


# Step 2: Deploy the model deployment.
ray.init(address='ray://localhost:10001')
serve.run(DAGDriver.bind(ModelDeployment.bind(), http_adapter=json_resolver), host="0.0.0.0", port=8888)

И здесь, изменяя значения в декораторе serve.deployment, вы можете управлять развертыванием, добавляя больше реплик (num_replicas), устанавливая периоды проверки работоспособности, максимальное количество одновременных запросов и многое другое, доступное в документации. (https://docs.ray.io/en/latest/serve/api/doc/ray.serve.deployment_decorator.html#ray-serve-deployment)

Класс ModelDeployment: оборачивает предварительно обученную модель анализа настроений с помощью декоратора @serve.deployment. Метод __init__ инициализирует модель, загружая модель «sentence-transformers/all-mpnet-base-v2». Метод __call__ принимает словарь в качестве входных данных, извлекает список текстов для встраивания и выполняет итерацию по ним, генерируя вложения с использованием модели Sentence Transformers. Вложения накапливаются и возвращаются в виде словарного ответа.

Развертывание модели: Функция ray.init инициализирует кластер Ray, указывая адрес, по которому работает кластер. Затем функция serve.run привязывает класс ModelDeployment к DAGDriver, используя функцию json_resolver для обработки входящих запросов. Развертывание размещается на «0.0.0.0» через порт 8888.

# Dockerfile

# Set the base image to Python 3.10
FROM python:3.10

# Set the working directory within the container
WORKDIR /app

# Upgrade pip
RUN pip3 install --upgrade pip

# Copy the requirements file and install the dependencies
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

# Copy the model_deployment.py script into the container
COPY model_deployment.py ./

# Expose port 8888 for external access
EXPOSE 8888

# Define the command to run when the container starts
CMD [ "bash", "-c", "ray start --head --block --object-manager-port=8076 --include-dashboard=true --dashboard-host=0.0.0.0 --dashboard-port=8266"]

Этот Dockerfile — это краткий и эффективный способ контейнеризации развертывания вашей модели машинного обучения. В нем указаны необходимые шаги для настройки среды, установки зависимостей и запуска кластера Ray.

Таким образом, этот Dockerfile настраивает среду Python 3.10, устанавливает необходимые зависимости из файла requirements.txt и копирует сценарий model_deployment.py в рабочий каталог контейнера. Директива EXPOSE делает порт 8888 доступным извне контейнера, обеспечивая связь с развернутой моделью. Наконец, инструкция CMD запускает кластер Ray с определенными конфигурациями, включая запуск панели управления Ray, доступной через порт 8266.

# requirements.txt

ray[serve]~=2.0.1
starlette==0.20.4
sentence-transformers==2.2.2
torch==2.0.0
pandas==2.0.1

В вашем файле requirements.txt перечислены необходимые зависимости для развертывания вашей модели машинного обучения с помощью Ray Serve. Он включает в себя определенные версии различных пакетов для обеспечения совместимости. Некоторые из них можно редактировать в зависимости от времени публикации этой статьи.

Теперь мы можем использовать команду docker-compose up, которая автоматически запускает контейнерную среду. Используя sudo docker exec -it container-id /bin/bash, вы можете получить доступ к терминалу внутри работающего контейнера. Это дает вам возможность напрямую участвовать в развертывании, проверять его поведение и при необходимости вносить изменения в режиме реального времени. Кроме того, запуск сценария model_deployment.py из контейнера позволяет развернуть модель машинного обучения, используя возможности Ray Serve для плавного развертывания и масштабирования.

Более того, мы можем настроить Dockerfile для запуска model_deployment.py при запуске, мгновенно развертывая модель.

Заключение

В этом посте мы быстро продемонстрировали, как мы можем развернуть модель преобразователя предложений (или любую другую модель) в масштабируемой среде без разработки сложной архитектуры, просто используя библиотеку Ray Serve.

Модель развернута. Приятного кодирования :)