Одноетапні: focal loss і anchor-free

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

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

ХворобаЧому одноетапні детектори програвали

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

крок 1 · позиції та якорі

Тіло мережі зменшує сцену 64 × 64 до карти ознак 8 × 8 — це 64 позиції, та сама сітка, що в темах 23 і 25. У кожній позиції стоять три квадратні якорі зі сторонами 12, 18 і 26 пікселів: разом 64 × 3 = 192 якорі.

крок 2 · рішення

Про кожен якір голова каже окремо для кожного класу: «це коло?», «це квадрат?», «це трикутник?».

рішень = позицій × якорів × класів = 64 × 3 × 3 = 576
крок 3 · скільки з них «так»

Замір по всіх 240 навчальних сценах: позитивних рішень у середньому 4.05 на сцену, тобто 0.703 % від 576. На один позитивний припадає 141 негативний — (576 − 4.05) ÷ 4.05 = 141.2.

Число 141 варте того, щоб стати поруч із двома вже відомими. Тема 23 дістала 85.4 % чистого фону серед клітинок сітки, тема 24 на дрібнішій карті й із суворішим порогом — 1 258 фонових якорів на один позитивний. Наші 141 — те саме явище на легшій конфігурації: більша клітинка, мʼякший поріг, якорі підібрані під наші предмети. Хвороба та сама, різна лише температура.

Обережно: ці числа лічать різні речі, і складати їх в одну криву не можна. Сцена й карта ознак у тем 23, 25 і 26 однакові — 64 × 64 пікселі, сітка 8 × 8. Різне у них двоє: скільки питань голова ставить у кожній клітинці і за яким правилом місце оголошують позитивним. Обидва різні — отже різний і знаменник частки.
дещо рахують за одне «рішення»коли воно позитивнеїх на сценуфон
тема 23клітинка сітки 8 × 8центр клітинки лежить у рамці — таких по 4.52 на предмет6485.4 %
тема 25рамка YOLO, дві на клітинкуцентр предмета впав у цю клітинку — рівно одна рамка на предмет12898.4 %
тема 26якір × клас, 3 × 3 у клітинціIoU якоря з рамкою вищий за 0.5, і клас той самий57699.3 %

Тобто зростання 85.4 → 98.4 → 99.3 — це не «фону побільшало». Фону стільки самого: сцена та сама, предмети ті самі. Побільшало питань про той самий фон (одне на клітинку, потім два, потім девʼять) і суворішим став поріг (у темі 23 позитивною ставала кожна клітинка, що зачепила предмет; тут — лише той якір, що на нього схожий). Обидві зміни працюють в один бік: кожне зайве питання про порожнє місце — це ще один голос у хорі, який співає «мене немає».

Скільки фону дивиться одноетапна голова

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

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

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

звідки втратасумачастка
фон9 286.31999.72 %
предмети26.1460.28 %
разом9 312.465100 %

99.72 % втрати — це фон. І тут потрібне те саме застереження, що й вище, тільки з іншого боку: 85.4 % теми 23 — це частка порожніх клітинок, а 99.72 % — частка втрати. Дві різні величини, і плутати їх не можна: сітка може бути порожньою на 85 %, а втрата від неї — на всі 99, бо кожна порожня позиція кричить окремо і за кожен клас.

Пряма пара для порівняння одна: у темі 25 частка втрати від фону на свіжій YOLO-подібній голові з рівними вагами дорівнювала 92.3 %, і вже тоді здавалася великою. Обидва числа заміряні однаково — сира сума доданків до першого кроку навчання, — тож їх можна ставити поруч. Різниця в знаменнику з таблиці вище: YOLO питає про предмет двічі на клітинку, щільна голова — девʼять разів, окремо про кожен якір і кожен клас. У 4.5 раза більше запитань про те саме порожнє місце — і 92.3 % перетворюються на 99.72 %. Кожен наступний винахід у цій темі бореться саме з цим множником, а не з архітектурою.

