Двоетапні детектори

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

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

Що вже стоїть. Постановку детекції, IoU, зіставлення рамок, точність-повноту, AP, mAP і притлумлення немаксимумів дала тема 23 «Постановка детекції». Тут вони вживаються як готові інструменти й не переказуються. Наскрізний приклад той самий, що в усьому блоці: сцени 64×64 з одним-трьома предметами трьох класів (коло, квадрат, трикутник), радіус 6-10 пікселів, шум 0.12, рамка рахується з маски й тому істинна за побудовою.

ПеребірНайпростіша ідея, яка працює

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

Цей прийом зветься ковзним вікном (sliding window). Беремо прямокутник заданого розміру, ставимо його в лівий верхній кут, вирізаємо, класифікуємо, зсуваємо на кілька пікселів праворуч, повторюємо. Дійшли до краю — переходимо на рядок нижче. Пройшли все зображення — беремо вікно іншого розміру, бо предмети бувають різні, і йдемо по колу знову.

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

Крок 1 · скільки положень має одне вікно

Зображення шириною W і висотою H, вікно шириною w і висотою h, зсув на s пікселів. По горизонталі вікно поміститься (W − w) ÷ s + 1 разів, по вертикалі — (H − h) ÷ s + 1. Разом добуток цих двох чисел.

положень = ((W − w) / s + 1) × ((H − h) / s + 1)
Крок 2 · підставляємо числа

Фотографія 600×1000 — тобто 1000 пікселів завширшки й 600 заввишки; саме такий кадр стоїть у фігурі нижче. Вікно 128×128, крок 4 пікселі. По горизонталі: (1000 − 128) ÷ 4 + 1 = 219. По вертикалі: (600 − 128) ÷ 4 + 1 = 119. Разом 26 061 положень — і це лише для одного розміру вікна.

Крок 3 · множимо на розміри й форми

Предмети бувають різні за величиною, тож потрібно кілька масштабів — візьмімо девʼять, від 32 до 512 пікселів. І різні за формою: людина стоїть вертикально, автобус лежить горизонтально, тож на кожен масштаб три співвідношення сторін.

Помножити 26 061 на 27 не можна: велике вікно поміщається в кадр рідше за мале, тож кожна з 27 комбінацій дає свою кількість положень, і їх треба скласти. На самому лише масштабі 128 три форми разом дають 76 241 положення (це число показує лічильник фігури нижче), а сума по всіх девʼятьох масштабах — 591 415 вікон на один кадр.

Пів мільйона вікон — це пів мільйона запусків класифікатора. Ми заміряли, скільки коштує один такий запуск: ResNet-18 на шматку 224×224 в одному потоці йде 63.2 мс. Множимо — і дістаємо 10.4 години на одну фотографію. Не на датасет, не на відео. На один кадр.

Зменшити крок не можна — навпаки, хочеться менший, бо при кроці 4 пікселі вікно рідко лягає точно на предмет. Збільшити крок до 16 знижує кількість вікон до 37 672 — це в 15.7 раза менше. Шістнадцять тут теоретична межа: учетверо рідше по горизонталі й учетверо по вертикалі дало б рівно 16, але кількість положень рахується як (W − w) / s + 1, і та одиниця наприкінці, разом із вікнами, що не влазять у кадр цілком, зʼїдає різницю. Але навіть повні шістнадцять нічого не рятують: тоді рамка гарантовано зсунута щодо істини на вісім пікселів, і з таблиці теми 23 ми знаємо, що для предмета сорок пікселів завширшки це вже втрата IoU нижче 0.75.

Ковзне вікно: скільки разів доведеться спитати мережу

Кадр 1000×600. Перший повзунок задає розмір вікна, яке видно на кадрі; лічильник «усього» підсумовує всі масштаби й три співвідношення сторін на кожен із них. Покрути крок і кількість масштабів — і подивись, як росте число. Ліворуч показано положення вікон (намальовано не всі, щоб не залити кадр суцільним чорним), праворуч — кількість вікон при різних кроках у логарифмічному масштабі.

