U-Net: скіп-зʼєднання й порогова глибина

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

Сегментація мусить відповісти за кожен піксель. Але щоб зрозуміти, що на картинці зображено, мережа стискає її: пулінг за пулінгом, доки від полотна 64 × 64 не лишиться карта 8 × 8. У цій карті багато знання про предмет і майже нічого про його край — один її піксель відповідає квадрату 8 × 8 вхідних. Тема 29 показала два способи жити з цим: не стискати взагалі (атрозні згортки) або розтягувати назад (ConvTranspose2d). Є третій, і він виявився найживучішим: стиснути, а деталі взяти з ранніх шарів, доки їх ще не викинули. Це U-Net. А головне питання теми — не «чи допомагають скіпи», а з якої глибини вони починають вирішувати, бо поки стискання невелике, губити просто нема чого.

ЗалишокЩо лишилось після теми 29

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

що вже єзвідки й що це означає
постановкатема 29: кожному пікселю — клас; вихід мережі має розмір входу
метрикитема 29: mIoU й Dice, звʼязані як Dice = 2·IoU/(1+IoU); на поганих масках Dice майже вдвічі оптимістичніший
дисбаланстема 29: фон зʼїдає близько 90 % полотна, і модель, що фарбує все фоном, дає стільки ж правильних пікселів при mIoU рівно нуль
білінійне збільшеннятема 29: дешево, але нічого не додає — розмиває те, що є
ConvTranspose2dтема 29: навчений спосіб розтягнути карту. Можливостей у неї більше, ніж у білінійного збільшення, але вигадувати деталі з нічого їй так само нема з чого — на сценах теми 29 вона програла навіть йому (0.1673 проти 0.2268)
атрозні згортки й ASPPтема 29: рецептивне поле росте без стискання, ціна — памʼять і час, бо карта лишається великою

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

Ось третя думка, і вона до смішного проста. Інформація нікуди не поділась — вона лежить у ранніх шарах тієї самої мережі. Карта після першого згорткового блоку має повну роздільність 64 × 64. Її просто ніхто не питав. Треба взяти її і подати декодерові прямо, в обхід усього стискання.

ІдеяІдея U-Net

U-Net (2015) — мережа у формі букви U. Ліва половина, енкодер, іде вниз: згортковий блок, пулінг, згортковий блок, пулінг. З кожним кроком карта вдвічі менша, а каналів удвічі більше. Права половина, декодер, іде вгору дзеркально: розтягнути карту вдвічі, обробити, розтягнути ще раз.

Уся новизна — у горизонтальних перемичках. На кожному рівні декодер отримує дві карти: ту, що прийшла знизу, з глибших шарів, і ту, що прийшла збоку, прямо з енкодера того самого рівня. Ця бічна перемичка й зветься скіп-зʼєднанням (skip connection), або скіпом.

Назва не про пропуск шарів. Англійське skip тут означає, що сигнал оминає середину мережі, а не що якийсь шар не працює. Усі шари працюють; просто в декодера зʼявляється друге джерело.

Будова U-Net: що саме йде скіпом

Мережа глибини 3 на нашому полотні 64 × 64, базова ширина 6 каналів. Натисни рівень, щоб побачити розмір його карти, кількість каналів і скільки чисел іде перемичкою в декодер.

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

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

вхід блоку декодера = cat( u , s ) уздовж осі каналів

cat — це конкатенація, тобто «поставити поруч». Якщо в u було 24 канали і в s теж 24, то на вході блоку їх стане 48. Жодне число не змінилось: вони просто лежать в одному стосі, і наступна згортка сама вирішує, кому яку вагу дати.

ЧомуЧому це працює

Щоб зрозуміти, чому перемичка дає так багато, треба побачити, що саме несуть карти на різних глибинах. Вони різні по двох осях одразу.

рання карта (рівень 0)глибока карта (дно)
роздільністьвисока: 64 × 64, піксель у піксельнизька: 8 × 8, один піксель на квадрат 8 × 8
семантикабідна: краї, плями, перепади яскравостібагата: «тут предмет, а не пляма шуму»
рецептивне полевузьке: кілька пікселівшироке: пів картинки

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