МилиціСпроби, які не спрацювали

До 2017 року дисбаланс лікували вибіркою: не давати мережі всі негативні приклади, а відібрати частину. Способів було три, і всі три живі досі.

спосібяк працюєде стояв
Збалансований мінібатчіз зображення береться близько 256 якорів, половина позитивних; решта у втрату не входитьRPN у Faster R-CNN
Жорсткий добір негативних (OHEM, hard negative mining)рахуємо втрату по всіх негативних і лишаємо найважчіSSD, Fast R-CNN
Співвідношення 1 : 3на кожен позитивний беремо три найважчі негативніSSD

Порахуймо ціну третього способу. Позитивних рішень 4.05, негативних лишається 3 × 4.05 = 12.15, разом у втраті 16.20 рішень із 576.

викинуто = (576 − 16.20) ÷ 576 = 97.2 %

Мережа не отримує градієнта від 97.2 % того, що бачить. І це не «зайві» дані, а весь фон, який вона щойно правильно вгадала, — той самий сигнал «так, тут нічого немає, тримайся цієї думки».

Проти вибірки є три заперечення, і кожне варте свого рядка.

Звідси питання: а чи обовʼязково вибирати? Може, лишити всі приклади, але дати їм різну вагу — не руками, а за правилом, яке само залежить від того, наскільки приклад легкий.

Focal lossFocal loss: не викидати, а стишити

Крос-ентропія для бінарного рішення — це −log p, де p — ймовірність, яку модель дала правильній відповіді. Знак log — це натуральний логарифм; нам важлива одна його властивість: коли p наближається до одиниці, −log p наближається до нуля.

Нехай мережа впевнено бачить фон: p = 0.9, тобто −log 0.9 = 0.105. Дрібниця. Тепер згадаймо, скільки таких дрібниць: на навченій мережі партія з 32 сцен дає 17 541 легке фонове рішення проти 129 позитивних. Помножимо — і дрібниця перестає бути дрібницею.

Головне число розділу. На навченій мережі зі звичайною крос-ентропією легкі негативні приклади дають 57.7 % усієї втрати — більше, ніж усі предмети разом (15.8 %). Мережа з них нічого не вивчить, але саме вони визначають наступний крок.

Focal loss розвʼязує це одним множником — до крос-ентропії дописується множник тлумлення (1 − p)γ.

крок 1 · чого ми хочемо

Потрібна величина, яка близька до нуля для впевнених прикладів і близька до одиниці для невпевнених. Найпростіша така величина — це 1 − p: при p = 0.9 вона дорівнює 0.1, при p = 0.1 — 0.9.

крок 2 · регулятор сили

Одного множника (1 − p) замало: він тлумить занадто мʼяко. Піднімемо його до степеня γ (гамма) — тоді сила тлумлення стає ручкою. 0.12 = 0.01, тобто вдесятеро сильніше, ніж 0.1.

множник тлумлення = (1 − p)γ
крок 3 · стала вага класів

Окремо додається α (альфа) — стала вага: одна для позитивних прикладів, інша (1 − α) для фону. Вона не залежить від упевненості й робить іншу роботу — про це наступний розділ.

крок 4 · разом
FL = −α · (1 − p)γ · log p

При γ = 0 множник дорівнює одиниці, і формула перетворюється на звичайну зважену крос-ентропію. Тобто focal loss не замінює її, а узагальнює.

Тепер підставимо числа. Ось втрата одного прикладу залежно від упевненості p і від γ — кожну клітинку можна перерахувати калькулятором за формулою −(1 − p)γ · log p. Множник α тут не беремо, щоб не заважав.

pCE (γ = 0)γ = 0.5γ = 1γ = 2γ = 5тлумлення при γ=2
0.90.105360.033320.010540.001050.00000100.0×
0.70.356670.195360.107000.032100.0008711.1×
0.50.693150.490130.346570.173290.021664.0×
0.31.203971.007320.842780.589950.202352.0×
0.12.302592.184422.072331.865091.359651.2×