вікон цього розміру—
вікон, усього—
час, ResNet-18—
час, крихітна CNN—
Три співвідношення сторін на кожен масштаб. Час порахований за двома заміряними числами: 63.2 мс на один прохід ResNet-18 по шматку 224×224 і 0.255 мс на прохід крихітної мережі з 3 828 ваг по шматку 32×32, обидва в одному потоці на процесорі. Навіть найдешевший класифікатор не рятує: при кроці 4 його доводиться запускати 591 415 разів на кадр.

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

КандидатиКандидат-області замість перебору

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

Отже, розділімо роботу. Спершу дешевий алгоритм переглядає зображення й пропонує кілька тисяч ділянок, які схожі на окремий предмет — неважливо який. Потім дорога мережа розбирається лише з ними. Такі ділянки звуть кандидат-областями (region proposals), а алгоритм, який їх видає, — генератором кандидатів. У розмові їх часто називають просто кандидатами; далі в темі вживається повна назва, бо саме вона стоїть у глосарії курсу.

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

Важлива тут не механіка, а зміна порядку величин. Selective Search видає близько двох тисяч кандидат-областей на зображення замість пів мільйона вікон — це у 295 разів менше. І кандидат-області не рівномірні: вони ліпляться до країв предметів, а не стоять сіткою.

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

R-CNNR-CNN і його ціна

R-CNN (regions with CNN features) — це перша архітектура, яка склала докупи згорткову мережу й кандидат-області та вперше показала на детекції результат, помітно кращий за класичні методи. Будова її буквально така, як щойно описано:

Етап 1 · кандидат-області

Selective Search дає близько 2 000 рамок. Мережа тут не бере участі взагалі — це окремий алгоритм на процесорі.

Етап 2 · ознаки

Кожна з 2 000 ділянок вирізається з картинки, розтягується до фіксованого розміру 224×224 і окремо проганяється крізь згорткову мережу. На виході — вектор ознак для кожної кандидат-області.

Етап 3 · рішення

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

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

Ми заміряли, скільки це коштує. Один прохід ResNet-18 по шматку 224×224 в одному потоці на процесорі — 63.2 мс.

Що рахуємоСкільки
прямих проходів на одне зображення2 000
час на одне зображення126.4 с
прямих проходів на 5 000 зображень10 000 000
час на 5 000 зображень176 год

Дві хвилини на кадр. Тиждень безперервної роботи, щоб один раз пройти датасет із пʼяти тисяч зображень. На нашій крихітній мережі в 3 828 ваг ті самі дві тисячі проходів займають 0.51 с на кадр і 43 хвилини на датасет — уже не трагедія, але й мережа тут іграшкова.

Про час і твою машину. Усі секунди в цій темі заміряні на процесорі в одному потоці, і машина при цьому не була вільною. У тебе вийдуть інші абсолютні числа — можливо, удвічі менші. Незмінне тут співвідношення: скільки разів працює тіло мережі. Саме воно, а не конкретна цифра, рухало всю історію.
Це не все. Крім часу R-CNN має ще одну ваду, яку видно не з секундоміра, а зі схеми: три окремі етапи навчаються окремо. Спершу донавчається згорткова мережа, потім на її заморожених ознаках навчаються класифікатори, потім — регресія рамок. Помилка останнього етапу ніяк не впливає на перший: сигнал не має куди повернутись, бо між етапами немає градієнта. Мережа не може підправити свої ознаки під те, що з ними роблять далі. І місця на диску це теж коштує: ознаки всіх кандидат-областей усього датасету доводилось складати у файл.

Fast R-CNNFast R-CNN: один прохід

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

Згортка не переставляє пікселі. Ми знаємо це з теми 07: карта ознак зберігає просторову будову входу, лише в дрібнішій сітці. Якщо мережа має сумарний крок 16, то клітинка карти з координатами (3, 5) відповідає ділянці зображення приблизно навколо точки (48, 80). Отже, не треба вирізати шматок із картинки й гнати його крізь мережу — можна пройти мережею по цілому зображенню один раз, а потім вирізати потрібну ділянку з готової карти ознак.

