Постановка детекції

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

Двадцять дві теми відповідали на питання «що зображено». Навіть CLIP із попередньої теми, який класифікує реченнями, а не числами зі списку, дає одну відповідь на кадр. Тепер питання міняється на «де саме» — і разом із ним міняється форма відповіді, спосіб її перевірити й навіть те, що вважати помилкою. Ця тема не будує жодного детектора: архітектури починаються з наступної. Тут ми ставимо задачу й вчимося міряти. Це нудніша половина роботи, і водночас та, у якій найлегше себе обдурити: число mAP@0.5 = 0.72 виглядає переконливо, поки не спитаєш, що саме за ним стоїть.

ФормаВідповідь стає списком

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

Детектор віддає список. У кадрі може не бути жодного предмета, може бути один, може бути сорок. Модель не знає наперед скільки — і саме тому не може видати вектор фіксованої довжини. Кожен елемент списку — це чотири числа рамки, мітка класу і впевненість, тобто число від нуля до одиниці, наскільки модель вірить, що там справді предмет.

передбачення = [ (x1, y1, x2, y2, клас, впевненість), … ] істина = [ (x1, y1, x2, y2, клас), … ]

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

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

Наскрізний приклад теми. Усі числа нижче — з одного детектора на одному датасеті. Сцени 64×64 пікселі, у кожній від одного до трьох предметів трьох класів: коло, квадрат, трикутник. Рамка кожного предмета порахована з його маски, тобто істинна за побудовою — розмітник тут не помиляється, і це рідкісна розкіш. Навчання: 400 сцен, 25 епох, AdamW зі швидкістю навчання 3·10−3, 61 096 параметрів, 35-40 секунд на процесорі без відеокарти. Перевірка — 120 інших сцен, у яких 247 істинних рамок. Кожне число лекції знято на цьому прогоні, і поруч із числом завжди сказано, на якій сцені воно поміряне: на одній показаній сцені, на 120 перевірних чи на 400 навчальних. Це не педантизм — далі буде видно, що дві частки фону з різних сіток відрізняються на десять відсоткових пунктів.

ЗаписРамка й три її формати

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

форматчотири числанаш квадратде живе
xyxyлівий-верхній і правий-нижній кут31, 40, 50, 59torchvision, Pascal VOC
xywhлівий-верхній кут, ширина, висота31, 40, 19, 19COCO
cxcywhцентр, ширина, висота40.5, 49.5, 19, 19YOLO, DETR
нормованіте саме, поділене на розмір кадру0.633, 0.773, 0.297, 0.297файли розмітки YOLO

Це один і той самий квадрат із нашої першої перевірної сцени. У torchvision переклад між форматами робить box_convert(boxes, in_fmt, out_fmt) — три рядки, жодної математики руками. Нормовані координати — окрема історія: вони не залежать від розміру кадру, тому файл розмітки лишається чинним після будь-якого масштабування. За це доводиться платити тим, що з чисел зникає одиниця виміру, і 0.297 не підказує, чи це 19 пікселів, чи 190.

Чому формати плутають

Тому що помилка тиха. Усі чотири записи — це чотири числа однакового вигляду. Якщо передати xywh у функцію, що чекає xyxy, жодного винятку не буде: бібліотека сумлінно порахує рамку з кутами (31, 40) і (19, 19). Вона вийде вивернутою, з відʼємними шириною й висотою, IoU з чим завгодно дорівнюватиме нулю, і в звіті просто буде поганий результат — без натяку, звідки він.

Ловиться це трьома звичками, і жодна з них не коштує часу. Перша: assert (boxes[:, 2] > boxes[:, 0]).all() одразу після завантаження розмітки. Друга: подивитись на діапазон чисел — нормовані координати не бувають більші за одиницю, піксельні не бувають дробовими на виході розмітника. Третя й найнадійніша: намалювати перші пʼять рамок поверх зображення й подивитися очима. Якщо вони лежать не там, це видно за секунду.

Ще одна дрібниця, яка коштує пів відсотка mAP. Наш квадрат займає стовпці з 31-го по 49-й включно. Ширина — 19 пікселів чи 20? Залежить від того, чи вважати праву межу включеною. Pascal VOC рахував x2 − x1 + 1, COCO рахує x2 − x1. Для рамки в 19 пікселів різниця в площі — понад десять відсотків, і на маленьких предметах вона добре видно в метриці. У нашому датасеті рамка береться як xs.max() + 1, тобто за угодою COCO, і ширина дорівнює 19.

ЗбігIoU: одне число на дві рамки

Щоб сказати, чи описують дві рамки один предмет, потрібне число. Це число — IoU (intersection over union, перетин, поділений на обʼєднання). Словами: поклади дві рамки одна на одну; частина площі належить обом — це перетин; уся площа, яку вони разом накривають, — це обʼєднання; IoU — перше, поділене на друге. Одиниця — рамки збіглися ідеально. Нуль — не торкаються.

IoU = площа перетину ÷ площа обʼєднання

Порахуймо руками на найменшому прикладі, який тільки можна вигадати. Рамка A має кути (10, 10) і (30, 30). Рамка B — (20, 20) і (40, 40). Обидві сторонами по 20 пікселів.

крок 1 · де починається перетин

Ліва межа перетину — правіша з двох лівих меж: більше з чисел 10 і 20, тобто 20. Верхня межа — нижча з двох верхніх: теж 20.

x1 = max(10, 20) = 20 y1 = max(10, 20) = 20
крок 2 · де він закінчується

Права межа перетину — лівіша з двох правих: менше з чисел 30 і 40, тобто 30. Нижня — так само 30.

x2 = min(30, 40) = 30 y2 = min(30, 40) = 30
крок 3 · площа перетину

Ширина перетину — 30 мінус 20, тобто 10. Висота так само 10. Якщо різниця вийшла відʼємною, рамки не перетинаються й площа дорівнює нулю — тому в коді завжди пишуть max(0, x2 − x1).

перетин = 10 × 10 = 100
крок 4 · площа обʼєднання

Кожна рамка має площу 20 × 20 = 400. Якщо просто скласти, спільну частину порахуємо двічі — тому її треба відняти один раз.

обʼєднання = 400 + 400 − 100 = 700
крок 5 · відповідь
IoU = 100 ÷ 700 = 0.142857…

torchvision.ops.box_iou на тих самих двох рамках дає 0.142857 — збіг до шостого знака. Усередині бібліотеки саме ці пʼять кроків і нічого більше.

IoU наживо

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

перетин—
обʼєднання—
IoU—
Поріг 0.5 — це не «половина рамки збіглась». Початкове положення: рамки однакові, 40 на 40, зсунуті рівно на 14 пікселів по горизонталі — і IoU вже 0.481, тобто нижче типового порога. Щоб дві рамки однакового розміру дістали IoU рівно 0.5, вони мусять накривати одна одну на дві третини площі.

Чого IoU не каже

IoU стискає взаємне розташування двох прямокутників в одне число, а отже, неминуче щось викидає. Найважливіше з викинутого: IoU не розрізняє, у чому саме модель помилилась. Ось три рамки, кожна з яких дає з тим самим квадратом 40×40 рівно 0.5, і три зовсім різні помилки за цим числом.

помилкащо зробила модельIoU
меншарамка на місці, але вдвічі менша за площею: сторона 28.3 замість 400.500
зсунутарамка того самого розміру, зсунута на 13.3 пікселя вбік0.500
повернутапредмет лежить під 45°, і рамка навколо нього вдвічі більша за площею0.500

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

Одне число, три різні помилки

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

характер помилки
IoU—
площа рамки—
Три криві помилки з однаковою оцінкою. При IoU 0.5 менша рамка має сторону 28.3 замість 40, зсунута — ті самі 40, але зміщена на 13.3 пікселя, а рамка навколо повернутого предмета виростає до 56.6. Три різні числа на екрані, одне й те саме в метриці.

