Vision Transformer

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

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

ІдеяСмілива ідея

Щоб оцінити зухвалість цього кроку, треба згадати, як виглядав компʼютерний зір до 2020 року. Вісімнадцять тем цього курсу — і майже кожна стоїть на згортці. Згортка не просто «якось працює»: вона вносить у мережу три припущення про світ, і всі три для зображень правдиві.

Припущення 1 · Локальність

Сусідні пікселі повʼязані сильніше, ніж далекі. Ядро 3×3 дивиться на девʼять пікселів поруч і не бачить решти — і це не обмеження, а підказка: у зображенні справді важливо, що поруч, а не що за двісті пікселів звідси.

Припущення 2 · Спільні ваги

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

Припущення 3 · Терпимість до зсуву

Зсунув предмет — карта ознак зсунулась разом із ним, а після пулінга відповідь майже не змінилась. У темі 06 це поміряно прямо: на зсунутих предметах повнозвʼязна мережа падає до 0.360 (рівень вгадування — 0.333), а згорткова тримає 0.607, маючи при цьому в тринадцять разів менше ваг.

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

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

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

ВхідЗображення як послідовність

Трансформер для тексту їсть послідовність векторів: одне слово — один вектор. Зображення — це не послідовність, це двовимірна сітка чисел. Треба якось перетворити одне на інше.

Найпростіше, що спадає на думку: нехай кожен піксель буде «словом». Для нашої картинки 28×28 це 784 токени. Увага рахує вагу для кожної пари токенів, тобто 784 × 784 = 614 656 пар — вже на першому шарі. Для звичайної фотографії 224×224 пікселів вийде 50 176 токенів і понад два з половиною мільярди пар. Це не «повільно», це неможливо.

Розвʼязок, який дає ViT, зумисне грубий: розріжемо зображення на квадратні патчі (patch) розміром P×P і оголосимо патч одним токеном. Для 28×28 і P = 4 виходить сітка 7×7, тобто 49 патчів — і 49 × 49 = 2 401 пара замість 614 656. У 256 разів дешевше.

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

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

Другий бік — просте стиснення, і воно тим сильніше, чим більший патч. Числа патча треба вкласти у вектор токена фіксованої довжини. У нас патч 4×4 дає 16 чисел, а токен має 48 — місця з запасом. Але візьми патч 14×14: це вже 196 чисел на ті самі 48, тобто вчетверо менше, ніж було, і частина дрібних відмінностей туди просто не вміщається. Те саме в дорослій моделі: ViT-B/32 тисне 32 × 32 × 3 = 3 072 числа патча у 768 — теж учетверо.

Ось чому розмір патча — компроміс, а не безкоштовне прискорення. Ця друга половина рахунку стоїть окремим стовпчиком «роздільність» у таблиці розділу 07 і в інтерактиві 5: при патчі 14×14 від кадру 28×28 лишається сітка 2×2 — чотири квадрати, усередині яких модель нічого не розрізняє.

Зображення в патчі

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

токенів—
довжина вектора—
пар уваги—
ваг у проєкції—
Зображення 28×28, один канал, розмірність токена 48 — така сама крихітна модель, на якій зняті всі числа цієї теми. «Ваг у проєкції» = P×P×48 + 48.

Тепер порахуємо руками, що саме відбувається з одним патчем. Беремо картинку 28×28 і патч 4×4.

  1. Ріжемо. 28 ділиться на 4 без остачі, тому сітка виходить 7×7 = 49 патчів. Патчі не перекриваються й не мають проміжків.
  2. Розгортаємо. Патч 4×4 з одного каналу — це 16 чисел. Виписуємо їх у рядок: спершу перший рядок патча зліва направо, потім другий, і так усі чотири. Вийшов вектор довжини 16.
  3. Множимо. Множимо цей вектор на матрицю розміром 16 × 48 і додаємо зсув із 48 чисел. Вийшов вектор довжини 48 — це й є токен.
токен = вектор_патча · W + b,   W: 16 × 48,   b: 48

