Swin і гібриди

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

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

ДіагнозЩо лишилось не так

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

Вада перша: ціна квадратична

У темі 19 ми порахували, у що обходиться увага на зображенні 28×28. Патч 2×2 дає 196 токенів, а 196 × 196 — це 38 416 пар. Кожну пару треба не просто «врахувати», а порахувати: взяти вектор запиту й вектор ключа й перемножити їх поелементно. Довжина цих векторів у темі 19 — 64 числа на одну голову уваги, тож пара коштує 64 множення, а весь шар:

38 416 пар × 64 = 2 458 624 множення на одну голову

Два з половиною мільйони множень на одному-єдиному шарі — для картинки завбільшки з долоню.

Сама по собі квадратична залежність — не вирок. Вирок починається там, де задача вимагає високої роздільності. Класифікації досить сітки 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. Це не помилка реалізації, це властивість задуму: трансформер для тексту теж не міняє довжину послідовності від шару до шару.

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

Одна причина на дві вади. Обидві проблеми ростуть з одного кореня — ViT відмовився від локальності. Локальність не лише вносила в мережу правильне припущення про світ, вона ще й обмежувала обчислення: ядро 3×3 дивиться на девʼять сусідів незалежно від розміру картинки, тому ціна згортки лінійна за кількістю пікселів. І саме локальність робила осмисленим пулінг: якщо звʼязки й так короткі, згрубити сітку не страшно.

Swin Transformer (від shifted windows — «зсунуті вікна») повертає обидві речі. Увага лишається головною операцією, але їй ставлять два обмеження: дивитись тільки всередині невеликого вікна й час від часу згрублювати сітку. Далі — по одному обмеженню за раз.

ВікнаУвага всередині вікна

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

Тепер порахуємо ціну. Це той рідкісний випадок, коли вся суть теми вміщується в дві формули з двох множників.

Крок 1 · Повна увага

Кожен з n токенів рахує вагу до кожного з n токенів. Пар виходить n × n.

пар = n2
Крок 2 · Скільки вікон

Одне вікно вміщує w × w = w2 токенів. Щоб покрити всі n токенів, потрібно n ділити на w2 вікон.

вікон = n ÷ w2
Крок 3 · Скільки пар в одному вікні

Усередині вікна працює звичайна повна увага, тільки над w2 токенами. Отже, пар у вікні — квадрат цього числа, тобто w4.

пар у вікні = (w2)2 = w4
Крок 4 · Множимо

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

пар = (n ÷ w2) × w4 = n × w2

Прочитай останній рядок повільно. Зліва в повній увазі стояло n2 — n множилось саме на себе. Тепер n множиться на w2, а w — це стала, її обирає розробник і не міняє при зміні розміру зображення. Отже, ціна перестала бути квадратичною за кількістю токенів і стала лінійною: подвоїв кількість токенів — подвоїв ціну, а не вчетверив.

Числа, які це показують. Вікно скрізь 7×7, патч 4×4, тобто саме те, що бере справжній Swin.

зображеннясіткатокенівповна увагавікна 7×7вікондешевше
28×287×7492 4012 4011—
56×5614×1419638 4169 60444×
112×11228×28784614 65638 4161616×
224×22456×563 1369 834 496153 6646464×
448×448112×11212 544157 351 936614 656256256×

Перший рядок таблиці — наша навчальна картинка 28×28 з патчем 4. Сітка виходить 7×7, тобто рівно одне вікно 7×7, і віконна увага збігається з повною до останнього біта. Це не збіг обставин, а корисна перевірка будь-якої реалізації віконної уваги: постав вікно на всю сітку — і результат мусить збігтися зі звичайною увагою. Не «майже», а точно: найбільша різниця між двома виходами дорівнює 0.0, бо це буквально той самий обчислювальний шлях, записаний іншими словами.

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

Ціна: повна увага проти вікон

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

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

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

Сітка вікон і лічильник пар

Сітка 28×28 токенів (це зображення 112×112 з патчем 4). Повзунок міняє розмір вікна. Лічильники показують окремо, скільки пар усередині одного вікна, і скільки їх виходить на всю сітку.

вікон—
токенів у вікні—
пар в одному вікні—
пар усього—
Сітка 28×28 = 784 токени. Повна увага на ній коштує 614 656 пар — це значення відповідає вікну 28. Справжній Swin бере вікно 7 в усіх чотирьох стадіях.