ЗіставленняЗіставлення передбачень з істиною

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

  1. Відсортуй усі передбачення за впевненістю, від найвпевненішого до найменш.
  2. Візьми перше. Знайди серед ще не зайнятих істинних рамок ту, з якою в нього найбільший IoU.
  3. Якщо цей IoU не менший за поріг — зарахуй передбачення як влучне (TP, true positive) і познач істинну рамку зайнятою. Інакше передбачення — хибне спрацювання (FP, false positive).
  4. Перейди до наступного передбачення й повтори.
  5. Істинні рамки, які лишились незайнятими, — це пропуски (FN, false negative).

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

Порядок за впевненістю теж не випадковий. Він робить алгоритм передбачуваним: найвпевненіше передбачення дістає першу спробу зайняти істинну рамку. Але жадібність — це угода, а не теорема. Вона не гарантує найкращого можливого розкладу: буває, що менш упевнена рамка зайняла б свою істину точніше, а тепер лишилась ні з чим. Оптимальний розклад дає угорський алгоритм, і саме він стоїть у навчанні DETR із теми 27. Але в оцінюванні всі — і COCO, і Pascal VOC — користуються жадібним, тому й ми рахуємо ним.

Хто з ким зіставився

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

зараховано—
хибних—
пропущено—
рамок усього—
Ті самі рамки, різна оцінка. Ані рамки, ані IoU між ними не міняються, коли ти рухаєш повзунок. Міняється тільки правило зарахування — і разом із ним вердикт. Це найкоротша відповідь на питання, чому в чужому звіті обовʼязково треба шукати, при якому порозі його рахували. Заразом видно й друге: із 18 рамок влучними стають щонайбільше три, бо кожну істинну рамку можна зайняти лише раз. Решта — дублікати, і саме їх прибиратиме NMS у розділі 09.

ДисбалансЧому «точність» тут безглузда

У класифікації є проста метрика: частка правильних відповідей. Спокуса перенести її на детекцію велика, і вона хибна одразу з двох боків.

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

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

61 ÷ 64 = 0.953 — тобто 95.3 % клітинок порожні

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

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

що рахуємозначення
клітинок усього (400 сцен × 64)25 600
клітинок із предметом3 732
клітинок фону21 868
частка фону85.4 %
предметів на сцену в середньому2.06
клітинок на один предмет4.52

Навіть у цьому щедрому варіанті мовчазна модель має 85.4 %. Два числа цього розділу — 95.3 % і 85.4 % — не суперечать одне одному й не заміняють одне одного: перше знято на одній сцені з трьома предметами за правилом «одна клітинка на предмет», друге — на всіх 400 навчальних сценах за правилом «центр клітинки в рамці». Щедріше правило й менше предметів на сцену тягнуть частку фону в різні боки, і різниця виходить у десять відсоткових пунктів. Тому будь-яка частка фону без назви сітки й набору — просто число.

А в справжніх детекторах, де сітка густіша й на кожну клітинку припадає кілька рамок-кандидатів, частка фону легко переходить за 99.9 %. Цей дисбаланс не лише ламає метрику — він ламає й навчання: якщо всі клітинки важать однаково, градієнт від фону затопить градієнт від предметів. Саме заради цієї проблеми вигадали focal loss, і про нього — тема 26.

Дві сітки — дві частки фону

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

