Modely založené na transformátorech jsou známé svou schopností analyzovat a interpretovat složitý text. Spoléhají na pochopení pořadí a kontextu slov – úkoly, u kterých tradiční metody pozičního kódování ukázaly své limity. Model ROFORMER, poháněný Rotary Position Embedding (RoPE), řeší tuto mezeru a nově definuje náš přístup k pozičnímu kódování.
Tradiční poziční kódování
Transformátory zacházejí s textem jako s řadou tokenů a umožňují paralelní zpracování sekvencí pro větší efektivitu. Tato síla však přinesla svou výzvu: vrozený agnosticismus modelu k symbolickému řádu. Poziční kódování bylo odpovědí, poskytující každému tokenu jedinečný podpis označující jeho sekvenční pozici.
Absolutní pozice vložení
Zpočátku modely jako BERT používaly vložení absolutní pozice, přiřazující pevný vektor každé pozici v sekvenci. Tato metoda, i když je přímočará, ze své podstaty postrádá schopnost přizpůsobit se variacím délky sekvence nebo zdůraznit relativní vzdálenosti mezi tokeny, což je kritické pro pochopení mnoha lingvistických konstruktů.
Vložení relativní pozice
Aby byla zachycena dynamická povaha jazyka, bylo zavedeno vkládání relativních pozic, které se zaměřovalo spíše na vzdálenost mezi tokeny než na jejich absolutní pozice. Navzdory své koncepční výhodě tato vložení zavedla výpočetní složitost a nedokázala se hladce začlenit do mechanismu sebepozorování Transformers, což omezovalo jejich účinnost.
ROFORMER a vložení s rotační polohou
ROFORMER si uvědomuje omezení stávajících strategií pozičního kódování a zavádí Rotary Position Embedding (RoPE), přístup, který kombinuje výhody absolutní a relativní informace o poloze bez jejich příslušných nevýhod.
Otočné zapuštění
RoPE kóduje informace o poloze pomocí rotačních matic, což modelu umožňuje nejen porozumět tomu, kde se token nachází, ale také jak souvisí s každým dalším tokenem v sekvenci.
Credit: ArXiv
Funguje prostřednictvím geometrické čočky a zachází s pozicemi tokenů jako s body ve vícerozměrném prostoru, které se otáčejí, aby označovaly jejich sekvenční vztahy. Tato rotace umožňuje modelu zachovat a využívat jak absolutní, tak relativní polohové podněty v rámci mechanismu sebepozorování.
Implementace RoPE
Implementace RoPE zahrnuje zakódování pozice každého tokenu do rotační matice a aplikaci této matice v rámci mechanismu sebepozorování Transformeru. Tento proces umožňuje flexibilní, dynamickou interpretaci pozičních informací, přizpůsobení se různým délkám sekvencí a zachycení podstaty vzájemných vztahů tokenů bez významné výpočetní režie.
Nejprve budete potřebovat funkci pro generování otočných vložení a poté tato vložení integrujete do svého modelu. Níže uvedený příklad předpokládá, že jste obeznámeni s vytvářením vlastních vrstev v Kerasu.
Krok 1: Definujte funkci Rotary Embedding
Tato funkce generuje rotační vložení při dané maximální délce sekvence a rozměrech vložení.
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))
Tento řádek počítá inverzní hodnotu exponenciálně škálovaných frekvencí na základě pozičních indexů. Tyto frekvence se používají při generování sinusových vzorů pro rotační vkládání, což pomáhá při kódování relativních polohových informací v sekvencích. Tento mechanismus je zvláště užitečný v úlohách, kde je klíčové pochopení pořadí a relativního umístění prvků, jako je zpracování přirozeného jazyka nebo analýza časových řad.
V detailech:
-
tf.range(0, dim, 2, dtype=tf.float32)
vytváří rozsah hodnot od 0 dodim
(exkluzivní), krokování po 2. Argumentdtype=tf.float32
určuje že prvky tohoto tenzoru jsou 32bitová čísla s plovoucí desetinnou čárkou. Pokud jedim
například 8, výsledkem by bylo[0, 2, 4, 6]
. -
Tenzor vytvořený
tf.range
je pak vydělen rozměrem (dim
) vložení. Tato operace zmenší tyto indexy na rozsah mezi 0 a 1 (neplatí, pokud je „dim“ sudé, mírně zahrnuje, pokud je „dim“ liché, protože krok rozsahu přeskakuje každou další hodnotu). Pokračujeme-li v příkladu sdim
= 8, po dělení 8 získáme[0,0, 0,25, 0,5, 0,75]
. -
Operace
10000 ** (...)
zvýší 10 000 na sílu každého prvku v dříve upraveném tenzoru. Základ 10 000 je poněkud libovolný, ale je zvolen tak, aby se frekvence měnily v širokém rozsahu, což modelu pomáhá efektivněji rozlišovat různé polohy. Pro[0.0, 0.25, 0.5, 0.75]
by aplikoval výkonovou operaci na každý, což vedlo k mnohem větším hodnotám pro vyšší prvky. -
Nakonec se inverzní frekvence získá převrácením (1/x) hodnot z předchozího kroku. Inverzní frekvence jsou menší pro vyšší indexy, což znamená, že prvky dále v sekvenci budou mít menší frekvence, což ovlivní, jak jsou jejich pozice zakódovány do modelu. To umožňuje vložením škálovat způsobem, kdy lze relativní polohy odvodit prostřednictvím mechanismů pozornosti modelu.
Linie:
freqs = tf.einsum('i,j->ij', t, inv_freq)
používá funkci tf.einsum
TensorFlow, nástroj, který umožňuje stručné a efektivní vyjádření tenzorových operací pomocí Einsteinovy sumační notace.
Tato operace efektivně vypočítá vnější součin vektorů t
a inv_freq
. Výsledkem je matice, kde každý prvek (i, j)
je součinem i
-tého prvku t
a j
-tý prvek inv_freq
. Tato matice ("freqs") představuje frekvence, které se používají ke generování sinusových vzorů pro rotační vestavby.
Krok 2: Vlastní vrstva Keras pro rotační vložení
Nyní vytvoříme vlastní vrstvu Keras, která aplikuje rotační vložení do vstupního tenzoru. Tato vrstva předpokládá, že vstupní tenzor má tvar (velikost_dávky, délka_sekvence, 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
Řádek embeddings = self.rotary_embeddings[:seq_len]
vybere vhodnou podmnožinu předem vypočítaných rotačních vložení na základě aktuální délky vstupní sekvence. Protože se délka sekvencí může u jednotlivých dávek lišit, tato operace dělení zajišťuje, že se použijí pouze vložení odpovídající skutečné délce sekvence.
Proměnná embeddings
nyní obsahuje tenzor tvaru (seq_len, embedding_dim)
, kde seq_len
je délka sekvencí v aktuální dávce a embedding_dim
je rozměrnost vložení. Tento tenzor obsahuje rotační polohové vložení pro každou pozici v sekvenci až po seq_len
.
emb = tf.concat((tf.cos(frekvence), tf.sin(frekvence)), axis=-1)
kombinuje sinusové a kosinové transformace polohových frekvencí do jediného tenzoru:
-tf.cos(freqs)
a tf.sin(freqs)
aplikují kosinusovou a sinusovou transformaci na tenzor,freqs. Tenzor "freqs" obsahuje hodnoty frekvence pro každou pozici ve vstupní sekvenci a každý rozměr vkládacího prostoru, vypočítané na základě pozic sekvence a inverzních frekvencí rozměrů vkládání. Funkce sinus a kosinus jsou aplikovány po prvcích, výsledkem jsou dva tenzory stejného tvaru jako
freqs`. Tyto transformace pomáhají při kódování pozice způsobem, který zachycuje cyklickou povahu pozičních vztahů a usnadňuje schopnost modelu porozumět relativním pozicím.
-tf.concat((tf.cos(frekv.), tf.sin(frekv.)), osa=-1)
zřetězí tenzory transformované na kosinus a sinus podél poslední osy (označené osa=-1
). Zřetězení těchto tenzorů vedle sebe efektivně zdvojnásobuje dimenzionalitu tenzoru 'freqs', přičemž první polovina představuje kosinusově transformované hodnoty a druhá polovina představuje sinusově transformované hodnoty pro každou pozici. Zřetězení zajišťuje, že každé polohové kódování obsahuje sinusovou i kosinovou informaci, což umožňuje uchování informací o amplitudě i fázi polohových signálů.
- Zřetězené tenzorové „emb“ nyní obsahuje kompletní rotační vložení pro vstupní pozice. Tvar
emb
bude stejný jakofreqs
v jeho prvních dvou dimenzích (odpovídajících pozicím sekvence a rozměrům vkládání), ale jeho poslední rozměr bude dvakrát tak velký, což bude odpovídat hodnotám sinus i cosinus. Tato vložení se používají k modulaci vstupních vložení přidáním informací o poloze rotačně ekvivalentním způsobem.
-cos_emb = embeddings[:, Žádné, :self.dim // 2]
:
-
První dvojtečka „:“ znamená „vyberte všechny prvky v této dimenzi“, což v tomto případě odkazuje na všechny pozice v sekvenci.
-
„Žádný“ se používá k přidání dalšího rozměru, čímž se tenzor stane 3-rozměrným. To se často provádí za účelem zajištění kompatibility s určitými operacemi, které očekávají vstupy určitého počtu dimenzí. Například při provádění násobení po prvcích s jiným tenzorem, který je 3-rozměrný, se tvary musí zarovnat podle pravidel vysílání.
-
:self.dim // 2
, vybere první polovinu rozměrů na poslední ose. Vzhledem k tomu, žeembedding_dimension
je zdvojnásoben, aby zahrnoval hodnoty sinus i kosinus, dělení 2 efektivně vybere pouze kosinové složky vložení.
Krok 3: Integrace s modelem Keras
Po definování RotaryEmbeddingLayer
ji můžete integrovat do svého modelu Keras. Tato vrstva by měla být aplikována na vaše vložení před jejich vložením do vrstev pozornosti nebo jakýchkoli následujících vrstev modelu.
Zde je zjednodušený příklad, jak integrovat otočné vložky do modelu:
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()