Фаза 04 · урок 15

Компьютерное зрение в реальном времени — развёртывание на периферии

Цель урока: Модель компьютерного зрения на этапе обучения — монстр с плавающей точкой. 100 млн параметров, 10 GFLOPs на один прямой проход, 2 ГБ VRAM. Ничто из этого не помещается в телефон, информационно-развлекательный блок автомобиля,…

Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.

Курс
AI Engineering from Scratch
Фаза
Компьютерное зрение
Чтение
12 мин.
Проверено
Содержание урока
  1. Цели обучения
  2. Проблема
  3. Концепция
  4. Три бюджета
  5. Дисциплина измерений
  6. FLOPs как приближённый показатель
  7. Квантование в одном абзаце
  8. Прореживание и дистилляция
  9. Среды выполнения инференса
  10. Выбор архитектуры для периферийного устройства
  11. Соберите это
  12. Шаг 1: Правильно измерьте задержку
  13. Шаг 2: Подсчёт параметров и FLOP
  14. Шаг 3: Статическое post-training quantisation
  15. Шаг 4: Экспорт в ONNX
  16. Шаг 5: Сравните и сопоставьте режимы
  17. Используйте это
  18. Внедрите это
  19. Упражнения
  20. Ключевые термины
  21. Дополнительные материалы

Периферийный инференс — это дисциплина, в которой модель с точностью 90 запускают с частотой 30 кадров/с на устройстве с 2 ГБ RAM. Каждый процентный пункт точности обменивается на миллисекунды задержки.

Тип: Изучите + соберите Языки: Python Предварительные требования: Фаза 4, урок 04 (Классификация изображений), фаза 10, урок 11 (Квантование) Время: ~75 минут

Цели обучения

  • Измерять задержку инференса, пиковую память и пропускную способность для любой модели PyTorch, а также интерпретировать компромисс между FLOPs / параметрами / задержкой
  • Квантовать модель компьютерного зрения до INT8 с помощью post-training quantisation PyTorch и проверить, что потеря точности составляет < 1 %
  • Экспортировать в ONNX и компилировать с ONNX Runtime или TensorRT; назвать три наиболее распространённые ошибки экспорта и их исправления
  • Объяснять, когда для ограничения периферийного устройства выбрать MobileNetV3, EfficientNet-Lite, ConvNeXt-Tiny или MobileViT

Проблема

Модель компьютерного зрения на этапе обучения — монстр с плавающей точкой. 100 млн параметров, 10 GFLOPs на один прямой проход, 2 ГБ VRAM. Ничто из этого не помещается в телефон, информационно-развлекательный блок автомобиля, промышленную камеру или дрон. Поставка системы компьютерного зрения означает уместить те же предсказания в бюджет, который в 100 раз меньше.

Основную работу выполняют три регулятора: выбор модели (меньшая архитектура с тем же рецептом), квантование (INT8 вместо FP32) и среда выполнения инференса (ONNX Runtime, TensorRT, Core ML, TFLite). Правильный выбор отличает демо, работающее на рабочей станции, от продукта, поставляемого на модуле камеры за $30.

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

Концепция

Три бюджета

Диаграмма к уроку «Компьютерное зрение в реальном времени — развёртывание на периферии»

  • Задержка (latency): p50, p95, p99. Усреднение только по p50 скрывает хвостовое поведение, важное для систем реального времени.
  • Пиковая память: максимум, который устройство когда-либо видит, а не среднее в установившемся состоянии. Это важно, потому что OOM фатальны для встраиваемых целей.
  • Мощность / энергия: миллиджоули на инференс на устройстве с питанием от батареи. Часто приближённо оценивается как загрузка CPU/GPU * время.

Таблица из (модель, задержка, память, точность) — это основа решения для периферийного устройства. Каждая ячейка измеряется на целевом устройстве, а не на рабочей станции.

Дисциплина измерений

Три правила, которым должен следовать каждый профиль периферийного устройства:

  1. Прогрейте модель 5–10 фиктивными прямыми проходами перед измерением. Холодные кэши и JIT-компиляция дают нерепрезентативные первые значения.
  2. Синхронизируйте нагрузки GPU с torch.cuda.synchronize() до и после измеряемого блока. Без этого вы измеряете диспетчеризацию ядер, а не выполнение ядер.
  3. Зафиксируйте размеры входа на разрешении, используемом в продукте. Задержка на 224x224 не равна задержке на 512x512.

FLOPs как приближённый показатель

FLOPs (операции с плавающей точкой на инференс) — недорогой, независимый от устройства приближённый показатель задержки. Он полезен для сравнения архитектур, но вводит в заблуждение как абсолютное время по часам. Модель с на 10 % большим числом FLOPs может на практике быть в 2 раза быстрее, потому что использует дружественные к аппаратуре операции (depthwise-свёртки хорошо компилируются, большие свёртки 7x7 — нет).

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

Квантование в одном абзаце

