Serving & Production
~25 мин

Model Serving

FastAPI, TorchServe, Triton, vLLM — деплой моделей, батчинг, кеширование.

Model Serving — модель без API бесполезна

Ты обучил модель, получил отличные метрики, похвастался коллегам. Но пока модель сидит в .pkl файле на твоём ноутбуке — она не приносит ни одного рубля. Бизнесу нужны предсказания, а не модели. Model serving — это всё, что превращает обученную модель в работающий сервис: API, инфраструктура, масштабирование, оптимизации.

Аналогия: обучить модель — это приготовить блюдо на домашней кухне. Model serving — это открыть ресторан: тебе нужны повара (inference серверы), официанты (API), менеджер зала (load balancer), система доставки (batch pipeline), контроль качества (мониторинг). И всё это должно работать 24/7, даже когда наплыв гостей в 10× больше обычного.

Три паттерна model serving: online API, batch scoring, streaming
Online для API, Batch для массовой обработки, Streaming для real-time событий

Большая картина: от модели до production-сервиса

Путь модели в продакшн выглядит так: 1. Модель — обученный артефакт (.pkl, .onnx, .pt, SavedModel). Загружается из Model Registry или S3. 2. Inference-сервер — процесс, который держит модель в памяти и отвечает на запросы. Это может быть FastAPI, Triton, TorchServe, vLLM — зависит от модели. 3. API Gateway / Load Balancer — распределяет запросы между несколькими репликами сервера. Nginx, Envoy, или облачный ALB. 4. Мониторинг — Prometheus + Grafana. Следит за latency, throughput, error rate, GPU utilization. Алертит, когда что-то пошло не так. 5. Auto-scaling — Kubernetes HPA или облачный аналог. Поднимает новые реплики при нагрузке, убирает лишние в тишине.

Весь pipeline: пользователь → API Gateway → один из N inference-серверов → модель делает предсказание → ответ обратно. Параллельно: Prometheus скрейпит метрики, Grafana рисует дашборды, HPA масштабирует при необходимости.

Batch vs Online inference: когда что использовать

Первый вопрос, который задаёшь перед деплоем: нужны ли предсказания в реальном времени? От ответа зависит вся архитектура.

Batch inference — предсказания заранее, для всех. Ставишь cron-джобу: каждую ночь прогоняешь модель на всей базе клиентов, результаты складываешь в таблицу. Утром маркетинг берёт оттуда скоры для email-рассылки.

• Рекомендации для email-рассылки (персонализация раз в день) • Кредитный скоринг всех заявок за ночь • Предсказание оттока — обновляется раз в неделю • Генерация эмбеддингов для каталога товаров

Online inference — предсказание по запросу, в реальном времени. Пользователь открыл приложение → backend отправил фичи в модель → модель ответила за 50ms → пользователь видит персональные рекомендации.

• Антифрод: блокировать транзакцию за <50ms • Поисковая выдача: ранжировать результаты за <100ms • Чат-бот: ответить пользователю за <2s • Автокомплит: предложить варианты за <30ms

Latency budget — ключевое ограничение для online serving. Типичные требования: автокомплит 30ms, антифрод 50ms, рекомендации 100-200ms, LLM генерация 1-5s (с streaming). Если модель не укладывается — оптимизируй (ONNX, квантизация) или переходи на batch.

💡 На практике

80% ML-моделей в продакшне работают в batch-режиме. Это проще, дешевле и надёжнее. Online inference нужен только когда контекст зависит от текущего запроса или latency критична. Не усложняй архитектуру без необходимости.

FastAPI: REST API для модели за 20 минут

Для CatBoost, LightGBM, sklearn, маленьких нейронок — FastAPI закрывает 80% задач. Асинхронный, быстрый, с автодокументацией (Swagger UI). Модель загружается один раз при старте приложения, дальше просто отвечает на запросы.

from fastapi import FastAPI
from pydantic import BaseModel
import joblib, numpy as np

app = FastAPI(title="Churn Prediction API")
model = joblib.load("models/churn_v3.pkl")  # загружаем ОДИН РАЗ при старте

