DETR: детекція як передбачення множини

Із детектора по черзі прибрали кандидат-області, потім якорі. Лишився NMS — останній крок конвеєра, який ніхто не вчить і який назавжди губить предмети, що стоять поруч. DETR прибирає і його, але не хитрішим фільтром: він міняє саме питання, на яке відповідає мережа.

Останні три теми були про те, як із детектора по черзі викидали рукотворні деталі. Тема 24 прибрала окремий етап пошуку кандидат-областей, тема 26 — якорі, тобто набір заготовлених рамок, які доводилось вгадувати під датасет. Лишилась одна деталь, і вона стоїть у самому кінці конвеєра: NMS. Мережа видає сотні рамок, серед них купа дублікатів, і окремий алгоритм прибирає зайві за жорстким порогом. Він не вчиться, він не знає нічого про картинку, і — найгірше — він регулярно викидає не дублікат, а справжній другий предмет. Ця тема прибирає й NMS. Не хитрішим фільтром, а зміною самого питання, на яке відповідає мережа.

ЗалишокЩо ще лишилось рукотворного

Зберемо докупи, що саме прибрали попередні теми, і чого це коштувало.

детальхто прибрав і чим замінив
кандидат-областітема 24 показала, чому їх виносили в окремий етап; тема 25 прибрала цей етап зовсім — вся сцена обробляється за один прохід
якорітема 26: замість заготовлених рамок позиція передбачає чотири відстані до країв предмета. Голова стала втричі легшою, а mAP зріс із 0.8610 до 0.9187
вибірка негативнихтема 26: focal loss тлумить легкі приклади замість того, щоб їх викидати
NMSнічим

Чому NMS узагалі потрібен, зрозуміло: щільна голова питає про предмет у кожній позиції карти ознак, тож сусідні позиції описують той самий предмет і дають майже однакові рамки. Наш anchor-free детектор із теми 26 на перевірному наборі видає 7.91 передбачення на сцену з оцінкою не нижчою за 0.3 — при тому що предметів у сцені в середньому 2.06. Пар передбачень, які накривають одне одного з IoU більшим за 0.7, у нього 10.63 на сцену: дублікати не виняток, а норма. NMS зрізає ці 7.91 до 2.25 і залишає нуль таких пар.

Здавалося б, працює. Але подивись на ціну. NMS діє за одним-єдиним правилом: узяти найвпевненішу рамку, викинути всі, що перекриваються з нею сильніше за поріг, повторити. Правило нічого не знає про те, чи перекриті рамки описують один предмет, чи два різні. Тема 23 заміряла це прямо: дві рамки 60 × 120, зсунуті на 10 пікселів, мають IoU 0.7143 — і при будь-якому з трьох звичних порогів (0.3, 0.5, 0.7) з двох рамок лишається одна. Це не крайній випадок: так виглядає будь-яка вулична сцена, де людина стоїть за людиною або машина за машиною.

Тут не допоможе підбір порога. Опустиш поріг — почнеш губити ще більше справжніх предметів. Піднімеш — повернуться дублікати, і точність впаде. Тема 23 показала цей обмін у числах: жадібний поріг 0.1 підняв точність, але вибив частину істинних рамок. Порога, який розрізняє «дублікат» і «другий предмет», не існує, бо на вході в NMS цієї інформації просто немає.

Отже, проблема не в порозі, а в тому, що дублікати взагалі зʼявляються. І тут напрошується інша думка: а що, як їх не породжувати?

МножинаДетекція як передбачення множини

Усі детектори, які ми розібрали, відповідають на питання «що в цій позиції?» — і повторюють це питання для кожної позиції сітки, кожного якоря, кожної кандидат-області. Відповідь виходить довгим списком оцінок, який потім треба причісувати.

DETR (detection transformer, 2020) ставить питання інакше: назви всі предмети сцени одразу, як один набір. Не «чи є предмет у клітинці (3, 5)», а «ось сцена — перелічи, що на ній є». Вихід мережі — не карта оцінок, а фіксований список із N позицій, кожна з яких або називає предмет із рамкою, або каже «порожньо».

Це звучить як косметична зміна формулювання, але з неї одразу випливає технічна проблема, якої не було раніше. Множина не має порядку. Якщо мережа сказала «коло ліворуч, квадрат праворуч», а розмітка каже «квадрат праворуч, коло ліворуч» — це та сама відповідь. Але функція втрати працює з числами, і якщо порівнювати передбачення з розміткою попарно за індексом (перше з першим, друге з другим), та сама правильна відповідь дістане різну втрату залежно від того, у якому порядку її записали. Мережа почне вчити не «що на сцені», а «в якому порядку про це звітувати».

