Instance segmentation: маска на кожен предмет

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

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

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

ПроблемаДві кулі на столі

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

Візьмімо 120 сцен і порахуймо дві речі. Перша — яка частка предметів торкається іншого предмета того самого класу. Друга — скільки звʼязних плям дає семантична маска проти справжньої кількості предметів. Звʼязна пляма (або звʼязна компонента, connected component) — це група пікселів одного класу, яку можна обійти, ходячи тільки по сусідніх пікселях і не відриваючись.

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

на 120 сценахрідкі (до 3)щільні (до 6)
предметів247420
торкається предмета свого класу24 (9.7 %)105 (25.0 %)
торкається будь-якого предмета83 (33.6 %)245 (58.3 %)
звʼязних плям у семантичній масці236364
сцен, де кількість плям збіглася107 (89.2 %)79 (65.8 %)

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

Останній рядок називає це прямо: кількість плям збіглася з кількістю предметів у 107 сценах зі 120 на рідких (89.2 %) і в 79 зі 120 на щільних (65.8 %). Тобто навіть на рідких сценах, де дотиків усього 9.7 %, кожна девʼята сцена вже дає хибне число.

Але важливіше зрозуміти не «як часто», а коли саме підрахунок по плямах бреше. Випадків три, і всі три можна викликати в інтерактиві нижче:

  1. Дві фігури одного класу торкаються. Пікселі зливаються в одну пляму, і плям стає менше, ніж предметів. Це головний і найчастіший випадок — саме він дає 364 плями на 420 предметів.
  2. Фігура іншого класу лягла впоперек. Вона розрізає нижню фігуру на дві частини, і плям стає більше, ніж предметів. Помилка в інший бік, але теж помилка: одну фігуру порахують за дві, а іноді й за три.
  3. Обидві помилки на одній сцені. Одна пара злиплась, одну фігуру розрізало — і лічильники зійшлись. Число правильне, розбір хибний, і жодна перевірка кількості цього не помітить.

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

Числа сходяться самі із собою. На рідких сценах торкаються 24 предмети — це близько 12 пар, і плям бракує 11 (247 − 236). На щільних торкаються 105, тобто близько 52 пар, і плям бракує 56. Кожна пара дотичних предметів одного класу коштує приблизно однієї загубленої плями — це прямий наслідок означення звʼязності. Точної рівності немає у два боки: ланцюжок із трьох дотичних фігур коштує вже двох плям, а фігура, розрізана навпіл чужою, навпаки додає зайву.

Одна пляма чи два предмети

Дві фігури на полотні 64×64. Повзунок розсуває їх; перемикач робить їх різними класами. Праворуч — семантична маска й лічильник звʼязних плям.

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

ПостановкаВихід — список невідомої довжини

Сформулюймо різницю точно, бо вона тонша, ніж здається.

семантична сегментація

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

instance segmentation

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

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

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

Три задачі — три різні виходи. Семантична сегментація: масив 64×64 із класами. Instance segmentation: список масок, кожна зі своїм класом. Panoptic: одна відповідь, у якій кожен піксель має і клас, і номер предмета. Тема 02 ввела всі три як постановки; блок 6 проходить їх по черзі, і panoptic чекає в темі 32.

НаївнеНаївне лікування: звʼязні компоненти

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

Алгоритм є в OpenCV одним рядком: cv2.connectedComponents. Запускаємо його окремо для кожного класу — інакше коло, що торкається квадрата, стане однією плямою.

маска класу «коло» → плями 1, 2, 3 маска класу «квадрат» → плями 4, 5 маска класу «трикутник» → пляма 6

Результат на наших сценах. Ми питаємо: для скількох істинних предметів знайшлася пляма, що збігається з ним хоча б на IoU ≥ 0.5 — та сама міра збігу, що в темі 29, тільки рахована по масках.

відновлено предметіврідкі сценищільні сцени
усі предмети0.9510.836
ті, що ні з ким не торкаються1.0001.000
ті, що торкаються свого класу0.5000.343

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

Отже, звʼязні компоненти — це не поганий метод. Це метод, який працює рівно доти, доки задача не почалась. Він розвʼязує все, крім єдиного випадку, заради якого існує instance segmentation.

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

Компоненти проти істини

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

