Назад
1446

YOLO history. Part 8

1446

Введение

Список ссылок на прошлые статьи

Part 1. Знакомимся с архитектурой YOLO, её уникальностью, сильными и слабыми сторонами YOLOv1.
Part 2. Рассказываем о трюках YOLOv2 и о том, как авторы смогли обучить модель предсказывать более 9000 классов.
Part 3. Подробно рассматриваем нововведения от создателя архитектуры YOLO — Джозафа Редмона.
Part 4. Разбираемся со внушительным числом аббревиатур, которые появились в новой версии YOLO после ухода Джозафа.
Part 5. Отвечаем на вопрос: в чём разница между 4-ой и 5-ой версиями YOLO.
Part 6. Узнаем, как сделать современный anchor-free детектор, и почему YOLOv7 вышла позже YOLOv6.
Part 7. Рассказываем, зачем нужны вспомогательные модели, что общего между 7-ой и 9-ой версиями и чем они отличаются от 4-ой.

Практически у каждого студента технического направления старших курсов возникает вопрос, где применить полученные знания. У студента, желающего развиваться по выбранному направлению, есть обычно два варианта: академия и индустрия. В первой исследователи придумывают новые модели и технологии, а во второй компании находят им применение.

Наглядно показать разницу между двумя направлениями можно на примере семейства YOLO. В прошлой статье мы посмотрели, как развитие YOLO семейства видит академическая группа исследователей из Тайваньского Института IIS: идея, SOTA, сомнения об истинности, описанной в статье, теории.

Сегодня поговорим о другом подходе — индустриальном. Как и в прошлый раз, обсудим сразу две модели: YOLOv8 и YOLOv11. Их выпустила Ultralytics с разницей примерно в полтора года: YOLOv8 — 10 января 2023, YOLOv11 — 30 сентября 2024. В чём заключается разница между этими подходами?

Во-первых, ни у одной модели Ultralyrics нет статей, в которых бы авторы подробно рассказали о внутреннем устройстве моделей 😢. Поэтому узнать, чем они отличаются и за счёт чего авторам удалось улучшить качество, будет непросто. Во-вторых, можно посмотреть на оформление репозиториев у YOLOv9 (тык) и YOLOv11 (тык). Видно, что вторые вкладываются в создание и продвижение продукта. Сегодня Ultralytics фактически вобрало в себя популярность сеймества YOLO и стала основным поставщиком детекторов. В репозитории есть веса для всех архитектур YOLO, начиная c 3-ей версии, а также и для других популярных детекторов, не входящих в семейство YOLO (пруф).

Но за счёт чего у Ultralytics получилось сделать новые SOTA в семействе YOLO-моделей? Давайте разбираться! 😊

CI-CD

Прежде, чем мы погрузимся в устройство архитектур, давайте поговорим об изменениях в экосистеме Ultralytics по сравнению с YOLOv5.

С 5-ой версии переехала возможность [доступные форматы] экспорта моделей в разные форматы (ONNX, TensorRT и др.). А также добавили много возможностей для ускоренного инференса, например, FP16 на GPU, Fuse Conv и BatchNorm из коробки, TensorRT-ядра, OpenVINOВ и другие.

Помимо оптимизаций, авторы реализовали множество других утилит: визуализацию результатов на изображениях и видео, построение графиков лоссов и метрик, автоматический расчет всех метрик [тык].

Авторы улучшили CLI (Command Line Interface): теперь из командной строки можно не только обучать и валидировать модели, но и, например, запускать бенчмарк для оценки скорости модели в разных режимах или форматах [тык]. А также — из консоли запускать не только детекторы, но и модели, обученные под другие задачи, например, segment, pose или obb. Ещё есть вспомогательные команды для проверки конфигурации, версии и проведения диагностики окружения (yolo settings, yolo version, yolo checks).

YOLOv8

Авторы решили не продолжать идею со вспомогательной ветвью из 7-ой части, а сосредоточиться на архитектуре. Новая версия содержит следующие улучшения:

  • изменения архитектуры: замена C3 из YOLOv5 на C2f, замена некоторых блоков в бэкбоуне;
  • использование Decoupled Head из YOLOv6;
  • переход к anchor-free подходу;
  • использование нового для YOLO-семейства DFL-лосса, который помог точнее решить задачу регрессии ббоксов;
  • минорные изменения в обучении, агументации и другие улучшения.

