Моделі на основі трансформаторів відомі своєю здатністю аналізувати та інтерпретувати складний текст. Вони покладаються на розуміння порядку та контексту слів - завдання, у яких традиційні методи позиційного кодування показали свої межі. Усуваючи цю прогалину, модель ROFORMER на базі Rotary Position Embedding (RoPE) переосмислює наш підхід до позиційного кодування.
Традиційне позиційне кодування
Transformers розглядають текст як серію токенів і дозволяють паралельну обробку послідовностей для більшої ефективності. Однак ця перевага принесла свій виклик: притаманний моделі агностицизм до символічного порядку. Позиційне кодування було відповіддю, забезпечуючи кожному маркеру унікальний підпис, що позначає його позицію в послідовності.
Вбудовування абсолютної позиції
Спочатку такі моделі, як 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)
використовує функцію TensorFlow tf.einsum
, інструмент, який дозволяє стисле та ефективне вираження тензорних операцій за допомогою нотації підсумовування Ейнштейна.
Ця операція фактично обчислює зовнішній добуток векторів t
і inv_freq
, у результаті чого отримується матриця, де кожен елемент (i, j)
є добутком i
-го елемента t
і j
-й елемент inv_freq
. Ця матриця (freqs
) представляє частоти, які використовуються для генерації синусоїдальних візерунків для поворотних вставок.
Крок 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
). Об’єднання цих тензорів пліч-о-пліч ефективно подвоює розмірність тензора «freqs», причому перша половина представляє значення, перетворені в косинус, а друга половина — значення, перетворені в синус для кожної позиції. Конкатенація гарантує, що кожне позиційне кодування містить як синусну, так і косинусну інформацію, що дозволяє зберегти інформацію як про амплітуду, так і про фазу позиційних сигналів.
— Конкатенований тензор emb
тепер містить повні поворотні вкладення для вхідних позицій. Форма emb
буде такою самою, як freqs
, у своїх перших двох вимірах (що відповідають позиціям послідовності та розмірам вбудовування), але його останній вимір буде вдвічі більшим, враховуючи як значення синуса, так і косинуса. Ці вбудовування використовуються для модуляції вхідних вбудовувань шляхом додавання позиційної інформації ротаційно-еквіваріантним способом.
-cos_emb = embeddings[:, None, :self.dim // 2]
:
-
Перша двокрапка
:
означає «вибрати всі елементи в цьому вимірі», що в даному випадку відноситься до всіх позицій у послідовності. -
None
використовується для додавання додаткового виміру, що робить тензор тривимірним. Це часто робиться для забезпечення сумісності з певними операціями, які очікують введення певної кількості розмірів. Наприклад, під час виконання поелементного множення з іншим тривимірним тензором фігури мають вирівнюватися відповідно до правил трансляції. -
: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()