R-CNN: вирізати → порахувати ознаки  ·  Fast R-CNN: порахувати ознаки → вирізати

Це і є Fast R-CNN. Порядок двох дій міняється місцями, і разом із порядком зникає повторний рахунок: скільки б кандидат-областей не було, згорткове тіло працює рівно один раз на кадр. Друга зміна не менш важлива: тепер класифікатор і регресія рамки — це звичайні шари тієї самої мережі, а не окремі моделі. Навчання стає одним, і градієнт від помилки координат доходить аж до першої згортки.

R-CNN проти Fast R-CNN: скільки разів працює тіло мережі

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

проходів тіла: R-CNN—
проходів тіла: Fast1
час R-CNN—
час Fast R-CNN—
Заміряно на процесорі в одному потоці. R-CNN: 63.2 мс на прохід ResNet-18 по шматку 224×224, помножені на кількість кандидат-областей. Fast R-CNN: 576 мс на один прохід згорткового тіла по кадру 600×1000 плюс 0.57 мс на кожну кандидат-область (вибірка з карти ознак і дві повнозвʼязні голови). Це справжні виміри, а не оцінка: на 2 000 рамках заміряно 1 717 мс, на 300 — 757 мс, на 100 — 642 мс.

Два висновки з цієї картинки, і другий цікавіший за перший.

Перший: на двох тисячах кандидат-областей виграш становить 74 рази — 126.4 с проти 1.717 с. Це та різниця, після якої задача перестає бути теоретичною.

Другий: подивись, як поводяться дві смуги, коли рухаєш повзунок. Смуга R-CNN росте пропорційно кількості кандидат-областей — від нуля. Смуга Fast R-CNN починається з 576 мс і росте повільно. Отже, у Fast R-CNN зʼявилося нове вузьке місце, якого не було видно раніше: сталий внесок одного проходу тіла. Поки кандидат-областей дві тисячі, головна витрата — усе ще вони (1 141 мс проти 576 мс). Але варто скоротити їх до трьохсот, і картина перевертається: 171 мс на кандидат-області проти 576 мс на тіло. Заміряно на 300 рамках 757 мс, а не 747 — проста сума «тіло плюс лінійна ціна рамки» трохи занижує, і це нормальна похибка такого перерахунку.

Це підказує, куди дивитись далі. І ще одне, чого на цій картинці немає взагалі: сам Selective Search. Він працює на процесорі й коштує близько двох секунд на зображення (число цитоване, ми його не міряли). Тобто у Fast R-CNN генератор кандидат-областей став дорожчим за всю мережу разом узяту.

RoI-пулінгЯк вирізати ділянку з карти ознак

Лишилась технічна деталь, без якої «вирізати з карти ознак» — порожні слова. Кандидат-області мають різні розміри: одна 30×40 пікселів, друга 180×95. А повнозвʼязна голова, яка робить остаточне рішення, вимагає сталої кількості чисел на вході. Треба перетворити прямокутник довільного розміру на сітку сталого розміру — скажімо, 7×7.

Операція, яка це робить, зветься RoI-пулінг (RoI — region of interest, ділянка інтересу). Механіка така:

  1. Координати рамки з пікселів зображення перераховуються в клітинки карти ознак: ділимо на крок мережі. Рамка 100.3…187.7 при кроці 16 дає 6.27…11.73 клітинки.
  2. Ділянка карти ділиться на 7×7 однакових комірок.
  3. У кожній комірці береться максимум (або середнє) — і виходить рівно 49 чисел на канал, скільки б не була завбільшки початкова рамка.

А тепер те, заради чого цей розділ існує. Крок 1 дає дробові координати, а індексувати масив дробовим числом не можна. Класичний RoI-пулінг розвʼязує це найпростішим способом: округлює. 6.27 стає 6, 11.73 стає 12. Потім, коли ділянку ріжуть на 7 комірок, межі комірок теж не потрапляють у цілі клітинки — і їх теж округлюють. Два округлення підряд.

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