Так как предыдущей моделью авторов была YOLOv5, давайте сравним с ней 8-ую версию. Общий вид модели YOLOv8:

Рисунок 1. Общая архитектура YOLOv8

Для заинтересовавшихся предлагаем посмотреть схему с описанием всей архитектуры на одной картинке: ссылка 😊

Блок С2F

Начнём с самого большого изменения в архитектуре — нового базового блока C2F. Он пришёл на смену C3. Давайте посмотрим, что изменилось:

Рисунок 2. Сравнение C3 и C2F блоков. C2F отличается от C2F-skip отсутствием skip connection в Bottleneck-блоке. Название «C2F-skip» есть только в нашей статье: мы добавили его, чтобы различать блоки между собой. Большая буква «S» обозначает операцию «Split», то есть мы разделяем вектор фичей на 2 части

Главное изменение — конкатенация фичемапов из bottleneck’ов. Теперь они не только применяются последовательно, но и конкатенируются к выходному вектору. Это позволяет ещё больше обогатить итоговые фичи контекстной информацией с ранних слоёв. Bottleneck-блок тоже незначительно изменился: авторы заменили свёртку с 1х1 на 3х3.

Split-блок предназначен для разделения фичей поровну. То есть только половина признаков проходит через bottleneck, остальные проходят блок без изменений. Так как после конкатенации количество фичей будет равно C_out * 1/2 * (N+2), последняя конволюция призвана выравнивать количество каналов до C_out.

Backbone and Neck

Базовая часть архитектуры осталась практически неизменной.

За основу бэкбоуна также взят CSPdarknet-53 с небольшими изменениями. Например, конволюция 6×6 заменилась двумя конволюциями 3х3 со страйдом 2.

Шея не изменилась с прошлой версии: авторы используют PAN с upsample (nearest neighbor upsampling) для увеличения пространственной размерности в top-down части и конволюцию 3х3 со страйдом 2 для уменьшения пространственной размерности в bottom-up части шеи.

У YOLOv5 голова состояла из одного конволюционного слоя. В 8-ой версии авторы заменили её на современную голову decoupled head, о которой мы рассказывали в 6-ой части [тык]. Важно отметить: тут нет confidence (objectness) части.

Куда пропал confidence?

Он спрятался в классификационной части. Есть несколько вариантов, как учитывать наличие объекта внутри ббокса:

  1. Использовать отдельный класс — «фон». При обучении все ббоксы, внутри которых нет объектов, будут иметь этот класс.
  2. Для ббоксов без объектов использовать классификационный таргет, состоящий из нулей (то есть ни один класс для данного ббокса не является верным). В этом случае на выходе модели нельзя использовать softmax (будет хотя бы один класс с большой вероятностью, а этого мы не хотим). На выходе у такой модели ставят sigmoid, чтобы получить независимые вероятности для каждого класса.

В YOLOv8 авторы решили использовать второй вариант.

Рисунок 3. Схема головы YOLOv8

Важно отметить: количество фичей на выходе из bbox ветки равно 4 * reg_max. По опыту предыдущих статей, мы знаем, что цифра 4 обычно отвечает за размеры ббокса. В YOLO-нотации это координаты верхнего левого угла, высота и ширина, однако в нашем случае всё не так просто 😟

Обучение

Поскольку мы начали говорить о ббоксах, давайте рассмотрим их в рамках обучения модели.

Основное изменение здесь — использование Distribution Focal Loss (DFL) в качестве составляющей лосса для ббоксов. Из названия видно, что мы имеем дело с каким-то распределением. Что именно можно представить в качестве распределения?

Давайте представим размеры ббокса как распределение, вместо того, чтобы работать со скалярной величиной, как было в предыдущих моделях.

А почему не хватает IoU-based лосса?

В YOLO-семействе, начиная с YOLOv4, в качестве детекционной части лосса используются разнообразные вариации IoU-лоссов. Они всегда завязаны на площади пересечения между предсказанным боксом и таргетом. Отсюда вытекает несколько недостатков. Например, для маленьких боксов IoU-based метрики слишком чувствительные: смещение предсказания на несколько пикселей приводит к падению метрики.

