Посмотреть как Markdown

Квантизация

Квантизация в LibreYOLO работает целиком в PyTorch: model.quantize() подменяет модули Conv2d и Linear модели квантизованными эквивалентами и калибрует их. Результат сохраняет обычный контракт predict, val, train и save, поэтому квантизованная модель оценивается теми же валидаторами, что и float-модель.

Вызов
model.quantize(recipe="int8", calib="coco128.yaml")
Команда
libreyolo quantize --model M.pt --recipe int8 --calib coco128.yaml
Дополнительно
Не нужно. Квантизация работает в PyTorch.
Семейства
yolo9, rfdetr, birefnet, feynobg
Рецепты
fp16, bf16, fp8, int8, w4a16, w4a8, nvfp4, mxfp4, int2
Артефакты развёртывания
export(format="pt") — упакованный чекпойнт, export(format="onnx") — QDQ-граф INT8

Установка

Квантизации не нужен extra. Подмена модулей, проход калибровки и симулируемая арифметика работают в PyTorch, так что pip install libreyolo — это всё, что требуется. Артефактам развёртывания нужно то, что нужно их собственному формату: для пути через ONNX это libreyolo[onnx].

Квантизация

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # Подмена структуры плюс калибровка. calib — небольшой НЕРАЗМЕЧЕННЫЙ набор# изображений, он читается только вперёд, чтобы вывести диапазоны активаций и масштабы.qmodel = model.quantize(recipe="int8", calib="coco128.yaml", samples=128) print(qmodel.quant_info())qmodel.val(data="coco8.yaml")          # те же валидаторы, что и для float-моделиqmodel.save("LibreYOLO9s-int8.pt")     # чекпойнт несёт quant-манифест
CLI
libreyolo quantize --model LibreYOLO9s.pt --recipe int8 --calib coco128.yaml
Аргументы
model.quantize(    recipe="int8",    calib="coco128.yaml",      # путь к data.yaml или встроенное имя; None пропускает калибровку    samples=128,               # максимум изображений для калибровки    batch=8,                   # размер батча калибровки    algorithm="auto",          # auto и minmax — одно и то же; альтернатива — percentile    keep_high_precision=None,  # None использует политику семейства    verbose=True,)

quantize() преобразует загруженную модель на месте и возвращает её. Градиенты не задействованы: подмена ставит квантизованные модули, а проход калибровки идёт только вперёд.

Получившийся чекпойнт — обычный чекпойнт LibreYOLO с приложенным манифестом quant, поэтому он загружается обратно с нетронутыми структурой и масштабами:

Квантизованный чекпойнт загружается обратно квантизованным
from libreyolo import LibreYOLO # quant-манифест восстанавливает квантизованную структуру и масштабы# ещё до загрузки весов.qmodel = LibreYOLO("LibreYOLO9s-int8.pt")print(qmodel.quant_info())

Чекпойнты, которые тренер пишет во время QAT-прогона, тоже несут манифест, а значит best.pt из такого прогона сам является квантизованным чекпойнтом.

Рецепты

Поддерживаются четыре семейства: yolo9, rfdetr, birefnet и feynobg.

РецептЧто делаетСемействаКалибровка
fp16Приведение к половинной точности с контрактом входа и выхода в float32. Только инференс.все четырене нужна
bf16Приведение к bfloat16, который сохраняет диапазон экспоненты float32. Решение на случай, когда fp16 переполняется на модели в стиле DETR. Только инференс.все четырене нужна
fp8Веса и активации E4M3 на Conv2d и Linear: поканальные масштабы весов, откалиброванные потензорные масштабы активаций.все четыренужна
int8W8A8 на Conv2d и Linear: поканальные симметричные веса, потензорные аффинные активации.все четыренужна, либо calib=None только для весов
w4a16Сгруппированные симметричные веса INT4, группа 128 вдоль in_features, float-активации, на Linear.rfdetr, birefnet, feynobgне требуется
w4a8Сгруппированные веса INT4 плюс откалиброванные активации INT8, на Linear.rfdetr, birefnet, feynobgнужна
nvfp4W4A4 NVFP4 на Linear: элементы E2M1, блоки по 16 элементов, масштабы блоков в FP8 E4M3, масштаб тензора в FP32. Динамическое масштабирование активаций.rfdetr, birefnet, feynobgне требуется
mxfp4OCP MXFP4 на Linear: элементы E2M1, блоки по 32 элемента, масштабы блоков E8M0 — степени двойки. Динамическое масштабирование активаций.rfdetr, birefnet, feynobgне требуется
int2Только для исследований: сгруппированные 2-битные веса, группа 64, плюс активации INT8, на Linear. Одна лишь посттренировочная квантизация непригодна, поэтому нужен QAT или QAD.rfdetrнужна

Рецепты ниже 8 бит нацелены на nn.Linear и намеренно отклоняются для yolo9: на нынешнем железе это ускорение работает только для GEMM, поэтому свёртки остаются в более высокой точности. Для YOLO9 берутся int8 или fp8. int2 отклоняется для birefnet и feynobg, потому что эти семейства работают только на инференс, а значит восстановления через QAT, на которое опирается рецепт, там нет.

Умолчания каждого семейства оставляют первый слой и головы во float, а свёртка DFL в YOLO9 не квантизуется никогда: это фиксированный оператор интегрального математического ожидания. Переопределяйте через keep_high_precision=("head.",), если есть причина.

Калибровочные данные — не обучающие данные

calib= принимает несколько сотен изображений, не читает меток и делает только прямой проход, чтобы оценить диапазоны активаций. data= в train() и val() — это размеченный датасет для градиентов и метрик. Это разные аргументы с разными задачами, и по умолчанию calib равен coco128.yaml.

algorithm="minmax" сохраняет абсолютные экстремумы, встреченные по калибровочным батчам, и именно его выбирает "auto". "percentile" берёт среднее из 0.1 и 99.9 перцентилей по каждому батчу; измерения показали, что он обрушивает точность семейства DETR, потому что выбросы в активациях трансформера несут нагрузку. Чувствительность маленьких моделей к INT8 на самом деле лечится калибровкой на достаточном числе батчей: с умолчанием coco128 YOLO9-t укладывается примерно в один пункт mAP от своего float-результата. Выбранный алгоритм записывается в манифест чекпойнта.

Восстановление точности

QAT — это обычный train() на квантизованной модели
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Это дообучение, а не запуск с нуля: берите скорости обучения для дообучения.qmodel.train(data="coco8.yaml", epochs=5, lr0=1e-4)
QAD добавляет уже существующие аргументы дистилляции
qmodel.train(    data="coco8.yaml",    epochs=5,    lr0=1e-4,    distill_model="LibreYOLO9m.pt",)
CLI
libreyolo train --model LibreYOLO9s-int8.pt --data coco8.yaml --epochs 5 --lr0 1e-4

Квантизованные модули хранят мастер-веса fp32 и применяют фейковую квантизацию со straight-through estimator, поэтому градиенты доходят до мастер-весов, а существующие тренеры работают без изменений: EMA, AMP, возобновление с чекпойнта и аргументы дистилляции сочетаются друг с другом.

QAT — это дообучение уже обученной модели. Берите скорости обучения для дообучения, а не умолчания для обучения с нуля, иначе короткий прогон уничтожит предобученные веса независимо от квантизации. Доступность QAD следует за поддержкой дистилляции в семействе, а это сегодня yolo9 и rfdetr.

Модели, квантизованные в fp16 и bf16, работают только на инференс, и тренер отклоняет их, указывая на amp=True.

Экспорт

Упакованный PyTorch-чекпойнт
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Записывает LibreYOLO9s-int8-final.pt: упакованные низкобитные веса и масштабы,# мастер-веса fp32 выброшены, неквантизованный остаток приведён к fp16.qmodel.export(format="pt") # remainder="fp32" сохраняет неквантизованные тензоры точными.qmodel.export(format="pt", remainder="fp32")
QDQ INT8 ONNX
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Пары QuantizeLinear/DequantizeLinear прямо в графе, несущие собственные# масштабы модели — откалиброванные или обученные в QAT.qmodel.export(format="onnx")
CLI
libreyolo export --model LibreYOLO9s-int8.pt --format onnx

format="pt" кристаллизует модель. Упакованные низкобитные веса и масштабы заменяют мастер-веса, а неквантизованный остаток приводится к fp16, если не передан remainder="fp32". Инвариант упаковки в том, что распаковка бит в бит воспроизводит симуляцию на том устройстве, где вы финализировали модель, поэтому финализированный файл даёт ровно тот результат, который вы проверили на валидации. Измерено: YOLO9-s int8 уменьшается с 29.5 МБ до 9.6 МБ, RF-DETR-n nvfp4 — со 122 МБ до 26 МБ. Загрузка такого файла даёт готовую к инференсу модель, а вызов train() на ней автоматически восстанавливает мастер-веса из упакованных.

format="onnx" применим к моделям int8 и выдаёт QDQ-граф с собственными масштабами модели — откалиброванными или обученными в QAT, — которые ONNX Runtime и TensorRT исполняют настоящими INT8-ядрами. Это другой путь, не тот, что export(format="onnx", int8=True) на float-модели, где ONNX Runtime выводит масштабы сам.

Рецептам-приведениям квантизованный экспортёр не нужен вовсе:

Возврат к float с сохранением весов, обученных в QAT
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt")qmodel.dequantize() # Теперь подходит любой float-экспортёр, с любой поддерживаемой им точностью.qmodel.export(format="tensorrt", half=True)

Ограничения

Квантизованная арифметика исполняется в симуляции: это фейковая квантизация, которая считается в островках float32 даже под AMP. Симуляция численно достоверна, поэтому результат val() на любом устройстве — настоящее утверждение о квантизованной арифметике. Но не утверждение о скорости.

Два исключения исполняются нативно. fp16 и bf16 — обычные приведения. Финализированные модули fp8 считают свой GEMM прямо на упакованных весах E4M3 через torch._scaled_mm на железе класса Ada, Hopper и Blackwell, с теми же откалиброванными масштабами активаций, что и в симуляции; LIBREYOLO_KERNELS=off возвращает в точности симулируемый путь везде.

Покрытие развёртывания уже, чем список рецептов. Пригодная к развёртыванию форма ONNX здесь есть только у int8; fp8 и линейные рецепты ниже 8 бит исполняются в PyTorch и кристаллизуются через format="pt". Запрос экспорта в ONNX для них вызывает ошибку с этой же подсказкой — как и запрос любого формата, кроме ONNX, для модели int8: движки для конечных сред выполнения собирайте из QDQ-графа.

Экспорт модели int8, активации которой ни разу не калибровались, пишет предупреждение и даёт граф только с квантизацией весов.

Прочитано из libreyolo/quant/api.py, libreyolo/models/base/model.py, libreyolo/cli/commands/quantize.py и docs/quantization.md в ветке dev. Цифры размера чекпойнтов — измеренные значения, записанные в docs/quantization.md.