ПроблемаВікна не спілкуються

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

Дослід такий. Беремо сітку 14×14 токенів і вікно 7×7 — виходить чотири вікна. Беремо два випадкові набори токенів, які відрізняються одним-єдиним токеном у лівому верхньому вікні. Проганяємо обидва набори крізь один шар віконної уваги з однаковими вагами й дивимось, у скількох токенах виходи розійшлися.

змінили 1 токен із 196 → на виході змінилось 49 токенів

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

Далі найцікавіше. Додаємо другий шар, третій, четвертий — і нічого не міняється.

шарів віконної уваги1234
зачеплено токенів із 19649494949

Скільки шарів не став — інформація не вийде за межу вікна ніколи. У згортковій мережі так не буває: там рецептивне поле росте з кожним шаром (тема 09), і за тринадцять шарів ядро 3×3 доростає до 27×27. Тут воно не росте зовсім. Мережа з віконної уваги без жодної добавки — це чотири окремі маленькі мережі, склеєні скотчем.

Хто з ким може обмінятись інформацією

Сітка 14×14 токенів. Рожевим позначено токен, який ми змінили; бірюзовим — усі токени, на які зміна вже вплинула. Додавай шари й перемикай режим. У режимі «звичайні» бірюзова пляма не росте ніколи.

режим вікон
зачеплено токенів—
частка сітки—
Змінений токен — рядок 3, стовпець 6, тобто край лівого верхнього вікна. Числа 49 · 100 · 196 не намальовані, а заміряні: два набори токенів, що відрізняються одним числом, проганяють крізь той самий шар уваги й рахують, у скількох виходах зʼявилась різниця.

ЛікуванняЗсунуті вікна

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

Для вікна 7 зсув дорівнює 7 ÷ 2 = 3 (ділимо націло). Шари чергуються: непарний — звичайні вікна, парний — зсунуті, і так до кінця стадії. Саме тому блоки в Swin завжди йдуть парами: блок зі зсувом сам по собі нічого не дає, працює саме чергування.

Подивимось, що робить цей зсув з різами, бо саме в них уся справа. Вікно ріже не картинку, а нумерацію: у звичайному шарі на сітці 14×14 рядки розрізані по лінії між 6 і 7, тому вони діляться на дві смуги — 0–6 і 7–13. Те саме зі стовпцями. Вікно — це перетин смуги рядків зі смугою стовпців, тому вікон чотири.

Зсув на 3 пересуває обидва рази на три клітинки: тепер вони проходять після рядка 2 і після рядка 9. Смуг стає три — 0–2, 3–9, 10–13, — і це головне, що взагалі відбувається у Swin:

звичайний шар: 0–6 | 7–13 зсунутий шар: 0–2 | 3–9 | 10–13

Візьми пару сусідів, розрізану старою межею: токени в стовпцях 6 і 7. У звичайному шарі вони в різних вікнах і не бачать одне одного взагалі. У зсунутому обидва потрапляють у смугу 3–9 — тобто в одне вікно, і вага між ними рахується. Ось що саме полагодив зсув: не «збільшив огляд», а переніс різи так, щоб те, що вчора було розрізане, сьогодні виявилось усередині.

Перевір це на собі. Легко подумати, що справа у зсуві як такому. Ні. Якщо зсунути усі шари однаково, розбиття на вікна знову буде однаковим у всіх шарах — і замір дасть ті самі 49 токенів назавжди. Ми це перевірили: «усі шари зсунуті» дає 49, 49, 49, 49. Працює тільки різниця між сусідніми шарами.

Тепер той самий дослід із чергуванням.

шарів1234
звичайні вікна49494949
чергування зі зсунутими49100196196
усі шари зсунуті однаково49494949

Числа 49, 100 і 196 не треба запамʼятовувати — вони виводяться зі смуг, які ми щойно виписали. Змінений токен стоїть у рядку 3, стовпці 6, тобто на самому краю лівого верхнього вікна.

Шар 1 · звичайні вікна → 49

Токен (3, 6) лежить у смузі рядків 0–6 і в смузі стовпців 0–6. Увага всередині вікна змішує його з усіма, хто там-таки, — і ні з ким іншим.

7 рядків × 7 стовпців = 49 токенів
Шар 2 · зсунуті вікна → 100