Округлення проти білінійної вибірки

Карта ознак 12×12, крок мережі 16 пікселів; значення клітинки дорівнює її стовпчику, тому колір — це просто координата. Рамка з дробовими координатами повзе праворуч по одному пікселю. Слідкуй за двома вертикальними мітками: суцільна темна — де центр рамки насправді, рожевий пунктир — де його бачить обраний спосіб вирізання.

спосіб вирізання
центр істинний—
центр вирізаного—
зсув, клітинок—
зсув, пікселів—
Числа рахує сама сторінка тим самим алгоритмом, що й ops.roi_align, і вони збігаються з нашим заміром. Округлення дає сходинку: рамка їде, а вирізана ділянка стоїть на місці, поки координата не перескочить наступне ціле. Модуль зсуву гуляє від 0.57 до 8.71 пікселя, у середньому 4.64. Білінійна вибірка тримає 0.00 у всіх шістнадцяти положеннях.

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

Наскільки це шкодить, залежить від кроку мережі й розміру предмета. При кроці 16 половина клітинки — це вісім пікселів. З таблиці теми 23 ми знаємо: щоб утримати IoU 0.75 на рамці сорок пікселів завширшки, її не можна зсувати більш ніж на 5.7 пікселя. Вісім — уже за межею. Для наших сцен 64×64 крок мережі всього 4 пікселі, тож і зсув удесятеро менший, і рятує нас саме дрібна сітка, а не акуратність операції.

RoI-AlignБілінійна інтерполяція замість округлення

Лікування знайшлося пізніше, у роботі про Mask R-CNN, і воно зветься RoI-Align. Ідея в одному реченні: не округлювати нічого.

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

Крок 1 · чотири сусіди

Хай потрібне значення в точці (y, x) = (2.3, 5.7). Цілі частини дають лівого верхнього сусіда (2, 5). Три інші — (2, 6), (3, 5), (3, 6).

Крок 2 · ваги з дробових частин

Дробові частини: по вертикалі 0.3, по горизонталі 0.7. Вага сусіда тим більша, чим він ближчий, тож для кожного напрямку беремо або дробову частину, або те, що лишилось до одиниці. Сусід (2, 5) стоїть угорі й ліворуч: (1 − 0.3) × (1 − 0.7) = 0.7 × 0.3 = 0.21. Сусід (2, 6) — угорі й праворуч: (1 − 0.3) × 0.7 = 0.7 × 0.7 = 0.49. Сусід (3, 5) — унизу й ліворуч: 0.3 × (1 − 0.7) = 0.09. Сусід (3, 6) — унизу й праворуч: 0.3 × 0.7 = 0.21. Сума всіх чотирьох ваг завжди рівно 1, і тут вона 0.21 + 0.49 + 0.09 + 0.21 = 1.00.

f(2.3, 5.7) = 0.21·f(2,5) + 0.49·f(2,6) + 0.09·f(3,5) + 0.21·f(3,6)
Крок 3 · кілька точок на комірку

У кожній із 49 комірок беруть не одну точку, а чотири (сітка 2×2 у середині комірки), рахують у них значення білінійно й усереднюють. Це згладжує результат і робить операцію придатною для навчання: похідна існує в кожній точці, бо ніде немає стрибка.

Що це дало за літературою: у роботі про Mask R-CNN заміна RoI-пулінгу на RoI-Align підняла якість масок приблизно на три пункти AP, причому виграш був тим більший, чим суворіший поріг IoU (число цитоване). Це узгоджується з нашим заміром: зсув у кілька пікселів майже не заважає при порозі 0.5 і стає вирішальним при 0.75.

У torchvision це операція ops.roi_align, і в неї є аргумент aligned, який коштує окремої уваги. Він відповідає за пів пікселя: центр нульового пікселя лежить у координаті 0.5, а не 0, і якщо це не врахувати, уся ділянка зсувається на рівно пів клітинки. Ми заміряли: при кроці 16 це 8.00 пікселя, стабільно, у будь-якому положенні рамки. Замовчування — aligned=False, тобто зі зсувом; правильне значення — True.