що показувати
предметів—
плям—
злиплось предметів—
розрізано предметів—
Помилка зʼявляється не поступово, а стрибком. Доки фігури розкидані, обидві картинки однакові. Варто двом фігурам одного класу зійтись — і праворуч плям стає менше, а ліворуч предметів стільки ж. Саме цей стрибок і дає 0.343 на щільних сценах. А тепер постав 7 фігур, сцену 3: плям і предметів порівну, і лічильник задоволений — але два предмети злиплись, а один розрізало навпіл, і помилки просто погасили одна одну. Рівність кількостей не є доказом, що розбір правильний.

Метрикаmask AP: чим міряти маски предметів

Перш ніж будувати модель, потрібна метрика. І тут гарна новина: винаходити нічого не треба. mask AP — це точнісінько box AP із теми 23, у якій IoU рахується не по рамках, а по масках. Уся інша механіка та сама.

крок 1 · збіг двох масок

IoU двох масок — це кількість спільних пікселів, поділена на кількість пікселів, зайнятих хоч однією з них. Символ ∩ означає «спільні», ∪ — «усі разом».

IoU = |A ∩ B| ÷ |A ∪ B|

Рахуємо руками. Коло радіуса 5 займає 81 піксель. Його рамка — квадрат 11×11, тобто 121 піксель. Коло цілком лежить усередині рамки, тож спільних пікселів 81, а разом їх 121. IoU = 81 ÷ 121 = 0.669.

крок 2 · зіставлення

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

крок 3 · AP

Далі — площа під огинальною кривої точність-повнота, окремо для кожного класу, і середнє по класах. Жодної відмінності від теми 23 тут немає.

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

передбаченняbox AP@0.5box@0.75box [.50:.95]mask AP@0.5mask@0.75mask [.50:.95]
ідеальні маски1.00001.00001.00001.00001.00001.0000
маска = вся рамка1.00001.00001.00000.89430.29120.4360
зсув форми на 1 px0.99670.64550.57470.97010.41340.4872
зсув форми на 2 px0.96750.00000.26590.69660.00000.1599
звʼязні компоненти0.76400.65530.67680.81440.67270.7021

Другий рядок — головний. Рамки ідеальні до пікселя, тому box AP дорівнює рівно одиниці при будь-якому порозі: кращого передбачення рамок не буває. А mask AP при суворому порозі падає до 0.2912, і в середньому по порогах дає 0.4360. Одне й те саме передбачення: за рамками — бездоганне, за масками — менш ніж половина від бездоганного.

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

фігураIoU «маска = рамка»звідки
квадрат0.9453квадрат майже і є своя рамка
коло0.6731коло не заповнює кутів рамки — те саме 81 ÷ 121 = 0.669, що ми порахували вище
трикутник0.4969трикутник — рівно половина рамки

Трикутник займає половину своєї рамки, тож його «маска-прямокутник» має IoU 0.4969 — нижче за поріг 0.5, і при порозі 0.75 такий прогноз не зараховується взагалі. Ось звідки береться падіння: рамка не бачить форми, а маска — тільки форму й бачить.

Але «маска суворіша» — не закон. Останній рядок таблиці показує зворотне: у звʼязних компонент mask AP (0.8144) вища за box AP (0.7640). Причина в тому, яка саме помилка сталася. Коли дві фігури злиплись в одну пляму, її рамка — це великий прямокутник довкола обох, і з рамкою окремої фігури він збігається погано, бо в нього ще й порожні кути. А сама пляма щільно облягає обидві фігури, тож із маскою більшої з них збігається краще. Правильне формулювання: рамка пробачає неправильну форму, маска пробачає неправильні межі предмета. Яка метрика виявиться суворішою, залежить від того, яку помилку робить твоя модель.

Та сама відповідь, дві метрики

Перемикач обирає, як зіпсовано передбачення. Смуги — box AP і mask AP при трьох порогах. Заміряно на 120 щільних сценах.

що не так із передбаченням
box AP@0.5—
mask AP@0.5—
box [.50:.95]—
mask [.50:.95]—
Дві метрики міряють різні помилки. На «маска = вся рамка» box AP не втрачає нічого, а mask AP валиться. На «звʼязних плямах» усе навпаки. Не існує однієї метрики, суворішої завжди — існує метрика, що відповідає твоїй помилці.

Зверху внизЗверху вниз: спершу рамка, потім маска

Розвʼязків історично два сімейства, і вони протилежні за напрямком думки.