Тут W — матриця, яку мережа вчить; вона одна на всі 49 патчів. b — вектор зсуву, теж один на всіх. Число 48 називають розмірністю токена: це ширина «шини», якою всі 49 векторів поїдуть далі крізь мережу. Ваг тут 16 × 48 + 48 = 816 — і цією ж матрицею обробляється кожен із 49 патчів.

Патч → вектор → токен

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

ряд · стовпець—
чисел у патчі16
найбільше в токені—
матрицята сама
Матриця W заповнена детермінованим генератором, щоб числа збігались у всіх читачів. Значення пікселів — 0…1, тому й числа токена невеликі.

Чесна детальПроєкція патча — це згортка

Прочитай ще раз, що ми щойно зробили: взяли квадратне вікно, розгорнули його в вектор, помножили на спільну матрицю, зсунули вікно на його ширину й повторили. Це дослівний опис згортки з ядром P×P і кроком (stride) P.

нарізання + розгортання + спільна матриця  ≡  Conv2d(канали, dim, kernel=P, stride=P)

Це не аналогія й не «майже те саме». Це перевірено прямим порівнянням: ми власноруч нарізали зображення циклом, склали патчі в матрицю 49 × 16, помножили на ту саму матрицю ваг — і звірили з тим, що видає Conv2d. Результат: np.allclose дає True, найбільша різниця 1.8e-07 — це чисельний шум float32, а не розбіжність. Наша нарізка збігається з torch.nn.functional.unfold уже побітово: різниця рівно 0.

Наслідок, який часто замовчують. Мережа, яку описують як «трансформер без згорток», має згортку першим шаром. У torchvision цей шар так і зветься — conv_proj. Тобто гасло «ViT довів, що згортки не потрібні» неточне навіть щодо самого ViT.

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

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

CLSCLS-токен

Після енкодера ми маємо ті самі 49 токенів — кожен по 48 чисел. Класифікатору потрібен один вектор, з якого він зробить шість чисел (по одному на клас). Як із 49 зробити 1?

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

ViT робить інакше. Він додає пʼятдесятий токен, який не відповідає жодному патчу зображення. Це просто вектор із 48 чисел, який мережа вчить разом з усім іншим, і він однаковий для будь-якої картинки на вході. Токен називають CLS (від classification) — прийом запозичено з мовної моделі BERT. Після енкодера беруть вихід саме цього токена й подають у класифікатор.

Логіка така. Токен патча мусить одночасно нести інформацію про свій патч і служити підсумком усього зображення — це дві різні роботи в одному векторі. CLS ніякого патча не представляє, тож уся його місткість вільна під підсумок. А механізм уваги дає йому право взяти з кожного патча рівно стільки, скільки треба: усереднення жорстко ставить усім вагу 1/49, CLS вибирає ваги сам.

Куди дивиться CLS-токен

Ваги уваги від CLS до кожного з 49 патчів у нашій навченій моделі. Перемикай фігуру, шар і голову. Рівномірна увага дала б скрізь 0.0204 = 1/49 — саме з цим числом і треба порівнювати те, що бачиш.

фігура
шар
голова
найбільша вага—
у скільки разів > 1/49—
Наша крихітна модель: патч 4×4, розмірність 48, два шари, чотири голови, 41 574 ваги, навчена на 600 прикладах до точності 0.332. Ваги уваги зняті прямим викликом self_attn з need_weights=True.

Картина повчальна й не така, як на красивих ілюстраціях у статтях. У першому шарі окремі голови ставлять майже всю вагу на один-єдиний патч: на колі найбільша вага доходить до 0.9505, на кільці — до 0.9950 при рівномірних 0.0204. Це в 46-49 разів більше за рівномірну, тобто голова просто вибрала одну клітинку й дивиться тільки туди.

У другому шарі на колі увага розмазується — максимум падає з 0.9505 до 0.2813. Але це не універсальне правило: на хресті картина інша, там максимум у першому шарі 0.5644, а в другому 0.5692, тобто гостроти не додалось і не зникло. Красиві силуети предмета на теплових картах, які показують у статтях, зʼявляються на великих навчених моделях, а не на двошаровій іграшці, яку вчили на 600 прикладах.