Замените веса и активации FP32 на INT8. Размер модели уменьшается в 4 раза, пропускная способность памяти — в 4 раза, вычисления ускоряются в 2–4 раза на аппаратуре с ядрами INT8 (на каждой современной мобильной SoC и каждом NVIDIA GPU с Tensor Cores). Потеря точности в задачах компьютерного зрения обычно составляет 0,1–1 процентных пункта при статическом post-training quantisation.

Типы:

  • Динамическое (dynamic) — квантует веса до INT8, активации вычисляются в FP. Просто, небольшой выигрыш в скорости.
  • Статическое (post-training) — квантует веса + калибрует диапазоны активаций на небольшом калибровочном наборе. Гораздо быстрее динамического.
  • Обучение с учётом квантования (quantisation-aware training, QAT) — имитирует квантование во время обучения, поэтому модель учится работать с ним. Лучшая точность, нужны размеченные данные.

Для компьютерного зрения post-training static quantisation даёт 95 % выгоды при 5 % усилий. Используйте QAT только тогда, когда потеря точности от PTQ неприемлема.

Прореживание и дистилляция

  • Прореживание (pruning) — удаляет неважные веса (на основе величины) или каналы (структурированно). Хорошо работает на сверхпараметризованных моделях; менее полезно для уже компактных архитектур.
  • Дистилляция (distillation) — обучает маленького ученика имитировать логиты большого учителя. Часто возвращает большую часть точности, потерянной при уменьшении модели. Стандарт для производственных периферийных моделей.

Среды выполнения инференса

  • PyTorch eager — медленно, не для развёртывания. Используйте только для разработки.
  • TorchScript — устаревший вариант. Заменён torch.compile и экспортом ONNX.
  • ONNX Runtime — нейтральная среда выполнения. CPU, CUDA, CoreML, TensorRT, OpenVINO имеют ONNX-провайдеры. Начните с неё.
  • TensorRT — компилятор NVIDIA. Лучшая задержка на NVIDIA GPU (рабочая станция и Jetson). Интегрируется с ONNX Runtime или используется отдельно.
  • Core ML — среда выполнения Apple для iOS/macOS. Требует .mlmodel или .mlpackage.
  • TFLite — среда выполнения Google для Android/ARM. Требует .tflite.
  • OpenVINO — среда выполнения Intel для CPU/VPU. Требует .xml + .bin.

На практике: экспортируйте PyTorch -> ONNX -> выберите среду выполнения для целевого устройства. ONNX — это lingua franca.

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

Бюджет Модель Почему
< 3M params MobileNetV3-Small Компилируется везде, хорошая базовая точка
3-10M EfficientNet-Lite-B0 Лучшая точность на параметр в TFLite
10-20M ConvNeXt-Tiny Лучшая точность на параметр, дружественна к CPU
20-30M MobileViT-S or EfficientViT Transformer с точностью ImageNet
30-80M Swin-V2-Tiny Если стек поддерживает оконное внимание

Квантуйте всё это до INT8, если нет конкретной причины этого не делать.

cnn-param-count

Соберите это

Шаг 1: Правильно измерьте задержку

import time
import torch

def measure_latency(model, input_shape, device="cpu", warmup=10, iters=50):
    model = model.to(device).eval()
    x = torch.randn(input_shape, device=device)
    with torch.no_grad():
        for _ in range(warmup):
            model(x)
        if device == "cuda":
            torch.cuda.synchronize()
        times = []
        for _ in range(iters):
            if device == "cuda":
                torch.cuda.synchronize()
            t0 = time.perf_counter()
            model(x)
            if device == "cuda":
                torch.cuda.synchronize()
            times.append((time.perf_counter() - t0) * 1000)
    times.sort()
    return {
        "p50_ms": times[len(times) // 2],
        "p95_ms": times[int(len(times) * 0.95)],
        "p99_ms": times[int(len(times) * 0.99)],
        "mean_ms": sum(times) / len(times),
    }

Прогревайте, синхронизируйте, используйте time.perf_counter(). Сообщайте перцентили, а не только среднее.

Шаг 2: Подсчёт параметров и FLOP

def parameter_count(model):
    return sum(p.numel() for p in model.parameters())

def flops_estimate(model, input_shape):
    """
    Rough FLOP count for a conv/linear-only model. For production use `fvcore` or `ptflops`.
    """
    total = 0
    def conv_hook(m, inp, out):
        nonlocal total
        c_out, c_in, kh, kw = m.weight.shape
        h, w = out.shape[-2:]
        total += 2 * c_in * c_out * kh * kw * h * w
    def linear_hook(m, inp, out):
        nonlocal total
        total += 2 * m.in_features * m.out_features
    hooks = []
    for m in model.modules():
        if isinstance(m, torch.nn.Conv2d):
            hooks.append(m.register_forward_hook(conv_hook))
        elif isinstance(m, torch.nn.Linear):
            hooks.append(m.register_forward_hook(linear_hook))
    model.eval()
    with torch.no_grad():
        model(torch.randn(input_shape))
    for h in hooks:
        h.remove()
    return total

Для реальных проектов используйте fvcore.nn.FlopCountAnalysis или ptflops; они корректно обрабатывают каждый тип модуля.

Шаг 3: Статическое post-training quantisation

def quantise_ptq(model, calibration_loader, backend="x86"):
    import torch.ao.quantization as tq
    model = model.eval().cpu()
    model.qconfig = tq.get_default_qconfig(backend)
    tq.prepare(model, inplace=True)
    with torch.no_grad():
        for x, _ in calibration_loader:
            model(x)
    tq.convert(model, inplace=True)
    return model

Три шага: настройте, подготовьте (вставьте наблюдатели), откалибруйте на реальных данных, преобразуйте (объедините + квантуйте). Требуется, чтобы модель была объединена (Conv -> BN -> ReLU -> ConvBnReLU); это выполняет torch.ao.quantization.fuse_modules.

Шаг 4: Экспорт в ONNX

def export_onnx(model, sample_input, path="model.onnx"):
    model = model.eval()
    torch.onnx.export(
        model,
        sample_input,
        path,
        input_names=["input"],
        output_names=["output"],
        dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}},
        opset_version=17,
    )
    return path