підхідпорядок дійзвідки береться кількість предметів
зверху вниз
top-down
знайти предмет рамкою, потім намалювати маску всередині цієї рамкиз детектора: скільки рамок, стільки предметів
знизу вгору
bottom-up
підписати кожен піксель, потім згрупувати пікселі в предметиз групування: скільки груп, стільки предметів

Класичний представник першого сімейства — Mask R-CNN (2017). Ідея в одному реченні: візьми готовий детектор Faster R-CNN із теми 24 й додай до нього третю гілку, яка для кожної знайденої рамки малює маску.

Чому це працює, хоч і виглядає як обхідний маневр? Тому що всередині рамки задача знову стає семантичною. Рамка вже виділила один предмет; усередині неї треба відповісти лише «цей піксель належить предметові чи ні»: сусід залишився за краєм рамки або принаймні не «головний». Важку частину — розділити предмети — виконав детектор, а маску малює маленька мережа тієї самої форми, що U-Net із теми 30, тільки на крихітному вході.

Ланцюжок повний. Тема 23 дала рамки й mAP, тема 24 — двоетапну схему й RoI-Align, тема 26 — anchor-free голову, тема 29 й тема 30 — як мережа малює маску. Instance segmentation зверху вниз — це не нова ідея, а складання вже відомих. Саме тому Mask R-CNN зʼявився так швидко після Faster R-CNN.

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

ГілкаМаска як окрема гілка

Побудуймо це самі — на найменшому детекторі, який у нас є. Беремо anchor-free голову з теми 26: карта 8×8, у кожній позиції три числа класу, чотири відстані до країв рамки й одне число centerness. Нічого в ній не міняємо. Додаємо третю гілку:

карта ознак 16×16 (32 канали) │ ├─ RoI-Align 14×14 за рамкою предмета │ ├─ Conv 3×3 → ReLU → Conv 3×3 → ReLU ├─ ConvTranspose 2×2 крок 2 ← 14×14 стає 28×28 └─ Conv 1×1 → три канали, по одному на клас

Це Mask R-CNN у мініатюрі: ділянка карти ознак під рамкою, кілька згорток, одне подвоєння роздільності й окремий канал на кожен клас. Навчаємо на істинних рамках (так само, як у статті), а на перевірці подаємо гілці ті рамки, які видав сам детектор. Три зерна, AdamW, 240 щільних сцен.

частинапараметрів
тіло60 576
голова рамок (клас + відстані + centerness)4 616
голова масок, канал на клас22 723
голова масок, один канал на всіх22 657
усього87 915

Результат на 120 щільних перевірних сценах. Знак ± тут і далі означає розкид — відстань між найгіршим і найкращим із трьох зерен, а не половину від неї: ±0.2779 читається як «найкращий прогін на 0.2779 вищий за найгірший».

гілка масокbox AP@0.5mask AP@0.5mask AP[.50:.95]
канал на клас0.8810 ±0.09790.8678 ±0.08740.4622
один канал0.8076 ±0.27790.8725 ±0.04760.4484

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

Друге: перевіримо себе на рідких сценах. Візьмімо одну модель — ту, що з зерна 0, — і прожену її крізь обидва набори. На щільних сценах вона дає box AP 0.9053 і mask AP 0.8807, на рідких — box AP 0.9372 і mask AP 0.9190. У темі 26 та сама anchor-free голова — без гілки маски й без centerness — дала на рідких сценах 0.9187. Числа не можна прикладати одне до одного впритул, бо голови трохи різні, але висновок із них однозначний: гілка маски рамкам не зашкодила, а просідання дають саме щільні сцени, а не маска.

Знахідка, якої я не чекав: маска стабільніша за рамку. У варіанті з одним каналом розкид box AP між зернами дорівнює 0.2779 — одне зерно дало 0.6366 проти 0.9145 в іншого, — а розкид mask AP на тих самих трьох прогонах усього 0.0476, у 5.8 раза менший. Причина в тому, що маска виправляє рамку: навіть коли рамка вийшла завеликою, голова масок зафарбує всередині лише сам предмет, і після порогу 0.5 маска щільно облягає фігуру. За рамкою такий прогноз — промах, за маскою — влучання. Не цитуй цю пару чисел як доказ, що один канал гірший: гірша тут рамка, і саме в одному прогоні з трьох.