Тепер поміряймо, скільки саме губиться при стисканні. Замір не потребує жодного навчання: візьмімо еталонну маску, стиснімо її в 2, 4, 8 і 16 разів (у кожній клітинці лишаємо клас, який займає в ній найбільшу площу), розтягнімо назад і порахуймо mIoU проти оригіналу. Це стеля: краще за неї не зробить навіть ідеальний декодер, який нічого не плутає, — бо решти інформації в карті просто немає.

рівеньстисненнякартаmIoUmIoU на межіточність межі
01 ×64×641.00001.00001.0000
12 ×32×320.93240.80520.8969
24 ×16×160.87530.65470.7996
38 ×8×80.77600.49280.6747
416 ×4×40.61320.38010.5920

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

Два класи, а не чотири. У цій темі мережа фарбує предмет проти фону, а не «коло / квадрат / трикутник». Причина проста: ми міряємо, скільки від межі лишається після стискання, і не хочемо, щоб у цей розрив домішувалась плутанина між колом і квадратом — вона до роздільності стосунку не має. Розрізняти форми вчила тема 29, і її числа з нашими напряму не порівнюються: там mIoU усереднювався по чотирьох класах, у нас по двох.

Що саме зникає при стисканні

Ліворуч — еталонна маска сцени. Праворуч — та сама маска, відновлена лише з карти вибраного рівня. Мережі тут немає взагалі: це чиста арифметика роздільності.

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

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

ПорігПорогова глибина

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

Навіщо три зерна. Одне навчання — це одна випадкова траєкторія. Дві мережі можуть відрізнятись на 0.05 просто тому, що одній пощастило з початковими вагами. Правило курсу жорстке: різниця, менша за розкид по зернах, різницею не є. Тому в кожній таблиці далі поруч із числом стоїть розкид, а не просто число.
глибинапараметрівmIoU зі скіпамиmIoU без скіпіврозриврозкид
1 рівень (2×)3 7460.9969 ±0.0030.9778 ±0.011+0.01910.0112
2 рівні (4×)16 7900.9988 ±0.0010.9466 ±0.029+0.05230.0294
3 рівні (8×)68 5100.9986 ±0.0010.8869 ±0.013+0.11170.0125
4 рівні (16×)274 4780.9983 ±0.0010.7448 ±0.013+0.25350.0129

Перше, що видно: мережа зі скіпами дає майже одне й те саме на будь-якій глибині — від 0.9969 до 0.9988. Тобто глибина сама по собі задачі не псує. Псує її глибина без скіпів: 0.9778, потім 0.9466, потім 0.8869, потім 0.7448. Чим сильніше стискання, тим більше втрачено — і тим гірший результат.

Друге: розрив росте не абияк, а приблизно вдвічі з кожним рівнем: 0.0191, 0.0523, 0.1117, 0.2535. Кожен зайвий пулінг подвоює ціну відсутнього скіпа.

Порогова глибина: де скіпи починають вирішувати

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

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

А тепер — власне порогова глибина. Формальний критерій у нас один: розрив більший за розкид. За ним поріг дорівнює одному рівню: уже при стисканні вдвічі розрив 0.0191 перевищує розкид 0.0112. Тобто на наших сценах скіпи вирішують від самого початку.

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

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

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

глибинастеля (розділ 3)мережа без скіпівзапас над стелеюпадіння стелі від 1 рівня
1 рівень (2×)0.93240.9778+0.0454—
2 рівні (4×)0.87530.9466+0.0713−0.0571
3 рівні (8×)0.77600.8869+0.1109−0.1564
4 рівні (16×)0.61320.7448+0.1316−0.3192

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

РозкидСкіпи стабілізують навчання

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

глибинарозкид зі скіпамирозкид без скіпіву скільки разів більший
1 рівень0.00250.01124.4 ×
2 рівні0.00050.029455.2 ×
3 рівні0.00130.01259.6 ×
4 рівні0.00130.01299.6 ×

⚠️ Останній стовпчик не ділиться з надрукованих: 0.0294 ÷ 0.0005 дає 58.8, а не 55.2. Обидва розкиди тут округлені до чотирьох знаків, а відношення пораховане до округлення — так само, як і решта відношень цієї теми. Дивись на порядок величини, а не на другий знак множника.