По схожей причине такие метрики хуже работают для огромных объектов: смещение предсказания на несколько пикселей практически не изменит значения метрики. Поэтому модели будет сложно находить правильные границы больших объектов.

Хорошим решением проблем является оптимиация размеров ббоксов через регрессию. Но со скалярными значениями работать сложнее, поэтому авторы решили вместо скаляров использовать распределение.

Рисунок 4. Представление размеров ббокса как распределение [источник]

В этом случае 4 цифры, о которых мы говорили раньше, представляют собой расстояния до краёв (левый, верхний, правый, нижний) или, по-другому, смещения от конкретного пикселя на выходной фичемапе. На рисунке 4 наглядно показан пример для одного из пикселей (синяя точка) и 4-х смещений (синие стрелки).

Мы помним, что размерность выходного вектора у bbox-ветви равняется 4 * reg_max. Параметр reg_max и отвечает за длину дискретного распределения, с помощью которого мы получим размер итогового ббокса. Обычно эта константа равна 16.

Хорошо, допустим сеть выдала 16 значений для всех смещений. Как эти предсказания перевести в размеры? В качестве примера рассмотрим дискретное распределение для одной из сторон. Оно будет выглядить как \(\{y_0, …, y_{reg\_max} \}\). Или \(\{0,…,15\}\), если подставить числа. Итоговое значение смещений из распределения считается по формуле:

\(\hat{y} = \sum_{i=0}^{reg\_max}P(out_i)y_i \text{, где}\)

\(P(out_i)\) — значение i-го выхода модели после softmax, то есть \(\sum_{i=0}^{reg\_max}P(out_i) = 1\).

А полученное число — это и есть нужное нам смещение? Не совсем, но мы уже близко 😊

Вспомним про ещё одну деталь: у YOLO-архитектуры несколько голов разного размера. У самой простой модели с 3-мя выходами обычно головы уменьшают исходное изображение в 8, 16 и 32 раза (порядок уменьшения также называют stride). Таким образом, чтобы перевести полученное число в смещение, нужно домножить на stride конкретной головы.

Разница в пространственной размерности выходов объясняется тем, что разные головы заточены искать различные по размеру объекты. Чем меньше пространственная размерность выходного вектора, тем крупнее объекты он может находить. Эта зависимость легко прослеживается и у нашего распределения: чем больше stride, тем больше максимальный размер ббокса мы можем получить.

Посмотрим на максимальное смещение, которое можно получить для ббокса. Так как вероятности суммируются в 1, максимальное значение \(\hat{y}\) = 16. Тогда для выхода со stride = 32, максимальное смещение для одной из сторон ббокса будет равно \(16*32 = 512\) пикселей.

Отсюда гармонично вытекают ограничения по размерам ббоксов для голов с меньшими значением stride. Так, для головы со stride = 8 максимальное смещение считается как \(16 * 8 = 128\), а для средней головы — \(16*16 = 256\). Напомним, что мы говорим о расстоянии от конкретной ячейки на выходном векторе фичей до границы ббокса, а не про размер ббокса (длина одной стороны ббокса будет, соответственно, в 2 раза больше).

Важно отметить: смещения считаются отдельно для каждого пикселя на выходной карте фичей и, так как это anchor-free детектор, количество ббоксов у головы будет равно H*W, где H — ширина карты фичей, а W — её длина. Следовательно, для каждой головы количество ббоксов будет разное.

С основной идеей разобрались. Осталось понять, как обучать модель генерировать распределение?

Тут всё очень просто — будем использовать лосс на основе кросс-энтропии. Допустим, \(y\) — истинное смещение до границы таргет бокса, которое может принимать значения в диапазоне от 0 до \(y_{max}\). А \(y_i\) и \(y_{i+1}\) — ближайшие к \(y\) границы из дискретного распределения \(\{0, … ,y_{max}\}\), являющиеся левым и правым бинами, тогда:

\(L_{DFL}(P(y_i), P(y_{i+1})) = -\bigl((y_{i+1} — y)log(P(out_i)) + (y-y_i)log(P(out_{i+1}))\bigr)\)