Третє, і воно теж не збіглося з очікуванням. Різниця між «каналом на клас» і «одним каналом» у mask AP дорівнює −0.0047 при розкиді 0.0874. Різниці немає взагалі — вона у вісімнадцять разів менша за розкид, та ще й у «неправильний» бік. І це логічно: у нас три класи простих фігур, які мережа розрізняє впевнено, тож розчіплювати клас і форму нема від чого. Виграш від окремих каналів зʼявляється там, де класи схожі за формою й модель у них плутається, — а це COCO з вісімдесятьма класами, а не три геометричні фігури. Правильне формулювання: канал на клас коштує майже нічого (66 ваг у нас, 0.05 % у Mask R-CNN) і не шкодить, тому його беруть за замовчуванням, — але доводити його користь треба на складному датасеті, а не на нашому.

СтеляРоздільність маски: стеля без навчання

Mask R-CNN передбачає маску розміру 28×28 — і цей розмір не залежить від того, наскільки великий предмет. Маска слона й маска мурахи однаково складаються з 784 чисел, які потім розтягуються під розмір рамки.

Скільки на цьому втрачається, можна порахувати без жодного навчання. Беремо істинну маску, стискаємо її до 14×14, 28×28 або 56×56, розтягуємо назад під розмір рамки й міряємо IoU з оригіналом. Це стеля методу: краще за це не вийде навіть у бездоганної мережі.

сторона предметамаска 7×714×1428×2856×56
16 px0.91131.00001.00001.0000
28 px0.92240.97841.00001.0000
56 px0.93260.96430.99251.0000
112 px0.93360.96940.98290.9970
224 px0.93570.96950.98520.9918

Перше, що впадає в око: колонки не спадають. Від 56 до 224 пікселів сторона предмета росте вчетверо, а втрата на масці 28×28 лишається того самого порядку: 1 − 0.9925 = 0.8 %, потім 1.7 %, потім 1.5 %. Вона не подвоюється разом із предметом і взагалі не має напрямку — гуляє в межах одного відсотка. Причина проста, і її варто проговорити: помилка стискання живе на смузі вздовж межі фігури, і ширина цієї смуги росте разом із фігурою. Відносна втрата від цього лишається приблизно сталою.

Отже, для гладкої опуклої фігури 28×28 — не обмеження, а розумний компроміс. Обмеження зʼявляється в іншому місці.

Де 28×28 справді ламається

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

гребінецьзубцівмаска 14×1428×2856×56
28 px40.94631.00001.0000
56 px90.57560.99551.0000
112 px180.54210.55260.9873
224 px370.56040.53360.5474

Ось воно. Доки предмет не більший за 56 пікселів, маска 28×28 тримає 0.9955. На предметі 112 пікселів вона провалюється до 0.5526: одна клітинка маски накриває чотири пікселі, а зубець завширшки три — зубці зливаються в суцільну пластину. І 56×56 рятує лише на крок, до наступного подвоєння розміру.

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

На наших сценах предмети маленькі — сторона рамки від 3 до 21 пікселя, у середньому 16.7. Тому:

маска 7×7 → IoU 0.9215 маска 14×14 → IoU 0.9927 маска 28×28 → IoU 1.0000 ← стискання немає, є розтягування маска 56×56 → IoU 1.0000

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

Скільки маска втрачає при стисканні

Ліворуч — істинна фігура, праворуч — вона ж після стискання до маски R×R і розтягування назад. Число рахується просто зараз, тим самим двопрохідним білінійним перетворенням, що й у таблицях вище. У таблиці гладких фігур числа усереднені по трьох фігурах, тут — по одній обраній, тож вони не мусять збігатися до знака; таблиця гребінця, навпаки, збігається з цією фігурою клітинка в клітинку.

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

ВирівнюванняRoI-Align: маска і є геометрія

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

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

Поміряймо. Для кожного предмета беремо його рамку, зсунуту на δ пікселів (справжній детектор ніколи не видає цілих координат), витягуємо маску 28×28 двома способами й порівнюємо з еталоном, порахованим руками: значення істинної маски в центрі кожної з 28×28 клітинок.

зсув рамки δRoI-AlignRoI-пулінгрізниця
0.00 px0.96170.8354+0.1263
0.25 px0.95430.8788+0.0755
0.50 px0.95700.7738+0.1832
0.75 px0.95460.8082+0.1464