class Features(BaseModel):
    age: int
    tenure_months: int
    monthly_charges: float
    total_charges: float

@app.post("/predict")
async def predict(f: Features):
    X = np.array([[f.age, f.tenure_months, f.monthly_charges, f.total_charges]])
    proba = model.predict_proba(X)[0][1]
    return {"churn_probability": round(float(proba), 4)}

@app.get("/health")
async def health():
    return {"status": "ok", "model_version": "v3"}

Для production — запускай через gunicorn с несколькими workers: gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker. Каждый worker — отдельный процесс с копией модели в памяти. 4 workers ≈ 4 параллельных предсказания. Для GPU-моделей обычно 1 worker (GPU не шарится между процессами просто так).

gRPC: когда REST недостаточно быстр

REST (JSON over HTTP/1.1) — универсальный и простой. Но у него есть overhead: JSON-сериализация, текстовый формат, HTTP/1.1 без мультиплексирования. Когда нужна максимальная скорость — используют gRPC: бинарный протокол (Protobuf) поверх HTTP/2.

Когда gRPC: • Микросервисная архитектура — сервисы общаются друг с другом тысячи раз в секунду • Latency-critical — экономишь 2-5ms на сериализации (важно, когда бюджет 30ms) • Большие payloads — Protobuf в 5-10× компактнее JSON • Streaming — gRPC поддерживает server-side streaming из коробки (для LLM генерации) Когда REST: • Внешнее API для клиентов (браузер, мобилка) — REST проще интегрировать • Прототип / MVP — FastAPI поднимается за 20 минут • Отладка — JSON читаемый, Protobuf — нет

// prediction.proto   API
syntax = "proto3";

service PredictionService {
  rpc Predict (PredictRequest) returns (PredictResponse);
  rpc PredictStream (PredictRequest) returns (stream Token);  //  LLM
}

message PredictRequest {
  repeated float features = 1;
  string model_name = 2;
}

message PredictResponse {
  float score = 1;
  string label = 2;
}

На практике: внутри ML-системы (между сервисами) — gRPC. Наружу для клиентов — REST. Triton и TorchServe поддерживают оба протокола из коробки.

Форматы моделей: ONNX, TorchScript, SavedModel

Обучил модель в PyTorch — но inference-сервер не обязан знать про PyTorch. Модель можно экспортировать в оптимизированный формат, который работает быстрее и не требует тяжёлых зависимостей.

ONNX (Open Neural Network Exchange) — универсальный формат. Конвертируешь из PyTorch, TensorFlow, sklearn — запускаешь через ONNX Runtime. Inference в 2-5× быстрее за счёт graph-level оптимизаций (operator fusion, constant folding). Поддерживается Triton, Azure ML, многими edge-платформами.

# Экспорт PyTorch → ONNX → inference
import torch
import onnxruntime as ort

# Экспорт
dummy_input = torch.randn(1, 768)  # пример входа
torch.onnx.export(model, dummy_input, "model.onnx",
                  input_names=["input"], output_names=["output"],
                  dynamic_axes={"input": {0: "batch"}})  # динамический batch

# Inference через ONNX Runtime (не нужен PyTorch!)
session = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
result = session.run(None, {"input": input_array})  # numpy in → numpy out

TorchScript — формат PyTorch. Два режима: torch.jit.trace (записывает execution graph на примере) и torch.jit.script (компилирует Python-код). Быстрее чистого PyTorch, не нужен Python runtime для inference. Используется в TorchServe и C++ inference. SavedModel — формат TensorFlow/Keras. Содержит граф вычислений + веса. Используется в TF Serving и Vertex AI. Что выбрать: ONNX — если нужна переносимость и скорость (самый универсальный). TorchScript — если экосистема PyTorch. SavedModel — если TensorFlow. Для классических ML-моделей (sklearn, CatBoost) — просто joblib.dump(), ONNX не нужен.

Масштабирование: горизонтальное, GPU sharing, batching

Один inference-сервер обрабатывает 100 RPS (requests per second). Тебе нужно 1000 RPS. Как масштабироваться?