Другими словами, мы учим модель максимизировать вероятности в двух соседних бинах с учётом расстояния от \(y\) до этих бинов.

Помимо DFL, составляющей лосса для ббоксов, в YOLOv8 используется также и стандартный CIoU-лосс. Он добавляет «глобальный» контекст о ббоксах. В отличие от DFL, который учитывает каждую сторону ббокса, CIoU работает с формой итогового ббокса, а также учитывает пересечение с таргетом и близость цетров предсказанного и истинного ббоксов.

С классификацией всё стандартно: авторы используют BCE-лосс (Binary Cross Entropy). Хотя в статье при описании DFL они применяют Quality Focal Loss (немного изменённая версия Focal Loss), авторы YOLOv8 утвержают, что в их экспериментах новый лосс не показал улучшений. Поэтому они вернулись к BCE-лоссу.

Итоговый лосс выглядит следующим образом:

\(L_{total} = 0.5 * L_{BCE} + 7.5 * L_{CIoU} + 1.5 * L_{DFL}\)

Дополнительные фишки обучения

Для решения задачи матчинга авторы использовали TAL, о котором мы рассказывали в 6-ой части.

Важной частью обучения стало применение большого количества аугментаций, в частности Mosaic, MixUp / CutMix, о которых мы говорили в 4-ой части [тык], а также афинные преобразования, отражения и цветовые сдвиги.

Авторы добавили интересную фишку: на последние 10 эпохах отключили Mosaic аугментации, что позволило повысить итоговое качество.

Мультизадачность

Другая особенность YOLOv8 — наличие моделей, предобученных не только на детекции. Вместе с выходом детектора авторы представили YOLOv8-seg, которая заточена на сегментацию. А чуть позже добавили ещё несколько моделей. Сейчас есть модели для решения следующих задач:

  • Pose estimation;
  • Orientated Bounding Boxes (OBB);
  • Classification.

Чтобы обучить модель решать новые задачи, авторы написали несколько дополнительных голов, по одной для каждой задачи (тык). Все головы, кроме классификационной, наследуются от детекционной и добавляют конволюционные слои для решения поставленных задач.

Результаты

В итоге, уже традиционно для семейства, авторы представили несколько моделей разного размера. Если сравнивать c предыдущей версией, они стали немного точнее.

Рисунок 5. Итоговое качество YOLOv8

Справка о качестве предыдущей модели: YOLOv7l — 51.4 mAP, YOLOv7x — 53.1 mAP.

YOLOv11

8-ая версия семейства вышла в отдельном репозитории и никак не пересекалась с YOLOv5. Но с YOLOv11 Ultralytics решили поступить иначе и пойти в сторону унификации. Авторы не только сделали одинаковую кодовую базу для YOLOv8 и v11, но и добавили совместимость с 9-ой и 10-ой версиями. И для обучения всех моделей они предложили использовать единый пайплайн обучения. Поэтому по части обучения разницы между 8-ой и 11-ой моделями не осталось. Но в чём же тогда отличие?

Разницу нужно искать в архитектуре. Как и в YOLOv8, главные изменения спрятались в новых блоках. Давайте для начала посмотрим на общую архитектуру:

Рисунок 6. Общая архитектура YOLOv11

Мы видим, что основная архитектура осталась неизменной. Снова изменились базовые блоки: на этот раз C2F заменился на C3K2, а также добавился C2PSA после SPFF-блока. Ещё немного поменялась голова (см. блок detect на рисунке 6). Авторы добавили 2 Depth-wise конволюции в ветке классификации.

C3K2

Рисунок 7. C3K2 и C3K. Внутри bottleneck с двумя свёрточными слоями 3х3

Блок C3K2 отличается от C2F только заменой bottleneck-блока на C3K. Фактически авторы добавили ещё одну вложенность, потому что внутренний блок C3K — копия блока С3 из YOLOv5 с другими свёртками внутри bottleneck. Основная идея здесь — ещё больше перемешать пространственные характеристики. Это позволяет уменьшить общее число базовых блоков в сети без потери качества.