А чи потрібен CLS насправді?

Це перевіряється заміром. Ми навчили ту саму модель двічі: з CLS-токеном і з усередненням по всіх 49 токенах, по три зерна на кожну.

як зводимо 49 токенів в 1три прогонисереднєрозкидзайвих ваг
CLS-токен0.332 · 0.435 · 0.3780.3820.10348
середнє по токенах0.372 · 0.437 · 0.3980.4020.0650

Різниця середніх — 0.021. А розкид від самої лише зміни зерна — 0.103, тобто впʼятеро більший. За правилом, яке ми вивели в темі 18, різниця, менша за розкид, різницею не є: на нашій задачі CLS не дає нічого. Він коштує 48 ваг і не окупає їх.

Це не вирок CLS. Наша задача — шість простих фігур на 600 прикладах. У великих моделях різниця теж невелика, і частина сучасних архітектур зору свідомо відмовилась від CLS на користь усереднення. Правильний висновок не «CLS зайвий», а «CLS — це зручна конвенція, а не джерело сили ViT». Джерело сили — сама увага.

БлокБлок енкодера

Тепер серце мережі. Енкодер ViT — це один і той самий блок, повторений кілька разів. У нашій моделі їх два, у ViT-B — дванадцять. Блок складається з чотирьох речей, і три з них ти вже знаєш.

Крок 1 · LayerNorm

Нормалізація по шару: для кожного токена окремо віднімаємо середнє його 48 чисел і ділимо на їхнє стандартне відхилення, потім множимо на вивчений масштаб і додаємо вивчений зсув. На відміну від батчнорму з теми 11, тут нічого не усереднюється по батчу: кожен токен нормалізується сам по собі. Ваг — рівно 2 × 48 = 96 на один LayerNorm.

Крок 2 · Увага

Багатоголова увага з теми 19. Кожен токен формує запит, ключ і значення, зважує всі інші токени й забирає з них зважену суму. Ваг при розмірності 48: три матриці 48×48 зі зсувами (запити, ключі, значення) — 3 × (48 × 48) + 3 × 48 = 6 912 + 144, і вихідна матриця 48×48 + 48 = 2 352. Разом 9 408.

Крок 3 · MLP

Дві повнозвʼязні операції з нелінійністю посередині, застосовані до кожного токена окремо: розширити 48 → 96, застосувати нелінійність, стиснути 96 → 48. Тут токени між собою не спілкуються взагалі — це чиста обробка кожного вектора нарізно. Ваг: (48 × 96 + 96) + (96 × 48 + 48) = 9 360.

Крок 4 · Залишкові звʼязки

І увага, і MLP додаються до входу, а не заміняють його — той самий скіп, що в темі 14. Ми не переказуватимемо, навіщо він; але саме через нього те, що проходить крізь весь стос блоків, прийнято називати залишковим потоком (residual stream).

Разом один блок при розмірності 48 важить 9 408 + 9 360 + 2 × 96 = 18 960 ваг. Наша модель має два такі блоки — 37 920 ваг, або 91 % усієї мережі. Усе інше — проєкція патча, позиційне кодування, CLS і класифікатор — це решта 9 %.

Схема · Блок енкодера цілком

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

Розмірність 48, 49 патчів плюс CLS — звідси 50 × 48 на вході й на виході. Норма стоїть на гілці, а не на магістралі — це і є pre-norm, про який ітиметься далі. Ваги: 96 на кожен LayerNorm, 9 408 на увагу, 9 360 на MLP, разом 18 960 на блок.

Норма спереду чи ззаду

Лишається питання, де саме стоїть LayerNorm, і воно зовсім не косметичне. Є два варіанти, і історично першим був другий.

норма спереду (pre-norm, norm_first=True): x = x + Увага(LN(x)) x = x + MLP(LN(x)) норма ззаду (post-norm, norm_first=False): x = LN(x + Увага(x)) x = LN(x + MLP(x))

Різниця в одному: у першому варіанті нормалізація стоїть на гілці, а магістраль від входу до виходу лишається чистою сумою. У другому LayerNorm сидить на самій магістралі і перемасштабовує її після кожного блоку. Тобто в post-norm наскрізного тотожного шляху, заради якого скіп і придумали, насправді немає.

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