Мережа зі скіпами дає практично одне й те саме з будь-якого початкового наближення: розкид від 0.0005 до 0.0025, тобто третій знак після коми. Без скіпів розкид у 4-55 разів більший. Усі вісім чисел зняті на тих самих перевірних сценах 64 × 64 у двох класах «предмет / фон» — це той самий замір, що й у таблиці розділу 4, по три навчання з різних початкових ваг на кожну точку.

Чому «4-55», а не одне число — і чому це не закон. Розмах у чотирнадцять разів усередині самого відношення виглядає підозріло, і має рацію той, хто на ньому спіткнувся. Подивись на два перші стовпчики окремо, і все стане на місце: Множник 55 виходить не тому, що на двох рівнях скіпи стабілізують якось особливо сильно, а тому, що там найбільший чисельник зустрівся з найменшим знаменником: 0.0294 поділити на 0.0005. Досить було б знаменникові вийти 0.0010 замість 0.0005 — і в таблиці стояло б «29 ×» замість «55 ×», хоча про скіпи це не сказало б нічого нового. Тому читати цей стовпчик треба як порядок величини: без скіпів розкид іде в сотих, зі скіпами — у тисячних, і це десь на порядок. Точне значення множника на кожному рядку — не результат.

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

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

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

ЗшиванняКонкатенація проти додавання

Тема 14 уже давала нам скіп — але інший. Залишковий блок ResNet рахує x + F(x): вхід блоку додається до його виходу поелементно. U-Net вхід і вихід зшиває. Різниця здається дрібною, тож розберімо, у чому вона насправді.

крок 1 · додавання

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

вихід[c, y, x] = знизу[c, y, x] + скіп[c, y, x]

Скільки каналів було, стільки й лишилось. Але два джерела тепер злиті: із суми 0.7 не дізнатись, чи це 0.5 + 0.2, чи 0.2 + 0.5.

крок 2 · конкатенація

Карти складають у один стос уздовж осі каналів. Кількість каналів додається; жодне число не змінюється.

вихід[c, y, x] = знизу[c, y, x], якщо c < 24 вихід[c, y, x] = скіп[c − 24, y, x], якщо c ≥ 24

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

крок 3 · ціна

Блок декодера після конкатенації має вдвічі більше вхідних каналів, отже вдвічі більше ваг у першій згортці. У нашій мережі глибини 3 це 68 510 параметрів проти 61 706 — на 11.0 % більше.

Що з цього виходить на практиці? Ми навчили обидва варіанти в однакових умовах, по три зерна.

спосібпараметрівmIoUmIoU на межічас навчання
конкатенація68 5100.9986 ±0.0010.995610.7 с
додавання61 7060.9973 ±0.0010.99189.8 с
без скіпа61 7060.8869 ±0.0130.67429.2 с

Результат несподіваний і вартий того, щоб його назвати прямо: спосіб зшивання майже не важить. Конкатенація виграє 0.0012 при розкиді 0.001 — тобто рівно стільки, скільки шум. На смузі межі перевага трохи виразніша, 0.0038, але й вона мала. А от сам факт скіпа коштує 0.1117 — у сто разів більше.

Зшити, додати чи не давати нічого

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

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

Чому ж усі пишуть U-Net саме через конкатенацію? Три причини, і жодна з них не про якість на іграшкових даних. По-перше, додавання вимагає однакової кількості каналів; в U-Net енкодер і декодер можуть мати різну ширину, і тоді доводиться вставляти згортку 1 × 1 лише заради узгодження. По-друге, конкатенація нічого не втрачає за побудовою — вона не може бути гіршою, бо додавання є її окремим випадком (згортка може навчитись просто скласти). По-третє, на справжніх задачах із десятками каналів і складнішими сценами запас потужності зазвичай окупається. Але на своїх даних це варто перевірити, а не припустити: якщо різниці немає, додавання економить 11 % ваг.

ЯкийЯкі саме скіпи важать

У мережі глибини 3 скіпів три: з рівня 0 (карта 64 × 64), з рівня 1 (32 × 32) і з рівня 2 (16 × 16). Прибираймо їх по одному й дивімось, чого бракує найбільше.

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