Тепер зачеплені вже 49 токенів, і вони займають рядки 0–6. У зсунутій нарізці це дві смуги: 0–2 і 3–9. Зі стовпцями так само. Отже зачеплені токени сидять у чотирьох зсунутих вікнах, і кожне з них розносить різницю на всі свої токени — тобто на цілі смуги 0–2 і 3–9.

рядки 0–9 × стовпці 0–9 = 10 × 10 = 100 токенів

Смуга 10–13 не зачеплена: серед перших 49 токенів не було жодного з рядків 10–13.

Шар 3 · знову звичайні → 196

Сто зачеплених токенів займають рядки 0–9, а це вже обидві звичайні смуги — 0–6 і 7–13. Зі стовпцями так само, тож задіяні всі чотири звичайні вікна, а разом вони й є вся сітка.

14 × 14 = 196 токенів — усе

Три шари, і крайній лівий верхній токен уже впливає на крайній правий нижній — при вікні, яке дивиться лише на 7 клітинок. Порівняй із згорткою 3×3: там інформація повзе рівно на одну клітинку за шар, тож із лівого верхнього кута сітки 14×14 у правий нижній вона дійшла б за 13 шарів — те саме число, що в розділі 3. Обмежена увага не програє в поширенні: вона розносить зміну стрибками завширшки з вікно, а не по клітинці.

Дві технічні деталі, без яких воно не працює

Зсув створює проблему на краях. Якщо просто посунути сітку вікон на три клітинки, з країв вилізуть неповні вікна: замість чотирьох вікон 7×7 вийде девʼять шматків різного розміру — 7×7, 7×4, 7×3, 4×4 і так далі. Рахувати увагу для девʼяти різних форм незручно й повільно.

  1. Циклічний зсув. Замість того щоб рухати межі вікон, рухають самі дані: беруть усю сітку й прокручують її на три клітинки вгору й уліво, як барабан. Те, що вилізло згори, підклеюється знизу. Тепер сітку знову можна різати на рівні вікна 7×7, і їх знову рівно чотири. У коді це один рядок — torch.roll.
  2. Маска. Прокручування підклеїло верхній край до нижнього — а це протилежні кінці зображення, вони не повинні бачити одне одного. Тому в матрицю уваги додають маску: пари, які зійшлися лише через прокручування, забороняють. Технічно це означає підставити −∞ перед softmax, і після softmax така пара дістає рівно нульову вагу.

Скільки пар доводиться заборонити на сітці 14×14 при вікні 7 і зсуві 3:

вікно після зсувузаблоковано парізчастка
ліве верхнє02 4010 %
праве верхнє1 1762 40149 %
ліве нижнє1 1762 40149 %
праве нижнє1 7762 40174 %

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

Зсув по кроках

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

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

ПірамідаІєрархія: обʼєднання патчів

Друга вада ViT — пласка роздільність. Лікують її прийомом, який ти вже знаєш під іншою назвою.

Обʼєднання патчів (patch merging) робить із чотирьох сусідніх токенів один. Механіка буквальна:

  1. Беремо квадрат 2×2 сусідніх токенів. У кожного вектор довжини C.
  2. Зчіплюємо чотири вектори в один — виходить вектор довжини 4C. Нічого не втрачено: усі числа на місці, просто складені в стовпчик.
  3. Пропускаємо крізь LayerNorm і лінійний шар 4C → 2C. Ось тут половина чисел і зникає — але зникає навчено, а не за правилом «бери найбільше», як у пулінгу.
сітка 56×56, C=96  →  сітка 28×28, C=192

Сторона сітки поділилась навпіл, кількість токенів упала вчетверо, а каналів стало вдвічі більше. Це дослівно правило подвоєння з теми 09, тільки замість пулінга й згортки з кроком 2 стоїть зчеплення й лінійний шар. Ідея та сама, механізм інший.

Тепер порахуємо форми наскрізь для справжнього Swin-T на зображенні 224×224. Патч 4×4, початкова розмірність 96, чотири стадії.

стадіясіткаканалівтокенівчисел у картіпар: вікна 7пар: повна увага
156×56963 136301 056153 6649 834 496
228×28192784150 52838 416614 656
314×1438419675 2649 60438 416
47×77684937 6322 4012 401

Порівняй із ResNet із теми 14, який працює з тим самим входом 224×224:

рівеньResNet: сіткаканалівSwin-T: сіткаканалів
156×566456×5696
228×2812828×28192
314×1425614×14384
47×75127×7768