Горизонтальное масштабирование — запускаешь N реплик сервера за load balancer. В Kubernetes: Deployment с replicas: 10 + Service + HPA (Horizontal Pod Autoscaler). HPA мониторит CPU/GPU utilization или custom metrics (queue length, latency p99) и автоматически добавляет/убирает поды.

Dynamic Batching — главная оптимизация для GPU. GPU эффективна на батчах: обработать 32 запроса одним батчем почти так же быстро, как 1 запрос. Inference-сервер накапливает входящие запросы за окно 1-10ms, собирает в батч, прогоняет через GPU одним вызовом. Throughput вырастает в 10-30×, latency увеличивается лишь на размер окна.

GPU sharing — одна GPU дорогая, а CPU-bound модели (CatBoost) не нужна вся GPU. Решения: • Multi-process service (MPS) — несколько процессов на одной GPU (NVIDIA MPS) • Time-slicing — GPU делит время между несколькими подами в Kubernetes • Multi-model serving — Triton держит несколько моделей на одной GPU, переключается по запросу • Правильный подход — CPU-модели на CPU-подах, GPU-модели на GPU-подах. Не ставь CatBoost на GPU-ноду.

Inference-серверы: Triton, TorchServe, BentoML

Когда FastAPI не хватает — есть специализированные inference-серверы, заточенные под production ML.

NVIDIA Triton Inference Server — Swiss Army knife для inference. Поддерживает PyTorch, TensorFlow, ONNX, XGBoost, Python-модели. Dynamic batching, multi-GPU, model ensembles (цепочка моделей в одном запросе), A/B testing между версиями модели. Ты кладёшь модель в model_repository/, пишешь config.pbtxt — Triton делает остальное.

# Структура Triton model repository
model_repository/
 churn_model/
    config.pbtxt          # конфигурация модели
    1/                    # версия 1
        model.onnx        # сама модель
 embedding_model/
    config.pbtxt
    1/
        model.pt

# Запуск Triton
docker run --gpus all -p 8000:8000 -p 8001:8001 
  -v (pwd)/model_repository:/models 
  nvcr.io/nvidia/tritonserver:24.01-py3 
  tritonserver --model-repository=/models

# HTTP: localhost:8000, gRPC: localhost:8001

TorchServe — inference-сервер от PyTorch. Проще Triton для чисто PyTorch-моделей. Поддерживает model versioning, logging, metrics из коробки. Батчинг, multi-GPU, custom handlers для пре/постпроцессинга.

BentoML — Python-first фреймворк: пишешь serving-логику как обычный Python-код, BentoML упаковывает в Docker, добавляет batching, API, метрики. Идеален для DS-ов, которые не хотят разбираться в Kubernetes. Код → bentoml build → Docker image → деплой куда угодно.

vLLM — специализированный сервер для LLM (GPT, LLaMA, Mistral). PagedAttention для эффективного использования GPU-памяти, continuous batching, OpenAI-совместимый API одной командой. Если деплоишь LLM — это стандарт де-факто.

💡 Что выбрать

CatBoost/sklearn/маленькие модели → FastAPI (просто и достаточно). GPU-модели (embeddings, NLP, CV) → Triton (dynamic batching, multi-model). Только PyTorch → TorchServe (проще настройки). DS без DevOps-экспертизы → BentoML (Python-first). LLM → vLLM (PagedAttention, continuous batching).

A/B testing и canary deployment для моделей

Новая модель показала AUC +2% на offline-метриках. Деплоим на 100% трафика? Нет. Offline-метрики не гарантируют улучшение бизнес-метрик. Нужно проверить на реальном трафике.

Shadow mode — новая модель получает тот же трафик, что и старая, но её предсказания никому не показываются. Ты сравниваешь ответы двух моделей без риска. Идеально для начала: ловишь баги, проверяешь latency, убеждаешься что модель не падает.

Canary deployment — направляешь 5% трафика на новую модель, 95% — на старую. Следишь за метриками: latency, error rate, бизнес-показатели (CTR, конверсия). Если всё ок — увеличиваешь до 25%, 50%, 100%. Если нет — мгновенный rollback на старую версию.

