Головна / Статті / Навіщо трансформерам синус і косинус для позиційного кодування

Навіщо трансформерам синус і косинус для позиційного кодування

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

1919 слів

Чому трансформери кодують позицію синусом і косинусом? Пронумерувати слова 1, 2, 3 здається значно простіше, але як слід відповісти на це питання складніше, ніж здається.

Увага не відчуває порядку

Як операція увага поводиться з входом як із мішком слів. Дайте трансформеру «кіт їсть мишу», а потім «миша їсть кота» без жодної інформації про позиції, і шар уваги обидва рази побачить ті самі три вектори. Він не може зрозуміти, що було першим, і скалярні добутки Q · K виходять однаковими. Це справжня проблема: перша фраза — це вечеря, а друга — сюрприз. Позицію треба кудись додати ще до першого шару уваги.

Очевидне рішення і чому воно не працює

Візьмімо фразу «cat and dog» — три токени: cat на позиції 1, «and» на позиції 2 і dog на позиції 3. Справжні ембединги слів — це не два-три числа. Бойові трансформери використовують вектори на 512, 768 і навіть більше вимірів. Ось cat і dog у 768 вимірах, показано перші шість елементів і останній, щоб було видно форму:

cat ≈ [ 0.82, -0.14, 0.36, 0.05, -0.71, 0.29, ... , -0.18 ]   (768 numbers)
dog ≈ [ 0.77, -0.09, 0.41, 0.11, -0.68, 0.24, ... , -0.15 ]   (768 numbers)

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

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

cat at position 1 → cat + 1 → [ 1.82, 0.86, 1.36, 1.05, 0.29, 1.29, ... , 0.82 ]

На позиції 1 це виглядає безневинно. А тепер уявіть те саме слово на позиції 500 у довгому документі:

cat at position 500 → cat + 500 → [ 500.82, 499.86, 500.36, 500.05, 499.29, 500.29, ... , 499.82 ]

Перша проблема — головна. Значення роздуваються, причому однаково в усіх вимірах. Те, що робило cat упізнаваним, його особлива послідовність 0.82, -0.14, 0.36 і так далі, те, що відрізняло «кота» від «собаки», перетворюється на похибку округлення поруч із 500. Сигнал тоне. Ділення на максимальну довжину послідовності не рятує: ця довжина невідома, коли модель працює, а короткі речення й дуже довгі документи опинилися б у несумісних масштабах.

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

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

Зменшімо задачу, щоб її було краще видно. Забудьте про 768 вимірів і уявіть лише 2. Cat на позиції 1 потрібен якийсь вектор [x, y], а «and» на позиції 2 — інший вектор [p, q]. Якими мають бути ці числа? Можна вибрати їх вручну:

position 1 → [0.2, 0.8]
position 2 → [0.4, 0.6]
position 3 → [0.6, 0.4]

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

Потрібна функція. Вона приймає цілу позицію й повертає пару [x, y], ставить сусідні позиції близько одна до одної і ніколи не дає числам вибухнути, хоч би якою великою була позиція. Геометрія розв'язала це давно. Це називається коло.

Годинник для позиції

Поставте точку на коло й пустіть її рухатися. Її координати — (cos θ, sin θ), тож кожен кут дає унікальну 2D-координату, а сусідні кути — сусідні координати. Подання гладке. Замість «позиція 17» можна записати:

position 17 → (sin 17, cos 17)

Два виміри кодують одну позицію, і це вже краще за скаляр. Значення лишаються в межах [-1, 1], тож нічого не вибухає, а позиції 17 і 18 виглядають майже однаково.

Лишається одна заковика: один годинник повторюється. Після повного оберту позиція 1 і позиція 1 + 2π виглядають однаково. За речення з 500 токенів і годинника з періодом близько 6 збіги будуть усюди.

Багато годинників із різною швидкістю

Уся схема працює завдяки тому, що годинників багато і кожен іде зі своєю швидкістю. Один робить оберт приблизно кожні 10 токенів, інший — кожні 100, ще інший — кожні 1 000, ще інший — кожні 10 000. Для кожної позиції записуються всі показники разом. Дві позиції можуть збігтися на швидкому годиннику, але не на всіх годинниках одночасно, тому загальний набір показників працює як відбиток пальця. Схожий трюк використовує GPS із кількома супутниками, а штрихкод кодує ID смужками різної ширини.

У справжній формулі розмірність ембединга d дає d/2 пар годинників, і пара i йде з частотою 1 / 10000^(2i/d). Мале i — швидкий годинник, велике i — повільний:

PE(pos, 2i)   = sin(pos / 10000^(2i/d))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d))

Ось і вся формула.

Розбір на прикладі: «cat and dog» у 12 вимірах

Абстрактна математика дає не так багато, тож порахуймо. За d = 12 виходить 6 пар годинників, кожна зі своєю частотою: пара 1 найшвидша, пара 6 найповільніша:

pair 1: 10000^0/12  ≈ 1        (fastest)
pair 2: 10000^2/12  ≈ 21.54
pair 3: 10000^4/12  ≈ 464.16
pair 4: 10000^6/12  ≈ 1000
pair 5: 10000^8/12  ≈ 2154.43
pair 6: 10000^10/12 ≈ 4641.59  (slowest)