Читаємо останню колонку. Легкий приклад із p = 0.9 при γ = 2 стає в 100 разів тихішим, важкий із p = 0.1 — лише в 1.2 раза. Ось уся ідея в двох числах: тлумлення тим сильніше, чим менше приклад може навчити.

Криві втрати й сила тлумлення

Втрата одного прикладу залежно від упевненості. Сіра крива — звичайна крос-ентропія, малинова — focal loss при обраному γ. Повзунок міняє γ, другий повзунок ставить точку, для якої рахується тлумлення.

крос-ентропія—
focal loss—
тихіше в—
множник—
Криві не перетинаються й не міняють порядок. Focal loss ніде не робить приклад важливішим — вона лише притискає праву частину кривої до нуля. Тому вона й не потребує жодного порога: перехід від «важливо» до «майже неважливо» плавний, і жодне значення p не є особливим.
Чому не просто поріг. Здається, простіше сказати: «приклади з p > 0.9 у втрату не входять». Це знову вибірка з усіма трьома її вадами: приклад із p = 0.9001 зникає, а з p = 0.8999 важить повністю; число 0.9 треба звідкись узяти; і межа стрибає, поки мережа вчиться. Степенева функція робить те саме плавно.

Своя реалізація проти бібліотечної

Формула коротка, тож напишімо її самі й звірмо з готовою — у torchvision вона зветься torchvision.ops.sigmoid_focal_loss. Тут чекає пастка: пряма реалізація «порахуй p через сигмоїду, потім візьми логарифм» не збігається з бібліотечною.

реалізаціянайбільше розходженняnp.allclose
наївна, буквально за формулою0.00489False
через binary_cross_entropy_with_logits9.54 · 10⁻⁷True

Причина суто арифметична. Логіт детектора легко доходить до 12, сигмоїда від нього дає p = 0.9999938, а різниця 1 − p — це 6.1 · 10⁻⁶. Віднімання майже рівних чисел у float32 зʼїдає значущі цифри, і логарифм від зіпсованого числа дає похибку близько 0.005 на кожному з десятків тисяч фонових прикладів. binary_cross_entropy_with_logits рахує ту саму величину, не переходячи через p. Це не прискіпливість: у детекторі великі логіти саме там, де прикладів найбільше.

γ і αЩо робить γ і що робить α

Два параметри роблять дві різні роботи, і плутати їх не можна.

параметрщо вирівнюєчи залежить від прикладу
γ (гамма)легкі проти важкихтак — через упевненість p
α (альфа)предмети проти фонуні — стала для всього класу

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

Контрінтуїтивна деталь із оригінальної статті. Здається, що α має бути велика для рідкісного класу. Насправді в RetinaNet узято α = 0.25, тобто предметам дають меншу вагу, ніж фону (0.75): γ = 2 вже прибрав фон із гри, і піднімати предмети означало б перегнути в другий бік. Ці два параметри добирають разом — у статті сказано, що найкраще α падає, коли γ росте. Число 0.25 цитоване, не наш замір.

Що дає γ на наших даних

Тепер замір. Той самий детектор, ті самі епохи, той самий AdamW — міняється лише γ, і кожна настройка навчена з трьох зерен.

γзерно 0зерно 1зерно 2середнєрозкид
00.80670.66460.79350.75490.1420
10.79880.69840.84790.78170.1495
20.88050.83610.86620.86100.0444
50.59300.59700.50690.56560.0902

Три висновки, і третій — найважливіший.

Перший: різницю між γ = 0 і γ = 2 самими середніми не довести. Виграш — 0.8610 − 0.7549 = 0.1061, а розкид усередині настройки γ = 0 дорівнює 0.1420, тобто більший за виграш. Якби ми зупинились на середніх, чесна відповідь була б «не знаємо» — саме так закінчився замір ваг втрати в темі 25.

