Modelele bazate pe transformatoare sunt renumite pentru capacitatea lor de a analiza și interpreta text complex. Ei se bazează pe înțelegerea ordinii și contextului cuvintelor - sarcini la care metodele tradiționale de codificare pozițională și-au arătat limitele. Remediind acest decalaj, modelul ROFORMER, alimentat de Rotary Position Embedding (RoPE), redefinește abordarea noastră cu privire la codificarea pozițională.
Codificare pozițională tradițională
Transformers tratează textul ca pe o serie de jetoane și permit procesarea paralelă a secvențelor pentru o eficiență mai mare. Cu toate acestea, această forță și-a adus provocarea: agnosticismul inerent al modelului la ordinea simbolului. Codificarea pozițională a fost răspunsul, furnând fiecărui jeton o semnătură unică care denotă poziția sa secvență.
Înglobări de poziție absolută
Inițial, modele precum BERT au folosit înglobare de poziție absolută, atribuind un vector fix fiecărei poziții dintr-o secvență. Această metodă, deși simplă, în mod inerent nu are capacitatea de a se adapta la variațiile de lungime a secvenței sau de a sublinia distanțele relative dintre jetoane, critice pentru înțelegerea multor constructe lingvistice.
Înglobări de poziție relativă
Pentru a surprinde natura dinamică a limbajului, au fost introduse înglobări de poziție relativă, concentrându-se mai degrabă pe distanța dintre jetoane decât pe pozițiile lor absolute. În ciuda avantajului lor conceptual, aceste înglobări au introdus complexitate computațională și nu au reușit să se integreze perfect în mecanismul de auto-atenție al Transformers, limitându-le eficacitatea.
ROFORMER și încorporarea poziției rotative
Recunoscând limitările strategiilor de codificare pozițională existente, ROFORMER introduce Rotary Position Embedding (RoPE), o abordare care combină beneficiile informațiilor despre poziție absolută și relativă, fără dezavantajele acestora.
Încorporarea poziţiei rotative
RoPE codifică informațiile de poziție folosind matrice de rotație, permițând modelului să înțeleagă nu doar unde se află un jeton, ci și cum se leagă cu fiecare alt jeton dintr-o secvență.
Credit: ArXiv
Funcționează printr-o lentilă geometrică, tratând pozițiile jetonelor ca puncte într-un spațiu multidimensional care sunt rotite pentru a marca relațiile lor secvențiale. Această rotație permite modelului să păstreze și să exploateze indicii poziționali absoluti și relativi în cadrul mecanismului său de autoatenție.
Implementarea RoPE
Implementarea RoPE implică codificarea poziției fiecărui jeton într-o matrice de rotație și aplicarea acestei matrice în cadrul mecanismului de auto-atenție al transformatorului. Acest proces permite o interpretare flexibilă și dinamică a informațiilor poziționale, găzduind lungimi variabile ale secvenței și captând esența interrelațiilor de simboluri fără suprasarcină de calcul semnificativă.
Mai întâi, veți avea nevoie de o funcție pentru a genera înglobările rotative, apoi veți integra aceste înglobări în modelul dvs. Exemplul de mai jos presupune că sunteți familiarizat cu crearea de straturi personalizate în Keras.
Pasul 1: Definiți funcția de încorporare rotativă
Această funcție generează înglobările rotative având în vedere lungimea maximă a secvenței și dimensionalitatea înglobărilor.
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))
Această linie calculează inversul frecvențelor scalate exponențial pe baza indicilor de poziție. Aceste frecvențe sunt utilizate în generarea de modele sinusoidale pentru înglobări rotative, ceea ce ajută la codificarea informațiilor de poziție relativă în secvențe. Acest mecanism este deosebit de util în sarcinile în care înțelegerea ordinii și a poziționării relative a elementelor este crucială, cum ar fi procesarea limbajului natural sau analiza seriilor de timp.
In detalii:
-
tf.range(0, dim, 2, dtype=tf.float32)
creează un interval de valori începând de la 0 până ladim
(exclusiv), trecând cu 2. Argumentuldtype=tf.float32
specifică că elementele acestui tensor sunt numere în virgulă mobilă de 32 de biți. Dacă „dim” este 8, de exemplu, aceasta ar produce „[0, 2, 4, 6]”. -
Tensorul produs de
tf.range
este apoi împărțit la dimensionalitatea (dim
) înglobărilor. Această operație micșorează acești indici la un interval între 0 și 1 (exclusiv dacădim
este par, ușor inclusiv dacădim
este impar, deoarece treapta de interval omite orice altă valoare). Continuând exemplul cudim
= 8, împărțind la 8 rezultă[0,0, 0,25, 0,5, 0,75]
. -
Operația
10000 ** (...)
ridică 10.000 la puterea fiecărui element din tensorul scalat anterior. Baza de 10.000 este oarecum arbitrară, dar este aleasă pentru a se asigura că frecvențele variază pe o gamă largă, ceea ce ajută modelul să diferențieze mai eficient diferitele poziții. Pentru[0.0, 0.25, 0.5, 0.75]
, ar aplica operația de putere fiecăruia, rezultând valori mult mai mari pentru elementele superioare. -
In final, frecventa inversa se obtine prin luarea reciproca (1/x) a valorilor din pasul anterior. Frecvențele inverse sunt mai mici pentru indici mai mari, ceea ce înseamnă că elementele mai departe din secvență vor avea frecvențe mai mici, afectând modul în care pozițiile lor sunt codificate în model. Acest lucru permite înglobărilor să se scaleze într-un mod în care pozițiile relative pot fi deduse prin mecanismele de atenție ale modelului.
Linia:
freqs = tf.einsum('i,j->ij', t, inv_freq)
folosește funcția tf.einsum
a lui TensorFlow, un instrument care permite exprimarea concisă și eficientă a operațiilor tensorilor folosind notația de însumare Einstein.
Această operație calculează efectiv produsul exterior al vectorilor t
și inv_freq
, rezultând o matrice în care fiecare element (i, j)
este produsul dintre i
-lea element al lui t
și j
-al-lea element al inv_freq
. Această matrice (frecvențe
) reprezintă frecvențele care sunt utilizate pentru a genera modelele sinusoidale pentru înglobările rotative.
Pasul 2: Strat Keras personalizat pentru înglobări rotative
Acum, să creăm un strat Keras personalizat care aplică înglobări rotative tensorului de intrare. Acest strat presupune că tensorul de intrare are forma (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
Linia embeddings = self.rotary_embeddings[:seq_len]
selectează subsetul adecvat de înglobări rotative precalculate pe baza lungimii secvenței de intrare curente. Deoarece lungimea secvențelor poate varia de la un lot la altul, această operație de tăiere asigură că sunt utilizate numai înglobările corespunzătoare lungimii reale a secvenței.
Variabila embeddings
deține acum un tensor de formă (seq_len, embedding_dim)
, unde seq_len
este lungimea secvențelor din lotul curent, iar embedding_dim
este dimensionalitatea înglobărilor. Acest tensor conține înglobările poziționale rotative pentru fiecare poziție din secvența până la seq_len
.
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
combină transformările sinus și cosinus ale frecvențelor poziționale într-un singur tensor:
-tf.cos(freqs)
și tf.sin(freqs)
aplică transformările cosinus și, respectiv, sinus la tensorul freqs
. Tensorul „frecvențe” conține valori de frecvență pentru fiecare poziție din secvența de intrare și fiecare dimensiune a spațiului de încorporare, calculate pe baza pozițiilor secvenței și a frecvențelor inverse ale dimensiunilor de încorporare. Funcțiile sinus și cosinus sunt aplicate în funcție de element, rezultând doi tensori de aceeași formă ca „frecvențe”. Aceste transformări ajută la codificarea poziției într-un mod care surprinde natura ciclică a relațiilor poziționale, facilitând capacitatea modelului de a înțelege pozițiile relative.
-tf.concat((tf.cos(freqs), tf.sin(freqs)), axis=-1)
concatenează tensorii transformați cosinus și sinus de-a lungul ultimei axe (notat cu axis=-1
). Concatenarea acestor tensori unul lângă altul dublează efectiv dimensionalitatea tensorului „frecvențe”, prima jumătate reprezentând valori transformate în cosinus, iar a doua jumătate reprezentând valori transformate în sinus pentru fiecare poziție. Concatenarea asigură că fiecare codificare pozițională conține atât informații sinus, cât și cosinus, ceea ce permite păstrarea informațiilor atât despre amplitudinea, cât și despre faza semnalelor poziționale.
- Tensorul concatenat
emb
deține acum înglobările rotative complete pentru pozițiile de intrare. Forma luiemb
va fi aceeași cufreqs
în primele două dimensiuni (corespunzând pozițiilor secvenței și dimensiunilor de încorporare), dar ultima sa dimensiune va fi de două ori mai mare, luând în considerare atât valorile sinusului, cât și ale cosinusului. Aceste înglobări sunt utilizate pentru a modula înglobarile de intrare prin adăugarea de informații de poziție într-o manieră echivalentă rotațional.
-cos_emb = embeddings[:, None, :self.dim // 2]
:
-
Primele două puncte
:
înseamnă „selectați toate elementele din această dimensiune”, care, în acest caz, se referă la toate pozițiile din secvență. -
„Niciunul” este folosit pentru a adăuga o dimensiune suplimentară, făcând tensorul tridimensional. Acest lucru se face adesea pentru a asigura compatibilitatea cu anumite operațiuni care așteaptă intrări de un anumit număr de dimensiuni. De exemplu, atunci când se efectuează înmulțirea în funcție de elemente cu un alt tensor care este tridimensional, formele trebuie să se alinieze conform regulilor de difuzare.
-
:self.dim // 2
, selectează prima jumătate a cotelor din ultima axă. Deoarece „embedding_dimension” este dublată pentru a include atât valorile sinus, cât și valorile cosinus, împărțirea la 2 selectează efectiv doar componentele cosinus ale înglobărilor.
Pasul 3: Integrarea cu un model Keras
După definirea „RotaryEmbeddingLayer”, îl puteți integra în modelul Keras. Acest strat ar trebui aplicat pe înglobările dvs. înainte de a le introduce în straturi de atenție sau în orice straturi de model ulterioare.
Iată un exemplu simplificat de integrare a înglobărilor rotative într-un model:
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()