Пороги и фильтрация
Какие предсказания выживут, решают четыре аргумента: conf, iou, max_det и classes. Только два из них применимы к каждому семейству, потому что предиктор множества декодирует фиксированный набор запросов и никогда не запускает NMS.
Четыре аргумента
| Аргумент | По умолчанию | Применяется |
|---|---|---|
conf | 0.25 | К каждому семейству |
iou | 0.45 | К семействам, которые запускают немаксимальное подавление (NMS) |
max_det | 300 | К каждому семейству |
classes | None | К каждому семейству |
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") result = model( SAMPLE_IMAGE, conf=0.25, # оставить предсказания с этой оценкой и выше iou=0.45, # порог перекрытия NMS — там, где NMS запускается max_det=300, # предел на изображение classes=None, # или список id классов)print(len(result.boxes))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") for conf in (0.1, 0.25, 0.5, 0.75): result = model(SAMPLE_IMAGE, conf=conf) print(conf, len(result.boxes))libreyolo predict model=LibreYOLO9s.pt conf=0.4 iou=0.5 max_det=100 \ source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpgДва из них универсальны, а два нет — это самое полезное, что стоит знать до того, как что-то настраивать.
У валидации значения по умолчанию другие, и это сделано намеренно: val()
работает при conf=0.001 и iou=0.6, потому что средняя точность считается по
полной кривой точности и полноты, а отсечка на 0.25 её обрезала бы.
conf
conf — это оценка, ниже которой предсказание отбрасывается. Он применяется к
каждому семейству, включая те, что никогда не запускают NMS, и это первая ручка,
за которую стоит браться, когда детекций слишком много или слишком мало.
Значение по умолчанию 0.25 подходит, чтобы просто смотреть на картинки. Если
предсказания уходят в следующую систему, обычно нужно значение выше; если
измеряется качество — гораздо ниже.
iou
iou — это перекрытие, выше которого немаксимальное подавление убирает ту из двух
рамок одного класса, у которой оценка ниже. Он что-то значит, только если
семейство вообще запускает подавление.
Предиктор множества декодирует фиксированное число запросов и берёт те, у
которых оценка выше. Дубликаты подавляются внутри архитектуры во время обучения,
а не на шаге постобработки, поэтому никакого порога здесь нет. Эти семейства
принимают iou ради совместимости API и игнорируют его:
CenterNet, DEIM, DETR, Deformable DETR, D-FINE, DINO-DETR, EdgeCrafter, Faster R-CNN, LW-DETR, Mask R-CNN, RF-DETR, RT-DETR и end-to-end голова YOLOv9. Варианты, построенные на этих декодерах, наследуют это поведение.
from libreyolo import LibreYOLO, SAMPLE_IMAGE # RF-DETR декодирует фиксированный набор запросов, поэтому iou здесь ничего не меняет.model = LibreYOLO("LibreRFDETRs.pt") loose = model(SAMPLE_IMAGE, iou=0.9)tight = model(SAMPLE_IMAGE, iou=0.1) # Количество одинаковое в обоих случаях. Работают именно conf и max_det.print(len(loose.boxes), len(tight.boxes))Большинство из них сообщают об этом в строках документации к постобработке, но
во время выполнения предупреждение не выдаётся, поэтому перебор iou на RF-DETR
даёт ровную линию, а не ошибку. Faster R-CNN и Mask R-CNN — случай чуть другой:
обе уже запустили NMS внутри модели, с фиксированным порогом из исходной
реализации, изменить который через iou штатным способом нельзя.
А эти семейства его используют: от YOLOv1 до YOLOv4, YOLOv7, YOLOv9, YOLOX, YOLO-NAS, RTMDet, PicoDet, EfficientDet, FCOS, RetinaNet и SSD.
Два параметра на этапе предсказания делают iou значимым даже для предиктора
множества, потому что оба объединяют рамки уже после того, как модель отработала:
tiling=Trueсогласует перекрывающиеся тайлы поклассовым NMS с порогомiouaugment=Trueобъединяет отражённые виды поклассовым NMS с порогомiou
Оба разобраны в разделе Производительность инференса.
У детекторов с открытым словарём своё правило. Семейство, чей процессор
запускает NMS, объявляет собственный порог по умолчанию и учитывает iou — так
устроен OMDet-Turbo. Семейства, которые ничего не подавляют, — Grounding DINO,
OWLv2 и OV-DEIM — выдают предупреждение, если передать iou. Это единственное
предупреждение такого рода во всей библиотеке.
max_det
max_det ограничивает, сколько предсказаний возвращается для одного
изображения. Он работает везде, но через разные механизмы: семейство с NMS
обрезает список после подавления, а предиктор множества использует его как
размер выборки top-k.
Некоторые семейства ограничивают результат сильнее, чем вы просите, потому что
так устроена их исходная эталонная конфигурация. SSD останавливается на 200,
сегментация экземпляров RTMDet — на 100, а FCOS — на собственном лимите детекций
на изображение. Поднимать max_det выше этих значений бесполезно.
Единственное место, где max_det применяется централизованно, а не в каждом
семействе, — потайловый инференс: объединённый список обрезается после
согласования тайлов.
Фильтрация классов
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") # Id классов индексируют model.names. В COCO 0 — это person.result = model(SAMPLE_IMAGE, classes=[0]) print({result.names[int(c)] for c in result.boxes.cls.tolist()})from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) wanted = {"person", "backpack"}ids = [i for i, name in result.names.items() if name in wanted]print(ids) filtered = model(SAMPLE_IMAGE, classes=ids)print(len(filtered.boxes))classes принимает список id классов и оставляет только те предсказания, чей
класс есть в списке. Id индексируют result.names, и надёжнее всего взять
нужный, прочитав names из результата, а не предполагая порядок классов в
датасете.
Фильтрация происходит централизованно, после постобработки каждого семейства, в единой точке, через которую проходит любой путь предсказания. Из этого следуют две вещи, которые стоит знать. Она работает на каждом семействе, включая те, где NMS нет. И заодно она фильтрует данные, привязанные к рамкам, так что маски, ключевые точки и повёрнутые рамки урезаются вместе с ними, а не остаются рассогласованными.
В командной строке classes принимает голое целое число, список или строку со
значениями через запятую:
libreyolo predict model=LibreYOLO9s.pt classes=0 source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
libreyolo predict model=LibreYOLO9s.pt classes="[0,2,5]" source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpgФильтрация не даёт качества бесплатно. Модель всё равно тратит свой бюджет на
предсказание классов, которые вы потом выбрасываете, а max_det применяется
семейством до фильтра, поэтому изображение, забитое ненужными классами, может
упереться в предел раньше, чем дойдёт до вашего класса. Если так вышло, снизьте
conf или поднимите max_det.
agnostic_nms
agnostic_nms принимается и ничего не делает. При передаче выдаётся
предупреждение о том, что это заглушка ради совместимости с командной строкой,
и сам аргумент отбрасывается.
Режима подавления без учёта классов нет. Каждый вызов NMS в библиотеке учитывает
класс, поэтому две перекрывающиеся рамки разных классов выживают обе, при любом
iou. Если это мешает, сначала отфильтруйте через classes или подавите
пересечения между классами сами, на result.boxes.
Что predict отвергает
Два аргумента не предупреждают, а выбрасывают исключение: visualize и embed —
оба с NotImplementedError. Для эмбеддингов загрузите модель с
task="embed" и вызывайте predict или embed как обычно.
Всё нераспознанное выбрасывает TypeError с перечислением поддерживаемых опций,
так что опечатка сразу приводит к ошибке, а не игнорируется молча.
Эти аргументы принимаются, вызывают предупреждение и отбрасываются: agnostic_nms,
boxes, dnn, half, line_width, retina_masks, show_conf, show_labels
и verbose.