Роздільності збігаються повністю, канали більші рівно в півтора раза. Це не випадковість, а свідоме рішення: Swin будували так, щоб його можна було вставити замість ResNet у будь-який готовий детектор чи сегментатор без переробки всього іншого. Про наслідки — у розділі 9.

Зверни увагу й на останній стовпчик попередньої таблиці. На четвертій стадії сітка 7×7 — це рівно одне вікно, і віконна увага там знову перетворюється на повну. Тобто Swin обмежує увагу вікном лише там, де це справді дорого, а на глибоких стадіях спокійно дивиться на всю карту.

Скільки додавати каналів при кожному обʼєднанні

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

каналів на стадії 4—
чисел у карті стадії 4—
ваг у трьох шарах обʼєднання—
Один шар обʼєднання коштує 8C ваг на LayerNorm плюс 4C×kC на лінійний шар без зсуву, де C — каналів до обʼєднання, k — множник. Для порівняння: уся мережа swin_t важить 28 288 354 ваги. «Без зсуву» тут означає bias=False — вектора зсуву в цього лінійного шару немає.

ГеометріяВідносне позиційне зміщення

У темі 20 ми бачили, що увага не знає порядку, і геометрію їй доводиться повідомляти окремо. ViT робить це абсолютним позиційним кодуванням: до кожного токена один раз на вході додається вектор, який означає «я токен номер 37». У ViT-B/16 це 151 296 ваг.

Swin так не може, і на те дві причини.

  1. Вікна зсуваються. Один і той самий токен у сусідніх шарах опиняється на різних місцях усередині свого вікна. Абсолютний номер токена в кадрі вікну нічого не каже.
  2. Усередині вікна важлива не адреса, а відстань. Питання, на яке відповідає увага, звучить не «ти токен номер 37?», а «ти мій сусід зверху чи через три клітинки праворуч?». Це відносна геометрія.

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

Про слово «зміщення». В оригіналі це relative position bias, а bias у нейроні в цьому курсі зветься «зсув». Тут ми свідомо кажемо зміщення: слово «зсув» у цій темі вже намертво зайняте зсунутими вікнами, і два різні «зсуви» в одному абзаці зробили б текст нечитабельним. Зустрінеш у коді relative_position_bias_table — це воно.
оцінка(i, j) = (qi · kj) ÷ √d  +  B[Δрядок, Δстовпець]

Тут 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, у якій ця пара бере своє число. Дивись на лічильник «таких пар у вікні».

таких пар у вікні—
клітинка B—
чисел у таблиці—
Кількість пар із заданим зсувом — це (7 − |Δрядок|) × (7 − |Δстовпець|). Складіть її по всіх 169 клітинках — вийде рівно 2 401, тобто всі пари вікна розкладені без залишку. Таблиця B однакова для всіх вікон стадії й для всіх зображень: вона описує геометрію, а не картинку.
стадіярозмірністьголівтаблиця зміщень
1963169 × 3 = 507
21926169 × 6 = 1 014
338412169 × 12 = 2 028
476824169 × 24 = 4 056
усього по всіх блоках23 322

Двадцять три тисячі ваг на всю геометрію мережі — це 0.08 % від 28 288 354. Порівняй: абсолютне кодування для самої лише першої стадії, де сітка 56×56 і 96 каналів, коштувало б 301 056 ваг — у тринадцять разів більше, ніж уся відносна геометрія Swin разом узята.

Побічна вигода, яку легко не помітити. Відносне зміщення дає мережі терпимість до зсуву задарма. Пересунь предмет на пʼять пікселів — абсолютні номери токенів зміняться всі, а взаємні відстані всередині вікна лишаться ті самі, і таблиця B дасть ті самі числа. Це та сама властивість, за яку ми любимо згортку (тема 07), тільки здобута іншим шляхом.

РозкладРозбір справжнього Swin-T

Тепер зберемо swin_t із torchvision і подивимось, де саме сидять його ваги. Як і в темі 20, будуємо з weights=None — нічого не завантажується, це чиста арифметика будови.