Множина не має порядку, а втрата за індексом — має

Ліворуч сцена: дві істинні рамки й чотири передбачення. Кнопки міняють лише порядок передбачень у списку — самі рамки не рухаються. Праворуч дві втрати: наївна попарно за індексом і та, що спершу зіставляє.

порядок передбачень
втрата за індексом—
втрата після зіставлення—
розмах по всіх 24 перестановках—
Обидві втрати рахуються тут же, у браузері, за формулою з розділу 5. Права цифра не змінюється жодного разу — зіставлення шукає найдешевшу пару, і йому байдуже, у якому рядку списку вона лежить. Ліва стрибає в девʼять разів, хоча жодна рамка не зрушила з місця.

Висновок із цієї картинки й є ключем до всієї теми. Щоб навчати мережу на множині, потрібна втрата, яка не залежить від порядку. Такі втрати будують так: спершу знайти найкращу відповідність між передбаченнями й істинами, потім штрафувати вже знайдені пари. Перший крок і зветься зіставленням (matching).

ЗапитиФіксований набір запитів

Скільки позицій має бути у списку? Предметів на сцені може бути один, а може двадцять. Мережа з виходом змінної довжини — річ незручна: тензори фіксованого розміру доводиться десь обрізати або доповнювати.

DETR робить простіше: список завжди однакової довжини N. Ця N — гіперпараметр, і вона свідомо береться з запасом, більшою за максимальну кількість предметів у сцені. В оригінальній статті на COCO вона дорівнює 100. У нашій маленькій моделі предметів у сцені щонайбільше три, тож ми беремо N = 10.

Кожна з N позицій називається запитом (query). Запит — це навчуваний вектор, з яким мережа приходить до карти ознак і питає: «а що тут для мене?». Запитів рівно N, вони однакові для всіх картинок, і кожен віддає одну відповідь: клас плюс рамка.

Але предметів же менше, ніж запитів. Куди дівати зайві? До списку класів додають ще один, службовий — «порожньо» (в оригіналі ∅, no object). Запит, якому нічого не дісталось, каже «порожньо», і його рамка нікого не цікавить.

Це не марнотратство. Запити не змагаються за обчислення: усі десять проходять крізь ту саму мережу одним тензором, і десятий коштує рівно стільки ж, скільки перший. Для порівняння: наша щільна голова з теми 26 рахує оцінку в 64 позиціях × 3 класи = 192 числа на сцену, тобто в девʼятнадцять разів більше рішень, ніж десять запитів DETR. Дорого не «мати запас», дорого — питати про кожну точку карти.

Скільки «порожньо» має бути — видно з простої арифметики, ще до всякого навчання. Запитів десять, а предметів у нашій сцені в середньому 2.06, тож навіть бездоганна модель мусила б відповідати «порожньо» приблизно (10 − 2.06) ÷ 10 = 79.4 % разів. Часте «порожньо» — це властивість постановки із запасом, а не ознака невдалого навчання: чим більший запас N, тим більша ця частка, і в оригінальній DETR зі ста запитами вона ще вища.

Наша DETR на 120 перевірних сценах видає 1 200 рішень, і 49.4 % із них — «порожньо»: найчастіша з чотирьох можливих відповідей, хоч і не більшість. Кожен із десяти запитів мовчить у 37-62 % сцен; жоден не мовчить завжди й жоден не говорить завжди. Не-порожніх відповідей виходить 5.06 на сцену при 2.06 істинних рамках — тобто модель говорить частіше за ідеал, а не рідше: перестраховується, але помірно. Щільна голова з теми 26 при порозі 0.3 віддає 7.91 передбачення, а до порога — усі 64 позиції.

ЗіставленняУгорське зіставлення

Задача звучить так. Є N передбачень і M істинних рамок, причому M зазвичай набагато менше. Треба кожній істині призначити рівно одне передбачення так, щоб сумарно вийшло якнайкраще. «Якнайкраще» треба чимось міряти — для кожної пари «передбачення i, істина j» рахують число, яке називають вартістю: чим воно менше, тим краще пара пасує одна одній. З цих чисел складається таблиця N × M — матриця вартості.

Розберімо руками. Три передбачення, дві істини, вартості такі (менше — краще):

І1 І2 П1 0.30 0.40 П2 0.35 0.90 П3 1.20 1.30

Найочевидніший спосіб — жадібний: знайти в усій таблиці найменше число, зафіксувати цю пару, викреслити її рядок і стовпець, повторити. Найменше тут — 0.30 у клітинці П1-І1. Викреслюємо перший рядок і перший стовпець. У тому, що лишилось, найменше — 0.90 у П2-І2. Сума: 0.30 + 0.90 = 1.20.