правило позитивної клітинки
клітинок із предметом—
клітинок фону—
«точність» мовчазної моделі—
Мовчазна модель не помиляється — вона просто нічого не знаходить. Права смуга і є її «точність», і вона тим вища, чим суворіше правило й чим менше предметів. На цій сцені з трьома предметами це 95.3 % за правилом «одна клітинка на предмет» і 78.1 % за правилом «центр клітинки в рамці»; по всіх 400 навчальних сценах, де предметів у середньому 2.06, друге правило дає 85.4 %. Три різні числа, і жодне з них не каже про детектор нічого.
Одне слово — два різні значення. У цьому розділі «точність» стоїть у лапках навмисне: там вона означає частку правильних відповідей (англійською accuracy) — скільки клітинок або картинок модель класифікувала правильно з усіх. Уже в наступному розділі те саме українське слово означатиме зовсім інше — precision, частку справжніх предметів серед намальованих рамок. Це не недогляд перекладу, а усталений збіг: у детекції accuracy як метрику якості не вживають саме тому, що вона безглузда, тож слово «точність» без лапок далі завжди означає precision. Друга частка — recall, повнота — теж чекає в наступному розділі.

ПараТочність і повнота

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

точність · precision

Із рамок, які я намалював, скільки виявились справжніми? Це питання про сміття у відповіді.

точність = TP ÷ (TP + FP) = влучні ÷ усі намальовані
повнота · recall

Із предметів, які насправді були, скільки я знайшов? Це питання про пропущене.

повнота = TP ÷ (TP + FN) = влучні ÷ усі справжні

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

Жодне з двох чисел саме по собі нічого не варте, і обдурити кожне окремо тривіально. Детектор, який малює одну-єдину найвпевненішу рамку, майже напевно матиме точність 1.0 — і повноту, близьку до нуля. Детектор, який засипає кадр тисячею рамок на всі випадки життя, матиме повноту 1.0 і точність, близьку до нуля. Отже, дивитись треба на обидва разом, і завжди питати, за якого порогу впевненості їх поміряли.

Поріг упевненості — це ручка, а не константа

Ось найважливіша річ у цьому розділі. Наш детектор навчено один раз. Його ваги зафіксовані. Але його точність і повнота міняються від 0.042 до 0.946 і від 0.988 до 0.773 відповідно — і жодне навчання при цьому не відбувається. Міняється тільки одне число: поріг, нижче за який ми викидаємо рамки, не дивлячись.

Усі числа таблиці нижче зняті на 120 перевірних сценах після притлумлення дублікатів із порогом 0.5 — того самого, про яке піде мова в розділі 09. Без нього рамок було б у кілька разів більше, і саме це число ми там і побачимо.

поріграмокточністьповнота
0.00015 7690.0420.988
0.0012 2130.1090.980
0.0035290.4570.980
0.013350.7220.980
0.052900.8280.972
0.52780.8630.972
0.952690.8880.968
0.9992020.9460.773

Зверни увагу на шкалу лівої колонки: цікаве відбувається не між 0.4 і 0.6, а між 0.0001 і 0.01. Це типово. Детектор, який добре навчився, ставить фоновим клітинкам не 0.3, а 0.000001, тому «природний» поріг 0.5 стоїть глибоко в пласкій частині, де майже нічого не міняється. Робочу точку нашого детектора ми взяли при 0.05: 290 рамок на 247 істинних, точність 0.828, повнота 0.972. Це означає 240 влучань, 50 хибних спрацювань і 7 пропущених предметів.

Ручка порога

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

точність—
повнота—
рамок у відповіді—
істинних рамок247
Ваги не міняються — міняється лише поріг. Від 0.0001 до 0.999 це буквально одна модель з одним набором чисел усередині. Тому фраза «наш детектор має точність 0.95» без згадки порога не означає нічого: її можна отримати з будь-якої моделі, достатньо задерти поріг і змиритися з утраченою повнотою.

КриваКрива й площа під нею

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

Дві криві порівнювати на око незручно, а звітувати кривою в таблиці неможливо. Тому беруть площу під нею — одне число від нуля до одиниці, яке зветься AP (average precision, середня точність). Ідея за площею проста: вона підсумовує поведінку моделі на всіх робочих точках одразу, а не на тій, яку автор звіту вибрав собі до смаку.