після скількох блоків14812
норма спереду0.9690.6680.3580.223
норма ззаду0.9530.6130.1640.029

Після дванадцяти блоків від початкового сигналу в pre-norm лишається 0.223, у post-norm — 0.029, у 7.7 раза менше. Практично це означає, що глибокий трансформер із нормою ззаду не навчається без обережного розігріву швидкості навчання на початку, а з нормою спереду — навчається просто так. Тому сучасні реалізації, включно з тією, що в torch.nn.TransformerEncoderLayer за прапорцем norm_first=True, беруть норму спереду. Ми теж.

Усередині немає магії. Ці чотири рядки — весь блок. Написані руками, вони дають те саме, що бібліотечний nn.TransformerEncoderLayer на тих самих вагах: різниця виходить рівно 0.0, не «майже нуль». Блок трансформера справді складається тільки з того, що перелічено вище.

РозкладРозклад справжнього ViT

Досі ми говорили про іграшку на 41 574 ваги. Подивимось на дорослу модель. torchvision уміє зібрати ViT-B — базовий Vision Transformer — двох різновидів: з патчем 16×16 і з патчем 32×32. Обидва працюють із зображенням 224×224 і мають розмірність токена 768 та дванадцять блоків. Різниця між ними рівно одна — розмір патча. Ми зібрали обидва (нічого не завантажуючи з мережі) і порахували, скільки ваг у кожній частині.

частинаViT-B/16ViT-B/32
токенів (із CLS)19750
проєкція патча590 5922 360 064
позиційне кодування151 29638 400
CLS-токен768768
шари енкодера85 054 46485 054 464
фінальний LayerNorm1 5361 536
класифікатор769 000769 000
усього86 567 65688 224 232

Головне число теми стоїть у передостанньому рядку: 85 054 464 в обох. Не «приблизно однаково» — однаково до біта. Дванадцять блоків енкодера не знають і не хочуть знати, звідки взялися токени: їм байдуже, чи це 196 патчів по 16×16, чи 49 патчів по 32×32. Розмір патча міняє тільки вхідний каскад.

Розклад ViT-B по частинах

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

модель
усього ваг—
токенів—
проєкція патча—
шари енкодера—
Обидві моделі зібрано через weights=None — це чиста арифметика будови, нічого не завантажується. Класифікатор на 1000 класів ImageNet: 768 × 1000 + 1000 = 769 000.

Перевірмо проєкцію рахунком, щоб число не було чарівним. Патч 32×32 із трьох кольорових каналів — це 32 × 32 × 3 = 3072 числа. Матриця переводить їх у 768: 3072 × 768 = 2 359 296, плюс 768 зсувів = 2 360 064. Збіглося. Для патча 16 те саме: 16 × 16 × 3 × 768 + 768 = 590 592.

А тепер найцікавіше. Патч 32 дає вчетверо менше токенів (50 замість 197), тобто увага стає дешевшою приблизно в шістнадцять разів — вона квадратична. Але матриця проєкції росте вчетверо. Підсумок: ViT-B/32 має більше параметрів, ніж ViT-B/16 — і рахується помітно швидше. Це вже третій випадок у курсі, коли кількість параметрів і кількість обчислень розходяться в різні боки; перші два були в темі 15.

Ще одна деталь, яку варто помітити: позиційне кодування ViT-B/16 — це 197 × 768 = 151 296 ваг, тобто 0.2 % моделі. Це вся геометрія, яку має Vision Transformer. Уся інформація про те, що патч номер 5 стоїть праворуч від патча номер 4, а не десь інде, сидить у цих 151 296 числах — і мережа мусить вивчити її з даних. Згортка знала це задарма.

КомпромісСкільки коштує розмір патча

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

патчтокенівпар увагиваг у проєкціїроздільність
2×219638 41624014×14
4×4492 4018167×7
7×7162562 4004×4
14×144169 4562×2