Другий: але тут є сильніше твердження. Найгірше зерно при γ = 2 дало 0.8361, а найкраще зерно при γ = 0 — 0.8067. Усі три прогони з focal loss кращі за всі три прогони без неї. Це порівняння не залежить від того, наскільки великий розкид усередині кожної купи, і воно переконливіше за різницю середніх.

Третій: focal loss зменшила сам розкид — із 0.1420 до 0.0444, тобто втричі. Коли градієнт не залежить від того, скільки випадкового фону трапилось у партії, навчання стає передбачуваним: мережа з γ = 0 на одному зерні виїжджає, на іншому провалюється до 0.66.

І окремо — γ = 5. Середній mAP падає до 0.5656, тобто на 0.2953 нижче за γ = 2. Цей провал більший за будь-який розкид у таблиці, тож перетлумити можна. При γ = 5 приклад із p = 0.5 важить у 32 рази менше за звичайний — а це вже не «легкий фон», це якраз те місце, де мережа вагається й де їй треба вчитись.

Хто дає втрату при різних γ

Стовпчики — фактичні складники втрати навченого детектора на партії з 32 сцен, поділені на три купи. Числа під стовпчиками — частки. Суми складників заміряні; перемикач обирає γ.

показник γ
легкі негативні—
їх штук17 541
предмети—
їх штук129
заміряний mAP@0.5 цього γ—
Кількість прикладів у купах не міняється — міняється лише їхня вага. Легких негативних завжди 17 541, важких 288, позитивних 129. При γ = 0 перша купа важить 57.7 %, при γ = 2 — 1.2 %. Мітка mAP під перемикачем — це середнє з трьох зерен для того самого γ, тож видно, де перерозподіл допоміг, а де зашкодив.

ЗсувІніціалізація зсуву: деталь, без якої нічого не працює

Тепер про річ, якої майже немає в переказах статті, зате вона є в коді кожної реалізації. Останній шар голови класифікації — це згортка, і в неї є зсув (bias), який torch за замовчуванням ініціалізує близько нуля.

Сигмоїда від нуля дорівнює 0.5. Тобто свіжа мережа, яка ще нічого не бачила, вважає, що предмет є всюди — у кожному з 576 місць кожної сцени з упевненістю 50 %. Кожен фоновий приклад голосно помиляється, і сума їхніх втрат величезна. Ліки прості: поставити зсув так, щоб початкова ймовірність предмета дорівнювала маленькому числу π. У RetinaNet узято π = 0.01.

крок 1 · обертаємо сигмоїду

Сигмоїда перетворює число z на ймовірність за формулою p = 1 ÷ (1 + e−z), де e — стала 2.71828… Нам треба навпаки: знаючи бажану p = π, знайти z. Розвʼязуємо рівняння й дістаємо:

зсув = −log((1 − π) ÷ π)
крок 2 · підставляємо

При π = 0.01 дріб дорівнює 0.99 ÷ 0.01 = 99, натуральний логарифм від 99 — це 4.5951, зі знаком мінус — −4.5951. Перевірка: сигмоїда від −4.5951 дає рівно 0.0100.

Тепер замір. Одна й та сама мережа, одна й та сама партія, різниця лише в одному числі — початковому зсуві.

зсувp на стартівтрата, γ = 0втрата, γ = 2
нульовий (як у torch)0.491872.19019.698
−4.5951 (π = 0.01)0.01032.2551.152
у скільки разів менше—32.0×17.1×

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

Це не наша вигадка — так стоїть у коді бібліотеки. Збери retinanet_resnet50_fpn і fcos_resnet50_fpn без ваг і подивись на зсув їхнього останнього шару класифікації. В обох стоїть −4.5951, тобто рівно −log(99). Дві різні архітектури, одна й та сама константа, зашита в конструктор.

Початкова ймовірність і втрата першого кроку

Повзунок задає π — ймовірність предмета, яку мережа має на старті. Крива показує втрату першого кроку, стовпчики — з чого вона складається. Перемикач міняє γ.