Читаємо. Колонка RoI-Align рівна: 0.954-0.962 при будь-якому зсуві. Колонка RoI-пулінгу стрибає між 0.77 і 0.88 залежно від того, куди саме округлились координати. Це і є головна ознака квантування: результат залежить не від якості моделі, а від того, чи пощастило рамці лягти близько до цілого числа.

Різниця розкладається на дві частини, і корисно бачити обидві:

Грабля з округленням, яку легко не помітити. У Python round(0.5) дорівнює 0, а не 1: вбудоване округлення банкірське, «до парного». У C, у JavaScript і всередині roi_pool округлення інше — «половина вгору», тобто 1. Половини — це рівно ті значення, що виникають при квантуванні координат, тож своя реалізація на Python і бібліотечна розійдуться саме там, де це найпомітніше. Пиши math.floor(x + 0.5) явно.

Звідки читає кожен спосіб

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

спосіб вибірки
IoU з еталоном—
рамка читання—
Число з таблиці, картинка з тієї самої причини. У RoI-Align сітка вибірки їде разом із рамкою й лишається всередині фігури. У RoI-пулінгу вона стрибає на ціле число й читає з іншого місця — при δ = 0.50 промах найбільший.

ЦінаСкільки коштує маска в Mask R-CNN

Тепер розберемо справжню модель. У torchvision є і детектор, і його версія з масками, тож ціну гілки маски можна дістати прямим відніманням. Обидві збираємо офлайн, без ваг:

fasterrcnn_resnet50_fpn(weights=None, weights_backbone=None) → 41 808 406 maskrcnn_resnet50_fpn (weights=None, weights_backbone=None) → 44 454 513 різниця → 2 646 107

А тепер розкладемо Mask R-CNN на частини:

частинапараметрівчасткащо робить
тіло ResNet-5023 508 03252.9 %перетворює картинку на карти ознак
FPN3 344 3847.5 %зшиває карти різних масштабів
RPN593 9351.3 %пропонує кандидат-області
голова рамок14 362 05532.3 %клас і уточнення рамки
голова масок2 646 1076.0 %маска 28×28 у кожній рамці
усього44 454 513100 %

Три висновки, і жоден не очевидний наперед.

Перший: різниця між двома моделями дорівнює голові масок точно, до одиниці. 44 454 513 − 41 808 406 = 2 646 107, і це рівно mask_head (2 360 320) плюс mask_predictor (285 787). Тобто Mask R-CNN — це буквально Faster R-CNN плюс одна гілка; тіло, FPN, RPN і голова рамок не змінились жодним параметром.

Другий: маска коштує 6.0 % моделі, а рамки — 32.3 %. Голова рамок у 5.4 раза важча за голову масок. Причина в тому, що голова рамок починається з двох повнозвʼязних шарів на 1024 нейрони, а голова масок — суцільно згорткова. Це, до речі, і пояснює, чому маска зберігає геометрію: у ній немає жодного шару, який перемішав би пікселі між собою.

Третій: маска передбачається для кожного класу окремо, і це майже безкоштовно. Останній шар голови масок — Conv2d(256, 91, 1): 91 канал на 91 клас COCO. Кожен клас коштує 257 ваг (256 множень плюс зсув), усі 91 разом — 23 387. З 44 мільйонів це 0.05 %. Для порівняння, у голові рамок клас коштує 5125 ваг — у 20 разів дорожче.

Навіщо маска на кожен клас, якщо клас уже відомий? Щоб розчепити дві задачі. Якби канал був один, той самий вихід мусив би одночасно означати «це кіт» і «ось де межа кота»: сигмоїда однієї маски бореться і за клас, і за форму. Коли каналів багато, класифікацію робить окрема гілка, а кожен канал маски вчиться лише формі свого класу й ніколи не змагається з іншими. У статті це називають розчепленням маски й класу, і саме воно дало головний приріст Mask R-CNN над попередниками. Коштує це, як ми щойно порахували, 0.05 % ваг.

Із чого складається Mask R-CNN

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

модель
усього ваг—
голова масок—
ціна класу в рамках—
ціна класу в масці—
Числа зібрані з named_children() справжньої моделі. Зсунь повзунок класів до одиниці: голова масок схудне на 23 130 ваг, а голова рамок — на 461 250. Клас у масці в 20 разів дешевший за клас у рамці, бо маска — це один канал 28×28, а рамка — чотири числа й повнозвʼязний шар.