Читати таблицю треба так. Токенів — довжина послідовності, з якою працює енкодер. Пар уваги — квадрат цього числа: стільки ваг рахує один шар уваги, і саме цей стовпчик визначає час. Ваг у проєкції — розмір вхідної матриці, P × P × 48 + 48. Роздільність — наскільки дрібні деталі модель узагалі здатна розрізнити: усередині патча вона нічого не бачить, там усе злилося в один вектор.

Ціна розміру патча

Чотири числа, які тягнуть у різні боки. Немає положення повзунка, де всі чотири були б добрі — саме тому це компроміс, а не налаштування, яке можна «поставити правильно».

токенів—
пар уваги—
ваг у проєкції—
роздільність—
Зображення 28×28, один канал, розмірність токена 48. «Пари уваги» порахована без CLS: N × N, де N — кількість патчів.

Ліворуч таблиці — патч 2×2: майже піксельна роздільність, дешева матриця проєкції, але 38 416 пар уваги на кожному шарі. Праворуч — патч 14×14: шістнадцять пар уваги, тобто увага коштує нічого, зате від зображення лишилось чотири квадрати, і всередині кожного модель сліпа.

Практичний вибір майже завжди лежить посередині й визначається розміром зображення: для 224×224 беруть 16, рідше 32; для нашої 28×28 — 4. Правило просте: цільова кількість токенів десь від кількох десятків до кількох сотень. Менше — модель не бачить деталей, більше — увага зʼїдає всю памʼять.

ЗамірViT проти CNN на нашій задачі

Тепер чесна перевірка. У нас є той самий наскрізний приклад, що й в усьому блоці: шість класів фігур 28×28, згенерованих формулами, шум 0.45, центр гуляє на ±5 пікселів. Порівнюємо три моделі на трьох обсягах даних, по два зерна на кожну точку. Перевірка — 600 фігур, вгадування навмання дає 0.167.

прикладівCNN, 4 926 вагViT, 41 574 вагиViT без позиційного
1500.5990.2600.237
6000.8120.3830.411
24000.9250.6070.642

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

І друге, важливіше: ViT не наздоганяє. Від 150 до 2400 прикладів, тобто при зростанні даних у шістнадцять разів, розрив скоротився з 0.339 до 0.318 — на два пункти. Такими темпами він закриється приблизно ніколи. Канонічне «ViT наздоганяє на великих даних» тут просто не настає: 2400 прикладів для трансформера — це нічого.

ViT проти CNN зі зростанням даних

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

CNN—
ViT—
ViT без позиційного—
розрив CNN − ViT—
AdamW, швидкість навчання 0.003, 30 епох, батч 64, один потік CPU. Вгадування навмання — 0.167. Кожна точка — середнє з двох зерен.

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

Один стенд — не рейтинг. Тема 18 застерігала: абсолютні числа залежать від рецепта не менше, ніж від архітектури. Той самий дослід на іншій згортковій мережі (5 094 ваги, з кроком 2 на вході) дав інші рівні: CNN 0.315 / 0.638 / 0.930, ViT 0.322 / 0.450 / 0.702, ViT без позиційного 0.382 / 0.502 / 0.703. Там на 150 прикладах CNN і ViT зрівнялись, а розрив із даними зростав. Спільними лишилися три висновки: CNN не програє ніде, ViT не наздоганяє, і варіант без позиційного кодування не гірший за варіант із ним. Цитувати як «рейтинг архітектур» не можна ані першу таблицю, ані другу.
Про оптимізатор. Протокол блоку — AdamW: на великих трансформерах простий SGD дає помітно гірші результати, і легко видати за властивість архітектури те, що є властивістю налаштувань. На нашій крихітній моделі це, до речі, не підтвердилось: SGD з моментом 0.9 дав 0.443 і 0.410 при двох швидкостях навчання проти 0.382 в AdamW — тобто не гірше. Два шари й 41 тисяча ваг — надто мала мережа, щоб на ній перевіряти правила, здобуті на великих. Числа наводимо, щоб ти не думав, ніби ми їх сховали.

ГоловнеПозиційне кодування допомагає не завжди