втрата
зсув—
втрата—
від фону—
від предметів—
Дивись не на висоту кривої, а на два стовпчики праворуч. Це рахунок за формулою для 17 829 фонових і 129 позитивних рішень партії — тих самих, що в таблиці вище. При π = 0.5 вона дає 72.02 при заміряних 72.19; при π = 0.01 — 2.19 при заміряних 2.25. Розбіжність у кілька відсотків — від того, що справжні логіти не однакові, а трохи розкидані навколо зсуву.

RetinaNetRetinaNet: де живе focal loss

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

частинащо робитьзвідки знайома
Тіло (backbone)ResNet-50 або ResNet-101 рахує карти ознактема 14
Шия: FPNзводить карти пʼяти рівнів до однакової товщини 256 каналіврозділ 07 нижче
Голова класифікаціїчотири згортки 3×3, потім згортка на A · C виходівтема 24, якорі
Голова регресіїтакі самі чотири згортки, потім згортка на A · 4 виходівтема 24, зсуви

Тут A — кількість якорів у позиції (девʼять: три розміри × три пропорції), C — кількість класів. Дві голови не мають спільних ваг: класифікація й локалізація — різні задачі. У темі 25 ми бачили, як та сама ідея повернулась у YOLOv8 під назвою «розділена голова».

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

Ось ваги retinanet_resnet50_fpn по частинах — модель зібрано офлайн, без завантаження навчених ваг, тож ці числа задає сама архітектура:

частинапараметрів
хребет: ResNet-50 + FPN27 376 704
голова класифікації4 248 115
голова регресії2 443 300
разом34 068 119

Голова класифікації майже вдвічі важча за голову регресії, хоча згортки в них однакові: різниця в останньому шарі, який видає 9 × 91 = 819 чисел проти 9 × 4 = 36.

FPNFPN: чому дрібним предметам потрібен свій рівень

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

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

Піраміда ознак (FPN, feature pyramid network) розвʼязує цей конфлікт, не вибираючи між рівнями. Вона будується у два ходи.

хід 1 · згори вниз

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

хід 2 · бічне зʼєднання

Додаємо карту того самого розміру з ранішого шару, стиснуту згорткою 1×1 до 256 каналів. Виходить карта, у якої й роздільність рання, і зміст глибокий.

хід 3 · повторюємо

Крок за кроком спускаємось до найбільшої карти. Кожен рівень має однакову товщину 256 каналів — саме тому одна голова може ходити по всіх.

Рівні звуться P3…P7: у P3 крок 8 пікселів, у P7 — 128, і кожен відповідає за свій діапазон розмірів. Ось скільки позицій дає така піраміда на кадрі 800 × 1216:

рівенькроккартапозиційчастка
P38100 × 15215 20075.0 %
P41650 × 763 80018.7 %
P53225 × 389504.7 %
P66413 × 192471.2 %
P71287 × 10700.3 %
разом——20 267100 %

Три чверті всієї роботи піраміди — це найдрібніший рівень, і там же живе майже весь фон. Отже, дисбаланс із розділу 01 — не просто спільна властивість одноетапних детекторів, а насамперед властивість найдрібнішого рівня. Focal loss рятує саме його.

Хто з рівнів відповідає за предмет

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

рівень—
крок—
позицій на рівні—
частка піраміди—
Вікна рівнів узяті з коду torchvision, а не з памʼяті. Правило там таке: P3 бере все, що менше за крок × 8 = 64; далі кожен рівень бере від крок × 4 включно до крок × 8 невключно, а P7 — усе, що від 512 і більше. Межу віддано старшому рівню, тому рівно 64 px — це вже P4. Розміри карт рахуються діленням із округленням угору, як у самій мережі.

ЯкоріЩо не так із якорями

З дисбалансом розібрались. Тепер друга частина теми — про сутність, яку прибрали цілком.