що прибралиmIoUmIoU на межівтрата
нічого0.9986 ±0.0010.9956 ±0.004—
скіп рівня 0 (64×64)0.9597 ±0.0330.8781 ±0.101−0.0388
скіп рівня 2 (16×16)0.9979 ±0.0010.9935 ±0.004−0.0007
усі три0.8869 ±0.0130.6742 ±0.026−0.1117

Інтуїція підтвердилась, і з великим запасом. Прибрати найглибший скіп коштує 0.0007 — це менше за розкид, тобто його там могло б і не бути. Прибрати найдрібніший коштує 0.0388, у пʼятдесят пʼять разів більше, а на смузі межі — 0.1175 проти 0.0021.

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

Але «майже нічого» — не «нічого». Зверни увагу на розкид: без найдрібнішого скіпа він підскочив із 0.001 до 0.033, а на межі до 0.101. Тобто мережа не просто стала трохи гіршою — вона стала непередбачуваною. Це той самий ефект, що в розділі 5, і він зʼявляється раніше за помітне падіння середнього.

Прибрати один скіп

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

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

ВмістЩо несе скіп

Досі ми міряли, що буває, коли скіпа немає. Тепер подивімось усередину навченої мережі й порівняймо дві карти, які зустрічаються в кожному блоці декодера: ту, що прийшла збоку скіпом, і ту, що прийшла знизу після ConvTranspose2d. Мережа для цього розбору дала mIoU 0.9995, тобто вона повністю навчена, і дивимось ми на робочі активації.

Чотири числа на кожну карту. Частка ненульових — скільки клітинок узагалі щось кажуть після ReLU. Дисперсія — наскільки карта контрастна. Кореляція з межею — наскільки середня карта активацій збігається з еталонною смугою межі; від −1 до +1. Висока частота — наскільки сильно карта міняється після згладжування 3 × 3, віднесено до величини самої карти; це міра дрібних деталей. Це відносний показник, а не відсоток: одиницю він переступати може й у таблиці нижче переступає (1.028). Читай його як «у скільки разів карта зерниста», порівнюючи рядки між собою, а не як «скільки сигналу зникло».

картарозмірненульовихдисперсіякореляція з межеювисока частота
скіп, рівень 064×640.5920.191+0.3510.194
скіп, рівень 132×320.3520.322+0.4330.146
скіп, рівень 216×160.3670.375+0.5060.191
знизу, на рівень 216×160.5020.213+0.1431.028
знизу, на рівень 132×320.5050.166+0.2960.853
знизу, на рівень 064×640.4820.092−0.0660.964

Порівняй два виділені рядки — це саме та пара карт, яку зшиває останній блок декодера. Скіп із рівня 0 має кореляцію з межею +0.351: він спалахує там, де в масці кордон. Карта, що прийшла знизу на той самий рівень, має −0.066 — тобто про положення межі вона не знає нічого. Дисперсія в неї теж удвічі менша (0.092 проти 0.191): після дворазового розтягування карта стала гладкою й невиразною.

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

⚠️ І не сплутай це з артефактом шахівниці з теми 29. Там візерунок виникав від нерівного покриття, коли kernel_size не ділиться на stride; ядро 2 при кроці 2 ділиться, і покриття в нього якраз рівне — кожна вихідна клітинка дістає рівно один внесок. Причина візерунка тут інша: не скільки разів клітинку накрито, а якою вагою. Цю мережа при бажанні здатна вирівняти навчанням, нерівне покриття — ні.

Два числа впорядковують ті самі карти навпаки

Шість карт із таблиці вище, двічі впорядковані: ліворуч — за тим, наскільки карта знає, де межа, праворуч — за тим, наскільки вона зерниста. Бірюзові — карти зі скіпів, рожеві — те, що приходить знизу після ConvTranspose2d.

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

І ще одна закономірність у стовпці кореляції: у скіпів вона росте з глибиною (0.351 → 0.433 → 0.506). Це не суперечить розділу 7. Глибша карта корелює з межею сильніше, бо в неї грубіша сітка: широка смуга в 16 × 16 накриває край упевненіше, ніж тонка лінія в 64 × 64. Але накриває вона його приблизно — з точністю до чотирьох пікселів. Для середнього mIoU цього досить, для точної межі — ні. Тому найкорисніший скіп усе одно найдрібніший.

МежаМежа — там, де все вирішується

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

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