Не пастка бібліотеки, а історія. aligned=False лишили замовчуванням заради сумісності зі старими навченими моделями: якщо мережу навчали зі зсувом, то з ним же треба й запускати, інакше ознаки поїдуть. У новому коді став aligned=True свідомо. І перевір це заміром на пандусі — це чотири рядки коду, і вони дають точну відповідь замість здогадки.

Faster R-CNNКандидат-області теж вчимо

Порахуймо, що лишилось у Fast R-CNN. Тіло по кадру — 576 мс. Дві тисячі кандидат-областей — 1 141 мс. Selective Search — близько двох секунд на процесорі. Найдорожча частина конвеєра — та єдина, що не є мережею й нічого не вчить.

Далі йде хід, після якого архітектура набуває сучасного вигляду. Карта ознак уже порахована. Вона лежить готова й описує все зображення. То чому б не попросити самі ці ознаки сказати, де тут предмети? Замість чужого алгоритму поставимо ще одну маленьку мережу, яка читає ту саму карту й видає кандидат-області. Вона зветься RPN (region proposal network, мережа кандидат-областей), а архітектура з нею — Faster R-CNN.

Вигода потрійна, і про останню часто забувають:

Лійка кандидатів: що саме прибрав кожен крок

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

крок історії
місць на кадр—
проходів тіла—
час мережі на кадр—
генератор кандидатів—
Ланцюг, який тримає всю тему: 591 415 → 2 000 → 300. Перший крок зрізав кількість місць у 295 разів, другий — кількість проходів тіла у дві тисячі разів, третій — і генератор кандидат-областей, і ще майже всемеро самих кандидатів (2 000 → 300). Час мережі: перші два числа пораховані з 63.2 мс на прохід, два останні заміряні прямо (1.717 с на 2 000 рамках і 0.757 с на 300). Selective Search у ці секунди не входить — його дві секунди на процесорі зникають лише на останньому кроці.

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

ЯкоріЯкорі: сітка заготовлених рамок

Відповідь називається якорями (anchors), і вона така: мережа не вигадує рамки з нічого. Ми заздалегідь розкладаємо по зображенню фіксований набір рамок-заготовок, а мережа лише каже про кожну дві речі — «тут предмет чи фон» і «наскільки цю заготовку треба посунути й розтягнути».

Крок 1 · сітка

Центри якорів стоять у центрах клітинок карти ознак. Карта 16×16 дає 256 центрів; карта 38×63 (кадр 600×1000 при кроці 16) — 2 394.

Крок 2 · масштаби

У кожному центрі — кілька рамок різного розміру, бо предмети бувають великі й малі. Типово три.

Крок 3 · співвідношення сторін

І кожен розмір — у трьох формах: витягнута вертикально, квадратна, витягнута горизонтально. Типово 1:2, 1:1, 2:1.

якорів = клітинок × масштабів × співвідношень
Крок 4 · що передбачає мережа

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

Тепер порахуємо. На карті 38×63 із девʼятьма якорями на клітинку виходить 21 546 якорів. Це вже багато, але сучасні детектори працюють не з однією картою, а з пірамідою карт різної дрібності. Ми взяли справжній генератор якорів із fasterrcnn_resnet50_fpn і прогнали його на кадрі 800×1216:

рівень пірамідикартаклітинокякорів
P2 (крок 4)200×30460 800182 400
P3 (крок 8)100×15215 20045 600
P4 (крок 16)50×763 80011 400
P5 (крок 32)25×389502 850
P6 (крок 64)13×19247741
разом—80 997242 991
Чому тут три якорі на клітинку, а не девʼять. На одній карті масштаб доводиться перебирати самому — звідси три розміри × три форми. У піраміді масштаб задає сам рівень: дрібні предмети ловить P2 з кроком 4, великі — P6 з кроком 64. Тому на кожній клітинці лишаються тільки три співвідношення сторін, і саме так налаштований fasterrcnn_resnet50_fpn. Це видно й у розборі ваг у розділі 10: на класифікацію «предмет чи фон» там іде 771 вага, тобто 256 × 3 + 3.