А тепер спробуймо інакше: П1 віддамо другій істині (0.40), а першу візьме П2 (0.35). Сума: 0.75. Це на 0.45 дешевше — і жодного хитрого прийому тут немає, просто жадібний алгоритм на першому кроці забрав П1 собі, не подивившись, що П1 — єдиний, хто взагалі непогано пасує до І2.

крок 1 · що ми шукаємо

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

крок 2 · перебрати не вийде

При N = 10 і M = 3 варіантів 10 × 9 × 8 = 720 — ще можна. Але на COCO N = 100, і навіть при M = 7 варіантів уже 80 678 106 432 000 — вісімдесят трильйонів. І це для однієї картинки з партії.

крок 3 · угорський алгоритм

Задача про призначення розвʼязується точно й швидко — за час, що росте як куб від розміру таблиці. Метод відомий із 1955 року й зветься угорським алгоритмом (Hungarian algorithm, метод Мункреса). У Python він лежить у scipy.optimize.linear_sum_assignment.

Ідея алгоритму пояснюється без формул. Якщо відняти те саме число від усіх елементів одного рядка, оптимальне призначення не зміниться: у будь-якому призначенні з цього рядка береться рівно одна клітинка, тож уся сума просто впаде на це число. Те саме для стовпця. Алгоритм користується цим, щоб зробити в таблиці якомога більше нулів, а потім знайти набір нулів, які не ділять між собою жодного рядка чи стовпця. Такий набір і є оптимальним призначенням — дешевше за нуль у невідʼємній таблиці бути не може.

Матриця вартості: жадібно проти оптимально

Клітинки таблиці можна міняти повзунком. Перемикач обирає спосіб зіставлення; підсвічені клітинки — обрані пари, число внизу — сумарна вартість.

жадібно—
оптимально—
Три вартості з шести можна крутити, решта закріплені (П2-І2 = 0.90, П3-І1 = 1.20, П3-І2 = 1.30). Жадібний спосіб програє не завжди: у більшості положень повзунків обидва дають однакову суму. Але щойно найдешевша клітинка стоїть у рядку, який потрібніший іншому стовпцю, — жадібний переплачує.

Наскільки часто це трапляється насправді? Міряємо двічі — спершу на вигаданих матрицях, потім на справжніх.

Замір перший, вигадані матриці. Беремо 2 000 випадкових матриць вартості розміру 10 × M: десять рядків — стільки ж, скільки запитів у нашій моделі, M стовпців — стільки, скільки істин на сцені. На них жадібний спосіб розходиться з оптимальним у 6.5 % випадків, переплачуючи в середньому 0.026, а в найгіршому випадку 1.72.

Замір другий, справжні матриці — по одній на кожну зі 120 перевірних сцен, знята з нашої DETR. Тут картина цікавіша:

матриці вартостірозійшлисьсер. переплатанайгірша
свіжа мережа14 зі 120 (11.7 %)0.03940.7557
навчена мережа0 зі 120 (0.0 %)0.00000.0000

Читається це так: зіставлення важливе саме тоді, коли мережа ще нічого не вміє. У свіжої мережі всі передбачення схожі одне на одне, вартості близькі, і жадібний вибір легко промахується — на кожній девʼятій сцені. У навченої мережі кожна істина має свого явного фаворита, і будь-який розумний спосіб дає ту саму відповідь. Але навченою вона стане тільки в тому разі, якщо на початку навчання її вели правильно.

Угорський алгоритм по кроках

Та сама таблиця 3 × 2, доповнена до квадратної третім стовпцем «порожньо». Кнопка «крок» веде алгоритм: віднімання по рядках, віднімання по стовпцях, вибір нулів. Кнопка «спочатку» повертає таблицю у вихідний стан.

крок—
нулів у таблиці—
сума призначення—
Третій стовпець — «порожньо»: він потрібен, щоб таблиця стала квадратною. Вартість «порожньо» однакова для всіх передбачень, тож на призначення справжніх пар вона не впливає — це лише спосіб прилаштувати зайві передбачення. Зверни увагу на суму: після віднімань вона падає, але вибір клітинок лишається тим самим, що й у вихідній таблиці.

Нашу власну реалізацію угорського алгоритму звірено з scipy.optimize.linear_sum_assignment на 300 випадкових матрицях різного розміру: розбіжностей нуль. Усередині бібліотеки магії немає — там ті самі віднімання по рядках і стовпцях, тільки акуратно запаковані.

ВартістьЗ чого складається вартість

Матрицю вартості ми досі брали з неба. Тепер розберімо, звідки беруться її числа. Пара «передбачення — істина» пасує одна одній тим краще, чим більше збігаються дві речі: клас і рамка. Тому вартість складають із трьох доданків.