Тема 24 вже показала два незручні числа. На її сцені 28.3 % істинних рамок не мали жодного якоря з IoU понад 0.7 — тобто майже для кожної третьої рамки не існувало заготовки, достатньо схожої на неї. І з девʼяти варіантів якоря (три розміри × три пропорції) на її даних спрацювали два, обидва квадратні. Сім із девʼяти — мертвий вантаж, який чесно множив час рахунку.

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

Пʼять рішень, і жодне не виводиться з даних. Наші три розміри взяті не зі стелі: рамка фігури радіусом 6-10 пікселів має сторону 12-21, і числа 12, 18, 26 її накривають. Якби ми скопіювали конфігурацію з чужої статті — скажімо, розміри 32, 64, 128 із RetinaNet для COCO, — жоден якір не перетнув би поріг, і рятівне правило лишилось би єдиним джерелом позитивних прикладів.

Це і є суть претензії до якорів. Не «вони погані», а «вони переносять частину задачі з даних на розробника». Набір якорів — це гіпотеза про форми предметів, записана числами до того, як мережа побачила хоч одне зображення.

Anchor-freeAnchor-free: чотири відстані замість заготовки

Ідея, до якої дійшли одночасно кілька груп, звучить майже нахабно: а навіщо взагалі заготовка? Нехай кожна позиція передбачає рамку прямо — не зсув від чогось, а саму рамку. Найпростіший спосіб це записати придумали в детекторі FCOS: у кожній позиції мережа видає чотири відстані від центра позиції до лівого, верхнього, правого й нижнього краю рамки. Їх позначають l, t, r, b — від left, top, right, bottom.

x1 = x − l·крок y1 = y − t·крок x2 = x + r·крок y2 = y + b·крок

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

Порівняймо дві голови на одній задачі. Те саме тіло, ті самі епохи, той самий γ = 2, ті самі три зерна.

що міряємоякірнаanchor-free
чисел на позицію217
параметрів у голові12 1174 039
параметрів усього72 69364 615
mAP@0.5, середнє з 3 зерен0.86100.9187
розкид по зернах0.04440.0383
прохід по сцені, мс≈ 1.7≈ 1.7
гіперпараметрів зіставленняпʼятьжодного

Голова схудла втричі, а mAP виріс на 0.0577 — більше за розкид обох настройок, тож різниця справжня.

А от час проходу не змінився: у двох прогонах того самого коду виходило 1.67 проти 1.65 мілісекунди й 1.71 проти 1.83, тобто різниця тоне в шумі машини. Голова не є вузьким місцем — час визначає тіло, яке в обох випадках однакове. Anchor-free виграє не швидкістю, а простотою й відсутністю гіпотез.

Що передбачає одна позиція

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

схема голови
чисел на позицію—
ціна класу—
треба вгадати наперед—
Повзунок якорів діє лише на якірну схему — в anchor-free його немає що міняти. У цьому й річ: ліва схема має ручку, яку треба крутити під дані, права — ні. При 91 класі COCO якірна голова з девʼятьма якорями видає 855 чисел на позицію, anchor-free — 95.

CenternessCenterness: чому крайові позиції дають гірші рамки

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

наскільки позиція центральнапозиційсередній IoU рамки
0.0 … 0.2 (край)2530.743
0.2 … 0.43670.782
0.4 … 0.62150.798
0.6 … 0.81110.806
0.8 … 1.0 (центр)230.857

Передумова підтвердилась: IoU росте монотонно від краю до центра, різниця між крайніми купами — 0.115. FCOS лікує це третьою гілкою голови — centerness, одним числом на позицію, яке відповідає на питання «наскільки я в центрі». Формула бере відношення меншої відстані до більшої по кожній осі:

centerness = √( min(l,r) ÷ max(l,r) · min(t,b) ÷ max(t,b) )

Прочитаймо словами. Якщо позиція рівно в центрі, l = r і t = b, обидва дроби дорівнюють одиниці, корінь з одиниці — теж одиниця. Якщо позиція біля лівого краю, перший дріб близький до нуля — і все значення падає. Корінь потрібен, щоб спад був не надто різким.