Візьмімо пару 1 і cat на позиції 1: формула дає

Стовпець — це повний 12-вимірний вектор PE слова, а найцікавіше — у рядках:

  • Рядок 1 сильно стрибає від слова до слова: 0.84, потім 0.91, потім 0.14. Швидкий годинник проходить майже цілий оберт лише за два тіки.
  • Рядок 6 майже не змінюється: тричі поспіль 1.0000. Повільний годинник ще як слід не тікнув. Він потрібен, щоб розрізняти далекі одна від одної позиції, наприклад 3 і 3000.
  • Кожен елемент повністю детермінований. Ніщо в таблиці не навчається.

Вхід трансформера — це просто поелементна сума:

input = word_embedding(cat) + PE(1)

Та сама форма, жодного хитрого злиття. Щоб побачити реальний результат, ембединги слів зменшено до 12 вимірів іграшкового PE (справжня модель використала б свої 768-вимірні вектори, а ці числа вигадано лише для демонстрації механізму):

cat_embedding ≈ [ 0.20,  0.70, -0.10,  0.40,  0.15, -0.30,  0.55,  0.10, -0.20,  0.05,  0.30, -0.15]
dog_embedding ≈ [ 0.18,  0.68, -0.05,  0.35,  0.20, -0.25,  0.50,  0.15, -0.18,  0.08,  0.28, -0.10]

Як і в 768-вимірних версіях, cat і dog спочатку близькі, бо обидва — тварини. Тепер додамо до кожного слова його PE з таблиці, вимір за виміром:

cat_final = cat_embedding + PE(1)
          = [1.0415, 1.2403, -0.0536, 1.3989, 0.1522, 0.7000, 0.5510, 1.1000, -0.1995, 1.0500, 0.3002, 0.8500]
dog_final = dog_embedding + PE(3)
          = [0.3211, -0.3100, 0.0888, 1.3403, 0.2065, 0.7500,
             0.5030, 1.1500, -0.1786, 1.0800, 0.2806, 0.9000]

Саме ці два вектори потрапляють у перший шар уваги. Порівняйте вимір 2: там cat і dog були майже однаковими (0.70 проти 0.68), а після позиційного сигналу стали 1.24 проти -0.31 — як небо і земля. Це швидкий годинник (пара 1, позиція 1 проти позиції 3) працює як задумано. Він залишає досить помітний позиційний відбиток, щоб «cat на початку речення» і «dog далі в реченні» стали явно різними векторами, хоча самі слова близькі за змістом.

Звідки модель знає, що [0.84, 0.54, ...] — це позиція 1?

Нізвідки. Жоден вимір у цьому векторі не каже «1», і ніщо його не розшифровує. Моделі ніколи не потрібно відновлювати номер позиції, це не її завдання.

Завдання вужче. Припустімо, query слова dog зустрічається з key слова cat у скалярному добутку. Чи покаже оцінка, що слова стоять на відстані двох позицій? На цьому шарі «розуміти позицію» означає саме це. Кодування — не шум. Це гладка суміш синусоїд з відомими частотами, тому навчені проєкції Q і K можуть уловлювати ці закономірності й реагувати на них. Query може зрештою означати «надавай перевагу key, що стоїть на одну позицію раніше», і жодна змінна з назвою «позиція» при цьому не бере участі.

Зручне порівняння — розпізнавання облич. У 512-вимірному ембедингу обличчя немає окремого виміру з ім'ям людини. Увесь вектор загалом і є цією людиною. 12-вимірний вектор PE(1) влаштований так само: у сукупності він і є позиція 1.

Навіщо і синус, і косинус

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

sin(x + Δ) = sin(x)cos(Δ) + cos(x)sin(Δ)
cos(x + Δ) = cos(x)cos(Δ) − sin(x)sin(Δ)

Знаючи [sin(x), cos(x)], можна обчислити [sin(x+Δ), cos(x+Δ)] за допомогою фіксованого лінійного перетворення, яке залежить лише від Δ, а не від x. Отже, зсув позиції на Δ — те саме перетворення, звідки б ви не почали. Це дає змогу увазі вивчити «подивися на Δ токенів назад» як одну багаторазову операцію, без окремого правила для кожної пари позицій. Самого синуса для цього замало: щоб повністю задати обертання, потрібні обидві половини пари.

Що відбувається під час зворотного поширення

Чи підлаштовуються позиційні кодування градієнтним спуском під час навчання? Синусоїдальні — ні. Вони не мають параметрів, що навчаються, формула фіксована. Градієнти проходять крізь суму (вхід = ембединг + PE) і не знаходять на боці PE нічого, що можна оновити, — приблизно як питати, чи навчається 5 в y = x + 5.

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

Варто згадати ще один варіант, бо більшість сучасних моделей насправді використовують саме його: навчувані позиції. Формули немає взагалі. Модель зберігає навчувану таблицю з одним рядком на позицію, до максимальної довжини послідовності й завширшки в розмірність моделі, і навчання саме вирішує, що має лежати в рядку 17. Про таке простіше міркувати, і в межах довжин, бачених під час навчання, він працює добре, але розтягнути його на довші послідовності, як синусоїдальне PE, не можна. Цей компроміс, як і новіші підходи на кшталт ALiBi та RoPE, — окрема тема.