частинавагчасткащо там усередині
проєкція патча4 8960.0 %Conv2d(3, 96, ядро 4, крок 4) + LayerNorm
стадія 1 · 2 блоки224 6940.8 %сітка 56×56, dim 96
обʼєднання 1→274 4960.3 %384 → 192
стадія 2 · 2 блоки891 7563.2 %сітка 28×28, dim 192
обʼєднання 2→3296 4481.0 %768 → 384
стадія 3 · 6 блоків10 658 95237.7 %сітка 14×14, dim 384
обʼєднання 3→41 182 7204.2 %1536 → 768
стадія 4 · 2 блоки14 183 85650.1 %сітка 7×7, dim 768
фінальна норма1 5360.0 %LayerNorm(768)
класифікатор769 0002.7 %768 → 1000 класів
усього28 288 354100 %

Перше, що впадає в око: 87.8 % ваг сидять у двох останніх стадіях, де токенів найменше. Це не парадокс, а пряме продовження правила подвоєння: чим глибше, тим товщі вектори, а ваги ростуть як квадрат розмірності. Стадія 4 має лише 49 токенів і половину всіх ваг мережі.

Тепер розкладемо самі блоки — за призначенням, а не за стадіями:

щовагчастка від усієї мережі
віконна увага8 667 16230.6 %
· з них відносне зміщення23 3220.08 %
MLP усередині блоків17 274 43261.1 %
LayerNorm усередині блоків17 6640.06 %
разом у блоках25 959 25891.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). Трансформер, який усі памʼятають як «мережу з уваги», за вагою переважно складається з простих лінійних перетворень каналів.

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

Розберемо ще й один блок стадії 3, щоб було видно кожне число:

частина блоку (dim 384)вагзвідки
LayerNorm перед увагою7682 × 384
qkv443 520384 × 1152 + 1152
вихідна проєкція уваги147 840384 × 384 + 384
відносне зміщення2 028169 × 12 голів
LayerNorm перед MLP7682 × 384
MLP 384 → 1536 → 3841 181 568у чотири рази ширший усередині
один блок1 776 492

І порівняння трьох моделей одного покоління, усі на 224×224 і 1000 класів:

модельвагувагароздільність усередині
vit_b_1686 567 656повна, на всі 197 токенів14×14 від початку до кінця
swin_t28 288 354у вікнах 7×7, зі зсувом56 → 28 → 14 → 7
convnext_tiny28 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 7820.361±0.03247.1 с46.7 с38 416
вікна 7×7 зі зсувом23 7820.436±0.01226.0 с38.5 с9 604
згортка21 4300.907±0.0187.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.361, 0.436, 0.907, — бо вони задані зернами й від завантаження машини не залежать зовсім. А секунди попливли: коли прогін ішов паралельно з іншою важкою задачею, виграш вікон скоротився з 1.8 раза до 1.2. Третій прогін, на вільнішій машині, дав 47.3 / 28.2 / 8.2 с, тобто знову 1.7 раза. Отже, надійне число тут — 9 604 проти 38 416 елементів матриці уваги, тобто рівно вчетверо; секунди його лише підтверджують, і то приблизно. У себе ти побачиш ще інші секунди — це нормально, і саме тому в курсі ми намагаємось зводити твердження до величини, яку можна порахувати, а не заміряти секундоміром.

Згортка виграє з великим відривом і швидша за всіх. Точність 0.907 проти 0.436 при меншій кількості ваг і за 7.9 секунди — утричі швидше за віконну модель і в шість разів за повну увагу. Це той самий висновок, що в темі 20: на тисячі прикладів жоден трансформер згортку не наздожене, і від зміни різновиду уваги це не міняється.

Що тут не доведено. Із цього заміру не випливає, що Swin кращий за ViT узагалі. Наша сітка — 14×14 токенів, тобто чотири вікна; справжній Swin працює з 3 136 токенами й 64 вікнами, і саме там його виграш у ціні стає вирішальним. Ми поміряли інше й скромніше: на маленькій задачі обмеження уваги вікном не шкодить точності й помітно економить час.

Три моделі однакового розміру

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

що показати
найкраща—
відрив—
найбільший розкид від зерна—
Три зерна на точку, AdamW, чотирнадцять епох, 1000 прикладів на навчання. Рівень випадкового вгадування на шести класах — 0.167. Час заміряний в один потік на чотирьох ядрах без відеокарти; на іншій машині секунди будуть іншими, стале тут — співвідношення, і то приблизно.

НаслідокЩо це дало галузі

Найважливіше в Swin — не приріст точності на ImageNet, а те, що після нього трансформер став придатним на роль backbone.

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