доданок 1 · клас

Беремо ймовірність, яку передбачення дає саме тому класу, що стоїть в істині, і ставимо її зі знаком мінус. Упевнене влучання в клас — велика ймовірність — робить вартість меншою.

вартістьклас = −pi(клас істини j)
доданок 2 · L1 по координатах

L1 — це сума модулів різниць по чотирьох числах рамки. Координати беруть нормованими: поділеними на розмір картинки, тобто в межах від 0 до 1. Без нормування та сама помилка в пікселях важила б по-різному на великих і малих картинках.

вартістьL1 = |Δx1| + |Δy1| + |Δx2| + |Δy2|
доданок 3 · GIoU

1 − GIoU, де GIoU — узагальнений IoU. Про нього докладніше нижче.

Навіщо третій доданок, якщо є другий? L1 має ваду, яку легко проґавити: одна й та сама його величина означає різне для великих і малих рамок. Промах у 4 пікселі на рамці 40 × 40 — це майже точне влучання, IoU дорівнює 0.82. Той самий промах на рамці 8 × 8 дає IoU 0.33 — тобто не проходить навіть наймʼякшого порога. Наші предмети мають бік від 13 до 21 пікселя, тобто розкид у півтора раза, і на COCO цей розкид на порядки більший.

IoU цієї вади не має — він відносний за побудовою. Але в нього є своя: коли рамки не перетинаються, IoU дорівнює нулю й перестає щось казати. Рамка поруч і рамка на іншому кінці картинки дістануть однакову оцінку 0, а отже, однаковий градієнт — тобто ніякий. Саме це й лікує GIoU: до IoU додається штраф за порожнє місце в найменшому прямокутнику, який накриває обидві рамки.

GIoU = IoU − (площа накривного прямокутника − площа обʼєднання) / площа накривного прямокутника

Словами: якщо рамки збігаються, накривний прямокутник дорівнює обʼєднанню, штраф нульовий і GIoU = IoU = 1. Якщо рамки далеко, обʼєднання займає малу частку накривного прямокутника, штраф великий, і GIoU йде в мінус. Ось що виходить для двох рамок 10 × 10, які розʼїжджаються по діагоналі — на однаковий зсув по кожній з осей:

зсув по кожній осіIoUGIoU
12 px0.0000−0.5868
20 px0.0000−0.7778
30 px0.0000−0.8750
40 px0.0000−0.9200

IoU у всіх чотирьох рядках однаковий і мовчить. GIoU монотонно спадає — тобто дає напрямок «наближайся» навіть там, де перетину ще немає. Готова функція лежить у torchvision.ops.generalized_box_iou; числа вище пораховані власною реалізацією й звірені з нею — розбіжність нуль до останнього біта.

Три доданки складають із вагами. Канонічні значення зі статті: 1 для класу, 5 для L1, 2 для GIoU.

вартість = 1 · (−pклас) + 5 · L1 + 2 · (1 − GIoU)

Ваги вартості вирішують, хто кому дістанеться

Сцена з двома істинами й трьома передбаченнями. П2 має ідеальну рамку для І2, але майже не вірить у її клас; П3 — навпаки. Повзунки міняють ваги трьох доданків; лінії показують поточне зіставлення.

І2 дістається—
сума вартості—
Кожна клітинка таблиці — вартість за формулою вище, порахована тут же для цього прикладу. При канонічних вагах 1 / 5 / 2 другу істину забирає П2 — рамка важить більше за клас. Підніми вагу класу до 1.5, і І2 переходить до П3, який стоїть на піксель гірше, зате знає, що там квадрат. Однакове збільшення обох геометричних ваг (10 і 4) нічого не міняє: важать не самі ваги, а їхнє співвідношення.

ВтратаВтрата після зіставлення

Зіставлення знайдено — тепер із нього роблять функцію втрати. Тут є приємна деталь: втрата рахується майже за тією самою формулою, що й вартість. Різниця у двох речах.

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

Друге, і важливіше. Втрата стосується всіх N запитів, а не лише зіставлених M. Запити, яким не дісталося істини, мусять навчитися казати «порожньо». І тут повертається знайома біда: якщо N = 100, а предметів сім, то 93 зі 100 доданків втрати — це «порожньо». Той самий дисбаланс, заради якого в темі 26 вигадали focal loss.

DETR лікує його простіше: класу «порожньо» дають меншу вагу у крос-ентропії. В оригінальній статті — 0.1, тобто вдесятеро менше за звичайний клас. Наша модель бере те саме число. Це грубіше за focal loss, зате не додає жодного показника, який треба підбирати кривими.

втрата = крос-ентропія(усі N запитів, вага «порожньо» = 0.1) + (5 · L1 + 2 · (1 − GIoU)) / кількість зіставлених пар

