Экспорт моделей для edge: ONNX и TensorRT

Задача страницы. Пошагово показать, как из обученной CV‑модели получить лёгкий и быстрый edge‑пакет (ONNX → TensorRT), не потеряв качество: аудит препроц/постпроц, экспорт в ONNX с динамическими формами, сборка TensorRT‑движка в FP16/INT8, валидация точности/латентности, упаковка в контейнер, телеметрия и безопасные обновления.

TL;DR

Когда нужен edge‑экспорт

  • Видео «на месте»: камеры/роботы/АТС, когда трафик дорог или приватность критична.
  • AR/мобильные сценарии: малая латентность, ограниченный бюджет GPU/энергии.
  • Полевые датчики: работа офлайн, периодическая синхронизация.
  • Фильтрация у источника: предварительное отсечение событий перед отправкой в облако.

Аудит модели перед экспортом

  1. Зафиксируйте препроц: resize + letterbox, порядок каналов, нормализацию, типы (uint8→fp16).
  2. Зафиксируйте постпроц: декод якорей/боксов, NMS (класс‑wise/общий), top‑K, пороги.
  3. Уберите недетерминизм (случайные аугментации, dropout).
  4. Задокументируйте входы/выходы и динамические формы (H×W, N).
  5. Сделайте контрольный набор (200–1000 изображений) для сверки метрик после экспорта.

См. основы детекции/параметры NMS — https://cloudcompute.ru/solutions/computer-vision/yolo/ • тюнинг производительности — https://cloudcompute.ru/solutions/performance-tuning/

Экспорт в ONNX (динамические формы) Рекомендации

  • Экспортируйте с explicit batch и динамикой по N,H,W.
  • Выделите постпроц в отдельный узел: либо экспортируйте NMS в графе, либо оставьте NMS на хосте/плагине при сборке TensorRT.
  • Используйте opset ≥ 13; проверьте, что все кастом‑операции заменены на стандартные (или опишите плагины).

Пример (PyTorch → ONNX, схематично):

import torch
model = load_trained_model().eval().half() # fp16 для совместимости с FP16 сборкой
dummy = torch.randn(1, 3, 640, 640).half().cuda()
dynamic_axes = {"images": {0: "N", 2: "H", 3: "W"},
 "pred": {0: "N"}}
torch.onnx.export(
 model, dummy, "model_edge.onnx",
 input_names=["images"], output_names=["pred"],
 opset_version=17, do_constant_folding=True,
 dynamic_axes=dynamic_axes
)

Проверка эквивалентности: прогоните ONNX на контрольном наборе и сравните mAP/IoU/Recall. Разница в пределах 0.1–0.5 п.п. обычно допустима; больше — ищите несоответствие препроц/постпроц.

Сборка TensorRT‑движка

Шаги

  1. Выберите precision: FP16 → INT8 (после калибровки).
  2. Включите тактики оптимизации (workspace) и плагины для NMS/декода при необходимости.
  3. Задайте профили динамических форм (минимум/оптимум/максимум).
  4. Сохраните .engine и версионируйте вместе с метаданными (см. ниже).

Схема на Python (сокращённо):

import tensorrt as trt
builder = trt.Builder(trt.Logger(trt.Logger.INFO))
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, builder.logger)
with open("model_edge.onnx", "rb") as f:
 parser.parse(f.read())
config = builder.create_builder_config()
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 2<<30) # 2GB
config.set_flag(trt.BuilderFlag.FP16)
# Профили динамических форм
profile = builder.create_optimization_profile()
profile.set_shape("images", min=(1,3,480,480), opt=(1,3,640,640), max=(4,3,1280,1280))
config.add_optimization_profile(profile)
# (опц.) INT8 калибратор
# config.set_flag(trt.BuilderFlag.INT8); config.int8_calibrator = MyCalibrator(...)
engine = builder.build_engine(network, config)
with open("model_edge_fp16.engine", "wb") as f:
 f.write(engine.serialize())