Придивись до останнього стовпчика таблиці. ViT без позиційного кодування ніде не програє, а на двох більших обсягах іде попереду: 0.411 проти 0.383 на 600 прикладах і 0.642 проти 0.607 на 2400. Ми викинули 2 400 ваг, які мали розповідати моделі про геометрію, — і стало не гірше.

Наскільки цьому можна вірити. Розкид від зерна тут великий, і чесно його назвати обовʼязково. На 150 прикладах різниця −0.023 при розкиді 0.113 — її просто немає. На 600 різниця +0.027 при розкиді 0.103 — теж немає. І лише на 2400 різниця +0.036 більша за розкид 0.010, тобто це єдина з трьох точок, де можна щось стверджувати. Правильне формулювання: кодування не допомагає, а не «шкодить».

Навіть із цим застереженням висновок лишається дивним: 2 400 ваг, які мали розповідати моделі про геометрію, не окупились ніде. Це схоже на спростування всього, що ми казали про потребу в позиційному кодуванні. Насправді це не спростування, а підказка про задачу. Гіпотеза така: наші фігури класифікуються за вмістом, а не за положенням. Коло лишається колом, де б воно не стояло; центр і так гуляє на ±5 пікселів. Задача майже позиційно інваріантна за побудовою — і тоді 2 400 ваг позиційного кодування не несуть корисної інформації, але їх усе одно треба вивчити з мізерних даних.

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

Задача «де фігура». Чотири класи — чотири чверті кадру. У кожному прикладі малюється випадкова фігура випадкового розміру, тож форма про клас не говорить нічого. Клас визначає лише те, у якій чверті стоїть предмет: центр — рівно (8, 8), (8, 20), (20, 8) або (20, 20). Зсув між чвертями дорівнює 12 пікселів, тобто рівно три патчі по чотири — це знадобиться за хвилину. Вгадування навмання дає 0.25.
модель«яка фігура», 6 класів«де фігура», 4 класи
CNN0.8121.000
ViT із позиційним0.3830.995
ViT без позиційного0.4110.248
вгадування навмання0.1670.250

Гіпотеза підтвердилась, і навіть різкіше, ніж очікувалось. На задачі «де фігура» ViT без позиційного кодування дає 0.248 — це рівно вгадування навмання, три зерна поспіль: 0.252, 0.243, 0.250. Модель не просто гірша, вона не працює взагалі. Той самий ViT із позиційним кодуванням розвʼязує ту саму задачу практично ідеально — 0.995.

Коли позиційне кодування потрібне

Перемикай задачу. На «яка фігура» кодування не окупається: різниця з ним і без нього тоне в розкиді від зерна. На «де фігура» без нього моделі просто немає. Пунктир — рівень вгадування навмання для відповідної задачі.

задача
ViT із кодуванням—
ViT без кодування—
розкид від зерна—
вгадування—
«Яка фігура»: 600 прикладів, 6 класів, 2 зерна. «Де фігура»: 600 прикладів, 4 класи, 3 зерна. Однакова модель, однаковий рецепт — відрізняється тільки наявність позиційного кодування.

Чому саме 0.25, а не «трохи гірше»

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

Крок 1

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

Крок 2

А CLS-токен (як і усереднення) не залежить від порядку взагалі. Тобто вся модель стає інваріантною щодо перестановки патчів: вона бачить не сітку, а невпорядкований мішок із 49 векторів.

Крок 3

Ми навмисне зробили зсув між чвертями рівним 12 пікселям = трьом патчам. Отже, той самий предмет у різних чвертях дає той самий набір патчів, просто розкладений по інших місцях сітки. Для моделі, яка бачить мішок, ці чотири картинки не відрізняються нічим.

Крок 4

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

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

модель (ненавчена)найбільша різниця виходів між чвертями
ViT без позиційного0.00000024
ViT із позиційним0.0130
CNN, доповнення нулями0.0027
CNN, доповнення циклічне0.0000000000

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

Несподіванка в останніх двох рядках

А ось чого ми не очікували. Наша CNN закінчується глобальним усередненням, яке за побудовою викидає просторову інформацію (тема 08). Здавалося б, вона теж мала б провалитись на «де фігура». Але вона дає 1.000 — ідеально.