Спершу — вирівнювання сходинок

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

pінт(r) = max { p(r′) : r′ ≥ r }

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

Два способи взяти площу

Метод усіх точок (сучасний, COCO). Площу беруть по кожній сходинці кривої: на скільки виросла повнота, помножити на інтерпольовану точність у цій точці, і все це скласти. На нашому детекторі виходить AP@0.5 = 0.9314.

Одинадцятиточковий метод (Pascal VOC до 2010 року). Беруть рівно одинадцять значень повноти — 0, 0.1, 0.2, …, 1.0 — зчитують інтерпольовану точність у кожному й усереднюють. На тому самому детекторі — AP@0.5 = 0.8663.

Різниця 0.065 при однаковій кривій. Звідки вона? Наш детектор ніколи не дістає повноти вище 0.988, а одинадцятиточковий метод усе одно питає про повноту 1.0 — і зараховує туди нуль. Один цей нуль важить одну одинадцяту всієї суми, тобто 0.091. Решта десять точок дають майже однакові 0.95, і саме недосяжна остання точка зʼїдає різницю. Метод усіх точок про недосяжну повноту просто не питає.

Наслідок для читання чужих звітів. Два числа, 0.9314 й 0.8663, порахували на тій самій кривій того самого детектора. Різниця тільки в способі взяти площу. Якщо в статті сказано «AP» без уточнення методу й року конвенції, порівнювати це з сучасним числом COCO не можна: рахували не те саме.

Крива й площа

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

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

УсередненняmAP: два усереднення

AP описує один клас при одному порозі IoU. Літера «m» на початку mAP — це mean, середнє. Усереднень насправді два, і плутають їх постійно.

Перше усереднення: по класах

Рахуємо AP окремо для кола, окремо для квадрата, окремо для трикутника, тоді беремо середнє арифметичне.

класістинних рамокAP@0.5AP@0.5:0.95
коло840.92160.4459
квадрат760.96200.5540
трикутник870.93420.4700
mAP2470.93930.4900

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

Друге усереднення: по порогах IoU

Поріг 0.5 обрано колись довільно, і сам по собі він поблажливий. Наскільки — показує пряма арифметика. Візьми рамку зі стороною 40 пікселів і посунь її вбік; ось на скільки її можна зсунути, зберігши заданий IoU:

потрібний IoUдопустимий зсувчастка сторони
0.5013.3 px33 %
0.755.7 px14 %
0.902.1 px5 %

Тобто mAP@0.5 зараховує рамку, зсунуту на третину власної сторони. Це дуже поблажливо. Щоб винагороджувати акуратність, COCO рахує AP при десяти порогах — 0.50, 0.55, …, 0.95 — і бере середнє. Це і є mAP@0.5:0.95, або просто mAP у сучасних статтях.

У таблиці нижче показано кожен другий поріг із десяти — 0.50, 0.60, 0.70, 0.80, 0.90; проміжні 0.55, 0.65, 0.75, 0.85 і 0.95 опущено, щоб таблиця читалась. Останній рядок усереднює всі десять, тож він менший за середнє пʼятьох показаних: пропущені пороги лежать нижче за своїх сусідів зліва.

поріг IoUколоквадраттрикутникmAP
0.500.9220.9620.9340.939
0.600.8790.9150.8470.880
0.700.4750.7290.6050.603
0.800.1620.4030.1260.230
0.900.0010.0150.0000.005
середнє по 10 порогах0.4460.5540.4700.490
Головна пара чисел цієї теми. mAP@0.5 = 0.939 і mAP@0.5:0.95 = 0.490 — це один і той самий детектор, на тих самих 120 сценах, з тими самими вагами. Від першого числа до другого зникла майже половина. Перше каже «рамка приблизно там, де предмет». Друге каже «рамка майже ідеально збігається з предметом». Коли хтось показує тобі mAP, перше питання — яке з двох.