Геометричні доданки ділять на кількість пар, а не на N: інакше сцена з одним предметом і сцена з трьома давали б градієнт різної величини просто через кількість предметів.

АрхітектураЩо всередині DETR

Тепер зберімо мережу. Вона складається з чотирьох частин, і три з них нам уже знайомі.

частинащо робить
тіло (backbone)звичайна згорткова мережа. Сцену 64 × 64 стискає до карти ознак 8 × 8 на 64 канали — те саме тіло, що в темах 23, 25 і 26
позиційне кодуваннякарту ознак розгортають у послідовність із 64 векторів. Порядок при цьому губиться, тому до кожного вектора додають позиційний — як у темі 20
енкодеркілька шарів self-attention із теми 19. Кожна позиція карти дивиться на всі інші
декодерN запитів. Кожен шар декодера має self-attention між запитами й крос-увагу від запитів до карти ознак

Прогін сцени крізь DETR: що з чого йде і якої воно форми

Верхній ряд — шлях картинки, нижній — шлях запитів. Дві операції декодера намальовані окремо, бо роблять різні речі: одна дивиться на картинку, друга — на сусідні запити.

Форма тензора підписана під кожним блоком, і саме по ній видно, де зникає сітка. До енкодера включно все має розмір 64 — стільки клітинок у карті 8 × 8. Після декодера залишається десять — стільки, скільки запитів, і це число не залежить ані від розміру сцени, ані від кількості предметів у ній. Саме тут детекція перестає бути «відповіддю в кожній позиції» й стає списком фіксованої довжини.

Дві операції декодера роблять різні речі, і саме з їхнього поділу росте вся поведінка DETR.

Крос-увага — це те, як запит дивиться на картинку. Механіка та сама, що в темі 19: softmax(QKT/√d)V, тільки запити Q беруться з N запитів, а ключі K і значення V — із карти ознак. Запит отримує зважену суму ознак тих місць, які його цікавлять. Ніхто не наказує йому, куди дивитися: ваги вчаться.

Розберімо запис по символах, бо він тут працює, а не прикрашає. QKT — це таблиця скалярних добутків «кожен запит проти кожної позиції карти»: десять рядків на 64 стовпці, і кожне число каже, наскільки це місце цікавить цей запит. d — довжина одного вектора; у нашій моделі карта має 64 канали, тож d = 64 і √d = 8. Ділення на √d потрібне тому, що скалярний добуток — це сума d доданків, і з ростом d вона розганяється: без ділення softmax отримав би на вхід завеликі числа й майже всю вагу віддавав би одній позиції, тобто градієнт для решти був би нульовий. softmax перетворює рядок таких чисел на частки, що дають у сумі одиницю, а множення на V складає ознаки місць саме з цими частками.

Self-attention між запитами — це те, як запити домовляються. Кожен запит бачить, що збираються відповісти інші, і може врахувати це у власній відповіді. Ось тут і зникає джерело дублікатів: якщо один запит уже впевнено взяв предмет, іншому вигідно взяти інший предмет або сказати «порожньо». Дублікат не викидають — його просто немає причини породжувати.

Одна деталь, яку легко проґавити. Увага еквіваріантна щодо перестановки: якщо переставити входи, виходи переставляться так само. Тому N однакових нульових запитів дали б N однакових відповідей. Щоб запити були різними, кожен із них — окремий навчуваний вектор. Різниця між ними — єдине, що взагалі дозволяє їм спеціалізуватися.

СпеціалізаціяЗапити спеціалізуються

Найкрасивіше, що відбувається в DETR, ніхто не програмував. Запити нічим не відрізняються один від одного за призначенням: усі десять мають однакову форму, ті самі шари, ту саму втрату. Але після навчання вони ділять між собою сцену.

Замір простий. Для кожного запиту беремо всі перевірні сцени, де він сказав не «порожньо», і рахуємо середній центр його рамки та середній розмір. Ось що вийшло в нашої моделі:

запитсцен, де говоритьсередній центр xсередній центр yсередній бік
05536.217.116.6
17525.436.819.1
26243.819.617.6
35445.227.717.7
47619.041.816.6
55944.640.416.5
66522.029.518.8
76922.421.516.3
84646.046.115.8
94631.546.816.0

Середні центри розкидані по x від 19.0 до 46.0, по y — від 17.1 до 46.8. Полотно має 64 пікселі, тобто запити розібрали між собою майже половину його ширини й висоти. Запит 4 сидить у лівому низу, запит 8 — у правому низу, запит 0 — угорі посередині.

