Попередня тема зібрала Vision Transformer і чесно назвала два місця, де він тріщить. Перше: увага рахує вагу для кожної пари токенів, тому ціна росте як квадрат їхньої кількості. Друге: роздільність у ViT не міняється від першого блоку до останнього — жодної піраміди, жодного пулінга. Цікаво, що обидві втрати — це рівно те, що згортка мала задарма. Ця тема про те, як обидві речі повернули назад, не викидаючи увагу.
ДіагнозЩо лишилось не так
Почнемо з чисел, які вже стоять у курсі, щоб не вигадувати проблему на порожньому місці.
Вада перша: ціна квадратична
У темі 19 ми порахували, у що обходиться увага на зображенні 28×28. Патч 2×2 дає 196 токенів, а 196 × 196 — це 38 416 пар. Кожну пару треба не просто «врахувати», а порахувати: взяти вектор запиту й вектор ключа й перемножити їх поелементно. Довжина цих векторів у темі 19 — 64 числа на одну голову уваги, тож пара коштує 64 множення, а весь шар:
Два з половиною мільйони множень на одному-єдиному шарі — для картинки завбільшки з долоню.
Сама по собі квадратична залежність — не вирок. Вирок починається там, де задача вимагає високої роздільності. Класифікації досить сітки 7×7: питання «що на фото» не потребує знати, де саме проходить межа предмета. А сегментація має пофарбувати кожен піксель, детекція — поставити рамку з точністю до пікселів. Обом потрібна густа сітка ознак, тобто багато токенів.
Порахуємо, у що це виливається. Беремо фотографію 224×224 і патч 4×4 — сітка виходить 56×56, тобто 3 136 токенів. Пар уваги: 3 136 × 3 136 = 9 834 496. Майже десять мільйонів пар на кожному шарі, а шарів дванадцять. Подвоїш сторону зображення — кількість токенів зросте вчетверо, а кількість пар у шістнадцять разів.
Вада друга: немає ієрархії
Згорткова мережа влаштована як лійка. У темі 08 ми додали пулінг, який ділить сторону карти навпіл, а в темі 09 сформулювали правило подвоєння: зменшив сторону вдвічі — подвой кількість каналів. Виходить послідовність рівнів, де перший бачить краї на дрібній сітці, а останній — цілі предмети на грубій.
ViT цього не робить взагалі. Усі дванадцять блоків ViT-B/16 працюють з однією й тією самою сіткою 14×14. Роздільність на вході й на виході однакова, каналів скрізь 768. Це не помилка реалізації, це властивість задуму: трансформер для тексту теж не міняє довжину послідовності від шару до шару.
Для класифікації воно й не заважає. Але детектори й сегментатори будуються на ознаках кількох масштабів одразу: дрібна сітка ловить маленькі предмети, груба — великі. Мережа, яка віддає лише один рівень, для них незручна.
Swin Transformer (від shifted windows — «зсунуті вікна») повертає обидві речі. Увага лишається головною операцією, але їй ставлять два обмеження: дивитись тільки всередині невеликого вікна й час від часу згрублювати сітку. Далі — по одному обмеженню за раз.
ВікнаУвага всередині вікна
Ідея настільки проста, що її можна описати одним реченням: поділимо сітку токенів на квадратні вікна w×w і рахуватимемо увагу окремо в кожному вікні. Токен із лівого верхнього вікна не бачить токенів із правого нижнього — просто тому, що вони не потрапили в одну матрицю уваги.
Тепер порахуємо ціну. Це той рідкісний випадок, коли вся суть теми вміщується в дві формули з двох множників.
Кожен з n токенів рахує вагу до кожного з n токенів. Пар виходить n × n.
Одне вікно вміщує w × w = w2 токенів. Щоб покрити всі n токенів, потрібно n ділити на w2 вікон.
Усередині вікна працює звичайна повна увага, тільки над w2 токенами. Отже, пар у вікні — квадрат цього числа, тобто w4.
Пар усього — це кількість вікон помножена на кількість пар у кожному. Два множники частково скорочуються, і лишається просто добуток.
Прочитай останній рядок повільно. Зліва в повній увазі стояло n2 — n множилось саме на себе. Тепер n множиться на w2, а w — це стала, її обирає розробник і не міняє при зміні розміру зображення. Отже, ціна перестала бути квадратичною за кількістю токенів і стала лінійною: подвоїв кількість токенів — подвоїв ціну, а не вчетверив.
Числа, які це показують. Вікно скрізь 7×7, патч 4×4, тобто саме те, що бере справжній Swin.
| зображення | сітка | токенів | повна увага | вікна 7×7 | вікон | дешевше |
|---|---|---|---|---|---|---|
| 28×28 | 7×7 | 49 | 2 401 | 2 401 | 1 | — |
| 56×56 | 14×14 | 196 | 38 416 | 9 604 | 4 | 4× |
| 112×112 | 28×28 | 784 | 614 656 | 38 416 | 16 | 16× |
| 224×224 | 56×56 | 3 136 | 9 834 496 | 153 664 | 64 | 64× |
| 448×448 | 112×112 | 12 544 | 157 351 936 | 614 656 | 256 | 256× |
Перший рядок таблиці — наша навчальна картинка 28×28 з патчем 4. Сітка виходить 7×7, тобто рівно одне вікно 7×7, і віконна увага збігається з повною до останнього біта. Це не збіг обставин, а корисна перевірка будь-якої реалізації віконної уваги: постав вікно на всю сітку — і результат мусить збігтися зі звичайною увагою. Не «майже», а точно: найбільша різниця між двома виходами дорівнює 0.0, бо це буквально той самий обчислювальний шлях, записаний іншими словами.
Другий висновок таблиці ще простіший: на маленьких зображеннях вікна не дають нічого. Виграш зʼявляється рівно тоді, коли токенів більше, ніж уміщує одне вікно, і росте разом із роздільністю. На 224×224 це вже 64 рази.
Ціна: повна увага проти вікон
Повзунок міняє роздільність зображення. Дві криві на логарифмічній шкалі: скільки пар уваги рахує один шар при повній увазі й при вікнах 7×7. Дивись не на самі числа, а на нахил: обидві осі логарифмічні, тому квадратична залежність лягає прямою рівно вдвічі крутішою за лінійну.
Тепер про сам розмір вікна. Це та сама ручка компромісу, що й розмір патча в темі 20, тільки з іншого боку. Велике вікно — ближче до повної уваги: більше видно, дорожче рахувати. Мале вікно — дешево, але токен майже нічого не бачить навколо себе.
Сітка вікон і лічильник пар
Сітка 28×28 токенів (це зображення 112×112 з патчем 4). Повзунок міняє розмір вікна. Лічильники показують окремо, скільки пар усередині одного вікна, і скільки їх виходить на всю сітку.
ПроблемаВікна не спілкуються
Тепер найважливіше місце теми. Вікна зробили увагу дешевою — і одночасно розрізали зображення на незалежні шматки, які нічого не знають одне про одного. Причому це не «майже не знають», а рівно нічого, і довести це можна без жодного навчання.
Дослід такий. Беремо сітку 14×14 токенів і вікно 7×7 — виходить чотири вікна. Беремо два випадкові набори токенів, які відрізняються одним-єдиним токеном у лівому верхньому вікні. Проганяємо обидва набори крізь один шар віконної уваги з однаковими вагами й дивимось, у скількох токенах виходи розійшлися.
Сорок девʼять — це рівно розмір одного вікна. Змінений токен вплинув на всіх своїх сусідів по вікну й ні на кого більше. Різниця в трьох інших вікнах дорівнює нулю — не «дуже маленькому числу», а нулю, бо ці числа взагалі не брали участі в тому самому обчисленні.
Далі найцікавіше. Додаємо другий шар, третій, четвертий — і нічого не міняється.
| шарів віконної уваги | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| зачеплено токенів із 196 | 49 | 49 | 49 | 49 |
Скільки шарів не став — інформація не вийде за межу вікна ніколи. У згортковій мережі так не буває: там рецептивне поле росте з кожним шаром (тема 09), і за тринадцять шарів ядро 3×3 доростає до 27×27. Тут воно не росте зовсім. Мережа з віконної уваги без жодної добавки — це чотири окремі маленькі мережі, склеєні скотчем.
Хто з ким може обмінятись інформацією
Сітка 14×14 токенів. Рожевим позначено токен, який ми змінили; бірюзовим — усі токени, на які зміна вже вплинула. Додавай шари й перемикай режим. У режимі «звичайні» бірюзова пляма не росте ніколи.
ЛікуванняЗсунуті вікна
Розвʼязок, який дали автори Swin, теж уміщується в одне речення: у сусідньому шарі зсунемо всю сітку вікон на пів вікна по діагоналі. Межі опиняться в інших місцях, і токени, які були по різні боки старої межі, тепер лежать в одному вікні.
Для вікна 7 зсув дорівнює 7 ÷ 2 = 3 (ділимо націло). Шари чергуються: непарний — звичайні вікна, парний — зсунуті, і так до кінця стадії. Саме тому блоки в Swin завжди йдуть парами: блок зі зсувом сам по собі нічого не дає, працює саме чергування.
Подивимось, що робить цей зсув з різами, бо саме в них уся справа. Вікно ріже не картинку, а нумерацію: у звичайному шарі на сітці 14×14 рядки розрізані по лінії між 6 і 7, тому вони діляться на дві смуги — 0–6 і 7–13. Те саме зі стовпцями. Вікно — це перетин смуги рядків зі смугою стовпців, тому вікон чотири.
Зсув на 3 пересуває обидва рази на три клітинки: тепер вони проходять після рядка 2 і після рядка 9. Смуг стає три — 0–2, 3–9, 10–13, — і це головне, що взагалі відбувається у Swin:
Візьми пару сусідів, розрізану старою межею: токени в стовпцях 6 і 7. У звичайному шарі вони в різних вікнах і не бачать одне одного взагалі. У зсунутому обидва потрапляють у смугу 3–9 — тобто в одне вікно, і вага між ними рахується. Ось що саме полагодив зсув: не «збільшив огляд», а переніс різи так, щоб те, що вчора було розрізане, сьогодні виявилось усередині.
Тепер той самий дослід із чергуванням.
| шарів | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| звичайні вікна | 49 | 49 | 49 | 49 |
| чергування зі зсунутими | 49 | 100 | 196 | 196 |
| усі шари зсунуті однаково | 49 | 49 | 49 | 49 |
Числа 49, 100 і 196 не треба запамʼятовувати — вони виводяться зі смуг, які ми щойно виписали. Змінений токен стоїть у рядку 3, стовпці 6, тобто на самому краю лівого верхнього вікна.
Токен (3, 6) лежить у смузі рядків 0–6 і в смузі стовпців 0–6. Увага всередині вікна змішує його з усіма, хто там-таки, — і ні з ким іншим.
Тепер зачеплені вже 49 токенів, і вони займають рядки 0–6. У зсунутій нарізці це дві смуги: 0–2 і 3–9. Зі стовпцями так само. Отже зачеплені токени сидять у чотирьох зсунутих вікнах, і кожне з них розносить різницю на всі свої токени — тобто на цілі смуги 0–2 і 3–9.
Смуга 10–13 не зачеплена: серед перших 49 токенів не було жодного з рядків 10–13.
Сто зачеплених токенів займають рядки 0–9, а це вже обидві звичайні смуги — 0–6 і 7–13. Зі стовпцями так само, тож задіяні всі чотири звичайні вікна, а разом вони й є вся сітка.
Три шари, і крайній лівий верхній токен уже впливає на крайній правий нижній — при вікні, яке дивиться лише на 7 клітинок. Порівняй із згорткою 3×3: там інформація повзе рівно на одну клітинку за шар, тож із лівого верхнього кута сітки 14×14 у правий нижній вона дійшла б за 13 шарів — те саме число, що в розділі 3. Обмежена увага не програє в поширенні: вона розносить зміну стрибками завширшки з вікно, а не по клітинці.
Дві технічні деталі, без яких воно не працює
Зсув створює проблему на краях. Якщо просто посунути сітку вікон на три клітинки, з країв вилізуть неповні вікна: замість чотирьох вікон 7×7 вийде девʼять шматків різного розміру — 7×7, 7×4, 7×3, 4×4 і так далі. Рахувати увагу для девʼяти різних форм незручно й повільно.
- Циклічний зсув. Замість того щоб рухати межі вікон, рухають самі дані:
беруть усю сітку й прокручують її на три клітинки вгору й уліво, як барабан.
Те, що вилізло згори, підклеюється знизу. Тепер сітку знову можна різати на
рівні вікна 7×7, і їх знову рівно чотири. У коді це один
рядок —
torch.roll. - Маска. Прокручування підклеїло верхній край до нижнього — а це протилежні кінці зображення, вони не повинні бачити одне одного. Тому в матрицю уваги додають маску: пари, які зійшлися лише через прокручування, забороняють. Технічно це означає підставити −∞ перед softmax, і після softmax така пара дістає рівно нульову вагу.
Скільки пар доводиться заборонити на сітці 14×14 при вікні 7 і зсуві 3:
| вікно після зсуву | заблоковано пар | із | частка |
|---|---|---|---|
| ліве верхнє | 0 | 2 401 | 0 % |
| праве верхнє | 1 176 | 2 401 | 49 % |
| ліве нижнє | 1 176 | 2 401 | 49 % |
| праве нижнє | 1 776 | 2 401 | 74 % |
Ліве верхнє вікно після прокручування зібралось із суцільного шматка сітки — блокувати нема чого. Праве нижнє склеїлось із чотирьох різних кутів зображення, тому в ньому дозволено лише чверть пар. Загальний рахунок такий: маска ріже частину роботи, але не міняє ані форми тензорів, ані кількості вікон — а саме це й потрібно, щоб операція лишалась швидкою.
Зсув по кроках
Найскладніша механіка теми, розкладена на чотири кроки. Тисни «наступний крок» і дивись, що відбувається з сіткою 14×14: звичайні вікна → зсунуті межі → циклічний зсув даних → маска. Кожен крок пояснено підписом під картинкою.
ПірамідаІєрархія: обʼєднання патчів
Друга вада ViT — пласка роздільність. Лікують її прийомом, який ти вже знаєш під іншою назвою.
Обʼєднання патчів (patch merging) робить із чотирьох сусідніх токенів один. Механіка буквальна:
- Беремо квадрат 2×2 сусідніх токенів. У кожного вектор довжини C.
- Зчіплюємо чотири вектори в один — виходить вектор довжини 4C. Нічого не втрачено: усі числа на місці, просто складені в стовпчик.
- Пропускаємо крізь LayerNorm і лінійний шар 4C → 2C. Ось тут половина чисел і зникає — але зникає навчено, а не за правилом «бери найбільше», як у пулінгу.
Сторона сітки поділилась навпіл, кількість токенів упала вчетверо, а каналів стало вдвічі більше. Це дослівно правило подвоєння з теми 09, тільки замість пулінга й згортки з кроком 2 стоїть зчеплення й лінійний шар. Ідея та сама, механізм інший.
Тепер порахуємо форми наскрізь для справжнього Swin-T на зображенні 224×224. Патч 4×4, початкова розмірність 96, чотири стадії.
| стадія | сітка | каналів | токенів | чисел у карті | пар: вікна 7 | пар: повна увага |
|---|---|---|---|---|---|---|
| 1 | 56×56 | 96 | 3 136 | 301 056 | 153 664 | 9 834 496 |
| 2 | 28×28 | 192 | 784 | 150 528 | 38 416 | 614 656 |
| 3 | 14×14 | 384 | 196 | 75 264 | 9 604 | 38 416 |
| 4 | 7×7 | 768 | 49 | 37 632 | 2 401 | 2 401 |
Порівняй із ResNet із теми 14, який працює з тим самим входом 224×224:
| рівень | ResNet: сітка | каналів | Swin-T: сітка | каналів |
|---|---|---|---|---|
| 1 | 56×56 | 64 | 56×56 | 96 |
| 2 | 28×28 | 128 | 28×28 | 192 |
| 3 | 14×14 | 256 | 14×14 | 384 |
| 4 | 7×7 | 512 | 7×7 | 768 |
Роздільності збігаються повністю, канали більші рівно в півтора раза. Це не випадковість, а свідоме рішення: Swin будували так, щоб його можна було вставити замість ResNet у будь-який готовий детектор чи сегментатор без переробки всього іншого. Про наслідки — у розділі 9.
Зверни увагу й на останній стовпчик попередньої таблиці. На четвертій стадії сітка 7×7 — це рівно одне вікно, і віконна увага там знову перетворюється на повну. Тобто Swin обмежує увагу вікном лише там, де це справді дорого, а на глибоких стадіях спокійно дивиться на всю карту.
Скільки додавати каналів при кожному обʼєднанні
Чотири стадії Swin. Роздільність падає вдвічі завжди — а от у скільки разів рости каналам, вирішує розробник. Покрути множник і подивись на дві речі: як худне карта ознак і у що обходяться самі шари обʼєднання. Пунктиром — ResNet для порівняння.
bias=False — вектора зсуву в цього лінійного шару
немає.ГеометріяВідносне позиційне зміщення
У темі 20 ми бачили, що увага не знає порядку, і геометрію їй доводиться повідомляти окремо. ViT робить це абсолютним позиційним кодуванням: до кожного токена один раз на вході додається вектор, який означає «я токен номер 37». У ViT-B/16 це 151 296 ваг.
Swin так не може, і на те дві причини.
- Вікна зсуваються. Один і той самий токен у сусідніх шарах опиняється на різних місцях усередині свого вікна. Абсолютний номер токена в кадрі вікну нічого не каже.
- Усередині вікна важлива не адреса, а відстань. Питання, на яке відповідає увага, звучить не «ти токен номер 37?», а «ти мій сусід зверху чи через три клітинки праворуч?». Це відносна геометрія.
Тому Swin додає до оцінки пари (тобто до числа перед softmax) окреме навчене число, яке залежить лише від того, на скільки клітинок один токен зсунутий відносно іншого.
bias у нейроні в цьому курсі
зветься «зсув». Тут ми свідомо кажемо зміщення: слово «зсув» у цій темі
вже намертво зайняте зсунутими вікнами, і два різні «зсуви» в одному абзаці
зробили б текст нечитабельним. Зустрінеш у коді relative_position_bias_table
— це воно.Тут q і k — запит і ключ із
теми 19: два вектори, які
модель робить із токена, щоб порівняти його з іншим. Крапка між ними — скалярний
добуток, тобто «наскільки вони схожі». d — довжина цих векторів в
одній голові; у Swin-T вона скрізь 32 (96 ÷ 3, 192 ÷ 6, 384 ÷ 12,
768 ÷ 24 — розмірності й голови стоять у таблиці нижче). Ділення
на √d — той самий масштаб, що й у темі 19: без нього довгі вектори
дають надто великі числа й softmax стає майже одиничним. B — таблиця
зміщень. Δрядок — різниця номерів рядків двох токенів усередині
вікна, Δстовпець — те саме для стовпців.
Порахуємо розмір таблиці руками. Вікно 7×7. Різниця рядків може бути від −6 до +6 — це 13 різних значень, тобто 2×7−1. Так само 13 значень для стовпців. Разом 13 × 13 = 169 можливих взаємних положень. По одному числу на кожне положення й на кожну голову уваги.
Варто побачити, звідки береться економія. Усередині вікна 7×7 пар рівно 49 × 49 = 2 401, а різних взаємних положень — лише 169. Отже, багато пар ділять між собою одне й те саме число з таблиці: пара «сусід праворуч» трапляється у вікні сорок два рази, а пара «протилежні кути» — один-єдиний.
2 401 пара, 169 чисел
Два повзунки задають взаємне положення двох токенів: на скільки рядків і на скільки стовпців один зсунутий відносно іншого. Ліворуч — вікно 7×7 з однією такою парою, праворуч — таблиця B, у якій ця пара бере своє число. Дивись на лічильник «таких пар у вікні».
| стадія | розмірність | голів | таблиця зміщень |
|---|---|---|---|
| 1 | 96 | 3 | 169 × 3 = 507 |
| 2 | 192 | 6 | 169 × 6 = 1 014 |
| 3 | 384 | 12 | 169 × 12 = 2 028 |
| 4 | 768 | 24 | 169 × 24 = 4 056 |
| усього по всіх блоках | 23 322 |
Двадцять три тисячі ваг на всю геометрію мережі — це 0.08 % від 28 288 354. Порівняй: абсолютне кодування для самої лише першої стадії, де сітка 56×56 і 96 каналів, коштувало б 301 056 ваг — у тринадцять разів більше, ніж уся відносна геометрія Swin разом узята.
РозкладРозбір справжнього Swin-T
Тепер зберемо swin_t із torchvision і подивимось,
де саме сидять його ваги. Як і в темі 20, будуємо з weights=None —
нічого не завантажується, це чиста арифметика будови.
| частина | ваг | частка | що там усередині |
|---|---|---|---|
| проєкція патча | 4 896 | 0.0 % | Conv2d(3, 96, ядро 4, крок 4) + LayerNorm |
| стадія 1 · 2 блоки | 224 694 | 0.8 % | сітка 56×56, dim 96 |
| обʼєднання 1→2 | 74 496 | 0.3 % | 384 → 192 |
| стадія 2 · 2 блоки | 891 756 | 3.2 % | сітка 28×28, dim 192 |
| обʼєднання 2→3 | 296 448 | 1.0 % | 768 → 384 |
| стадія 3 · 6 блоків | 10 658 952 | 37.7 % | сітка 14×14, dim 384 |
| обʼєднання 3→4 | 1 182 720 | 4.2 % | 1536 → 768 |
| стадія 4 · 2 блоки | 14 183 856 | 50.1 % | сітка 7×7, dim 768 |
| фінальна норма | 1 536 | 0.0 % | LayerNorm(768) |
| класифікатор | 769 000 | 2.7 % | 768 → 1000 класів |
| усього | 28 288 354 | 100 % |
Перше, що впадає в око: 87.8 % ваг сидять у двох останніх стадіях, де токенів найменше. Це не парадокс, а пряме продовження правила подвоєння: чим глибше, тим товщі вектори, а ваги ростуть як квадрат розмірності. Стадія 4 має лише 49 токенів і половину всіх ваг мережі.
Тепер розкладемо самі блоки — за призначенням, а не за стадіями:
| що | ваг | частка від усієї мережі |
|---|---|---|
| віконна увага | 8 667 162 | 30.6 % |
| · з них відносне зміщення | 23 322 | 0.08 % |
| MLP усередині блоків | 17 274 432 | 61.1 % |
| LayerNorm усередині блоків | 17 664 | 0.06 % |
| разом у блоках | 25 959 258 | 91.8 % |
І тут головна несподіванка розділу. Увага — не найдорожча частина
трансформера. На неї припадає 30.6 % ваг, а на звичайні поточкові
MLP-шари — вдвічі більше, 61.1 %. І це не особливість Swin — те саме
випливає прямо з формул для будь-якого блоку трансформера. Увага має чотири
матриці d×d (запити, ключі, значення й вихід), тобто 4d2
ваг. MLP має дві матриці d×4d, тобто 8d2 — рівно вдвічі
більше. Підстав d = 384 нашого блоку стадії 3: 4 × 3842
= 589 824 проти 8 × 3842 = 1 179 648, і в таблиці вище
стоять саме ці числа плюс вектори зсуву (bias). Трансформер, який усі памʼятають як «мережу з уваги», за вагою
переважно складається з простих лінійних перетворень каналів.
Розберемо ще й один блок стадії 3, щоб було видно кожне число:
| частина блоку (dim 384) | ваг | звідки |
|---|---|---|
| LayerNorm перед увагою | 768 | 2 × 384 |
| qkv | 443 520 | 384 × 1152 + 1152 |
| вихідна проєкція уваги | 147 840 | 384 × 384 + 384 |
| відносне зміщення | 2 028 | 169 × 12 голів |
| LayerNorm перед MLP | 768 | 2 × 384 |
| MLP 384 → 1536 → 384 | 1 181 568 | у чотири рази ширший усередині |
| один блок | 1 776 492 |
І порівняння трьох моделей одного покоління, усі на 224×224 і 1000 класів:
| модель | ваг | увага | роздільність усередині |
|---|---|---|---|
| vit_b_16 | 86 567 656 | повна, на всі 197 токенів | 14×14 від початку до кінця |
| swin_t | 28 288 354 | у вікнах 7×7, зі зсувом | 56 → 28 → 14 → 7 |
| convnext_tiny | 28 589 128 | немає взагалі | 56 → 28 → 14 → 7 |
Swin утричі легший за ViT-B/16 — і не тому, що хтось урізав розмірності, а тому, що піраміда сама по собі економить: більшість токенів живе на стадіях, де вектор короткий. Про третій рядок цієї таблиці — окремий розділ, і це найцікавіша частина теми.
ЗамірТри моделі на нашій задачі
Теорія каже, що вікна дешевші за повну увагу. Перевіримо це на нашій задачі — шість фігур 28×28, шум 0.45, центр гуляє на ±5 пікселів. Той самий датасет, що в блоках 2 і 3.
Щоб порівняння було чесним за мірками теми 18, дві перші моделі відрізняються рівно одним: розміром вікна. Патч 2×2 дає сітку 14×14, тобто 196 токенів. Перша модель рахує увагу на всіх 196 одразу, друга — у вікнах 7×7 із чергуванням зсуву. Усе інше — розмірність, кількість блоків, голови, MLP, позиційне кодування, рецепт навчання — однакове. Тому й ваг у них однаково, до одиниці. Третя модель — маленька згорткова мережа, підігнана під той самий розмір.
Рецепт: AdamW, швидкість навчання 0.003, чотирнадцять епох, 1000 прикладів на навчання, 600 на перевірку. Три зерна на кожну точку. Рецепт однаковий для всіх трьох — саме цього вимагає тема 18.
| модель | ваг | точність | розкид | час: вільна машина | час: машина зайнята | пар уваги на шар |
|---|---|---|---|---|---|---|
| повна увага | 23 782 | 0.361 | ±0.032 | 47.1 с | 46.7 с | 38 416 |
| вікна 7×7 зі зсувом | 23 782 | 0.436 | ±0.012 | 26.0 с | 38.5 с | 9 604 |
| згортка | 21 430 | 0.907 | ±0.018 | 7.9 с | 10.6 с | — |
Читаємо результат чесно, а не так, як хотілося б.
Вікна виявились не гіршими, а кращими. 0.436 проти 0.361 при однаковій кількості ваг. Різниця — 0.075, а найбільший розкид від зерна серед цих двох моделей — ±0.032, тобто різниця більша за розкид, і за правилом теми 18 її можна називати різницею.
Звучить це дивно: вікно бачить менше, а виходить краще. Найімовірніше пояснення таке саме, як у темі 20 з позиційним кодуванням. На тисячі прикладів повна увага мусить вивчити з даних, що сусідні патчі важливіші за далекі. Вікно отримує це припущення задарма, разом із будовою. Обмеження виявилось підказкою — рівно так само, як локальність згортки була підказкою в темі 06.
Вікна швидші. 26.0 с проти 47.1 с при однаковій кількості ваг — майже вдвічі. Параметри ті самі, а обчислень менше: замість однієї матриці уваги 196×196 рахуються чотири по 49×49, тобто 9 604 елементи замість 38 416.
Згортка виграє з великим відривом і швидша за всіх. Точність 0.907 проти 0.436 при меншій кількості ваг і за 7.9 секунди — утричі швидше за віконну модель і в шість разів за повну увагу. Це той самий висновок, що в темі 20: на тисячі прикладів жоден трансформер згортку не наздожене, і від зміни різновиду уваги це не міняється.
Три моделі однакового розміру
Перемикай, що показувати: точність, час чи ваги. Для точності всі три прогони показані окремими рисками просто на стовпчику — видно, наскільки результат хиткий. Для часу рисок немає: там у таблиці середнє з трьох прогонів, а самі секунди залежать від машини.
НаслідокЩо це дало галузі
Найважливіше в Swin — не приріст точності на ImageNet, а те, що після нього трансформер став придатним на роль backbone.
Термін уже був у темі 15: backbone — це частина детектора чи сегментатора, яка просто перетворює зображення на карти ознак; голови зверху вже вирішують конкретну задачу. Класичні детектори вимагають від backbone чотирьох рівнів ознак різного масштабу — і саме тому ViT їм не підходив: у нього рівень один.
У Swin рівні є, і роздільності в них ті самі, що в ResNet: 56, 28, 14, 7. Практичний наслідок буквальний — щоб перевести детектор із ResNet на Swin, достатньо підмінити backbone. Решта архітектури лишається.
Звідси й повна назва роботи: «ієрархічний трансформер зору». Слово «ієрархічний» тут головніше за «трансформер».
РозворотConvNeXt: згортка за рецептом трансформера
Тепер найцікавіше в темі. Поки одні дослідники повертали в трансформер локальність та ієрархію, інші поставили зворотне питання: а що, як причина переваги трансформерів узагалі не в увазі?
Дослід був такий. Узяли ResNet-50, нічого в ньому концептуально не міняючи, і почали крок за кроком приміряти на нього все, що прийшло разом із трансформерами: спершу сучасний рецепт навчання, потім по одній архітектурній дрібниці. Після кожного кроку міряли точність на ImageNet. Результат назвали ConvNeXt.
Спершу — арифметика будови, її ми поміряли самі:
| модель | ваг | стем | блоків по стадіях | каналів |
|---|---|---|---|---|
| swin_t | 28 288 354 | Conv2d 4×4, крок 4 → 4 896 ваг | 2, 2, 6, 2 | 96, 192, 384, 768 |
| convnext_tiny | 28 589 128 | Conv2d 4×4, крок 4 → 4 896 ваг | 3, 3, 9, 3 | 96, 192, 384, 768 |
| resnet50 | 25 557 032 | Conv2d 7×7, крок 2 | 3, 4, 6, 3 | 256, 512, 1024, 2048 |
Різниця в розмірі між Swin-T і ConvNeXt-T — 300 774 ваги, тобто 1.1 %. Стем збігається до одиниці: обидві мережі починають із згортки 4×4 з кроком 4, і в обох це 4 896 ваг. Тільки в Swin ця операція зветься «проєкція патча», а в ConvNeXt — «patchify stem». Це буквально той самий шар під двома іменами.
Що ConvNeXt узяла в трансформера
Розберемо блок ConvNeXt. Ось він повністю, для першої стадії:
Порівняй із блоком трансформера з теми 20: норма, операція змішування, норма, MLP «вужче → ширше вчетверо → вужче». Форма та сама. Різниця одна: на місці уваги стоїть згортка по глибині з ядром 7×7.
| елемент | звідки | що було в ResNet |
|---|---|---|
| Ядро 7×7 | від трансформера | 3×3 — Swin дивиться у вікно 7×7, ядро 7×7 накриває стільки ж |
| GELU замість ReLU | від трансформера | ReLU |
| LayerNorm замість BatchNorm | від трансформера | BatchNorm після кожної згортки |
| Одна активація на блок | від трансформера | три ReLU у блоці |
| Співвідношення блоків 3, 3, 9, 3 | від Swin (2, 2, 6, 2) | 3, 4, 6, 3 |
| Стем 4×4 з кроком 4 | від ViT | згортка 7×7 з кроком 2 і пулінг |
| Згортка по глибині | своє, від MobileNet | звичайна 3×3 — див. тему 15 |
| Залишковий звʼязок | своє, від ResNet | він самий — тема 14 |
Кількість шарів нормалізації та активації ми порахували прямо: у блоці ConvNeXt їх разом два — один LayerNorm і один GELU. У блоці ResNet-50 таких модулів пʼять, причому ReLU застосовується тричі. Мережа стала помітно «тихішою».
Скільки в ConvNeXt лишилось від згортки
Питання не риторичне, на нього є число. Розкладемо ваги
convnext_tiny за типом шару:
| тип шару | штук | ваг | частка |
|---|---|---|---|
| Linear (поточкові) | 37 | 26 680 648 | 93.3 % |
| Conv2d (усі) | 22 | 1 885 536 | 6.6 % |
| · з них по глибині, 7×7 | 18 | 331 200 | 1.2 % |
| LayerNorm | 23 | 16 320 | 0.06 % |
| масштаб шару (layer scale) | 18 | 6 624 | 0.02 % |
| усього | 28 589 128 | 100 % |
Ось вона, головна цифра розділу. У мережі, яка вважається згортковою, усе просторове змішування — тобто всі шари, що взагалі дивляться на сусідів, — важить 331 200 ваг, або 1.2 %. Решта 93 % — поточкові перетворення каналів, які в трансформері звуться MLP.
Порівняй із ResNet-50, де 53 згортки тримають 23 454 912 ваг, тобто 91.8 % усієї мережі. Це два зовсім різні способи витратити однаковий бюджет параметрів.
І тепер той самий погляд на Swin: увага 30.6 %, MLP 61.1 %. Обидві мережі — і ConvNeXt, і Swin — переважно складаються з поточкових лінійних шарів, а відрізняються тим, як саме змішують сусідів: вікном уваги чи ядром 7×7 згортки по глибині. І займає це в обох випадках меншість ваг.
Що дав кожен крок — числа з літератури
Тепер результати самого дослідження. Ці числа взяті з опублікованої роботи, а не заміряні нами — одне навчання ResNet-50 на ImageNet займає дні на кількох відеокартах, і повторити його в межах курсу неможливо. Точність на ImageNet-1K, зображення 224×224:
| крок | точність | приріст |
|---|---|---|
| ResNet-50 як є | 76.1 | — |
| той самий ResNet-50, сучасний рецепт навчання | 78.8 | +2.7 |
| співвідношення блоків 3, 3, 9, 3 | 79.4 | +0.6 |
| стем 4×4 з кроком 4 | 79.5 | +0.1 |
| згортка по глибині, каналів 64 → 96 | 80.5 | +1.0 |
| перевернуте пляшкове горло | 80.6 | +0.1 |
| згортку по глибині підняли на початок блоку | 79.9 | −0.7 |
| ядро 7×7 замість 3×3 | 80.6 | +0.7 |
| ReLU → GELU | 80.6 | +0.0 |
| менше активацій | 81.3 | +0.7 |
| менше нормалізацій | 81.4 | +0.1 |
| BatchNorm → LayerNorm | 81.5 | +0.1 |
| окремі шари зменшення | 82.0 | +0.5 |
| Swin-T для порівняння | 81.3 |
Два спостереження, і обидва варті окремої уваги.
Найбільший стрибок дав рецепт, а не архітектура. +2.7 бали з загальних +5.9 — це половина всього шляху, і вона отримана без жодної зміни в мережі. Той самий ResNet-50, який у 2015-му дав 76.1, у 2022-му дає 78.8 — просто тому, що його навчали довше, іншим оптимізатором і з іншими аугментаціями.
Це прямо продовжує тему 18, де ми поміряли те саме на своїй задачі: зміна рецепта дала 0.467 і 0.367, а зміна архітектури — 0.083 і 0.017, тобто у 5.6 раза менше в першій парі й у 22 рази — у другій. Різні дані, різний масштаб, той самий висновок.
Заміна ReLU на GELU не дала нічого. Рівно нуль. А це, мабуть, найчастіша «модернізація», яку копіюють у чужі мережі, бо «так у трансформерах». Корисною вона стала лише разом із наступним кроком — прибрати зайві активації, — і ось там уже +0.7.
ConvNeXt: що звідки взялося
Перемикай елемент — і дивись, що саме він міняє у блоці порівняно з ResNet, а також скільки цей крок дав за опублікованими числами. Одна зміна дає рівно нуль; знайди її.
Головна думка цього розділу не «згортка перемогла» і не «увага не потрібна». Вона така: частина переваги трансформерів була рецептом, а не архітектурою. Коли згортковій мережі дали той самий рецепт і кілька ідей про будову блоку, вона наздогнала й перегнала — при тому самому бюджеті ваг і тій самій швидкості.
СумішіГібриди взагалі
Swin і ConvNeXt — два крайні випадки. Між ними живе ціле сімейство мереж, які беруть від згортки й від уваги по шматку. Три найпоширеніші прийоми, коротко.
Згортковий стем перед трансформером
Перше, що ламається в чистому ViT, — початок. Проєкція патча ріже зображення одним грубим кроком 16×16 і одразу викидає всю дрібну структуру. Заміна цього шару на кілька звичайних згорток 3×3 із кроком 2 дає ту саму роздільність на виході, але з поступовим згрубленням. Мережа після цього навчається стійкіше й терпить ширший діапазон швидкостей навчання.
Причина зрозуміла з теми 20: локальність на перших шарах — правда про світ, і вивчати її з даних невигідно, якщо можна вписати в будову.
Згортка всередині MLP-блоку
MLP у блоці трансформера обробляє кожен токен окремо й нічого не знає про сусідів. Прийом: вставити між двома лінійними шарами згортку по глибині з ядром 3×3. Ваг вона додає копійки (для dim 384 і ядра 3×3 це 3 456 чисел), а токен дістає ще один канал звʼязку з сусідами — незалежний від уваги.
Помітно, що це зустрічний рух: ConvNeXt зробила згортковий блок схожим на трансформерний, а тут трансформерний блок отримує згортку всередину.
Увага лише на глибоких стадіях
Найпрагматичніший гібрид. Перші дві стадії — звичайні згортки, останні дві — блоки уваги. Логіка проста й видно її просто з наших таблиць: на першій стадії 3 136 токенів і увага там найдорожча, зате саме там потрібні прості локальні ознаки, з якими згортка чудово впорається. На четвертій стадії токенів 49, увага майже безкоштовна, а користь від «кожен бачить кожного» найбільша.
Саме за цим принципом побудована більшість мереж, які сьогодні стоять у мобільних застосунках: там бюджет обчислень жорсткий, і платити за увагу на високій роздільності ніхто не готовий.
ВибірЩо обирати на практиці
Тепер коротко й по суті. Спираємось на числа з цієї теми й на тему 18.
| ситуація | що брати | чому |
|---|---|---|
| Своя невелика задача, тисячі прикладів, немає передтренованих ваг | звичайна CNN | 0.907 проти 0.436 у нашому замірі, і втричі швидше. Трансформеру нема з чого вивчити локальність |
| Класифікація, є передтреновані ваги, важлива швидкість | ConvNeXt або сучасна CNN | та сама точність за той самий бюджет, простіший код, менше сюрпризів із налаштуванням |
| Детекція чи сегментація, потрібні ознаки кількох масштабів | Swin або ConvNeXt | обидві дають чотири рівні з роздільностями ResNet і вставляються замість нього |
| Дуже великі дані або передтренування на мільярдах зображень | ViT | найменше вбудованих припущень, тому найбільше виграє від масштабу — див. тему 20 |
| Потрібні ознаки, сумісні з текстом | ViT | про це — наступна тема |
| Дуже високі роздільності, щільне передбачення | Swin або гібрид | лінійна ціна замість квадратичної: на 448×448 різниця вже 256-кратна |
І одне правило поверх усієї таблиці, здобуте в темі 18 й підтверджене тут числами ConvNeXt: перш ніж міняти архітектуру, полагодь рецепт. Половина шляху від ResNet-50 до ConvNeXt пройдена без жодної зміни в мережі.
ПідсумокПідсумок і місток далі
Що ми зібрали:
- Увага у вікнах робить ціну лінійною. Замість n2 пар виходить n×w2, де w — стала. На 224×224 це 153 664 пари замість 9 834 496, тобто в 64 рази дешевше.
- Вікна самі по собі розрізають зображення. Доведено без навчання: зміна одного токена зачіпає рівно 49 із 196 і скільки шарів не став — так і лишається 49.
- Лікує це чергування зі зсунутими вікнами, а не зсув як такий. Із чергуванням: 49 → 100 → 196. Якщо зсунути всі шари однаково — знову 49, 49, 49.
- Обʼєднання патчів будує піраміду 56 → 28 → 14 → 7 із подвоєнням каналів — ті самі роздільності, що в ResNet, тому Swin можна поставити в детектор замість нього.
- Геометрія коштує 0.08 %. Відносне зміщення для вікна 7×7 — це 169 чисел на голову, разом 23 322 ваги на всю мережу.
- Увага — не найдорожча частина трансформера. У swin_t на неї припадає 30.6 % ваг, а на MLP — 61.1 %.
- На малій задачі вікна виявились кращими за повну увагу — 0.436 проти 0.361 при однакових вагах, і рахуються вони вчетверо дешевше (9 604 елементи матриці уваги замість 38 416) — а згортка перемогла обидві з великим відривом (0.907).
- ConvNeXt важить майже стільки ж, скільки Swin (28 589 128 проти 28 288 354), має той самий стем до одиниці, і в ній усе просторове змішування — 1.2 % ваг.
- Половина шляху від ResNet-50 до ConvNeXt — це рецепт навчання, +2.7 бали з +5.9, без жодної зміни в мережі.
І тепер про те, що обʼєднує геть усі мережі цього блоку — ViT, Swin, ConvNeXt, і заразом усі мережі блоків 2 і 3. Кожна з них навчена на розмічених зображеннях: хтось узяв мільйон фотографій і до кожної приписав номер класу з наперед складеного списку. Уся сила цих моделей упирається в цей список: мережа знає рівно ті тисячу понять, які були в розмітці, і жодного більше.
Розмітка дорога, список завжди короткий, а нове поняття вимагає нової розмітки й нового навчання. У темі 16 ми вже пробували обійтися без міток — і брали нагляд із самого зображення.
Наступна тема, 22 «CLIP і мультимодальність», бере зовсім інше джерело нагляду: текст, який і так лежить поруч із фотографією в інтернеті. Підпис, назва файлу, alt-текст. Ніхто його не складав спеціально для навчання — і саме тому його мільярди. Побачимо, що з цим можна зробити і чому мережа, навчена так, раптом уміє впізнавати класи, яких їй ніхто не показував.