A Transformer-alapú modellek híresek összetett szövegek elemzésére és értelmezésére való képességükről. A szavak sorrendjének és kontextusának megértésére támaszkodnak – olyan feladatok, amelyeknél a hagyományos pozíciókódolási módszerek megmutatták korlátaikat. Ezt a hiányosságot orvosolva a ROFORMER modell, amelyet a Rotary Position Embedding (RoPE) hajt meg, újradefiniálja a helyzetkódoláshoz való hozzáállásunkat.
Hagyományos pozíciókódolás
A transzformátorok a szöveget tokenek sorozataként kezelik, és lehetővé teszik a sorozatok párhuzamos feldolgozását a nagyobb hatékonyság érdekében. Ez az erő azonban meghozta a maga kihívását: a modellben rejlő agnoszticizmust jelképes sorrendben. A pozíciós kódolás volt a válasz, minden jogkivonat egyedi aláírással látva el a szekvencia pozícióját.
Abszolút pozíció beágyazások
Kezdetben az olyan modellek, mint a BERT, abszolút pozíció beágyazást használtak, és egy fix vektort rendeltek a sorozat minden pozíciójához. Ez a módszer, bár egyszerű, hiányzik a szekvenciahossz-változásokhoz való alkalmazkodás képessége, vagy a tokenek közötti relatív távolságok hangsúlyozása, ami számos nyelvi konstrukció megértéséhez kritikus.
Relatív pozíció beágyazások
A nyelv dinamikus természetének megragadására a relatív pozíció beágyazásokat vezették be, amelyek a tokenek közötti távolságra helyezték a hangsúlyt az abszolút pozíciójuk helyett. Koncepcionális előnyük ellenére ezek a beágyazások számítási bonyolultságot vezettek be, és nem tudtak zökkenőmentesen integrálódni a Transformers önfigyelő mechanizmusába, ami korlátozta hatékonyságukat.
ROFORMER és Rotary Position beágyazás
Felismerve a meglévő pozíciókódolási stratégiák korlátait, a ROFORMER bevezeti a Rotary Position Embedding (RoPE) megoldást, amely egyesíti az abszolút és relatív pozícióinformációk előnyeit a megfelelő hátrányok nélkül.
Rotary Position Beágyazás
A RoPE forgási mátrixok segítségével kódolja a helyzetinformációkat, lehetővé téve a modell számára, hogy ne csak azt tudja, hol van egy token, hanem azt is, hogyan viszonyul a sorozat összes többi tokenjéhez.
Credit: ArXiv
Geometriai lencsén keresztül működik, és a token pozíciókat pontként kezeli egy többdimenziós térben, amelyeket elforgatva jelöli egymás utáni kapcsolataikat. Ez a forgatás lehetővé teszi, hogy a modell megőrizze és kihasználja mind az abszolút, mind a relatív pozíciójeleket az önfigyelő mechanizmusán belül.
Kötél megvalósítása
A RoPE megvalósítása magában foglalja az egyes token pozíciók forgatási mátrixba való kódolását, és ennek a mátrixnak a transzformátor önfigyelő mechanizmusán belüli alkalmazását. Ez a folyamat lehetővé teszi a pozícióinformációk rugalmas, dinamikus értelmezését, alkalmazkodva a változó sorozathosszokhoz, és jelentős számítási többletköltség nélkül megragadja a token kölcsönhatások lényegét.
Először is szüksége lesz egy függvényre a forgó beágyazások létrehozásához, majd integrálja ezeket a beágyazásokat a modelljébe. Az alábbi példa feltételezi, hogy ismeri az egyéni rétegek létrehozását a Kerasban.
1. lépés: Határozza meg a Rotary beágyazási funkciót
Ez a funkció a maximális sorozathossz és a beágyazások méretei alapján állítja elő a forgó beágyazásokat.
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))
Ez a sor az exponenciálisan skálázott frekvenciák inverzét számítja ki a pozícióindexek alapján. Ezeket a frekvenciákat a forgó beágyazások szinuszos mintázatainak generálására használják, ami segít a relatív helyzetinformáció szekvenciákba való kódolásában. Ez a mechanizmus különösen hasznos olyan feladatokban, ahol az elemek sorrendjének és egymáshoz viszonyított elhelyezkedésének megértése döntő fontosságú, mint például a természetes nyelvi feldolgozás vagy az idősorelemzés.
Részletesen:
-
A
tf.range(0, dim, 2, dtype=tf.float32)
0-tóldim'-ig (kizárólag) értéktartományt hoz létre, 2-vel lépve. A
dtype=tf.float32` argumentum határozza meg hogy ennek a tenzornak az elemei 32 bites lebegőpontos számok. Ha például a „dim” értéke 8, akkor ez „[0, 2, 4, 6]” értéket eredményez. -
A "tf.range" által létrehozott tenzort ezután elosztják a beágyazások dimenziójával ("dim"). Ez a művelet leskálázza ezeket az indexeket egy 0 és 1 közötti tartományra (kivéve, ha a "dim" páros, kissé beleértve, ha a "dim" páratlan, mert a tartomány lépése minden más értéket kihagy). Ha a példát a „dim” = 8-cal folytatjuk, 8-cal osztva a következőt kapjuk: „[0,0, 0,25, 0,5, 0,75]”.
-
Az
10000 ** (...)
művelet 10 000-et emel az előzőleg skálázott tenzor minden elemének hatványára. A 10 000-es alap némileg önkényes, de azért választották, hogy a frekvenciák széles tartományban változzanak, ami segít a modellnek a különböző pozíciók hatékonyabb megkülönböztetésében. A "[0,0, 0,25, 0,5, 0,75]" esetén mindegyikre alkalmazza a teljesítményműveletet, ami sokkal nagyobb értékeket eredményez a magasabb elemeknél. -
Végül az inverz frekvenciát úgy kapjuk meg, hogy az előző lépésből származó értékek reciprokát (1/x) vesszük. Az inverz frekvenciák kisebbek magasabb indexeknél, ami azt jelenti, hogy a szekvenciában távolabbi elemek kisebb frekvenciájúak lesznek, ami befolyásolja, hogy pozíciójuk hogyan kerül kódolásra a modellbe. Ez lehetővé teszi a beágyazások méretezését oly módon, hogy a relatív pozíciókra a modell figyelmi mechanizmusain keresztül lehet következtetni.
A vonal:
freqs = tf.einsum('i,j->ij', t, inv_freq)
a TensorFlow "tf.einsum" függvényét használja, amely lehetővé teszi a tenzorműveletek tömör és hatékony kifejezését az Einstein-összegzési jelölés használatával.
Ez a művelet hatékonyan kiszámítja a "t" és az "inv_freq" vektorok külső szorzatát, ami egy mátrixot eredményez, ahol minden egyes "(i, j)" elem a "t" "i"-edik elemének és a ' j-edik eleme az
inv_freq`-nek. Ez a mátrix ("frekvenciák") azokat a frekvenciákat jelenti, amelyek a forgó beágyazások szinuszos mintáinak generálására szolgálnak.
2. lépés: Egyéni Keras réteg forgó beágyazásokhoz
Most hozzunk létre egy egyéni Keras-réteget, amely forgó beágyazásokat alkalmaz a bemeneti tenzorra. Ez a réteg feltételezi, hogy a bemeneti tenzor (batch_size, series_length, embedding_dim)
alakú.
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
Az embeddings = self.rotary_embeddings[:seq_len]
sor kiválasztja az előre kiszámított forgó beágyazások megfelelő részhalmazát az aktuális bemeneti sorozathossz alapján. Mivel a sorozatok hossza kötegenként változhat, ez a szeletelési művelet biztosítja, hogy csak a tényleges sorozathossznak megfelelő beágyazások kerüljenek felhasználásra.
Az 'embeddings' változó most egy '(seq_len, embedding_dim)' alakú tenzorral rendelkezik, ahol a 'seq_len' a sorozatok hossza az aktuális kötegben, az 'embedding_dim' pedig a beágyazások dimenziója. Ez a tenzor tartalmazza a forgó pozíciós beágyazásokat a szekvencia minden pozíciójához a seq_len
-ig.
Az emb = tf.concat((tf.cos(frekvenciák), tf.sin(frekvenciák)), axis=-1)
egyetlen tenzorba egyesíti a helyzeti frekvenciák szinuszos és koszinuszos transzformációit:
- "tf.cos(freqs)" és "tf.sin(freqs)" a koszinusz-, illetve szinusztranszformációt alkalmazza a 'freqs' tenzorra. A "frekvenciák" tenzor frekvenciaértékeket tartalmaz a bemeneti szekvencia minden pozíciójához és a beágyazási tér minden dimenziójához, amelyeket a sorozatpozíciók és a beágyazási dimenziók inverz frekvenciái alapján számítanak ki. A szinusz- és koszinuszfüggvények elemenként kerülnek alkalmazásra, ami két, a `frekvenciával megegyező alakú tenzort eredményez. Ezek a transzformációk segítenek a pozíció kódolásában oly módon, hogy megragadja a pozícióviszonyok ciklikus jellegét, megkönnyítve a modell azon képességét, hogy megértse a relatív pozíciókat.
-tf.concat((tf.cos(frekvenciák), tf.sin(frekvenciák)), axis=-1)
összefűzi a koszinusz- és szinusztranszformált tenzorokat az utolsó tengely mentén (az axis=-1
jelöléssel). Ezeknek a tenzoroknak egymás mellé fűzése hatékonyan megduplázza a "frekvencia" tenzor dimenzióját, az első fele koszinusz-transzformált értékeket, a második fele pedig szinusz-transzformált értékeket jelent minden pozícióhoz. Az összefűzés biztosítja, hogy minden pozíciókódolás szinuszos és koszinuszos információt is tartalmazzon, ami lehetővé teszi a helyzetjelek amplitúdójával és fázisával kapcsolatos információk megőrzését.
- Az összefűzött tenzor
emb
most a teljes forgó beágyazást tartalmazza a bemeneti pozíciókhoz. Az 'emb' alakja megegyezik a 'freqs' alakjával az első két dimenziójában (amely a szekvencia pozícióinak és a beágyazási dimenzióknak felel meg), de az utolsó dimenzió kétszer akkora lesz, és figyelembe veszi a szinusz- és koszinuszértékeket is. Ezek a beágyazások a bemeneti beágyazások modulálására szolgálnak helyzetinformációk hozzáadásával, rotációs ekvivariáns módon.
-cos_emb = beágyazások[:, Nincs, :self.dim // 2]
:
-
Az első kettőspont ":" azt jelenti, hogy "az összes elemet ki kell választani ebben a dimenzióban", ami ebben az esetben a sorozat összes pozíciójára vonatkozik.
-
A "Nincs" egy további dimenzió hozzáadására szolgál, így a tenzor háromdimenziós. Ezt gyakran azért teszik, hogy biztosítsák a kompatibilitást bizonyos műveletekkel, amelyek meghatározott számú dimenzió bemenetét várják. Például, amikor elemenkénti szorzást hajt végre egy másik, háromdimenziós tenzorral, az alakzatoknak a sugárzási szabályok szerint kell igazodniuk.
-
:self.dim // 2
, az utolsó tengely méreteinek első felét választja ki. Mivel az "embedding_dimension" megduplázódik, hogy szinuszos és koszinuszos értékeket is tartalmazzon, a 2-vel való elosztás gyakorlatilag csak a beágyazások koszinuszkomponenseit választja ki.
3. lépés: Integráció Keras modellel
A "RotaryEmbeddingLayer" meghatározása után integrálhatja a Keras modelljébe. Ezt a réteget fel kell vinni a beágyazásokra, mielőtt betáplálná őket a figyelemfelkeltő rétegekbe vagy bármely további modellrétegbe.
Íme egy egyszerűsített példa a forgó beágyazások modellbe való integrálására:
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()