Щоб число мало сенс, його треба з чимось порівняти. Усередині одного запиту центр гуляє від сцени до сцени зі стандартним відхиленням 7.4 пікселя — це природно, бо предмети щоразу в різних місцях. А розмах середніх між запитами — 27.0 і 29.7 пікселя, тобто вчетверо більший. Отже, різниця між запитами не тоне в розкиді всередині запиту: вони справді дивляться в різні місця.

Чесна половина результату. За розміром спеціалізації майже немає: середній бік рамки в усіх десяти запитів лежить у вузькій смузі 15.8-19.1 пікселя. І це очікувано — наші предмети мають бік від 13 до 21 пікселя, розкид усього в півтора раза, тож ділити за розміром просто нічого. В оригінальній DETR на COCO, де предмети відрізняються в десятки разів, запити ділять і розмір теж. Не переноси наш результат на великі датасети: розмах, якого немає в даних, мережа вигадати не може.

Карта запитів

Кожна точка — один запит, поставлений у свій середній центр; діаметр точки пропорційний середньому боку рамки. Перемикач показує або карту центрів, або частку сцен, де запит мовчить.

центр запиту—
середній бік—
говорить у сценах—
відстань до сусіда—
Усі десять точок — заміри по навченій моделі, не малюнок від руки. Сірий хрестик у центрі полотна — середній центр усіх істинних рамок; якби спеціалізації не було, всі десять точок злиплися б навколо нього. Відстань до найближчого сусіда показує, наскільки щільно запити стоять: у найтіснішому місці карти вона менша за розкид усередині одного запиту, тобто там два запити справді ділять одну ділянку.

Звідки береться ця спеціалізація? Її ніхто не задає — вона наслідок зіставлення. Якщо запит 4 випадково частіше опинявся найдешевшим для предметів у лівому низу, йому частіше діставались саме вони, він на них учився, ставав для них ще дешевшим — і так далі. Це те, що зазвичай зветься самозакріпленням: маленька початкова перевага росте сама.

А тепер найважливіше: що буде, якщо зіставлення прибрати. Уявімо наївний варіант — хай кожна істина тягне до себе всі запити одразу, без розподілу відповідальності. Навчимо таку модель 12 епох і зміряймо розкид середніх центрів між запитами:

як навчалирозкид центрів між запитамиmAP@0.5
без зіставлення0.18 px0.0120
зі зіставленням9.16 px0.0306

0.18 пікселя — це нуль. Усі десять запитів злиплися в одну точку й передбачають, по суті, ту саму рамку: коли кожну істину тягнуть усі, найкраще, що може зробити запит, — стати посередині між ними. Мережа з десятьма запитами перетворюється на мережу з одним. Отже, однозначність зіставлення — не технічна дрібниця для зручності, а те, без чого вся конструкція вироджується.

Своя DETRСвоя маленька DETR

Зберімо все й перевірмо на тих самих сценах 64 × 64, що в темах 23, 25 і 26. Тіло — те саме, далі позиційне кодування, два шари енкодера, два шари декодера з N = 10 запитами, дві голови: клас (три класи плюс «порожньо») і рамка (cx, cy, w, h через сигмоїду). Навчання — AdamW, 36 епох, три зерна, як і в суперника.

модельпараметрівmAP@0.5розкид по зернахmAP@0.5:0.95
DETR237 4160.38690.06360.1520
anchor-free (тема 26)64 6150.93800.07840.5213

Одна засторога, перш ніж читати таблицю. 0.9380 — це наш власний прогін anchor-free голови поруч із DETR, на тих самих 36 епохах і тих самих трьох зернах. Тема 26 для тієї самої голови називає 0.9187 — там її вчили своїм розкладом. Різниця 0.0193 менша за розкид по зернах у кожному з двох замірів (0.0784 тут, 0.0383 там), тобто це не суперечність, а два прогони однієї моделі. Порівнювати DETR треба з числом із цієї таблиці: воно зняте в тих самих умовах.

Прочитаймо це чесно. Наша DETR при однаковому бюджеті епох сильно програє — 0.3869 проти 0.9380, і це при тому, що параметрів у неї майже вчетверо більше. Три зерна дали 0.4189, 0.3866 і 0.3553, тобто розкид не пояснює розриву. Це не наша помилка збірки й не випадковість: так DETR і поводиться, і причину ми розберемо в наступному розділі.

Але тепер поміряймо те, заради чого DETR узагалі вигадали. Порахуємо, скільки пар передбачень із IoU більшим за 0.7 віддає кожна модель до будь-якого NMS.

модельпередбачень ≥ 0.3 на сценупар IoU > 0.7сцен із дублікатами
DETR6.530.4033 %
щільна голова7.9110.6398 %
щільна голова + NMS(0.5)2.250.000 %