зсув від центра рамки 20×20lrcenterness
0 px10.010.01.000
2 px12.08.00.816
4 px14.06.00.655
6 px16.04.00.500
8 px18.02.00.333

Гілка навчається окремою втратою, а під час передбачення оцінка рамки множиться на неї: оцінка = √(ймовірність класу × centerness). Сенс — опустити рамки з крайових позицій нижче, щоб NMS викидав саме їх.

Що це дало нам: чесний результат

варіантmAP@0.5розкид
гілки centerness немає взагалі0.91870.0383
гілка є, оцінка без множника0.81730.1932
гілка є, оцінка × centerness0.79380.2629

На наших даних centerness не дає нічого — навпаки, забирає. І це при тому, що сама гілка навчилась чудово: кореляція між її передбаченням і її ціллю дорівнює 0.903. Мережа зрозуміла завдання, а завдання виявилось некорисним.

Чому так — видно з попередніх чисел. Кореляція між centerness позиції й IoU її рамки лише 0.191: тенденція є, та слабка, і різниця в 0.115 IoU не перекриває розкид усередині купи. Наші предмети маленькі — сторона 12-21 піксель при кроці карти 8, тобто чотири позиції на предмет, і крайових серед чотирьох мало. На фотографії, де предмет накриває сотню позицій, картина інша, але це вже твердження про інші дані, і ми його не міряли.

Не переноси цей висновок на справжні дані — переноси метод. Перевіряй передумову окремо від наслідку: ми спершу довели, що крайові позиції справді гірші, і лише потім поміряли, чи допомагають ліки. Якби ми міряли тільки mAP, вийшло б «centerness — марна вигадка», а це неправда.

Позиція, її centerness і якість рамки

Рамка предмета й позиція всередині неї. Повзунки рухають позицію по горизонталі й вертикалі; праворуч — чотири відстані, значення centerness і заміряна якість рамки для цього рівня центральності.

l, r—
t, b—
centerness—
заміряний IoU—
Права цифра — не розрахунок, а замір. Це середній IoU рамок, побудованих із позицій такої самої центральності, по 969 позиціях перевірного набору. Пройди повзунком від краю до центра: centerness падає з одиниці до нуля, а IoU міняється лише в межах 0.743 … 0.857. Саме тому множник і не допоміг.

ЗіставленняЗіставлення без якорів: хто відповідає за предмет

Прибравши якорі, ми прибрали й старе правило зіставлення, яке спиралося на IoU з заготовкою. Потрібне нове, і воно виявилось окремою темою для досліджень.

Правило FCOS складається з трьох умов, і всі три читаються прямо в коді torchvision:

умова 1 · усередині рамки

Центр позиції має лежати всередині істинної рамки. Це те саме правило, з якого ми починали розділ 09.

умова 2 · центральна вибірка

Мало бути всередині — треба бути біля центра: не далі ніж 1.5 × крок від центра рамки по кожній осі. Так відсікаються ті самі крайові позиції, заради яких вигадували centerness — тільки тут вони не зважуються, а просто не стають позитивними.

умова 3 · діапазон масштабів

Найбільша з чотирьох відстаней має потрапити у вікно свого рівня піраміди: від крок × 4 до крок × 8. Так anchor-free детектор розводить предмети по рівнях — без жодних заготовок.

Якщо після трьох умов позиція підходить кільком предметам, береться той, у якого рамка найменша за площею: у великого предмета позицій багато, у малого мало.

Наступний крок цієї історії — відмова від фіксованих порогів узагалі. Метод ATSS (adaptive training sample selection) для кожної істинної рамки бере кілька найближчих кандидатів, рахує середнє й стандартне відхилення їхніх IoU і оголошує порогом середнє + відхилення. Поріг рахується з даних окремо для кожної рамки: дрібний предмет, для якого всі кандидати погані, дістає низький поріг і не лишається без позитивних прикладів; великий — високий.