Наш детектор передбачає рамку з клітинки 8×8 пікселів, тому 2-3 пікселі похибки для нього норма — і при порозі 0.9 від AP не лишається нічого. Для порівняння, цитовані числа з літератури на COCO: у типового сучасного детектора mAP@0.5 близько 0.6-0.7, а mAP@0.5:0.95 — близько 0.4-0.5. Пропорція та сама, що й у нас: суворіший поріг зрізає приблизно третину-половину.

ДублікатиNMS: прибирання дублікатів

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

Наш детектор при порозі впевненості 0.05 віддає 1134 рамки на 120 сцен, у яких 247 предметів. Точність такого списку — 0.215, тобто чотири рамки з пʼяти зайві. І це не помилка навчання: усі вони правильні, просто повторюють одна одну.

Лікує це NMS (non-maximum suppression, притлумлення немаксимумів). Ідея в одному реченні: серед рамок, які сильно перекриваються, лиши найвпевненішу, а решту викинь.

  1. Відсортуй рамки за спаданням упевненості.
  2. Візьми найвпевненішу, поклади її у відповідь.
  3. Викинь із черги всі рамки, у яких IoU з нею більший за поріг.
  4. Повтори з наступною вцілілою, поки черга не спорожніє.

У torchvision це ops.nms(boxes, scores, iou_threshold). Функція повертає індекси рамок, які треба лишити, і працює саме за цими чотирма кроками — жодної додаткової хитрості всередині немає. Написати такий самий NMS самотужки — це десяток рядків, і на однакових входах він дає той самий список індексів; фігура нижче рахує його прямо в браузері.

поріг NMSрамок лишилосьточністьповнота
0.12200.9730.866
0.22290.9560.887
0.32440.9390.927
0.52900.8280.972
0.74850.5010.984
0.91 0240.2380.988
без NMS1 1340.2150.988

Тут видно і користь, і ціну. NMS із порогом 0.5 викидає 844 рамки з 1134 і піднімає точність із 0.215 до 0.828, віддавши за це лише 0.016 повноти. А жадібний поріг 0.1 піднімає точність до 0.973 — і забирає повноту з 0.988 аж до 0.866, тобто губить кожен восьмий предмет.

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

NMS на справжніх передбаченнях

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

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

МежаДе NMS ламається

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

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

зсувIoUпоріг 0.3поріг 0.5поріг 0.7
10 px0.714загубилизагубилизагубили
15 px0.600загубилизагубилиобидві
20 px0.500загубилиобидвіобидві
25 px0.412загубилиобидвіобидві

Читай середню колонку. При загальноприйнятому порозі 0.5 два предмети, що стоять на відстані 10 або 15 пікселів один від одного, перетворюються на один: NMS викидає задній назавжди. Ніякий наступний етап його не поверне — рамки більше немає, і жоден шар про це не дізнається. У метриці це виглядатиме як звичайний пропуск, і причина пропуску в звіті не зʼявиться.

Тут же видно, чому просто підняти поріг не можна. Ми вже бачили це в таблиці попереднього розділу: поріг 0.9 повертає повноту 0.988, але точність падає до 0.238. Виходить вибір без доброї відповіді: низький поріг губить справжні предмети, високий лишає дублікати. NMS не має способу відрізнити одне від одного, бо дивиться тільки на геометрію.

Два справжні предмети під ножем NMS

Дві рамки 60×120 — це два різні предмети, обидва справжні. Повзунок розсуває їх, кнопки міняють поріг NMS. Вердикт каже, скільки з двох доживе до відповіді.

поріг NMS
IoU між ними—
лишилось рамок—
Це не крайній випадок. Дві людини, що йдуть на відстані десяти пікселів у кадрі, дають IoU 0.714 — це набагато вище за будь-який робочий поріг. Щоб NMS зберіг обох при порозі 0.5, вони мусять розійтися щонайменше на 20 пікселів, тобто на третину ширини рамки.

Спроба лікування: Soft-NMS