INT8‑калибрация

  • 300–1000 репрезентативных изображений препроцить ровно так же, как в проде.
  • Лучше per‑channel для свёрток, исключить “хрупкие” слои из INT8, если качество падает.
  • Сравните метрики до/после; откатитесь к FP16, если потеря качества недопустима.

Подробности о форматах вычислений — https://cloudcompute.ru/solutions/fp8-bf16/

Профили устройств и ожидания

**Класс устройства** **Память** **Режимы** **Заметки**
**Микро‑edge (iGPU/мобильные dGPU)** 4–8 ГБ FP16, реже INT8 Одномодельные пайплайны, жёсткие лимиты тепла/энергии
**Jetson/«корневые» edge‑узлы** 8–32 ГБ FP16/INT8 1–3 модели, декод+инференс, лёгкий overlay
**Малые коробки с dGPU** 8–24 ГБ FP16/INT8 Мультикамерные пайплайны, батч из потоков
**Near‑edge в ЦОД** 24–80 ГБ BF16/FP16/INT8 Консолидация сайтов, многомодельные графы

Баланс latency↔throughput — https://cloudcompute.ru/solutions/throughput-vs-latency/ Препроц/постпроц: как не «уронить» качество

  • Letterbox: одинаковая реализация в train/export/edge.
  • Нормализация: одинаковые mean/std, порядок каналов.
  • NMS: одинаковые iou/conf, единая схема (class‑wise vs unified), top‑K и лимиты.
  • Декод боксов: строго те же формулы (якоря/stride).
  • Проверка: прогон контрольного набора в три режима — PyTorch, ONNX, TensorRT.

Параллелизм и конвейер

  • Zero‑copy кадра в GPU, pinned memory для входов/выходов.
  • Несколько CUDA‑стримов: decode → preprocess → infer → postproc с перекрытием.
  • Микробатч для изображений 2–8, если это не бьёт по p95.
  • Мультимодельные графы (например, детекция → классификация ROI) упаковываются как ансамбль — см. https://cloudcompute.ru/solutions/triton-inference-server/

Упаковка, версии и обновления (edge‑CI/CD)

  • Образ контейнера: model.engine + конфиг препроц/постпроц + точные версии CUDA/TensorRT.
  • Манифест (JSON): model_id, arch, preproc_hash, postproc_hash, trt_version, calib_digest, shapes_profile.
  • A/B‑раскатка и быстрый откат при деградации.
  • OTA‑обновления через пайплайн — см. https://cloudcompute.ru/solutions/containers-ci-cd/

Наблюдаемость на устройстве

Собирайте (см. https://cloudcompute.ru/solutions/monitoring-logging/ и https://cloudcompute.ru/solutions/llm-inference/observability/):

  • Latency p50/p95, QPS/FPS, drop‑rate, очередь ожидания.
  • GPU: util/HBM, VRAM пик, тепловой троттлинг.
  • Качество: mAP/IoU по контрольным кадрам, доля пустых/переполненных выходов.
  • События: ошибки десериализации .engine, несовпадение форм ввода, таймауты.

Экономика и sizing для edge‑пула

Пусть t_inf — время инференса на движке (сек), α — накладные препроц/постпроц/декода, fps — целевой FPS/поток, U — целевая загрузка GPU (например, 0.7).

t_frame ≈ t_inf + α

Max_streams_per_gpu ≈ (1 / (t_frame × fps)) × U

Для корневых узлов (несколько камер): суммируйте потоки и проверяйте пиковое потребление VRAM (движок + кэш + буферы). Планирование — https://cloudcompute.ru/solutions/cost-planner/

Конфиги (YAML): export→engine→runtime

A)Export (PyTorch→ONNX)

export:
 input: checkpoints/yolo_s_best.pt
 onnx_out: artifacts/yolo_s_dyn.onnx
 opset: 17
 dynamic: { N: [1, 4], H: [480, 1280], W: [480, 1280] }
 postproc: separate # nms/decode вынесены наружу
 preprocess:
 letterbox: { enabled: true, fill: 114 }
 normalize: { mean: [0.485,0.456,0.406], std: [0.229,0.224,0.225] }

