Модели на основе трансформаторов известны своей способностью анализировать и интерпретировать сложный текст. Они полагаются на понимание порядка и контекста слов — задачи, в которых традиционные методы позиционного кодирования показали свои ограничения. Чтобы устранить этот пробел, модель ROFORMER, основанная на Rotary Position Embedding (RoPE), переопределяет наш подход к позиционному кодированию.
Традиционное позиционное кодирование
Преобразователи рассматривают текст как серию токенов и позволяют параллельную обработку последовательностей для большей эффективности. Однако эта сила принесла с собой проблему: присущий модели агностицизм по отношению к символическому порядку. Ответом стало Позиционное кодирование, предоставляющее каждому токену уникальную подпись, обозначающую его позицию в последовательности.
Встраивание абсолютных позиций
Первоначально такие модели, как BERT, использовали встраивание абсолютных позиций, присваивая фиксированный вектор каждой позиции в последовательности. Несмотря на простоту этого метода, по своей сути не хватает возможности адаптироваться к изменениям длины последовательности или подчеркнуть относительные расстояния между токенами, что имеет решающее значение для понимания многих лингвистических конструкций.
Встраивание относительных позиций
Чтобы отразить динамическую природу языка, были введены встраивания относительных позиций, ориентированные на расстояние между токенами, а не на их абсолютные позиции. Несмотря на свое концептуальное преимущество, эти встраивания создавали вычислительную сложность и не смогли плавно интегрироваться в механизм самообслуживания Трансформеров, что ограничивало их эффективность.
ROFORMER и встраивание вращающихся позиций
Признавая ограничения существующих стратегий позиционного кодирования, ROFORMER представляет Rotary Position Embedding (RoPE) — подход, который сочетает в себе преимущества информации об абсолютном и относительном положении без их соответствующих недостатков.
Встраивание поворотного положения
RoPE кодирует информацию о положении с помощью матриц вращения, позволяя модели понимать не только то, где находится токен, но и то, как он связан с каждым другим токеном в последовательности.
Credit: ArXiv
Он действует через геометрическую линзу, рассматривая позиции токенов как точки в многомерном пространстве, которые вращаются, чтобы обозначить их последовательные отношения. Такое вращение позволяет модели сохранять и использовать как абсолютные, так и относительные позиционные сигналы в рамках механизма самообслуживания.
Реализация RoPE
Реализация RoPE включает кодирование позиции каждого токена в матрицу вращения и применение этой матрицы в механизме самообслуживания Transformer. Этот процесс обеспечивает гибкую, динамическую интерпретацию позиционной информации, учитывая различную длину последовательности и улавливая суть взаимосвязей токенов без значительных вычислительных затрат.
Сначала вам понадобится функция для создания вращающихся внедрений, а затем вы интегрируете эти внедрения в свою модель. В приведенном ниже примере предполагается, что вы знакомы с созданием пользовательских слоев в Keras.
Шаг 1. Определите функцию поворотного внедрения
Эта функция генерирует вращающиеся вложения с учетом максимальной длины последовательности и размерности вложений.
from tensorflow.keras.layers import Layer
import numpy as np
def get_rotary_embedding(dim, max_seq_len):
inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
t = tf.range(max_seq_len, dtype=tf.float32)
freqs = tf.einsum('i,j->ij', t, inv_freq)
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
return emb
inv_freq = 1.0 / (10000 ** (tf.range(0, dim, 2, dtype=tf.float32) / dim))
Эта строка вычисляет обратную экспоненциально масштабированную частоту на основе индексов положения. Эти частоты используются при создании синусоидальных шаблонов для вращающихся вложений, что помогает кодировать информацию об относительном положении в последовательностях. Этот механизм особенно полезен в задачах, где понимание порядка и относительного расположения элементов имеет решающее значение, например, при обработке естественного языка или анализе временных рядов.
Подробно:
-
tf.range(0, dim, 2, dtype=tf.float32)
создает диапазон значений от 0 доdim
(исключающий), шагая на 2. Аргументdtype=tf.float32
указывает что элементами этого тензора являются 32-битные числа с плавающей запятой. Например, еслиdim
равен 8, это создаст[0, 2, 4, 6]
. -
Тензор, созданный с помощью
tf.range
, затем делится на размерность (dim
) вложений. Эта операция масштабирует эти индексы до диапазона от 0 до 1 (исключительно, еслиdim
четное, слегка включительно, еслиdim
нечетное, поскольку шаг диапазона пропускает все остальные значения). Продолжая пример сdim
= 8, деление на 8 дает[0,0, 0,25, 0,5, 0,75]
. -
Операция
10000 ** (...)
возводит 10 000 в степень каждого элемента в ранее масштабированном тензоре. База в 10 000 несколько произвольна, но выбрана для того, чтобы частоты варьировались в широком диапазоне, что помогает модели более эффективно различать разные положения. Для[0,0, 0,25, 0,5, 0,75]
к каждому из них будет применена степенная операция, в результате чего значения для более высоких элементов будут намного больше. -
Наконец, обратная частота получается путем взятия обратной величины (1/x) значений из предыдущего шага. Обратные частоты меньше для более высоких индексов, а это означает, что элементы, расположенные дальше в последовательности, будут иметь меньшие частоты, что влияет на то, как их позиции кодируются в модели. Это позволяет встраиваниям масштабироваться таким образом, что относительные положения могут быть выведены с помощью механизмов внимания модели.
Линия:
freqs = tf.einsum('i,j->ij', t, inv_freq)
использует функцию tf.einsum TensorFlow, инструмент, который позволяет кратко и эффективно выражать тензорные операции с использованием нотации суммирования Эйнштейна.
Эта операция эффективно вычисляет внешнее произведение векторов t
и inv_freq
, в результате чего получается матрица, в которой каждый элемент (i, j)
является произведением i
-го элемента t
и j
-й элемент inv_freq
. Эта матрица («частоты») представляет частоты, которые используются для генерации синусоидальных структур для вращающихся вложений.
Шаг 2. Пользовательский слой Keras для вращающихся вложений
Теперь давайте создадим собственный слой Keras, который применяет вращающиеся внедрения к входному тензору. Этот слой предполагает, что входной тензор имеет форму (batch_size, Sequence_length, embedding_dim)
.
class RotaryEmbeddingLayer(Layer):
def __init__(self, dim, max_seq_len, **kwargs):
super().__init__(**kwargs)
self.dim = dim
self.max_seq_len = max_seq_len
self.rotary_embeddings = get_rotary_embedding(dim, max_seq_len)
def call(self, inputs):
seq_len = tf.shape(inputs)[1]
embeddings = self.rotary_embeddings[:seq_len]
cos_emb = embeddings[:, None, :self.dim // 2]
sin_emb = embeddings[:, None, self.dim // 2:]
# Decompose inputs into sine and cosine components
inputs_cos = inputs[..., :self.dim // 2]
inputs_sin = inputs[..., self.dim // 2:]
# Apply rotary embeddings
rotated_cos = inputs_cos * cos_emb - inputs_sin * sin_emb
rotated_sin = inputs_sin * cos_emb + inputs_cos * sin_emb
return tf.concat([rotated_cos, rotated_sin], axis=-1)
def get_config(self):
config = super().get_config()
config.update({
"dim": self.dim,
"max_seq_len": self.max_seq_len
})
return config
Строка embeddings = self.rotary_embeddings[:seq_len]
выбирает подходящее подмножество предварительно вычисленных вращающихся вложений на основе текущей длины входной последовательности. Поскольку длина последовательностей может варьироваться от одного пакета к другому, эта операция нарезки гарантирует, что используются только вложения, соответствующие фактической длине последовательности.
Переменная embeddings
теперь содержит тензор формы (seq_len, embedding_dim)
, где seq_len
— это длина последовательностей в текущем пакете, а embedding_dim
— размерность вложений. Этот тензор содержит вращающиеся позиционные вложения для каждой позиции в последовательности до seq_len.
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
объединяет синусоидальные и косинусоидальные преобразования позиционных частот в один тензор:
-tf.cos(freqs)
и tf.sin(freqs)
применяют косинусное и синусоидальное преобразования соответственно к тензору freqs
. Тензор freqs
содержит значения частоты для каждой позиции во входной последовательности и каждого измерения пространства внедрения, рассчитанные на основе позиций последовательности и обратных частот измерений внедрения. Функции синуса и косинуса применяются поэлементно, в результате чего получаются два тензора той же формы, что и freqs
. Эти преобразования помогают кодировать положение таким образом, чтобы отразить циклический характер позиционных отношений, облегчая способность модели понимать относительные положения.
-tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
объединяет тензоры, преобразованные в косинус и синус, вдоль последней оси (обозначается axis=-1
). Объединение этих тензоров рядом эффективно удваивает размерность тензора частот: первая половина представляет значения, преобразованные в косинус, а вторая половина представляет значения, преобразованные в синусоиду для каждой позиции. Конкатенация гарантирует, что каждое позиционное кодирование содержит как синусоидальную, так и косинусоидальную информацию, что позволяет сохранить информацию как об амплитуде, так и о фазе позиционных сигналов.
— Объединенный тензор emb
теперь содержит полные вращающиеся вложения для входных позиций. Форма emb
будет такой же, как freqs
в первых двух измерениях (соответствующих позициям последовательности и размерам внедрения), но его последнее измерение будет в два раза больше, с учетом значений как синуса, так и косинуса. Эти внедрения используются для модуляции входных внедрений путем добавления позиционной информации вращательно-эквивариантным способом.
-cos_emb = embeddings[:, None, :self.dim // 2]
:
-
Первое двоеточие
:
означает «выбрать все элементы в этом измерении», что в данном случае относится ко всем позициям в последовательности. -
«Нет» используется для добавления дополнительного измерения, что делает тензор трехмерным. Это часто делается для обеспечения совместимости с определенными операциями, которые ожидают ввода определенного количества измерений. Например, при выполнении поэлементного умножения с другим трехмерным тензором фигуры должны выравниваться в соответствии с правилами трансляции.
-
:self.dim // 2
, выбирает первую половину размеров на последней оси. Посколькуembedding_dimension
удваивается и включает значения как синуса, так и косинуса, деление на 2 эффективно выбирает только косинусные компоненты вложений.
Шаг 3. Интеграция с моделью Keras
После определения RotaryEmbeddingLayer вы можете интегрировать его в свою модель Keras. Этот слой следует применять к внедренным объектам перед их передачей в слои внимания или любые последующие слои модели.
Вот упрощенный пример того, как интегрировать вращающиеся вставки в модель:
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Dense
max_seq_len = 512
embedding_dim = 64
inp = Input(shape=(max_seq_len,))
x = Embedding(input_dim=10000, output_dim=embedding_dim)(inp)
x = RotaryEmbeddingLayer(dim=embedding_dim, max_seq_len=max_seq_len)(x)
# Add your model's layers here, e.g., Transformer blocks
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=inp, outputs=x)
model.summary()