Чверть мільйона рамок-кандидатів на один кадр. Проти пів мільйона вікон із першого розділу це не таке вже й полегшення — але тепер жодна з них не проганяється крізь мережу окремо: усі 242 991 обробляються двома згортками 1×1 за один прохід.

Як якір дізнається, що він позитивний

Щоб навчити RPN, потрібна мітка для кожного якоря. Правило просте й спирається на IoU з теми 23:

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

Якорі на нашій сцені: скільки їх і скільки з них позитивні

Сцена 64×64 з трьома предметами, карта ознак 16×16 (крок 4). Пунктир — істинні рамки. Рожеві — якорі, які перетнули поріг. Сірі точки — центри якорів; самі якорі малюються не всі, бо їх дві тисячі.

масштаб якоря
співвідношення сторін
якорів—
позитивних—
частка—
фону на 1 предмет—
Сцена взята з датасету теми, IoU рахується прямо в браузері тією самою формулою, що й box_iou. Найкращий якір на цій сцені має IoU 0.694 з кожною з трьох істин, тож при стандартному порозі 0.7 позитивних нуль — без рятівного правила навчати було б нічим. По 200 сценах картина така сама: 366 позитивних із 460 800 якорів, тобто 0.079 %, і 28.3 % істинних рамок не мають жодного якоря з IoU понад 0.7.

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

Перший: якорів-фону в тисячу разів більше, ніж якорів-предметів. На 200 сценах ми дістали 366 позитивних із 460 800, тобто на один позитивний якір припадає 1 258 негативних. Якщо просто скласти втрату по всіх якорях, мережа вивчить одну відповідь — «фон» — і матиме 99.9 % правильних відповідей при нульовій користі. Це та сама проблема дисбалансу, яку тема 23 показала на клітинках сітки. Faster R-CNN лікує її грубо, але дієво: на кожне зображення береться вибірка приблизно з 256 якорів, у якій позитивних і негативних порівну. Решта у втрату не входить. Тема 26 покаже інший спосіб — переважити втрату замість того, щоб викидати приклади.

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

Третій: без рятівного правила навчання просто не почалося б. Постав поріг 0.7 і вимкни галочку — позитивних нуль. Це не рідкісний випадок: по 200 сценах 28.3 % істинних рамок не мають жодного якоря, який дотягнувся б до 0.7. Дрібна сітка й дискретний набір розмірів просто не покривають усі можливі положення предмета.

ЗамірRPN на нашій задачі

Тепер зберемо спрощений RPN самі й перевіримо головне: скільки кандидат-областей треба, щоб не втратити предмети. Будова — рівно та, що описана вище: згорткове тіло перетворює сцену 64×64 на карту 16×16, спільна згортка 3×3 готує ознаки, дві згортки 1×1 видають упевненість і зсув для кожного з девʼяти якорів у клітинці.

Далі виходи сортуються за впевненістю, проганяються крізь притлумлення немаксимумів із порогом 0.7, і беруться перші k. Міряємо повноту: яка частка істинних рамок має серед цих k кандидат-областей хоча б одну з достатнім IoU. Три зерна, AdamW, швидкість навчання 3e-3, чотирнадцять епох на 400 сценах — близько 25 секунд на прогін.

Скільки кандидат-областей потрібно

Повнота кандидат-областей залежно від їх кількості. Зелена крива — навчений RPN, сіра — стільки ж якорів, узятих навмання без будь-якого навчання. Вісь кількості логарифмічна.

поріг IoU для зарахування
повнота, RPN—
повнота, навмання—
розкид по зернах—
час на сцену—
Числа заміряні на 400 навчальних і 120 перевірочних сценах (244 істинні рамки), три зерна на кожну точку. Один прогін RPN на сцені — 15.73 мс. Крива «навмання» — це k якорів, вибраних випадково з тих самих 2 304, усереднено по трьох спробах.