Чому це важливіше, ніж здається. У роботі про ATSS показано: якщо взяти якірний RetinaNet і anchor-free FCOS і вирівняти в них лише правило зіставлення, різниця в точності майже зникає. Тобто головна перевага anchor-free була не в тому, що прибрали заготовки, а в тому, що разом із ними прибрали старе правило зіставлення. Це висновок із літератури, не наш замір, але він пояснює, чому далі всі зайнялися саме зіставленням: SimOTA в YOLOX, динамічний добір у YOLOv8.

СпадокЩо з цього стоїть у сучасних детекторах

Обидва винаходи теми пережили свої архітектури: RetinaNet і FCOS сьогодні майже не запускають, а от їхні частини стоять усюди.

ідеяде живе зараз
Focal lossусі щільні детектори; у варіантах (quality focal loss, varifocal loss) ціллю є не одиниця, а якість рамки
Ініціалізація зсуву π = 0.01−4.5951 у RetinaNet і FCOS з torchvision, те саме в ultralytics
Anchor-free головаYOLOv6, YOLOv7, YOLOv8, YOLOX, DETR і його нащадки
Розділена головаYOLOv8: окрема гілка для рамки, окрема для класів
Динамічне зіставленняATSS, SimOTA, TaskAlignedAssigner у YOLOv8
Centernessздебільшого замінено: якість рамки вбудували в саму ціль класифікації

Тема 25 уже показала це на числах: у голові YOLOv8 немає окремої впевненості, є дві окремі гілки й немає жодного якоря. Тепер видно, звідки кожна риса взялася. Порівняймо дві моделі torchvision, зібрані офлайн:

частинаRetinaNetFCOS
хребет: ResNet-50 + FPN27 376 70427 376 704
голова класифікації4 248 1152 572 123
голова регресії2 443 3002 373 893
разом34 068 11932 322 720

Хребет у них однаковий до останнього числа. Уся різниця — 1 745 399 параметрів — сидить у головах, і майже вся вона в останній згортці класифікації: у RetinaNet це Conv2d(256, 819), бо 819 = 9 якорів × 91 клас, у FCOS — Conv2d(256, 91), бо позиція одна. У голові регресії FCOS, крім чотирьох відстаней, є ще й bbox_ctrness — та сама centerness окремою згорткою.

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

ідеяу чому суть
Дисбаланс у втраті576 рішень на сцену, 4.05 позитивні; фон дає 99.72 % втрати свіжої мережі
Милиці не годятьсявибірка 1 : 3 викидає 97.2 % рішень і додає гіперпараметр
Focal lossмножник (1−p)γ: приклад із p = 0.9 стає в 100 разів тихішим, із p = 0.1 — у 1.2 раза
γ вирішуєусі три зерна при γ = 2 кращі за всі три при γ = 0; при γ = 5 mAP падає до 0.5656
Ініціалізація зсуву72.19 проти 2.25 на першому кроці — та сама мережа, різний один зсув
Anchor-freeчотири відстані замість заготовки: голова легша втричі, mAP 0.9187 проти 0.8610
Centernessпередумова істинна (IoU 0.743 на краю проти 0.857 у центрі), а виграшу на наших даних немає

Головна пара чисел теми — 72.19 і 2.25: втрата на першому кроці однієї й тієї самої мережі, у якої відрізняється лише початкове значення одного зсуву. Тридцять два рази за рядок коду, якого немає в жодному переказі статті.

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

Але один рукотворний вузол ми так і не чіпали. Детектор досі видає тисячі рамок, серед яких повно дублікатів, і досі прибирає їх окремим алгоритмом — NMS. Це не частина мережі: його не навчають, у нього свій поріг, і в темі 23 ми бачили, як він назавжди губить один із двох предметів, що стоять поруч із IoU 0.714. Усе інше вже вчиться з даних, а останній крок конвеєра лишився жорстко запрограмованим.

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