Позиция
«Мать любит дочь» и «дочь любит мать» состоят из одних и тех же токенов: в русском у этих слов совпадают формы именительного и винительного падежа, и смысл несёт только порядок. Их эмбеддинги — те же три вектора, лишь в другом порядке. Внимание, сердце трансформера (глава 5), сравнивает каждый токен с каждым другим, и это сравнение смотрит только на векторы, а не на то, где они стоят. Предоставленная сама себе, модель читала бы предложение как мешок слов: знала бы, какие слова в нём есть, но не знала бы, кто кого любит. (Это небольшое упрощение: модели в духе GPT получают немного сведений о порядке из правила, что токен видит только слова перед собой, — об этом в конце главы.)
Те же строки, только в другом порядке. Внимание сравнивает только эти числа, поэтому «мать» и «дочь» совпадают одинаково сильно в обеих фразах, и ничто не подсказывает модели, кто кого любит.
Решение — подмешать в вектор каждого токена сведения о том, где он стоит. Есть два больших семейства способов: прибавить вектор позиции к эмбеддингам в самом начале или встроить позицию прямо в вычисление внимания. Современные LLM в основном используют второй.
Синусоидальные векторы позиций
Самая простая идея — записать в вектор сам номер позиции: 0, 1, 2 и так далее. Но числа в эмбеддинге маленькие, порядка ±1, и позиция 3000 стала бы огромным числом, которое заглушит смысл. Разделить на длину текста тоже не помогает: тогда 0,5 означает 5-е слово в тексте из 10 слов, но 500-е — в тексте из 1000. Нужен код, который остаётся маленьким, у каждой позиции свой и меняется плавно, чтобы соседи получали похожие коды.
Исходный трансформер (Vaswani et al., 2017) строит такой код из волн. Представьте часы с множеством стрелок. Первые измерения — секундная стрелка, которая крутится быстро; дальние измерения — минутная и часовая, они поворачиваются медленно. Все стрелки вместе дают каждой позиции уникальный код, а соседние позиции получают похожие коды. В формулах:
Здесь — позиция (0, 1, 2, …), — длина вектора, а нумерует пары измерений. В каждой паре лежат синус и косинус одного и того же угла — это просто две координаты кончика одной стрелки. У первой пары угол растёт на 1 радиан (около 57°) за позицию: быстрая стрелка. У дальних пар делитель огромен, и стрелка почти не движется.
Маленький пример с , то есть с двумя стрелками: одна поворачивается на 1 за позицию, другая — на .
Быстрая пара от позиции 3 к 4 сильно меняется, медленная — почти нет, а вместе они никогда не повторяются. Этот вектор прибавляется к эмбеддингу токена, число к числу, перед первым блоком.
Сверху: строки — измерения, столбцы — позиции. Кривая снизу: насколько код каждой позиции похож на код позиции 12. Пик — на 12: ни у одной другой позиции нет такого же кода, а ближайшие соседи похожи сильнее всего. Наведите, нажмите или двигайте стрелками.
Многие модели (GPT-2, BERT) вместо этого выучивают вектор для каждой позиции — ровно как таблицу эмбеддингов, только с индексом по позиции. Это хорошо работает, но имеет жёсткий предел: у модели, обученной на 1 024 позициях, просто нет вектора для позиции 1 025.
RoPE: позиция как поворот
Прибавленный вектор позиции сообщает токену его абсолютное место: «я слово № 517». Но языку важнее относительное положение: прилагательное прямо перед существительным играет одну и ту же роль и на первой странице, и на сотой. С абсолютными кодами модели приходится самой учиться вычислять расстояния. RoPE встраивает расстояние напрямую.
Большинство современных открытых моделей, включая Llama, Mistral, Qwen и DeepSeek, используют ротационные позиционные эмбеддинги (RoPE) (Su et al., 2021). Вместо того чтобы что-то прибавлять к эмбеддингам, RoPE поворачивает векторы запроса и ключа внутри внимания — те два вектора, которые внимание сравнивает, решая, какой токен на какой смотрит, — на угол, пропорциональный их позиции.
Разбейте вектор на пары чисел и считайте каждую пару стрелкой на плоскости. У токена на позиции каждая пара поворачивается на :
Внимание сравнивает запрос на позиции с ключом на позиции через их скалярное произведение, а оно зависит только от длин двух стрелок и угла между ними (глава 3). Поворот длин не меняет. Запрос на позиции 5 поворачивается на , ключ на позиции 3 — на , и угол между ними меняется на . Перенесите пару на позиции 105 и 103 — угол между ними изменится ровно на те же . В символах:
Оценка зависит только от относительного расстояния , а не от того, где именно в тексте стоит пара.
- q повёрнут на
- 2 × θ = 52°
- k повёрнут на
- 0 × θ = 0°
- расстояние m − n
- 2
- q · k до
- 0.747
- q · k после RoPE
- 0.894
У настоящей головы внимания (одного из нескольких блоков внимания, работающих бок о бок) размерности 128 таких пар 64, и у каждой своя скорость — та же идея стрелок часов, что и у синусоид. Быстрые пары различают соседние токены, медленные отслеживают расстояния в тысячи токенов.
Другие способы: ALiBi и вовсе без позиций
ALiBi (Press et al., 2021) не добавляет векторов и ничего не поворачивает. Он вычитает из каждой оценки внимания штраф, пропорциональный расстоянию: , где — на сколько токенов ключ стоит позади запроса . Здесь — фиксированный наклон, а не позиция, и у каждой головы он свой, поэтому одни головы смотрят на последние несколько токенов, а другие дотягиваются далеко назад.
У каждой головы свой наклон. Крутые головы смотрят только на последние токены, пологие дотягиваются далеко назад.
Неожиданное открытие: модели-декодеры — модели в духе GPT, о которых весь этот учебник, — могут работать вообще без позиционного кодирования (NoPE). Порядок просачивается через саму каузальную маску — правило, по которому токен видит только предыдущие токены: токен 1 видит один токен, токен 100 — сотню, и модель учится извлекать позицию из этого. Некоторые недавние модели чередуют слои NoPE со слоями RoPE, чтобы лучше вести себя на длинных входах.
Общая нить у всех схем: для языка важнее всего то, насколько далеко назад стояло слово, — ровно это и кодируют RoPE и ALiBi.
Углы RoPE для расстояний, которых модель не видела при обучении, ей незнакомы, поэтому модель, обученная на 8 тыс. токенов, разваливается на 32 тыс. Если замедлить повороты (интерполяция позиций, NTK-масштабирование, YaRN), длинные расстояния снова отображаются на знакомые углы. Глава 12 показывает, как это работает и что ещё ломается на длинных входах.
Итоги
- Само по себе внимание порядок не учитывает, поэтому позицию нужно добавить.
- Синусоидальные и выученные кодировки прибавляют вектор позиции к каждому эмбеддингу. Синусоиды работают как стрелки часов с разной скоростью: маленькие числа, уникальный код для каждой позиции, похожие коды у соседей.
- RoPE поворачивает запросы и ключи в зависимости от позиции, и оценки внимания начинают зависеть только от относительного расстояния. Сегодня это стандарт в открытых моделях.
- ALiBi напрямую штрафует расстояние, а NoPE полагается на одну каузальную маску.
Теперь, когда порядок учтён — в самих векторах или внутри внимания, — можно посмотреть, как токены обмениваются информацией: внимание.