Знизу вгоруЗнизу вгору: пікселі голосують за центр

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

що передбачає мережа

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

що робимо з виходом

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

групування

Зводимо голоси в сітку 64×64, злегка розмиваємо й беремо локальні максимуми — це й будуть центри предметів. Далі кожен піксель приписуємо до найближчого до його голосу центра. Скільки центрів — стільки предметів.

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

Мережа — маленький U-Net на два рівні стискання, зі скіпами (тема 30 показала, що при двох рівнях скіпи ще майже нічого не дають, але вони й не коштують). 118 710 параметрів, вісім епох, три зерна.

спосібщільні: mask AP@0.5щільні: AP[.50:.95]рідкі: mask AP@0.5рідкі: AP[.50:.95]
звʼязні плями0.81440.70210.95150.8855
зверху вниз0.8678 ±0.08740.46220.91900.5705
знизу вгору0.9553 ±0.01890.76000.9611 ±0.03840.8355

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

На щільних сценах групування дає 0.9553 проти 0.8678 при розкиді 0.0874 у підходу зверху вниз — тобто виграш рівно на межі розкиду, і саме так це й треба назвати. А от при суворому усередненні по порогах різниця стає безсоромною: 0.7600 проти 0.4622, тобто в 1.6 раза, і жодним розкидом її вже не пояснити.

Звідки такий розрив саме на суворих порогах? Ось причина, і вона пояснює половину теми. Зверху вниз малює маску не на зображенні, а всередині рамки. Маска народжується як клаптик 28×28, потім розтягується під рамку, потім вклеюється. Кожен із цих кроків додає похибку в один-два пікселі на межі — і при порозі 0.5 це нікого не турбує, а при порозі 0.9 зʼїдає все. Знизу вгору працює прямо на пікселях: маска предмета складається з тих самих пікселів, які мережа й підписувала, тому її межа точна за побудовою.

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

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

Три способи на одній сцені

Та сама сцена, три різні способи знайти предмети. Стовпчики — mask AP із наших замірів, по три навчання на кожен навчений спосіб.

спосіб
сцени
mask AP@0.5—
AP[.50:.95]—
розкид по зернах—
Порядок місць міняється разом із порогом. При IoU 0.5 зверху вниз і звʼязні плями майже рівні. При суворому усередненні [.50:.95] звʼязні плями раптом обходять зверху вниз — бо там, де вони не помилились, маска в них точна до пікселя, а зверху вниз щоразу перемальовує маску з клаптика 28×28.

ВибірКоли що працює

Зберімо все в одну таблицю, з якої можна вибирати.

спосіббери, колиламається на
звʼязні плямипредмети фізично не можуть торкатись: клітини на розрідженому мазку, деталі на конвеєрі, будинки на аерознімкубудь-якому дотику двох предметів свого класу
зверху вниз
Mask R-CNN
предмети різнорідні за формою й розміром, є готовий детектор, потрібна проста й передбачувана схемапропущеній рамці — маски не буде; точності межі; сильному перекритті, коли в одну рамку влазять два предмети
знизу вгору
групування
предметів багато, вони дрібні, схожі й торкаються; потрібна точна межапредметах без очевидного центра: довгих, вигнутих, кільцевих, розірваних на частини

І одне практичне правило понад таблицею. Спершу поміряй, скільки предметів у тебе взагалі злипається. Ми почали тему саме з цього числа й не випадково: на рідких сценах усі три способи дають 0.92-0.96 і вибирати майже нема з чого, а на щільних вони розʼїжджаються на 0.14 за AP@0.5 і на 0.30 за AP[.50:.95]. Якщо в твоєму датасеті дотиків 2 %, то instance segmentation тобі, найімовірніше, не потрібна — вистачить семантичної маски й cv2.connectedComponents.

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

Що варто забрати з теми.

І тепер про те, чого ми досі не вміємо. У нас є дві різні відповіді на одну картинку. Семантична мережа з теми 29 добре фарбує фон — небо, дорогу, траву, — але не рахує предметів. Модель цієї теми добре знаходить предмети, але фон її не цікавить зовсім: вона видає список масок, і те, що між ними, лишається нічиїм. Гірше: маски двох предметів можуть перекриватись, і питання «а цей піксель чий?» відповіді не має.

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