mx = max_pool2d(маска, 5, крок 1, доповнення 2) mn = −max_pool2d(−маска, 5, крок 1, доповнення 2) смуга = (mx ≠ mn)

Мінімум околу тут дістається тим самим пулінгом: мінімум набору чисел дорівнює мінус максимуму їхніх протилежних значень. Така смуга накриває 12.05 % полотна — приблизно восьму частину.

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

глибинарозрив за загальним mIoUрозрив на смузі межіу скільки разів більший
1 рівень+0.0191+0.05793.04 ×
2 рівні+0.0523+0.15763.02 ×
3 рівні+0.1117+0.32152.88 ×
4 рівні+0.2535+0.55112.17 ×

На смузі межі розрив більший у два-три рази: 3.04, 3.02, 2.88 і 2.17. Це не інша задача й не інша модель — це ті самі мережі, поміряні на тих самих сценах, просто без розчинення. Зверни увагу, що множник не сталий: на трьох перших глибинах він тримається біля трьох, а на четвертій падає до 2.17. Найпростіше прочитання — на глибині 4 мережа без скіпів помиляється вже не тільки на межі, тож «легкі» пікселі перестають бути легкими й розчиняють помилку слабше. Але перевірити це можна лише окремим заміром по зонах, якого ми не робили, тож лишаємо як здогад: надійне тут одне — міряти на межі варто завжди, і що краща мережа, то більше це важить.

Та сама пара мереж, три різні метрики

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

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

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

ТонкеДрібні структури

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

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

Спершу подивись, скільки місця вони займають. Кільця всіх товщин разом — це 1.30 % пікселів; окремо стінка в 1 піксель — 0.30 %. Для порівняння, суцільні фігури займали 10.07 % полотна — тобто корисного сигналу тут майже увосьмеро менше, а фону навколо стільки ж.

товщина стінкичастка пікселівIoU зі скіпамиIoU без скіпіврозрив
1 піксель0.30 %0.7052 ±0.0020.1775 ±0.009+0.5277
2 пікселі0.47 %0.8656 ±0.0010.3439 ±0.024+0.5217
3 пікселі0.54 %0.8385 ±0.0010.4599 ±0.022+0.3786

Порівняй ці розриви з розділом 4. На суцільних фігурах тієї самої глибини розрив був 0.1117. На кільці зі стінкою в 2 пікселі він 0.5217 — майже впʼятеро більший. Мережа без скіпів на найтоншому кільці дає 0.1775: вона його практично не бачить.

Арифметика цього провалу проста, і її можна порахувати на пальцях. Мережа глибини 3 стискає полотно у вісім разів: одна клітинка карти дна відповідає квадрату 8 × 8 вхідних пікселів, тобто 64 пікселям. Скільки з них належить стінці завтовшки 2 пікселі? Смуга завширшки 2 через квадрат 8 × 8 — це близько 16 пікселів, чверть площі. Клітинка, у якій чверть — предмет і три чверті — фон, після взяття переважного класу стає фоном. Кільце зникає ще до того, як декодер узагалі почав працювати.

Кільце: товщина стінки вирішує

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

зі скіпами—
без скіпів—
розрив—
Зверни увагу на ліву колонку табло: навіть зі скіпами кільце в 1 піксель дається гірше (0.7052), ніж у 2 (0.8656). Одному пікселю ніде помилитись: зсув передбачення на один крок обнуляє перетин.

Тепер про втрату. Тема 29 міряла втрати на суцільних фігурах; на тонкій структурі картина інша, бо крос-ентропія дивиться на кожен піксель окремо й легко змиряється з тим, щоб загубити 0.3 % полотна. Dice так не вміє: він рахує перекриття цілої фігури, тож утрата тонкої структури бʼє по ньому одразу.

глибинавтратаIoU предметастінка 1 pxстінка 3 px
2 рівнікрос-ентропія0.9929 ±0.0040.65360.8326
2 рівні+ Dice0.9973 ±0.0020.68870.8375
3 рівнікрос-ентропія0.9973 ±0.0010.68910.8365
3 рівні+ Dice0.9993 ±0.0000.70520.8385