Головне число теми — тут. При порозі 0.5 навчений RPN дає повноту 0.997 уже на десяти кандидат-областях; випадковим якорям для тієї самої повноти треба тисяча. Це рівно те, за що платять навчанням кандидат-областей: у сто разів менше кандидатів при тій самій повноті. Перекладаючи на конвеєр із четвертого розділу: 0.57 мс на кандидат-область, десять кандидат-областей замість тисячі — це 5.7 мілісекунди замість 570.

Друге, що видно, — стеля. Перемкни поріг на 0.7: повнота росте до 20 кандидат-областей і далі стоїть на 0.955, скільки б їх не додавати. Ще суворіший поріг 0.75 дає 0.861. Отже, додавати кандидат-області понад пʼятдесят на нашій задачі безглуздо — вони не приносять нових предметів, а лише годують другий етап зайвою роботою. Стеля береться не з кількості, а з точності: регресія зсуву здатна підправити якір лише до певної межі.

Задача мусить бути чесною. Наші сцени легкі: до трьох предметів, чіткі межі, шум 0.12. Тому повнота 1.000 при порозі IoU 0.5 досягається вже на двадцяти кандидат-областях, а справжні детектори беруть 300 — на фотографії з двадцятьма предметами, половина з яких перекрита, крива виглядала б інакше. Не перенось число «десять» на свої дані — перенось спосіб його дістати — побудувати криву повноти від кількості й узяти точку, де вона виходить на полицю.

РозбірСправжня модель по частинах

Досі ми будували все самі й на іграшкових розмірах. Тепер розберемо справжню fasterrcnn_resnet50_fpn із torchvision. Її можна зібрати без жодного байта з мережі — weights=None, weights_backbone=None дає порожню архітектуру з правильною будовою, і ваги в ній можна перерахувати.

Прогін кадру крізь Faster R-CNN

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

карта P2—
якорів на кадр—
ваг у кроці—
частка ваг—
Розміри карт і кількість якорів рахуються тією самою формулою, що в torchvision: карта P2 — це округлена вгору чверть сторони, далі кожен рівень удвічі дрібніший, три якорі на клітинку. На кадрі 800×1216 формула дає рівно 242 991 — стільки ж, скільки видає справжній AnchorGenerator із torchvision у нашому замірі. Ваги взяті з розбору зібраної моделі.

Ось як 41 808 406 ваг розкладаються по частинах:

частинавагчастка
хребет: тіло ResNet-5023 508 03256.23 %
хребет: FPN3 344 3848.00 %
RPN593 9351.42 %
голова: fc6 і fc713 895 68033.24 %
голова: клас і рамка466 3751.12 %
разом41 808 406100 %

Три речі варто прочитати з цієї таблиці окремо.

RPN — найдешевша частина мережі. 593 935 ваг, тобто 1.42 %. З них 590 080 — одна згортка 3×3 з 256 каналів у 256; на класифікацію «предмет чи фон» іде 771 вага, на регресію зсуву — 3 084. Механізм, який замінив дві секунди Selective Search, важить менше відсотка й губиться в таблиці. Це рідкісний випадок, коли ідея, а не обчислення, дає весь виграш.

Другий етап дорожчий за перший у двадцять чотири рази. 14 362 055 проти 593 935. І майже все це — один шар: fc6, повнозвʼязний шар з 256×7×7 = 12 544 входів у 1 024 виходи, тобто 12 846 080 ваг. Той самий сюжет, який ми бачили в темі 13: ваги збираються там, де карта ознак розгортається в один вектор.

Хребет — це дві третини моделі. 26 852 416 із 41 808 406, тобто 64.23 %. Детекторна частина, заради якої вся тема, — це третина. Ось чому перенос навчання з теми 12 для детекції не просто корисний, а обовʼязковий: більшість ваг детектора — це класифікаційна мережа, яку вже хтось навчив.

ЦінаЧим платять два етапи