Останні два рядки таблиці пояснюють, звідки вона бере позицію. Різниця виходів у звичайної CNN — 0.0027, тобто вона розрізняє чверті. А та сама мережа з циклічним доповненням країв (коли замість нулів по краях підставляється протилежний край) дає рівно 0.0000000000 — не «майже нуль», а нуль до останнього знака float32. Отже, джерело позиції — саме доповнення нулями. Згортка біля межі бачить смугу штучних нулів, і по тому, як близько ця смуга, мережа відновлює абсолютні координати.

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

ЧесноЧому справжній ViT усе-таки працює

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

Числа нижче — цитовані з літератури, а не заміряні нами. На цій машині ми не можемо ані завантажити ImageNet, ані навчити 86-мільйонну модель. Тому вони наводяться як орієнтир масштабу — на відміну від усіх інших чисел цієї теми, які заміряні на цій машині.

В оригінальній роботі про Vision Transformer (Dosovitskiy та співавтори, 2021) картина була така. Навчений тільки на ImageNet-1k — приблизно 1.3 мільйона зображень — ViT програвав порівнянній залишковій мережі. Після передтренування на ImageNet-21k (близько 14 мільйонів зображень) він зрівнявся. А після передтренування на закритому наборі JFT-300M (близько 300 мільйонів зображень) найбільший варіант, ViT-H/14, вийшов на 88.55 % точності на ImageNet — тобто попереду всіх згорткових мереж свого часу.

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

Через рік стало зрозуміло, що річ не лише в даних. Робота DeiT (Touvron та співавтори, 2021) показала, що ViT-B можна навчити на самому ImageNet-1k до 81.8 % — якщо застосувати важку аугментацію, сильну регуляризацію й довге навчання; дистиляція знань від згорткової мережі-вчителя піднімає результат ще вище. Іншими словами, бракувало не тільки даних, а й рецепта — рівно те, про що попереджала тема 18.

Наші 2400 прикладів на цьому тлі — це навіть не крапля. Тому правильний висновок із розділу 8 не «ViT гірший за CNN», а «на 2400 прикладах ViT не має шансів, і жодна таблиця з ImageNet цього не змінює».

СпадокЩо ViT дав галузі

Якби вся заслуга ViT зводилась до «переміг згортку на дуже великих даних», тема не варта була б окремої лекції. Значення в іншому.

1 · Спільна архітектура для тексту й зображень

Після ViT текст і зображення обробляються однаковим блоком. Речення — послідовність токенів, картинка — послідовність токенів; далі та сама увага, той самий MLP, той самий залишковий потік. Це відкрило дорогу моделям, які працюють з обома видами даних одночасно, і саме про них буде тема 22.

2 · Передбачувана масштабованість

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

3 · Простота

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

4 · Він підштовхнув самі згорткові мережі

Найпарадоксальніший наслідок. Розібравшись, що саме в ViT працює, дослідники перенесли ці рішення назад у згортки — великі ядра, норма на гілці, широкий MLP-блок, менше нелінійностей. Так зʼявився ConvNeXt, який на тих самих даних тримається нарівні з трансформерами. У нашому середовищі обидва є: convnext_tiny має 28 589 128 ваг, swin_t — 28 288 354.

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

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

Що ми зібрали в цій темі:

І що лишилось невирішеним. Два обмеження ViT ми не прибрали, а лише назвали.

Перше — квадратична ціна. Кількість пар уваги дорівнює квадрату кількості токенів. Для 28×28 з патчем 4 це 2 401 пара, і це дрібниця. Для зображення 1024×1024 з тим самим патчем це вже 65 536 токенів і понад чотири мільярди пар на кожному шарі. Детекція й сегментація саме таких роздільностей і потребують.

Друге — відсутність ієрархії. У згортковій мережі роздільність падає від шару до шару: 28×28 → 14×14 → 7×7, і кожен рівень працює зі своїм масштабом деталей. У ViT усі дванадцять блоків працюють з однією й тією самою сіткою 7×7 або 14×14. Пірамідки, на якій стоять і детекція, і сегментація, просто немає.

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