A/B test — полноценный эксперимент: 50/50 (или другое соотношение), рандомизация по пользователям, статистический критерий для принятия решения. Длится дни-недели. Измеряешь бизнес-метрику (revenue, retention), а не только ML-метрику.

Типичный rollout: shadow mode (1-2 дня) → canary 5% (1 день) → canary 25% (2-3 дня) → A/B 50/50 (1-2 недели) → full rollout 100%. В Kubernetes реализуется через Istio/Argo Rollouts, в Triton — через model versioning.

🎯 На собеседовании

Junior

Как задеплоить ML-модель? Обернуть в FastAPI, загрузить модель при старте, endpoint /predict. Docker для упаковки, gunicorn для нескольких workers. • Batch vs online inference? Batch: предсказания заранее, cron + pandas/Spark. Online: по запросу в реальном времени, API с latency <200ms. • Что такое ONNX? Универсальный формат моделей. Конвертируешь из PyTorch/TF → inference через ONNX Runtime, в 2-5× быстрее. • Зачем health check endpoint? Load balancer проверяет, жив ли сервис. Если /health не отвечает — перестаёт слать трафик на этот инстанс.

Middle

Как ускорить inference? Dynamic batching (собрать запросы в батч для GPU), ONNX Runtime (graph-level оптимизации), квантизация (INT8 вместо FP32, 2-4× speedup), кеширование в Redis (одинаковые запросы), distillation (маленькая модель учится у большой). • Когда gRPC вместо REST? Между микросервисами (бинарный Protobuf в 5-10× компактнее JSON), latency-critical (<50ms), server-side streaming для LLM. Наружу — REST (проще интеграция). • Как масштабировать inference? Горизонтально: N реплик за load balancer, Kubernetes HPA по CPU/GPU utilization. Вертикально: dynamic batching, ONNX, квантизация. • Что такое canary deployment для модели? 5% трафика на новую модель, 95% на старую. Следишь за latency + бизнес-метриками. Если ок — плавно увеличиваешь.

Senior

Спроектируй serving-систему для рекомендаций (10K RPS, <100ms). Двухстадийный pipeline: candidate generation (ANN index, batch-precomputed) → ranking model (online, Triton + ONNX + dynamic batching). Feature store (Redis) для user/item фичей. 20 GPU-реплик за Envoy, HPA по p99 latency. Canary через Istio, мониторинг Prometheus + бизнес-дашборд. • Как обеспечить zero-downtime deployment модели? Rolling update в Kubernetes (maxSurge/maxUnavailable), graceful shutdown (дождаться in-flight запросов), readiness probe (новый под отвечает только когда модель загружена). Для GPU-моделей: pre-pull image + warm-up requests. • Triton vs vLLM vs TorchServe — когда что? Triton: multi-framework, multi-model, model ensembles, maximum control. vLLM: LLM-specific (PagedAttention, continuous batching, OpenAI-compat API). TorchServe: чистый PyTorch, проще Triton. Выбор по модели и команде. • Как обрабатывать GPU OOM в production? Лимиты памяти в config, dynamic batching с max_batch_size, мониторинг GPU memory через DCGM, graceful degradation (откат на CPU или уменьшение batch).

Собираем всё вместе

Model serving — это мост между обученной моделью и пользователем. Batch inference — для предсказаний заранее (cron + Spark). Online inference — для реального времени (API + load balancer + autoscaling). FastAPI — для старта и простых моделей. Triton/TorchServe/BentoML — для production GPU-моделей. vLLM — для LLM. ONNX — для ускорения и переносимости. Dynamic batching — для эффективного использования GPU. Canary deployment — для безопасного обновления.

Если запомнить одну вещь из этой ноды: модель приносит ценность только когда она отвечает на запросы в продакшне. Всё остальное — инструменты, чтобы это было надёжно, быстро и масштабируемо.

Дальше на роадмапе: Monitoring — как отслеживать деградацию модели на проде, A/B Testing — полный цикл ML-эксперимента, Docker — упаковка модели в контейнер.