У Swin рівні є, і роздільності в них ті самі, що в ResNet: 56, 28, 14, 7. Практичний наслідок буквальний — щоб перевести детектор із ResNet на Swin, достатньо підмінити backbone. Решта архітектури лишається.

Звідси й повна назва роботи: «ієрархічний трансформер зору». Слово «ієрархічний» тут головніше за «трансформер».

Місток уперед. Блок 5 цього курсу — про детекцію, блок 6 — про сегментацію. Там ми будемо збирати мережу з backbone, «шиї» й голови, і питання «що поставити в backbone» стане робочим. Swin і ConvNeXt із цієї теми — саме ті відповіді, які там знадобляться.

РозворотConvNeXt: згортка за рецептом трансформера

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

Дослід був такий. Узяли ResNet-50, нічого в ньому концептуально не міняючи, і почали крок за кроком приміряти на нього все, що прийшло разом із трансформерами: спершу сучасний рецепт навчання, потім по одній архітектурній дрібниці. Після кожного кроку міряли точність на ImageNet. Результат назвали ConvNeXt.

Спершу — арифметика будови, її ми поміряли самі:

модельвагстемблоків по стадіяхканалів
swin_t28 288 354Conv2d 4×4, крок 4 → 4 896 ваг2, 2, 6, 296, 192, 384, 768
convnext_tiny28 589 128Conv2d 4×4, крок 4 → 4 896 ваг3, 3, 9, 396, 192, 384, 768
resnet5025 557 032Conv2d 7×7, крок 23, 4, 6, 3256, 512, 1024, 2048

Різниця в розмірі між Swin-T і ConvNeXt-T — 300 774 ваги, тобто 1.1 %. Стем збігається до одиниці: обидві мережі починають із згортки 4×4 з кроком 4, і в обох це 4 896 ваг. Тільки в Swin ця операція зветься «проєкція патча», а в ConvNeXt — «patchify stem». Це буквально той самий шар під двома іменами.

Що ConvNeXt узяла в трансформера

Розберемо блок ConvNeXt. Ось він повністю, для першої стадії:

Conv2d(96 → 96, ядро 7×7, груп 96) ← згортка по глибині, змішує сусідів LayerNorm(96) Linear(96 → 384) ← у чотири рази ширше GELU Linear(384 → 96)

Порівняй із блоком трансформера з теми 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 (поточкові)3726 680 64893.3 %
Conv2d (усі)221 885 5366.6 %
· з них по глибині, 7×718331 2001.2 %
LayerNorm2316 3200.06 %
масштаб шару (layer scale)186 6240.02 %
усього28 589 128100 %

Ось вона, головна цифра розділу. У мережі, яка вважається згортковою, усе просторове змішування — тобто всі шари, що взагалі дивляться на сусідів, — важить 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, 379.4+0.6
стем 4×4 з кроком 479.5+0.1
згортка по глибині, каналів 64 → 9680.5+1.0
перевернуте пляшкове горло80.6+0.1
згортку по глибині підняли на початок блоку79.9−0.7
ядро 7×7 замість 3×380.6+0.7
ReLU → GELU80.6+0.0
менше активацій81.3+0.7
менше нормалізацій81.4+0.1
BatchNorm → LayerNorm81.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, а також скільки цей крок дав за опублікованими числами. Одна зміна дає рівно нуль; знайди її.

що змінили
приріст—
точність після—
звідки взято—
Точності — з опублікованої роботи про ConvNeXt, ImageNet-1K, 224×224. У перемикачі сім кроків із дванадцяти, що є в таблиці вище, — тому «було» одного кроку не завжди дорівнює «стало» попереднього. Ліворуч блок ResNet-50, праворуч блок ConvNeXt; підсвічено те, що міняє обраний крок.

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

СумішіГібриди взагалі

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 пройдена без жодної зміни в мережі.

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

Що ми зібрали:

І тепер про те, що обʼєднує геть усі мережі цього блоку — ViT, Swin, ConvNeXt, і заразом усі мережі блоків 2 і 3. Кожна з них навчена на розмічених зображеннях: хтось узяв мільйон фотографій і до кожної приписав номер класу з наперед складеного списку. Уся сила цих моделей упирається в цей список: мережа знає рівно ті тисячу понять, які були в розмітці, і жодного більше.

Розмітка дорога, список завжди короткий, а нове поняття вимагає нової розмітки й нового навчання. У темі 16 ми вже пробували обійтися без міток — і брали нагляд із самого зображення.

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