7.12. Постпроцессоры

Сеть обнаружения не выдаёт рамки. Она выдаёт один или несколько тензоров, структура которых зависит от архитектуры, на которой обучалась модель: 2-мерный тензор кандидатных предсказаний для детектора семейства YOLO, пара тензоров (boxes, scores) для детектора MediaPipe, плоский список координат ключевых точек для сети поз. Приложение не может прочитать ничего из этого напрямую; то, что ему нужно – список рамок, список ключевых точек, разбивка по классам – приходится декодировать из сырого тензора.

Этот декодер – постпроцессор. Модуль ml.postprocessing группирует их по экосистеме-источнику.

7.12.1. Darknet

ml.postprocessing.darknet декодирует модели изначальной эпохи YOLO. YOLO v2 ввёл идеи сетки и якоря, которые большинство более поздних детекторов унаследовали в той или иной форме, поэтому компоновка v2 – наиболее ясная отправная точка.

YOLO v2 начинает с разбиения входного изображения на грубую сетку – компоновка 13 на 13 для канонического входа в 416 пикселей, меньше для меньших моделей – и обучает сеть так, чтобы каждая ячейка сетки отвечала за обнаружение любого объекта, центр которого попадает внутрь неё. Пространственная компоновка выходного тензора повторяет компоновку входа: одна позиция в выходе на каждую ячейку в изображении.

В каждой ячейке сетки сеть не предсказывает рамку из ничего. Она выбирает из нескольких заранее заданных эталонных форм, называемых якорями – фиксированных пар (width, height), выведенных офлайн путём кластеризации размеров рамок в обучающем наборе так, чтобы они охватывали типичные объекты, которые модель должна увидеть. Задача сети в каждой ячейке – предсказать для каждого якоря небольшое смещение центра рамки внутри ячейки, масштаб ширины и высоты якоря, оценку объектности (вероятность того, что там что-то есть) и вектор вероятностей по классам. Сетка 13 на 13 с 5 якорями по умолчанию и 20 классами выдаёт поэтому 13 * 13 * 5 * (4 + 1 + 20) = 21,125 чисел на один вывод.

YoloV2 декодирует эту компоновку: он обходит ячейки, применяет смещения и масштабы каждого якоря для восстановления абсолютных координат рамки, объединяет объектность с вероятностью класса в оценку по классам, отсекает по порогу и передаёт уцелевших в NMS. Класс принимает аргумент конструктора anchors=, когда модель обучалась на пользовательской таблице якорей, и в противном случае откатывается к встроенному значению по умолчанию. Варианты, настроенные под конкретные наборы классов, поставляются в том же подмодуле.

7.12.2. Ultralytics

ml.postprocessing.ultralytics декодирует более новые поколения YOLO. YoloV8 читает выход в столбцовом порядке, где каждый столбец – это предсказание одного якоря, содержащее координаты рамки и вектор оценок по классам; канал объектности, который несли более ранние выходы YOLO, в v8 убран, и оценки классов стоят самостоятельно. Разбор YOLOv8 проходит по декодированию тензор за тензором. Более старые версии эпохи Ultralytics поставляются в том же подмодуле для моделей, обученных под их компоновки.

7.12.3. MediaPipe

ml.postprocessing.mediapipe декодирует лёгкое семейство Google для работы на устройстве. BlazeFace – это детектор лиц, рассмотренный в hello-blazeface: быстрый детектор на основе якорей, который выдаёт рамки и шесть координат опорных точек на каждое лицо, возвращаемых в виде кортежей (box, score, keypoints) с опорными точками, прикреплёнными к каждой рамке, а не как отдельный список вывода. Модели обнаружения рук, опорных точек и поз из того же семейства поставляются вместе с ним и следуют той же форме возврата с прикреплёнными ключевыми точками.

7.12.4. Выбор подходящего

Правильный постпроцессор определяется архитектурой, на которой обучалась модель, а не тем, что хочет приложение. .tflite модели YOLOv8 корректно декодируется только через YoloV8; .tflite модели BlazeFace – только через BlazeFace. Выбор постпроцессора – часть выбора модели. Когда архитектура модели не представлена ни одним поставляемым постпроцессором, написать свой несложно.

Сети классификации – исключение. Их единственный выходной тензор уже представляет собой то, что хочет приложение – список оценок по классам – и постпроцессор не нужен. Загрузка модели без postprocess= и чтение результата предсказания как плоского ndarray – правильный путь, как было описано в разделе ввод/вывод тензоров.