opset_version=17 — безопасное значение по умолчанию в 2026 году. dynamic_axes позволяет запускать модель ONNX с произвольным размером batch.

Шаг 5: Сравните и сопоставьте режимы

import torch.nn as nn
from torchvision.models import mobilenet_v3_small

def compare_regimes():
    model = mobilenet_v3_small(weights=None, num_classes=10)
    params = parameter_count(model)
    flops = flops_estimate(model, (1, 3, 224, 224))
    lat_fp32 = measure_latency(model, (1, 3, 224, 224), device="cpu")
    print(f"FP32 MobileNetV3-Small: {params:,} params  {flops/1e9:.2f} GFLOPs  "
          f"p50={lat_fp32['p50_ms']:.2f}ms  p95={lat_fp32['p95_ms']:.2f}ms")

Запустите ту же функцию для resnet50, efficientnet_v2_s и convnext_tiny, и у вас будет сравнительная таблица, необходимая для решения о развёртывании.

Используйте это

Производственные стеки сходятся к одному из трёх путей:

  • Web / serverless: PyTorch -> ONNX -> ONNX Runtime (провайдер CPU или CUDA). Самый простой, достаточно хороший для большинства случаев.
  • NVIDIA edge (Jetson, GPU server): PyTorch -> ONNX -> TensorRT. Лучшая задержка, наибольшие инженерные усилия.
  • Mobile: PyTorch -> ONNX -> Core ML (iOS) или TFLite (Android). Квантуйте перед экспортом.

Для измерений torch-tb-profiler, nvprof / nsys и Instruments на macOS дают послойную детализацию. benchmark_app (OpenVINO) и trtexec (TensorRT) дают независимые числа через CLI.

Внедрите это

Этот урок создаёт:

  • outputs/prompt-edge-deployment-planner.md — промпт, который выбирает backbone, стратегию квантования и среду выполнения по целевому устройству и SLA задержки.
  • outputs/skill-latency-profiler.md — навык, который пишет полный скрипт бенчмаркинга задержки с прогревом, синхронизацией, перцентилями и отслеживанием памяти.

Упражнения

  1. (Легко) Измерьте задержку p50 для resnet18, mobilenet_v3_small, efficientnet_v2_s и convnext_tiny при 224x224 на CPU. Представьте таблицу и определите, у какой архитектуры лучшая точность на миллисекунду.
  2. (Средне) Примените статическое post-training quantisation к mobilenet_v3_small. Представьте задержку FP32 по сравнению с INT8 и потерю точности на отложенном подмножестве CIFAR-10 или аналогичного набора.
  3. (Сложно) Экспортируйте convnext_tiny в ONNX, запустите его через onnxruntime с CPUExecutionProvider и сравните задержку с базовой линией PyTorch eager. Определите первый слой, на котором ONNX Runtime быстрее, и объясните почему.

Ключевые термины

Термин Как говорят Что это в действительности означает
Задержка «Как быстро» Время от входа до выхода; перцентили p50/p95/p99, а не среднее
FLOPs «Размер модели» Операции с плавающей точкой на прямой проход; грубый показатель стоимости вычислений
Квантование INT8 «8-битное» Заменяет веса/активации FP32 8-битными целыми; ~в 4 раза меньше, в 2–4 раза быстрее
PTQ «Post-training quantisation» Квантует обученную модель без переобучения; просто, обычно достаточно
QAT «Quantisation-aware training» Имитирует квантование при обучении; лучшая точность, нужны размеченные данные
ONNX «Нейтральный формат» Формат обмена моделями, поддерживаемый каждой основной средой выполнения инференса
TensorRT «Компилятор NVIDIA» Компилирует ONNX в оптимизированный движок для NVIDIA GPU
Дистилляция «Учитель -> ученик» Обучает маленькую модель имитировать логиты большой; возвращает большую часть потерянной точности

Дополнительные материалы


Источник: Real-Time Vision — Edge Deployment 04.14 — Трансформеры для зрения (ViT) · Фаза 04 — Компьютерное зрение · 04.16 — Полный конвейер компьютерного зрения: итоговый проект · Полный каталог