Різниця в 26.6 раза. Щільна голова дає дублікати практично на кожній сцені — 98 % проти 33 %. І звертай увагу: кількість передбачень у двох моделей майже однакова (6.53 і 7.91), різна саме їхня структура. DETR теж перестраховується, але її зайві передбачення розкидані по сцені, а не складені стосом на одному предметі.

Дублікати до NMS

Скільки пар передбачень накривають одне одного сильніше за поріг. Перемикач обирає модель, повзунок — поріг IoU, за яким пару вважають дублікатом. Обидва ряди заміряні на тих самих 120 перевірних сценах.

пар на сцену—
у скільки разів більше—
Повзунок має пʼять положень — рівно ті пороги, на яких робився замір (0.5, 0.6, 0.7, 0.8, 0.9). Зверни увагу на праву колонку при найсуворішому порозі: майже ідентичних пар у DETR 0.01 на сцену, тобто одна на сотню, а в щільної голови 1.18 — по одній на кожну сцену.

Тепер головна перевірка теми. Тема 23 показала сцену, на якій NMS ламається: два справжні предмети, чиї рамки перекриваються з IoU 0.714, і при звичайному порозі 0.5 один із них зникає назавжди. Побудуймо таку сцену в нашому світі: два предмети одного класу, зсунуті рівно так, щоб IoU їхніх істинних рамок дорівнював заданому. По 60 сцен на кожне значення, обидві навчені моделі, повнота при IoU 0.5.

IoU істинних рамокDETR (без NMS)щільна + NMS(0.5)щільна без NMS
0.1990.9580.9501.000
0.4080.9580.8421.000
0.5420.9830.6171.000
0.7030.9830.5001.000

Так, DETR цю сцену розвʼязує. На перекритті 0.703 — практично тому самому, що в темі 23, — DETR знаходить 98.3 % предметів, а щільний детектор із NMS рівно половину. Половина — це не «майже», це буквально: з кожної пари виживає один.

А тепер найважливіший рядок таблиці, останній. Щільна голова без NMS знаходить обидва предмети в 100 % випадків. Тобто мережа їх бачила. Її передбачення були правильні. Їх знищив фільтр, дописаний після мережі, який за означенням не вміє відрізнити другий предмет від дубліката першого. Це і є та ціна, яку DETR не платить — не тому, що краще бачить, а тому, що в її конвеєрі цього кроку немає.

Сцена, на якій NMS помиляється

Два предмети одного класу з заданим перекриттям. Ліворуч — сцена й рамки, праворуч — повнота трьох варіантів конвеєра. Повзунки міняють перекриття істинних рамок і поріг NMS.

DETR—
щільна + NMS—
щільна без NMS1.000
DETR не залежить від порога NMS, бо NMS у ній немає — її стовпчик нерухомий. Підняти поріг до 0.9 і справді рятує обидва предмети, але це той самий обмін, що в темі 23: разом із ними повертаються дублікати — 10.63 пари на сцену з попереднього інтерактиву. Поріг, який лишає обидва предмети й не пускає дублікатів, у щільної голови не існує.

ЦінаЦіна: збіжність

Повернімося до розриву в mAP. Ось криві навчання обох моделей — середнє по трьох зернах, той самий датасет, той самий оптимізатор, та сама кількість епох.

епохаDETRanchor-free
30.00410.0455
60.01100.5897
90.03930.7767
120.05090.9165
180.16120.9407
240.27440.9359
300.38240.9564
360.38690.9380

Anchor-free детектор виходить на 0.92 вже на дванадцятій епосі й далі лише дрібно коливається. DETR на дванадцятій епосі має 0.0509 — у вісімнадцять разів менше — і ще росте на тридцять шостій. Ми обірвали навчання не там, де крива вийшла на полицю, а там, де скінчився наш бюджет на навчання.

Це не властивість нашої маленької збірки, а відома вада DETR, названа в самій статті: оригінальна DETR навчалась 500 епох на COCO, тоді як Faster R-CNN у стандартному розкладі — 12. Різниця більш ніж у сорок разів. Ми дістали ту саму картину на іграшковій задачі й за лічені хвилини замість тижнів.

чому так · причина 1

Зіставлення нестабільне на початку. Поки мережа нічого не вміє, вартості близькі одна до одної, і той самий запит на сусідніх кроках дістає то один предмет, то інший. Ми це поміряли: на свіжій мережі навіть жадібний і оптимальний способи розходяться на 11.7 % сцен. Запит отримує суперечливі вказівки й довго не може ні на чому зупинитися.

чому так · причина 2

