Modele oparte na transformatorach słyną z możliwości analizowania i interpretowania złożonego tekstu. Polegają na zrozumieniu kolejności i kontekstu słów – zadań, w przypadku których tradycyjne metody kodowania pozycyjnego pokazały swoje ograniczenia. Wypełniając tę lukę, model ROFORMER, wyposażony w Rotary Position Embedding (RoPE), na nowo definiuje nasze podejście do kodowania pozycyjnego.
Tradycyjne kodowanie pozycyjne
Transformatory traktują tekst jako serię tokenów i umożliwiają równoległe przetwarzanie sekwencji dla większej wydajności. Jednak ta siła niosła ze sobą wyzwanie: wrodzony agnostycyzm modelu w stosunku do symbolicznego porządku. Odpowiedzią było kodowanie pozycyjne, zapewniające każdemu tokenowi unikalny podpis określający jego pozycję w sekwencji.
Osadzanie pozycji bezwzględnej
Początkowo modele takie jak BERT stosowały osadzanie pozycji bezwzględnych, przypisując stały wektor do każdej pozycji w sekwencji. Ta metoda, choć prosta, z natury nie ma możliwości dostosowania się do zmian długości sekwencji ani podkreślenia względnych odległości między tokenami, co jest krytyczne dla zrozumienia wielu konstrukcji językowych.
Osadzenia w pozycji względnej
Aby uchwycić dynamiczną naturę języka, wprowadzono osadzanie pozycji względnych, koncentrując się na odległości między tokenami, a nie na ich pozycjach bezwzględnych. Pomimo przewagi koncepcyjnej, te osadzania wprowadziły złożoność obliczeniową i nie udało się płynnie zintegrować z mechanizmem samouważności Transformatorów, ograniczając ich skuteczność.
ROFORMER i osadzanie w pozycji obrotowej
Uznając ograniczenia istniejących strategii kodowania pozycyjnego, ROFORMER wprowadza Rotary Position Embedding (RoPE), podejście, które łączy zalety informacji o pozycji bezwzględnej i względnej bez ich wad.
Osadzanie w pozycji obrotowej
RoPE koduje informacje o pozycji za pomocą macierzy rotacji, umożliwiając modelowi zrozumienie nie tylko tego, gdzie znajduje się token, ale także jego powiązania z każdym innym tokenem w sekwencji.
Credit: Original Paper
Działa poprzez soczewkę geometryczną, traktując pozycje żetonów jako punkty w wielowymiarowej przestrzeni, które są obracane w celu zaznaczenia ich sekwencyjnych relacji. Rotacja ta pozwala modelowi zachować i wykorzystać zarówno bezwzględne, jak i względne wskazówki pozycyjne w ramach mechanizmu samouważności.
Wdrażanie RoPE
Wdrożenie RoPE polega na zakodowaniu pozycji każdego tokena w macierzy rotacji i zastosowaniu tej macierzy w ramach mechanizmu samouważności Transformatora. Proces ten pozwala na elastyczną, dynamiczną interpretację informacji o położeniu, dostosowując się do różnych długości sekwencji i przechwytując istotę wzajemnych powiązań tokenów bez znacznych narzutów obliczeniowych.
Najpierw będziesz potrzebować funkcji do generowania osadzania obrotowego, a następnie zintegrujesz te osady ze swoim modelem. W poniższym przykładzie założono, że znasz tworzenie niestandardowych warstw w Keras.
Krok 1: Zdefiniuj funkcję osadzania obrotowego
Ta funkcja generuje osadzania obrotowe, biorąc pod uwagę maksymalną długość sekwencji i wymiarowość osadzania.
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))
Linia ta oblicza odwrotność częstotliwości skalowanych wykładniczo na podstawie wskaźników położenia. Częstotliwości te są wykorzystywane do generowania wzorów sinusoidalnych dla osadzania obrotowego, co pomaga w kodowaniu informacji o względnym położeniu w sekwencjach. Mechanizm ten jest szczególnie przydatny w zadaniach, w których kluczowe jest zrozumienie kolejności i względnego rozmieszczenia elementów, np. w przetwarzaniu języka naturalnego lub analizie szeregów czasowych.
W szczegółach:
-
tf.range(0, dim, 2, dtype=tf.float32)
tworzy zakres wartości począwszy od 0 dodim
(wyłącznie), z krokiem 2. Argumentdtype=tf.float32
określa że elementy tego tensora są 32-bitowymi liczbami zmiennoprzecinkowymi. Jeśli na przykład „dim” wynosi 8, wyświetli się „[0, 2, 4, 6]”. -
Tensor utworzony przez „tf.range” jest następnie dzielony przez wymiarowość („dim”) osadzania. Ta operacja skaluje te indeksy w dół do zakresu od 0 do 1 (wyłącznie, jeśli „dim” jest parzyste, nieznacznie włączając, jeśli „dim” jest nieparzyste, ponieważ krok zakresu pomija każdą inną wartość). Kontynuując przykład z
dim
= 8, podzielenie przez 8 daje[0,0, 0,25, 0,5, 0,75]
. -
Operacja
10000 ** (...)
podnosi 10 000 do potęgi każdego elementu w poprzednio skalowanym tensorze. Podstawa wynosząca 10 000 jest nieco dowolna, ale została wybrana w celu zapewnienia, że częstotliwości różnią się w szerokim zakresie, co pomaga modelowi skuteczniej różnicować różne pozycje. W przypadku „[0,0, 0,25, 0,5, 0,75]” do każdego z nich zastosowano operację potęgową, co dałoby znacznie większe wartości dla wyższych elementów. -
Na koniec częstotliwość odwrotną uzyskuje się poprzez odwrotność (1/x) wartości z poprzedniego kroku. Odwrotne częstotliwości są mniejsze w przypadku wyższych indeksów, co oznacza, że elementy znajdujące się dalej w sekwencji będą miały mniejsze częstotliwości, co wpływa na sposób kodowania ich pozycji w modelu. Umożliwia to skalowanie osadzania w sposób umożliwiający wywnioskowanie względnych pozycji na podstawie mechanizmów uwagi modelu.
Linia:
freqs = tf.einsum('i,j->ij', t, inv_freq)
wykorzystuje funkcję tf.einsum
TensorFlow, narzędzie pozwalające na zwięzłe i wydajne wyrażanie operacji tensorowych przy użyciu notacji sumowania Einsteina.
Ta operacja skutecznie oblicza iloczyn zewnętrzny wektorów t
i inv_freq
, dając w wyniku macierz, w której każdy element (i, j)
jest iloczynem i
-tego elementu t
i j
-ty element inv_freq
. Ta macierz („częstotliwości”) reprezentuje częstotliwości używane do generowania wzorów sinusoidalnych dla osadzania obrotowego.
Krok 2: Niestandardowa warstwa Keras do osadzania obrotowego
Utwórzmy teraz niestandardową warstwę Keras, która zastosuje osadzanie obrotowe do tensora wejściowego. W warstwie tej zakłada się, że tensor wejściowy ma kształt (rozmiar_wsadu, długość_sekwencji, wymiar_osadzania)
.
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]
wybiera odpowiedni podzbiór wstępnie obliczonych osadzań obrotowych w oparciu o bieżącą długość sekwencji wejściowej. Ponieważ długość sekwencji może różnić się w zależności od partii, ta operacja krojenia zapewnia, że użyte zostaną tylko osadzania odpowiadające rzeczywistej długości sekwencji.
Zmienna „embeddings” zawiera teraz tensor kształtu „(seq_len, embedding_dim)”, gdzie „seq_len” to długość sekwencji w bieżącej partii, a „embedding_dim” to wymiarowość osadzania. Tensor ten zawiera obrotowe osadzania pozycyjne dla każdej pozycji w sekwencji aż do „seq_len”.
emb = tf.concat((tf.cos(freqs), tf.sin(freqs)), oś=-1)
łączy transformacje sinus i cosinus częstotliwości pozycyjnych w jeden tensor:
-tf.cos(freqs)
i tf.sin(freqs)
stosują odpowiednio transformacje cosinus i sinus do tensora freqs
. Tensor „częstotliwości” zawiera wartości częstotliwości dla każdej pozycji w sekwencji wejściowej i każdego wymiaru przestrzeni osadzania, obliczone na podstawie pozycji sekwencji i odwrotnych częstotliwości wymiarów osadzania. Funkcje sinus i cosinus stosuje się elementarnie, w wyniku czego powstają dwa tensory o tym samym kształcie co „częstości”. Transformacje te pomagają w kodowaniu pozycji w sposób, który oddaje cykliczną naturę relacji pozycyjnych, ułatwiając modelowi zrozumienie pozycji względnych.
-tf.concat((tf.cos(freqs), tf.sin(freqs)), oś=-1)
łączy tensory przekształcone cosinus i sinus wzdłuż ostatniej osi (oznaczonej jako oś=-1
). Łączenie tych tensorów obok siebie skutecznie podwaja wymiarowość tensora „częstotliwości”, przy czym pierwsza połowa reprezentuje wartości przekształcone cosinus, a druga połowa reprezentuje wartości przekształcone sinusoidalnie dla każdej pozycji. Konkatenacja zapewnia, że każde kodowanie pozycyjne zawiera zarówno informację sinusową, jak i cosinusową, co pozwala na zachowanie informacji zarówno o amplitudzie, jak i fazie sygnałów pozycyjnych.
- Połączony „emb” tensora zawiera teraz pełne osadzania obrotowe dla pozycji wejściowych. Kształt „emb” będzie taki sam jak „freqs” w pierwszych dwóch wymiarach (odpowiadających pozycjom sekwencji i wymiarom osadzania), ale jego ostatni wymiar będzie dwukrotnie większy, uwzględniając zarówno wartości sinus, jak i cosinus. Te osadzania są używane do modulowania osadzania wejściowego poprzez dodawanie informacji o położeniu w sposób rotacyjnie równoważny.
-cos_emb = osadzania[:, Brak, :self.dim // 2]
:
-
Pierwszy dwukropek
:
oznacza „wybierz wszystkie elementy w tym wymiarze”, co w tym przypadku odnosi się do wszystkich pozycji w sekwencji. -
„Brak” służy do dodania dodatkowego wymiaru, dzięki czemu tensor jest trójwymiarowy. Robi się to często, aby zapewnić zgodność z określonymi operacjami, które wymagają danych wejściowych o określonej liczbie wymiarów. Na przykład podczas wykonywania mnożenia elementowego z innym tensorem, który jest trójwymiarowy, kształty muszą być wyrównane zgodnie z regułami nadawania.
-
:self.dim // 2
, wybiera pierwszą połowę wymiarów na ostatniej osi. Ponieważ „wymiar_osadzenia” jest podwojony, aby uwzględnić zarówno wartości sinus, jak i cosinus, dzielenie przez 2 skutecznie powoduje wybranie tylko cosinusowych składowych osadzania.
Krok 3: Integracja z modelem Keras
Po zdefiniowaniu RotaryEmbeddingLayer
możesz zintegrować ją ze swoim modelem Keras. Warstwę tę należy nałożyć na elementy osadzone przed wprowadzeniem ich do warstw uwagi lub kolejnych warstw modelu.
Oto uproszczony przykład integracji osadzania obrotowego z modelem:
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()