Найвідоміша спроба виправити це називається Soft-NMS і міняє один крок алгоритму. Замість викидати перекриту рамку, їй знижують упевненість — тим сильніше, чим більший IoU з переможцем. Рамка з IoU 0.95 отримує майже нульову впевненість і фактично зникає; рамка з IoU 0.6 лишається в списку з помітно меншим числом. Далі спрацьовує поріг упевненості: якщо задній предмет був розпізнаний упевнено, зниження його не вбʼє, і в відповідь він потрапить. Виграш на щільних сценах реальний, але невеликий, а плата — ще один гіперпараметр і повільніший прохід. Радикальніші відповіді на ту саму проблему дає DETR із теми 27, який позбувається NMS взагалі: угорське зіставлення під час навчання прямо забороняє двом виходам описувати один предмет, тому дублікатів немає й прибирати нічого.

МовчанняЧого метрики не кажуть

mAP — добра метрика. Вона зводить довгий список рамок до одного порівнюваного числа, не залежить від обраної робочої точки й однаково поводиться з класами різної частоти. Але одне число не може сказати всього, і корисно точно знати, про що саме воно мовчить.

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

Чого коштує помилка. Хибне спрацювання й пропуск входять у формули симетрично. У медичному застосуванні пропущена пухлина коштує незрівнянно більше, ніж зайва рамка, яку лікар відкине за секунду. Хто хоче це врахувати, рахує метрику при фіксованій повноті — наприклад, «яка точність при повноті 0.99» — і на mAP не спирається.

Скільки це коштує часу. У mAP немає ані мілісекунд, ані мегабайтів. Детектор, який дає 0.52 за 8 мілісекунд, і детектор, який дає 0.54 за 300, у таблиці стоять поруч, а в житті вирішують різні задачі. Порівнювати треба парою «якість і швидкість», як робила тема 18.

Наскільки правильна сама розмітка. Метрика мовчазно вважає еталонну розмітку істиною. У нашому датасеті це справді так — рамка порахована з маски формулою. У справжніх даних розмітник ставив її рукою за 15 секунд, і різниця між двома розмітниками на тому самому предметі спокійно дає IoU 0.85. Це означає, що при порозі 0.9 метрика частково міряє не детектор, а тремтіння руки.

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

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

Тема не побудувала жодної мережі, зате поставила задачу так, що її тепер можна чесно міряти. Коротко про те, що маємо.

поняттяу чому суть
Список замість міткивихід невідомої довжини, тому перед метрикою потрібне зіставлення
IoUперетин на обʼєднання; на наших рамках порахований руками — 0.142857
Зіставленняжадібне за впевненістю; одна істина — не більше одного влучання
Точність і повнотадві частки з різними знаменниками; жодна сама по собі не працює
APплоща під кривою точність-повнота; не залежить від обраного порога
mAPсереднє AP по класах, а часто ще й по десяти порогах IoU
NMSпісляобробка проти дублікатів; ламається на щільних сценах

Головне число, яке варто винести: mAP@0.5 = 0.939 і mAP@0.5:0.95 = 0.490 — це один детектор. Тепер, зустрівши в чужому звіті mAP@0.5 = 0.72, ти знаєш чотири питання, які треба поставити: який поріг NMS, який поріг упевненості, який метод площі й чи це середнє по класах. Без відповідей на них число не порівнюване ні з чим.

Задачу поставлено, метрики є, залишилось збудувати те, що вони міряють. Наступна тема береться за перший робочий підхід до детекції — сімейство R-CNN, яке виграло детекційне змагання ImageNet і показало, що згорткові ознаки годяться не лише для класифікації. Ідея там проста до незручності: спершу знайти кілька тисяч місць, де може бути предмет, а потім класифікувати кожне окремо. Проста — і повільна; історія блоку 5 значною мірою і є історія боротьби з цією повільністю, від YOLO до одноетапних детекторів і DETR.