Ефект є, але скромний, і його варто назвати чесно. Додавання Dice дає +0.0351 на найтоншій стінці при глибині 2 і +0.0161 при глибині 3. На товстій стінці (3 пікселі) виграш падає до 0.005-0.002, тобто зникає в розкиді. Тобто Dice допомагає рівно там, де структура найтонша, — і тим менше, чим вона товща. Це логічно: чим більше в предмета нутрощів, тим менше крос-ентропія має шансів його проґавити.

І окремо помітно, що глибина 3 краща за глибину 2 на кільці (0.7052 проти 0.6887 на найтоншій стінці). Це вже не про скіпи: глибша мережа має ширше поле зору й краще розуміє, що перед нею кільце, а не дві дуги. Тобто стискання — не тільки шкода. Воно потрібне; шкода лише в тому, щоб не повернути втрачене.

ОригіналДеталі оригіналу

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

Симетрія енкодера й декодера

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

декодерпараметрівmIoUmIoU на межічас
симетричний68 5100.9986 ±0.0010.9956 ±0.00410.7 с
удвічі тонший51 5780.9968 ±0.0020.9901 ±0.0078.8 с

Параметрів менше на 24.7 %, а mIoU змінився на −0.0018 при розкиді 0.0023. Різниця, менша за розкид, різницею не є: симетрія не обовʼязкова. Це не дрібниця — на справжніх задачах чверть ваг означає чверть памʼяті й помітно швидший прохід. Сучасні реалізації U-Net так і роблять: тонкий декодер, товстий енкодер.

Дзеркальне доповнення на краях

Друга особливість тонша. Оригінальна U-Net працює без доповнення взагалі: кожна згортка 3 × 3 зʼїдає по пікселю з кожного боку, карта меншає, і вихідна маска виходить меншою за вхід (у статті — 388 × 388 із 572 × 572). Щоб дістати маску аж до краю зображення, автори доповнювали вхід дзеркально: відбивали смугу пікселів назовні, як у дзеркалі.

Сучасні реалізації беруть padding='same' із нулями — простіше й маска одразу потрібного розміру. Але нуль на краю означає «тут чорне», а це неправда: там просто нічого не видно. Порівняймо, поглянувши окремо на смугу завширшки 3 пікселі вздовж краю зображення — вона накриває 17.87 % полотна.

доповненняmIoUmIoU на межіточність на краю зображення
нулі (same)0.9986 ±0.0010.99560.9999 ±0.0001
дзеркальне0.9990 ±0.0010.99680.9996 ±0.0001
Чесно: цей замір нічого не довів, і ось чому. Обидва числа на краю — 0.9999 і 0.9996 — це фактично одиниця. Причина в наших даних: генератор ставить центри фігур так, щоб фігура цілком уміщалась у полотно, тож предмети до краю не доходять, і на всій смузі краю правильна відповідь майже завжди «фон». Мережа вгадує її з будь-яким доповненням. Замір без предметів на краю не має влади розрізнити два способи доповнення — це не результат «різниці немає», а результат «ми не мали чим міряти».

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

Що ж лишилось від оригіналу в сучасних U-Net? Форма U, скіпи через конкатенацію й ідея навчання на шматках зображення — усе це на місці, і всі наші мережі теж училися на випадкових шматках 32 × 32, а мірялися на цілих сценах 64 × 64. Пішли: відсутність доповнення (незручно), сувора симетрія (дорого) і зважена втрата з розділенням дотичних клітин — задачу «два предмети поруч» тепер розвʼязують інакше, і про це тема 31.

ДаліДе U-Net сьогодні

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

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

ПідсумокЩо з цього забрати

Коротко, по пунктах.

  1. Скіп — це третій спосіб повернути роздільність. Не «не стискати» і не «розтягнути назад», а «взяти деталі там, де вони ще цілі».
  2. Скіпи потрібні тоді, коли є що втрачати. Порогова глибина — вимірювана величина, і на своїх даних її треба міряти, а не вгадувати.
  3. Скіпи зшивають, а не додають. Конкатенація лишає обидва сигнали окремими; додавання з теми 14 їх змішує.
  4. Середня метрика приховує ефект. Міряй окремо на смузі межі — там живе вся різниця.
  5. Скіпи роблять навчання передбачуванішим. Це окремий виграш, який видно тільки при кількох зернах.

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

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