B)Build (ONNX→TensorRT)

tensorrt:
 onnx: artifacts/yolo_s_dyn.onnx
 engine: artifacts/yolo_s_fp16.engine
 precision: fp16 # fp16 | int8
 workspace_gb: 2
 profiles:
 - name: p1
 min: [1,3,480,480]
 opt: [1,3,640,640]
 max: [4,3,1280,1280]
 int8:
 calibrate: false
 cache: artifacts/calib.cache
 images_dir: calib_samples/

C) Runtime (edge‑контейнер)

runtime:
 engine: /opt/models/yolo_s_fp16.engine
 inputs: { name: images, dtype: fp16, nchw: true }
 outputs: { name: pred, dtype: fp16 }
 nms:
 kind: batched
 iou: 0.6
 conf: 0.35
 topk: 300
 streams: 3
 batch: 1
 limits:
 max_res: [1280,1280]
 timeout_ms: 100
observability:
 metrics: prometheus
 traces: otlp

Траблшутинг

**Симптом** **Причина** **Что сделать**
Падение mAP после экспорта Несовпадение препроц/постпроц Проверить letterbox/нормализацию/NMS, синхронизировать пороги
Ошибка десериализации .engine Версия TensorRT/арх несовместима Пересобрать движок под целевое устройство/версию
OOM/вылеты при старте Неверный профиль форм/большие буферы Уменьшить max‑формы, снизить workspace, проверить batch
INT8 ухудшил качество Неполная/нерепрезентативная калибровка Увеличить выборку, исключить хрупкие слои из INT8, вернуться на FP16
Задержки «скачут» Декод/копии, отсутствие стримов NVDEC/zero‑copy, pinned memory, 2–4 CUDA‑стрима
Разные результаты на устройствах Разные версии/seed Зафиксировать версии библиотек, один манифест/контейнер

Как запустить в cloudcompute.ru

Хаб шаблонов — https://cloudcompute.ru/solutions/templates/

Чек‑лист перед продом

  • Зафиксированы препроц/постпроц, версии весов и контрольный набор.
  • Экспорт ONNX с динамическими формами; проверена эквивалентность метрик.
  • Собран TensorRT FP16; протестирован INT8‑движок (если нужен) и калибровка.
  • Задан профиль форм (min/opt/max), протестированы реальные разрешения.
  • Упакован контейнер с манифестом версий и limits; настроена A/B‑раскатка.
  • Подключены метрики/трейсы/алерты; подтверждён SLA p95.
  • Рассчитан Max_streams_per_gpu и проверен VRAM пик на устройстве.

Навигация по разделу «Компьютерное зрение»

Хаб: https://cloudcompute.ru/solutions/computer-vision/ • Детекция: https://cloudcompute.ru/solutions/computer-vision/yolo/ • Интерактивная сегментация: https://cloudcompute.ru/solutions/computer-vision/sam/ • Видео‑аналитика: https://cloudcompute.ru/solutions/computer-vision/video-analytics/ • Трекинг/ReID: https://cloudcompute.ru/solutions/computer-vision/tracking/ • OCR: https://cloudcompute.ru/solutions/computer-vision/ocr/ • Эмбеддинги: https://cloudcompute.ru/solutions/computer-vision/dinov2/ • Экспорт на edge (эта страница) • Инфраструктура/тюнинг/наблюдаемость/экономика: https://cloudcompute.ru/solutions/performance-tuning/https://cloudcompute.ru/solutions/fp8-bf16/https://cloudcompute.ru/solutions/monitoring-logging/https://cloudcompute.ru/solutions/llm-inference/observability/https://cloudcompute.ru/solutions/throughput-vs-latency/https://cloudcompute.ru/solutions/cost-planner/ • Шаблоны и CI/CD: https://cloudcompute.ru/solutions/templates/https://cloudcompute.ru/solutions/containers-ci-cd/

Готовы запустить?

Запустить GPU-сервер