На один предмет — один градієнт. У щільної голови кожен предмет навчають десятки позицій одразу: сигнал приходить із багатьох місць. У DETR предмет дістається рівно одному запиту, решта девʼять учаться казати «порожньо». Корисного сигналу на крок у рази менше.

чому так · причина 3

Крос-увага спершу дивиться всюди. На старті ваги уваги майже рівномірні, тож запит бачить розмиту суму всієї карти. Щоб він навчився дивитись у конкретне місце, потрібно багато кроків — і саме це прискорили наступні роботи.

ДрібніДрібні предмети

Друга вада виходить із першої частини архітектури. Енкодер працює на одній карті ознак — найглибшій і найгрубішій. У нашій моделі це 8 × 8, тобто одна клітинка на 8 пікселів сцени. Предмет із боком 6 пікселів займає менш ніж клітинку, і всі подробиці його форми вже усереднені.

У темі 26 ця біда вже виникала, і там її лікували FPN — пірамідою карт різної дрібності, де дрібні предмети шукають на детальнішому рівні. DETR цього не робить: карта одна. Причина проста й неприємна — ціна уваги. Self-attention рахує вагу для кожної пари позицій, тож витрати ростуть як квадрат їхньої кількості. Карта 8 × 8 — це 64 позиції й 4 096 пар. Карта 64 × 64 — це 4 096 позицій і 16 мільйонів пар: у чотири тисячі разів більше. Взяти детальнішу карту неможливо не через принцип, а через арифметику.

Розвʼязок знайшли за рік. Deformable DETR замінює повну увагу на розріджену: кожен запит дивиться не на всі позиції, а на кілька десятків точок, які сам собі обирає. Витрати перестають рости квадратично, і тоді вже можна взяти кілька карт різної дрібності, як у FPN. Заявлений результат: збіжність за 50 епох замість 500 і помітно кращі дрібні предмети. Далі йшли Conditional DETR і DAB-DETR, які додали запиту явну привʼязку до місця, і DINO, що звів усе докупи.

Спільне в усіх продовженнях одне. Ніхто з них не повернув ані якорі, ані NMS. Правили те, як запит знаходить своє місце в картинці, а не саму постановку «видати множину й зіставити її з розміткою».

СпадокЩо з цього лишилось

DETR у чистому вигляді не витіснила згорткові детектори — вона надто дорога й надто повільно вчиться. Але її головна ідея розійшлася далеко за межі трансформерів.

ПідсумокПідсумок і місток далі

ідеяу чому суть
Множина замість спискунаївна попарна втрата міняється в девʼять разів від самої лише перестановки передбачень (2.72 … 24.59); після зіставлення вона стала
Фіксовані N запитіву нашій моделі 10; 49.4 % рішень — «порожньо», не-порожніх відповідей 5.06 на сцену при 2.06 істинних рамках
Угорське зіставленняжадібний спосіб розходиться з оптимальним на 11.7 % сцен свіжої мережі; на навченій — на 0 %
Однозначність обовʼязковабез неї запити злипаються: розкид центрів між запитами 0.18 px замість 9.16
GIoU крім L1L1 не знає масштабу, IoU мовчить без перетину: 0.0000 на всіх відстанях проти GIoU від −0.59 до −0.92
NMS не потрібендублікатів 0.40 пари на сцену проти 10.63 у щільної голови — у 26.6 раза менше
Ціна0.3869 проти 0.9380 при однаковому бюджеті; на 12-й епосі 0.0509 проти 0.9165

Головна пара чисел теми — 0.983 і 0.500. Це повнота на сценах, де два предмети одного класу перекриваються з IoU 0.703 — рівно тому випадку, який тема 23 назвала провалом NMS. І ключ до цієї пари в останньому стовпці тієї таблиці: 1.000. Щільна голова знаходить обидва предмети. Втрачає їх не мережа, а жорсткий фільтр після неї.

Але не читай тему як «DETR перемогла». На нашій задачі вона програє anchor-free детектору більш ніж удвічі за mAP і має майже вчетверо більше параметрів. Правильний висновок інший: детекція як передбачення множини — це постановка, а не архітектура. Постановка виявилась настільки вдалою, що її перенесли в згорткові детектори, у сегментацію й у трекінг, а від самого трансформера в багатьох із них нічого не лишилось.

На цьому блок про детекцію закінчено. Ми пройшли всі підходи: двоетапні з кандидат-областями, одноетапні з сіткою й якорями, anchor-free і, нарешті, множинні. Кожен наступний прибирав рукотворну деталь попереднього — і жоден не виявився «правильним» на всі випадки.

Наступна тема — про вибір. Який детектор брати під конкретну задачу, скільки даних йому треба, як їх розмічати, що робити з рідкісними класами й де в реальному конвеєрі ховаються помилки, яких немає в жодній статті.