Двоетапні детектори роками лишалися найточнішими, і причина в самій формі відповіді. Перший етап відповідає на легше питання — «тут узагалі щось є?» — і має право помилятися в бік надлишку. Другий етап дивиться лише на кілька сотень уже відібраних місць, і саме тому може дозволити собі дорогу голову з тринадцятьма мільйонами ваг: 300 разів на кадр, а не 242 991.

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

Платять за це двома речами.

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

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

Куди йдуть ваги: Faster R-CNN проти RetinaNet

Обидві моделі на тому самому хребті ResNet-50 з FPN. Повзунок міняє кількість класів — і саме на ньому видно різницю між головою, що працює по кількох сотнях кандидат-областей, і головою, що працює по всіх якорях одразу.

Faster R-CNN—
RetinaNet—
хто важча й на скільки—
ваг на клас—
Числа звірені з реально зібраними моделями в кількох точках: при 91 класі 41 808 406 проти 34 068 119, при 500 — 43 904 531 проти 42 552 824, при 587 RetinaNet уперше стає важчою. У Faster R-CNN клас коштує 5 125 ваг, у RetinaNet — 20 745, бо її голова передбачає клас для кожного з девʼяти якорів у кожній точці згорткою 3×3.

Отже, звична фраза «одноетапні детектори легші» справджується лише в звичному діапазоні кількості класів. При 91 класі COCO RetinaNet важить на 7.7 мільйона менше — це вагомо. При пʼятистах класах різниця майже зникає, а десь коло 587 класів перевертається. Причина в тому, де живе голова: у Faster R-CNN вона обробляє 300 векторів по 1 024 числа, у RetinaNet згортка проходить по всіх клітинках піраміди — на кадрі 800×1216 їх 80 997, і в кожній вона передбачає клас для девʼятьох якорів. Дорога дискретизація одноетапної моделі — не в кількості ваг, а в тому, скільки разів ці ваги застосовуються.

ПідсумокТри вузькі місця, три ходи

Уся тема вкладається в одну таблицю, і в ній важливий не список назв, а стовпчик «що саме прибрали».

КрокВузьке місцеХід
Ковзне вікно591 415 вікон на кадр—
R-CNN2 000 проходів мережі на кадр, 126 сдешевий генератор кандидат-областей замість перебору
Fast R-CNNSelective Search на процесорі, дві секундиознаки рахуються раз, вирізаються з карти
Faster R-CNNдва етапи: точка синхронізації посередині й сім рухомих частинкандидат-області теж передбачає мережа

І окремо — технічна деталь, яка не дає прискорення, зате дає точність: RoI-Align. Округлення координат у RoI-пулінгу зсуває вирізані ознаки на кілька пікселів, білінійна вибірка не зсуває їх зовсім, і різницю видно саме там, де вимога до рамки сувора.

що заміряночисло
вікон на кадр 1000×600, крок 4591 415
R-CNN: час на кадр126.4 с
Fast R-CNN: час на кадр, 2 000 рамок1.717 с
Fast R-CNN: час на кадр, 300 рамок0.757 с
зсув від округлення, крок 160.57 … 8.71 px
зсув RoI-Align0.00 px
якорів на кадр 800×1216242 991
позитивних якорів на наших сценах0.079 %
повнота RPN на 10 кандидат-областях0.997
повнота випадкових якорів на 100.052
ваг у RPN справжньої моделі593 935 (1.42 %)

Лишається питання, яке ця тема поставила, але не закрила. Другий етап — це третина ваг і вся складність конвеєра. Він потрібен, бо дає точність. А чи потрібен він обовʼязково? Що станеться, якщо мережа передбачатиме клас і координати відразу для кожного якоря, без відбору й без другого проходу?

Швидкість зросте очевидно. А от точність, як покаже тема 25 «YOLO», впаде не так сильно, як здається, — і головна причина падіння виявиться не в архітектурі, а рівно в тому числі, яке ми щойно заміряли: 0.079 % позитивних якорів. Одноетапний детектор мусить рахувати втрату по всіх кандидатах одразу, і дисбаланс, від якого двоетапний рятувався вибіркою, бʼє по ньому на повну. Як із цим упоралися, розберуть теми 25 і 26.