Небольшой спойлер: изменение блоков позволило значительно уменьшить число параметров. Например, самая маленькая версия YOLOv8n имела 3.2м параметров, а YOLOv11n — всего 2.6м. Уменьшение общего числа параметров практически на 20% относительно 8-ой версии!

C2PSA

В нашем разборе мы уже подобрались максимально близко к настоящему времени, но ещё ни разу не говорили про самый популярный блок последних нескольких лет — attention. Время исправить это!

Блок C2PSA расшифровывается как Convolutional Block with Partial Spatial Attention. Давайте посмотрим, из чего состоит этот блок.

Уточнение на счёт порядка

На самом деле YOLOv11 — не первая модель из основного семейства, где используется Attention. Он появился ещё в YOLOv10, об этом мы поговорим в следующий раз 😊

Рисунок 8. Блоки C2PSA и PSA

Если вы знакомы только с базовой концепцией attention’a (с ней можно познакомиться тут и тут) — у вас может возникнуть закономерный вопрос: «Как attention-блок использовать в свёрточной сети?». Стандартный механизм внимания работает с одномерными последовательностями и оперирует полносвязными слоями.

Идея использования attention для картинок не новая, и Ultralytics не придумали её сами, а только адаптировали хорошо известные подходы. Так же, как и с ванильным attention-блоками, их используется сразу несколько (в литературе называется multi-head attention). По дефолту в YOLOv11 применяется 8 блоков одновременно. Разберём подробно один такой Attention-блок:

  1. Обозначим Q, K, V как результат применения свёрточного слоя 1х1 с нормализацией, но без активации ко входному набору фичей. То есть \(Q/K/V=conv_{1×1}(input)\).
  2. Далее мы делаем из Q и K вектора и считаем \(attn = softmax(\frac{Q@K^T}{\sqrt{dim_k}})\), чтобы получить матрицу соответствий запросов и ключей.
  3. Считаем итоговый скор внимания: \(score = attn @ V\).
  4. После этого к скору добавляется depthwise свёртка 3х3 (для каждого канала независимый набор весов) от \(V\). Этим мы моделируем позиционное кодирование, добавляя локальный контекст. \(pos\_score = score + depthwise_{3×3}(V)\)
  5. И в конце добавляем финальную свёртку 1х1. Она общая для всех голов Attention: \(out = conv_{1×1}(pos\_score)\).

В итоге на выходе мы получаем набор признаков, где каждый элемент обогащён глобальным (self‑attention) и локальным (depthwise‑PE) контекстами.

На схеме PSA можно заменить ещё один незнакомый нам блок — FFN. Это просто 2 конволюционных слоя 1х1.

Результаты

Благодаря новым блокам авторам удалось значительно понизить количество параметров у модели по сравнению с 8-ой версией, добившись значительного ускорения. А также повысить общее качество в сравнении с предыдущими версиями (как с 8-ой, так и с 10-ой).

Рисунок 9. Итоговое качество YOLOv11 на COCO
Результаты YOLOv10 (осторожно, спойлеры😊)
Рисунок 10. Итоговое качество YOLOv10 на COCO

Мы видим, что у предыдущей версии качество чуть хуже, но зато параметров ещё меньше! Как так получилось? Узнаем в следующей части! 😎

Заключение

Сравнив академический и индустриальный подходы, мы можем сказать: хоть и в первом меньше оригинальных идей, он больше приближен к жизни. Авторы делали точечные изменения, собирая опыт других исследователей и применяя лучшие идеи для создания своих моделей. Помимо архитектурных изменений, важной частью в улучшении моделей стала оптимизация и экосистема: фьюзинг, экспрот в удобные форматы для инференса и др. Всё это позволило Ultralytics построить популярную библиотеку с точными детекторами.

Мы с вами перепрыгнули через 10-ую версию, где авторы смогли уйти от использования операции Non-Maximum Suppression! Оставайтесь с нами, чтобы узнать, как им это удалось 😊

Ссылки

Телеграм-канал

DeepSchool

Короткие посты по теории ML/DL, полезные
библиотеки и фреймворки, вопросы с собеседований
и советы, которые помогут в работе

Открыть Телеграм

Увидели ошибку?

Напишите нам в Telegram!