• Русский
  • Позиция

    «Мать любит дочь» и «дочь любит мать» состоят из одних и тех же токенов: в русском у этих слов совпадают формы именительного и винительного падежа, и смысл несёт только порядок. Их эмбеддинги — те же три вектора, лишь в другом порядке. Внимание, сердце трансформера (глава 5), сравнивает каждый токен с каждым другим, и это сравнение смотрит только на векторы, а не на то, где они стоят. Предоставленная сама себе, модель читала бы предложение как мешок слов: знала бы, какие слова в нём есть, но не знала бы, кто кого любит. (Это небольшое упрощение: модели в духе GPT получают немного сведений о порядке из правила, что токен видит только слова перед собой, — об этом в конце главы.)

    Рисунок 4.1 · Проблема порядка
    матьпоз. 0любитпоз. 1дочьпоз. 2
    мать0.90−0.300.400.10
    любит−0.200.80−0.500.60
    дочь0.500.200.90−0.70
    =
    дочьпоз. 0любитпоз. 1матьпоз. 2
    дочь0.500.200.90−0.70
    любит−0.200.80−0.500.60
    мать0.90−0.300.400.10

    Те же строки, только в другом порядке. Внимание сравнивает только эти числа, поэтому «мать» и «дочь» совпадают одинаково сильно в обеих фразах, и ничто не подсказывает модели, кто кого любит.

    Без позиций правая фраза — просто перестановка тех же строк, и модель не может отличить её от левой. Включите «+ позиция», чтобы прибавить к каждой строке код её места: входы станут по-настоящему разными.

    Решение — подмешать в вектор каждого токена сведения о том, где он стоит. Есть два больших семейства способов: прибавить вектор позиции к эмбеддингам в самом начале или встроить позицию прямо в вычисление внимания. Современные LLM в основном используют второй.

    Синусоидальные векторы позиций

    Самая простая идея — записать в вектор сам номер позиции: 0, 1, 2 и так далее. Но числа в эмбеддинге маленькие, порядка ±1, и позиция 3000 стала бы огромным числом, которое заглушит смысл. Разделить на длину текста тоже не помогает: тогда 0,5 означает 5-е слово в тексте из 10 слов, но 500-е — в тексте из 1000. Нужен код, который остаётся маленьким, у каждой позиции свой и меняется плавно, чтобы соседи получали похожие коды.

    Исходный трансформер (Vaswani et al., 2017) строит такой код из волн. Представьте часы с множеством стрелок. Первые измерения — секундная стрелка, которая крутится быстро; дальние измерения — минутная и часовая, они поворачиваются медленно. Все стрелки вместе дают каждой позиции уникальный код, а соседние позиции получают похожие коды. В формулах:

    PE(pos, 2i)=sin⁡ ⁣(pos100002i/d)PE(pos, 2i+1)=cos⁡ ⁣(pos100002i/d)PE_{(pos,\,2i)} = \sin\!\left(\frac{pos}{10000^{2i/d}}\right) \qquad PE_{(pos,\,2i+1)} = \cos\!\left(\frac{pos}{10000^{2i/d}}\right)

    Здесь pospos — позиция (0, 1, 2, …), dd — длина вектора, а ii нумерует пары измерений. В каждой паре лежат синус и косинус одного и того же угла — это просто две координаты кончика одной стрелки. У первой пары угол растёт на 1 радиан (около 57°) за позицию: быстрая стрелка. У дальних пар делитель 100002i/d10000^{2i/d} огромен, и стрелка почти не движется.

    Маленький пример с d=4d = 4, то есть с двумя стрелками: одна поворачивается на 1 за позицию, другая — на 1/1001/100.

    ИзмерениеПозиция 3Позиция 4
    быстрое: sin⁡(pos)\sin(pos)0,14−0,76
    быстрое: cos⁡(pos)\cos(pos)−0,99−0,65
    медленное: sin⁡(pos/100)\sin(pos/100)0,030,04
    медленное: cos⁡(pos/100)\cos(pos/100)1,001,00

    Быстрая пара от позиции 3 к 4 сильно меняется, медленная — почти нет, а вместе они никогда не повторяются. Этот вектор прибавляется к эмбеддингу токена, число к числу, перед первым блоком.

    Рисунок 4.2 · Синусоидальное позиционное кодирование
    позиция 0 →→ 63
    12позиция4измерение (sin)−0.61значение19.9длина волны (позиций)

    Сверху: строки — измерения, столбцы — позиции. Кривая снизу: насколько код каждой позиции похож на код позиции 12. Пик — на 12: ни у одной другой позиции нет такого же кода, а ближайшие соседи похожи сильнее всего. Наведите, нажмите или двигайте стрелками.

    64 позиции × 32 измерения. Акцентный цвет — +1, оранжевый — −1. Младшие измерения (сверху) быстро мерцают от позиции к позиции, старшие (снизу) меняются медленно. Кривая внизу сравнивает код выбранной позиции со всеми остальными.

    Многие модели (GPT-2, BERT) вместо этого выучивают вектор для каждой позиции — ровно как таблицу эмбеддингов, только с индексом по позиции. Это хорошо работает, но имеет жёсткий предел: у модели, обученной на 1 024 позициях, просто нет вектора для позиции 1 025.

    RoPE: позиция как поворот

    Прибавленный вектор позиции сообщает токену его абсолютное место: «я слово № 517». Но языку важнее относительное положение: прилагательное прямо перед существительным играет одну и ту же роль и на первой странице, и на сотой. С абсолютными кодами модели приходится самой учиться вычислять расстояния. RoPE встраивает расстояние напрямую.

    Большинство современных открытых моделей, включая Llama, Mistral, Qwen и DeepSeek, используют ротационные позиционные эмбеддинги (RoPE) (Su et al., 2021). Вместо того чтобы что-то прибавлять к эмбеддингам, RoPE поворачивает векторы запроса и ключа внутри внимания — те два вектора, которые внимание сравнивает, решая, какой токен на какой смотрит, — на угол, пропорциональный их позиции.

    Разбейте вектор на пары чисел и считайте каждую пару стрелкой на плоскости. У токена на позиции mm каждая пара поворачивается на mθm\theta:

    R(mθ)=(cos⁡mθ−sin⁡mθsin⁡mθcos⁡mθ)R(m\theta) = \begin{pmatrix} \cos m\theta & -\sin m\theta \\ \sin m\theta & \cos m\theta \end{pmatrix}

    Внимание сравнивает запрос на позиции mm с ключом на позиции nn через их скалярное произведение, а оно зависит только от длин двух стрелок и угла между ними (глава 3). Поворот длин не меняет. Запрос на позиции 5 поворачивается на 5θ5\theta, ключ на позиции 3 — на 3θ3\theta, и угол между ними меняется на 2θ2\theta. Перенесите пару на позиции 105 и 103 — угол между ними изменится ровно на те же 2θ2\theta. В символах:

    (R(mθ) q)⋅(R(nθ) k)=q⋅R((n−m)θ) k\big(R(m\theta)\,\mathbf{q}\big) \cdot \big(R(n\theta)\,\mathbf{k}\big) = \mathbf{q} \cdot R\big((n-m)\theta\big)\,\mathbf{k}

    Оценка зависит только от относительного расстояния m−nm - n, а не от того, где именно в тексте стоит пара.

    Рисунок 4.3 · Ротационные эмбеддинги на одной паре измерений
    k@0q@2
    q повёрнут на
    2 × θ = 52°
    k повёрнут на
    0 × θ = 0°
    расстояние m − n
    2
    q · k до
    0.747
    q · k после RoPE
    0.894
    Оценка внимания q·k в зависимости от расстояния m − n
    −120+12
    Задайте позиции запроса и ключа. Пунктирные стрелки — векторы до поворота, бледные дуги показывают, на сколько повернулся каждый. Нажмите «Сдвинуть оба», чтобы сдвигать оба токена на позицию вправо: стрелки продолжают поворачиваться, но затенённый угол между ними, а значит и оценка внимания, не меняется. График показывает оценку как функцию одного лишь расстояния.

    У настоящей головы внимания (одного из нескольких блоков внимания, работающих бок о бок) размерности 128 таких пар 64, и у каждой своя скорость θi=10000−2i/d\theta_i = 10000^{-2i/d} — та же идея стрелок часов, что и у синусоид. Быстрые пары различают соседние токены, медленные отслеживают расстояния в тысячи токенов.

    Другие способы: ALiBi и вовсе без позиций

    ALiBi (Press et al., 2021) не добавляет векторов и ничего не поворачивает. Он вычитает из каждой оценки внимания штраф, пропорциональный расстоянию: scoreij−m (i−j)\text{score}_{ij} - m\,(i - j), где i−ji - j — на сколько токенов ключ jj стоит позади запроса ii. Здесь mm — фиксированный наклон, а не позиция, и у каждой головы он свой, поэтому одни головы смотрят на последние несколько токенов, а другие дотягиваются далеко назад.

    Рисунок 4.4 · ALiBi: штраф за расстояние во внимании
    Добавка к каждой оценке: −m × расстояние
    Котсиделнатёпломстаромковрике
    Кот0.00
    сидел−0.500.00
    на−1.00−0.500.00
    тёплом−1.50−1.00−0.500.00
    старом−2.00−1.50−1.00−0.500.00
    коврике−2.50−2.00−1.50−1.00−0.500.00
    Внимание от «коврике», если бы все исходные оценки были равны
    Кот3.4%
    сидел5.6%
    на9.2%
    тёплом15.2%
    старом25.1%
    коврике41.4%

    У каждой головы свой наклон. Крутые головы смотрят только на последние токены, пологие дотягиваются далеко назад.

    Матрица — добавка к каждой оценке «запрос–ключ» (строки — запросы, заштрихованные клетки — закрытое маской будущее, на которое токену смотреть нельзя). Полоски показывают, как последний токен распределил бы внимание, будь все исходные оценки равны: чем круче наклон, тем сильнее тяга к недавним токенам.

    Неожиданное открытие: модели-декодеры — модели в духе GPT, о которых весь этот учебник, — могут работать вообще без позиционного кодирования (NoPE). Порядок просачивается через саму каузальную маску — правило, по которому токен видит только предыдущие токены: токен 1 видит один токен, токен 100 — сотню, и модель учится извлекать позицию из этого. Некоторые недавние модели чередуют слои NoPE со слоями RoPE, чтобы лучше вести себя на длинных входах.

    Общая нить у всех схем: для языка важнее всего то, насколько далеко назад стояло слово, — ровно это и кодируют RoPE и ALiBi.

    Чтение за пределами обучающей длины

    Углы RoPE для расстояний, которых модель не видела при обучении, ей незнакомы, поэтому модель, обученная на 8 тыс. токенов, разваливается на 32 тыс. Если замедлить повороты (интерполяция позиций, NTK-масштабирование, YaRN), длинные расстояния снова отображаются на знакомые углы. Глава 12 показывает, как это работает и что ещё ломается на длинных входах.

    Итоги

    • Само по себе внимание порядок не учитывает, поэтому позицию нужно добавить.
    • Синусоидальные и выученные кодировки прибавляют вектор позиции к каждому эмбеддингу. Синусоиды работают как стрелки часов с разной скоростью: маленькие числа, уникальный код для каждой позиции, похожие коды у соседей.
    • RoPE поворачивает запросы и ключи в зависимости от позиции, и оценки внимания начинают зависеть только от относительного расстояния. Сегодня это стандарт в открытых моделях.
    • ALiBi напрямую штрафует расстояние, а NoPE полагается на одну каузальную маску.

    Теперь, когда порядок учтён — в самих векторах или внутри внимания, — можно посмотреть, как токены обмениваются информацией: внимание.