Rotary Positional Embeddings — это современная техника позиционного кодирования для обработки естественного языка (NLP). Многие популярные большие языковые модели, такие как Llama, Llama2, PaLM и CodeGen, уже используют её вместо абсолютных эмбеддингов, которые использовались в оригинальной статье "Attention is all you need", или относительного позиционного кодирования, предложенного в статье "Self-Attention with Relative Position Representations".
До появления Rotary Positional Embedding не было однозначного ответа на вопрос, что лучше — абсолютное или относительное позиционное кодирование. Однако, как всегда, при сравнении двух техник ответ заключается в их комбинации, что и даёт нам Rotary Positional Embedding.
Тут мы обсудим:
Для начала:
Мы знаем, что нам нужно позиционное кодирование, потому что self-attention не учитывает позицию слова в последовательности.
Абсолютное позиционное кодирование — это форма позиционного кодирования, где мы используем синусоидальные функции для создания позиционного эмбеддинга (t, d), где каждый элемент (d,) соответствует позиции (pos1, pos2, ..., pos_T). Абсолютные позиционные кодировки добавляются поэлементно к входным эмбеддингам токеннов.
Относительное позиционное кодирование — это форма позиционного кодирования, где мы используем парные расстояния между одной позицией и другими позициями. Относительные позиционные кодировки добавляются поэлементно к матрице внимания формы (B, T, T) перед слоем Softmax.
Хотя абсолютное позиционное кодирование захватывает позиционную информацию для слова, оно не захватывает позиционную информацию для всего предложения (или последовательности!).
Пример:
Общий способ создания абсолютного позиционного кодирования длиной 3 — это случайная инициализация.
→ Предположим, мы получаем следующее: [0.1, 0.01, 0.5]. (Это абсолютное позиционное кодирование обеспечит, чтобы одинаковые слова в разных позициях имели разные выходы внимания) Однако обратите внимание, что происходит, если мы проанализируем абсолютное позиционное кодирование: [0.1, 0.01, 0.05]:
(1) Нет отношений между позициями.
(2) Относительные расстояния непостоянны.
Необходимо создать дополнительный шаг для само-внимания позже.
Во время инференса для ускорения работы хочется использовать метод, называемый KV cache, который помогает увеличить скорость вычислений.
Одно из требований для использования KV cache заключается в том, чтобы позиционное кодирование для слов, которые уже были сгенерированы, не изменялось при генерации новых слов (что обеспечивает абсолютное позиционное кодирование).
Относительные позиционные кодировки поэтому не подходят для KV-cache, потому что вложения для каждого токена изменяются на каждом новом временном шаге.
Пример:
Когда ваша последовательность имеет длину 2, относительные позиции для слова будут [-1, 0, 1]. Когда ваша входная последовательность имеет длину 3, относительные позиции для слова будут [-2, -1, 0, 1, 2]. Поскольку мы используем эти относительные позиции для получения относительных позиционных кодировок для каждого слова, когда набор относительных позиций изменяется, ваши позиционные кодировки для каждого слова изменяются.
Это означает, что для предложения ['это', 'супер', 'круто'], ваше позиционное кодирование слова 'это' изменяется на каждом временном шаге, когда вы генерируете токены для вывода.
Поэтому относительное позиционное кодирование сейчас почти не используется.
Rotary Positional Encoding — это тип позиционного кодирования, который кодирует абсолютную позиционную информацию с помощью матрицы вращения и естественным образом включает явную зависимость относительной позиции в формулировку само-внимания.
Что такое матрица вращения?
Как следует из её названия, матрица вращения — это матрица, которая вращает вектор в другой вектор на некоторый угол. Матрица вращения выводится из тригонометрических свойств синуса и косинуса, которые мы изучали в школе, и должно быть достаточно интуитивно понятной матрицы вращения, используя двумерную, как показано ниже! (См. ссылку ниже для вывода двумерной матрицы вращения, если вы не уверены).
Рис.1. Двумерное вращение
Мы видим, что матрица вращения сохраняет величину (или длину) исходного вектора \(r\), как видно на изображении выше, и единственное, что изменяется, — это угол к оси x.
Как двумерная матрица вращения участвует в Rotary Positional Encoding?
Рис.2. Применяем двумерное вращение к компнентам эмбеддинга токена
Формула вычисления углов для Rotary Positional Embedding (RoPE) зависит от номера позиции m и пары размерностей эмбеддинга. Для каждой пары размерностей i угол \(θ(m,i)\) рассчитывается как:
\[ \begin{align} θ(m,i)=m⋅θ_i \\ θ_i= 10000^{-2(i-1) / d} \\ i = 1, 2, \ldots, \frac{d}{2} \\ \end{align}\]
Здесь:
Эта формула гарантирует, что углы вращения зависят от относительного положения токенов и уникальны для каждой пары размерностей.
Гиперпараметры d и 10000 (base) задаются заранее и не изменяются в процессе обучения.
Первый вопрос, который возникает когда это видишь: "Почему?"
Поскольку наши векторы имеют гораздо более двух измерений, и легко понять двумерную матрицу вращения, авторы придумали умный способ использовать двумерную матрицу вращения для вращения всего d-мерного вектора.
Идея заключается в использовании различных двумерных матриц вращения для каждой пары измерений (каждый набор векторов размером 2) на некоторый угол. Поскольку мы используем "пары измерений", нам потребуется, чтобы \(d\) делилось на 2, что не проблема, все размерности моделей четны.
Пример:
Дано предложение [this, is, awesome], давайте посмотрим, как оно вращает позиционное кодирование для слов 'this' и 'is'. Предположим, что вложения для слов являются шестимерными векторами:
Мы знаем, что:
Для каждого слова, поскольку у нас есть 3 пары двумерных векторов, у нас будет 3 разных θ, которые разделяются между словами 'this' и 'is'.
Поскольку у нас есть 3 пары двумерных векторов (в наших примерах вложения являются шестимерными векторами), у нас будет: (1) 3 матрицы вращения для каждого слова, и, следовательно, (2) 3 угла для каждого слова (рассчитанные как \(m * θ_i\)) для вращения.
Если мы подставим эти значения в матрицу вращения выше и умножим наш шестимерный вектор для слова 'this':
Если мы подставим эти значения в матрицу вращения выше и умножим наш шестимерный вектор для слова 'is':
Это означает, что каждый эмбеддинг токена вращается тремя парами матриц вращения, каждая с различными вращениями, которые зависят от позиций каждого токена.
Это означает, что позиционное кодирование будет учитывать позицию слова, и теперь мы сможем генерировать разные выходы внимания для одного и того же слова в разных позициях.
Rotary Positional Encoding преодолевает недостатки, комбинируя два подхода - абсолютного и относительного позиционного кодирования.
Абсолютное позиционное кодирование в Rotary Positional Encoding
Обратите внимание в нашем примере для Rotary Positional Encoding для 'this' и 'is', что матрица вращения зависит только от \(m * \theta_i\), где \(\theta_i\) разделяется между всеми словами, а \(m\) — это просто позиция слова (без учета позиций других слов). Подобно абсолютному позиционному кодированию, у нас есть одно позиционное кодирование для одной позиции (не зависящее от позиций других слов).
Относительное позиционное кодирование в Rotary Positional Encoding
Поскольку угол вращения зависит от \(m * \theta_i\), слова в начале предложения будут иметь меньший угол вращения, а слова, достигающие конца предложения, будут иметь больший угол вращения, делая расстояния относительными.
Подобно относительному позиционному кодированию, существует связь между позиционными кодировками разных позиций.
Теперь, чтобы объяснить, почему оно преодолевает недостатки, давайте рассмотрим недостатки обоих методов:
Абсолютное позиционное кодирование:
Относительное позиционное кодирование:
Поэтому мы знаем, что Rotary Positional Encoding должно иметь следующие свойства:
Рис.3. Rotary Positional Encoding включает относительную позиционную информацию
(Свойство 1) Rotary Positional Encoding включает относительную позиционную информацию
Rotary Positional Encoding включает относительную позиционную информацию, делая углы в матрице вращения зависимыми от текущей позиции слова, и благодаря свойствам матрицы вращения, говорит нам, насколько далеко друг от друга находятся два слова. Например, на рис. 3 выше, угол между 'свиньей' и 'собакой' одинаков, даже если они (1) появляются в двух предложениях разной длины, и (2) 'свинья' и 'собака' появляются в разных позициях предложения!
Другой способ взглянуть на это — построить график угла внутри матрицы вращения для разных позиций:
Помните, что угол внутри матрицы вращения обозначается как: \(m * \theta_i\)
Для графика ниже вы должны смотреть на восходящий сдвиг всего графика, чтобы исследовать влияние изменения m (позиции слова).
Рис.4. График углов внутри матрицы вращения для разных позиций от pos-1 до pos-7.
Из графика мы видим последовательный шаблон: когда t увеличивается от 1 до 7, угол вращения также увеличивается (как показано сдвигом всего графика). Это означает, что если позиционное кодирование слова имеет больший угол вращения, слово находится дальше в предложении. → Разрыв между t=1 и t=3 больше, чем разрыв между t=1 и t=2.
Из графика мы замечаем, что величина различий между двумя последовательными парами позиций одинакова. → Это означает, что изменение на одном временном шаге вызвало примерно такое же изменение угла вращения. Например, разрывы между t=1 и t=2 такие же, как между t=2 и t=3, и так далее. → Это означает, что относительные расстояния согласованы. Например, любые два слова, находящиеся на расстоянии трех слов друг от друга, будут иметь одинаковый угол вращения.
Другая перспектива для свойства 1 RoPE
До сих пор мы говорили только об "угле вращения", но где та часть, которая кодирует относительные расстояния от одного слова до других позиций? Ответ кроется в следующем определении скалярного произведения:
Если мы вычислим скалярное произведение между позиционным кодированием вращения позиции 1 и другими позициями, мы получим cos(θ), который зависит только от угла вращения (более явно от разницы в угле вращения от позиции 1 до позиции другого слова).
Угол вращения для позиции = 1 меньше углов вращения для позиций > 1. Это означает, что чем дальше другое слово от позиции 1, (1) тем больше разница в угле вращения, (2) тем меньше скалярное произведение (потому что значение косинуса падает от 0 до \(\pi\)), (3) тем дальше другое слово от позиции 1.
Мы приходим к выводу, что позиционное кодирование вращения включает относительную позиционную информацию.
Как использовать Rotary Positional Encoding:
Пример реализации на RoPE на pytorch https://github.com/lucidrains/rotary-embedding-torch/blob/main/rotary_embedding_torch/rotary_embedding_torch.py
(Sвойство 2) Rotary Positional Encoding вычислительно эффективно
Может показаться, что использование Rotary Positional Encoding не является вычислительно эффективным, потому что нам нужно создать матрицу вращения, но исследователи нашли вычислительно эффективную формулу.
Рис.5. Rotary Positional Embeddings (RoPE)
(Sвойство 3) Rotary Positional Encoding подходит для инференса
Поскольку мы уже знаем, что Rotary Positional Encoding похоже на абсолютное позиционное кодирование в том, что кодировки зависят только от текущей позиции слова, позиционные кодировки для слов, которые уже были сгенерированы, не изменяются, что снова делает возможным использование KV cache во время вывода.
Итого: