Фаза 04 · урок 15

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

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

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

Курс
AI Engineering с нуля
Фаза
Компьютерное зрение
Чтение
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 paramsMobileNetV3-SmallКомпилируется везде, хорошая базовая точка
3-10MEfficientNet-Lite-B0Лучшая точность на параметр в TFLite
10-20MConvNeXt-TinyЛучшая точность на параметр, дружественна к CPU
20-30MMobileViT-S or EfficientViTTransformer с точностью ImageNet
30-80MSwin-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 — Полный конвейер компьютерного зрения: итоговый проект · Полный каталог