8.8 KiB
3. Token Embeddings
Token Embeddings
Po tokenizacji danych tekstowych, kolejnym kluczowym krokiem w przygotowaniu danych do trenowania dużych modeli językowych (LLM) takich jak GPT jest tworzenie token embeddings. Token embeddings przekształcają dyskretne tokeny (takie jak słowa lub pod-słowa) w ciągłe wektory numeryczne, które model może przetwarzać i z których może się uczyć. To wyjaśnienie rozkłada token embeddings, ich inicjalizację, zastosowanie oraz rolę osadzeń pozycyjnych w poprawie zrozumienia sekwencji tokenów przez model.
{% hint style="success" %}
Celem tej trzeciej fazy jest bardzo proste: Przypisanie każdemu z poprzednich tokenów w słowniku wektora o pożądanych wymiarach do trenowania modelu. Każde słowo w słowniku będzie punktem w przestrzeni o X wymiarach.
Zauważ, że początkowo pozycja każdego słowa w przestrzeni jest po prostu inicjowana "losowo", a te pozycje są parametrami, które można trenować (będą poprawiane podczas treningu).
Co więcej, podczas osadzania tokenów tworzona jest kolejna warstwa osadzeń, która reprezentuje (w tym przypadku) absolutną pozycję słowa w zdaniu treningowym. W ten sposób słowo w różnych pozycjach w zdaniu będzie miało różne reprezentacje (znaczenia). {% endhint %}
Czym są Token Embeddings?
Token Embeddings to numeryczne reprezentacje tokenów w ciągłej przestrzeni wektorowej. Każdy token w słowniku jest powiązany z unikalnym wektorem o stałych wymiarach. Te wektory uchwycają informacje semantyczne i syntaktyczne o tokenach, umożliwiając modelowi zrozumienie relacji i wzorców w danych.
- Rozmiar słownika: Całkowita liczba unikalnych tokenów (np. słów, pod-słów) w słowniku modelu.
- Wymiary osadzenia: Liczba wartości numerycznych (wymiarów) w wektorze każdego tokena. Wyższe wymiary mogą uchwycić bardziej subtelne informacje, ale wymagają więcej zasobów obliczeniowych.
Przykład:
- Rozmiar słownika: 6 tokenów [1, 2, 3, 4, 5, 6]
- Wymiary osadzenia: 3 (x, y, z)
Inicjalizacja Token Embeddings
Na początku treningu, token embeddings są zazwyczaj inicjowane małymi losowymi wartościami. Te początkowe wartości są dostosowywane (dostosowywane) podczas treningu, aby lepiej reprezentować znaczenia tokenów na podstawie danych treningowych.
Przykład PyTorch:
import torch
# Set a random seed for reproducibility
torch.manual_seed(123)
# Create an embedding layer with 6 tokens and 3 dimensions
embedding_layer = torch.nn.Embedding(6, 3)
# Display the initial weights (embeddings)
print(embedding_layer.weight)
Wynik:
luaCopy codeParameter containing:
tensor([[ 0.3374, -0.1778, -0.1690],
[ 0.9178, 1.5810, 1.3010],
[ 1.2753, -0.2010, -0.1606],
[-0.4015, 0.9666, -1.1481],
[-1.1589, 0.3255, -0.6315],
[-2.8400, -0.7849, -1.4096]], requires_grad=True)
Wyjaśnienie:
- Każdy wiersz odpowiada tokenowi w słowniku.
- Każda kolumna reprezentuje wymiar w wektorze osadzenia.
- Na przykład, token o indeksie
3
ma wektor osadzenia[-0.4015, 0.9666, -1.1481]
.
Dostęp do osadzenia tokena:
# Retrieve the embedding for the token at index 3
token_index = torch.tensor([3])
print(embedding_layer(token_index))
Wynik:
tensor([[-0.4015, 0.9666, -1.1481]], grad_fn=<EmbeddingBackward0>)
Interpretacja:
- Token na indeksie
3
jest reprezentowany przez wektor[-0.4015, 0.9666, -1.1481]
. - Te wartości to parametry, które model będzie dostosowywał podczas treningu, aby lepiej reprezentować kontekst i znaczenie tokena.
Jak działają osadzenia tokenów podczas treningu
Podczas treningu każdy token w danych wejściowych jest konwertowany na odpowiadający mu wektor osadzenia. Te wektory są następnie używane w różnych obliczeniach w modelu, takich jak mechanizmy uwagi i warstwy sieci neuronowej.
Przykładowy scenariusz:
- Rozmiar partii: 8 (liczba próbek przetwarzanych jednocześnie)
- Maksymalna długość sekwencji: 4 (liczba tokenów na próbkę)
- Wymiary osadzenia: 256
Struktura danych:
- Każda partia jest reprezentowana jako tensor 3D o kształcie
(batch_size, max_length, embedding_dim)
. - W naszym przykładzie kształt będzie
(8, 4, 256)
.
Wizualizacja:
cssCopy codeBatch
┌─────────────┐
│ Sample 1 │
│ ┌─────┐ │
│ │Token│ → [x₁₁, x₁₂, ..., x₁₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
│ Sample 2 │
│ ┌─────┐ │
│ │Token│ → [x₂₁, x₂₂, ..., x₂₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
│ ... │
│ Sample 8 │
│ ┌─────┐ │
│ │Token│ → [x₈₁, x₈₂, ..., x₈₂₅₆]
│ │ 1 │ │
│ │... │ │
│ │Token│ │
│ │ 4 │ │
│ └─────┘ │
└─────────────┘
Wyjaśnienie:
- Każdy token w sekwencji jest reprezentowany przez wektor o wymiarach 256.
- Model przetwarza te osadzenia, aby nauczyć się wzorców językowych i generować prognozy.
Osadzenia Pozycyjne: Dodawanie Kontekstu do Osadzeń Tokenów
Podczas gdy osadzenia tokenów uchwycają znaczenie poszczególnych tokenów, nie kodują one z natury pozycji tokenów w sekwencji. Zrozumienie kolejności tokenów jest kluczowe dla zrozumienia języka. Tutaj wkraczają osadzenia pozycyjne.
Dlaczego Osadzenia Pozycyjne Są Potrzebne:
- Kolejność Tokenów Ma Znaczenie: W zdaniach znaczenie często zależy od kolejności słów. Na przykład, "Kot usiadł na macie" vs. "Mata usiadła na kocie."
- Ograniczenie Osadzenia: Bez informacji pozycyjnej model traktuje tokeny jako "worek słów", ignorując ich sekwencję.
Rodzaje Osadzeń Pozycyjnych:
- Absolutne Osadzenia Pozycyjne:
- Przypisują unikalny wektor pozycji do każdej pozycji w sekwencji.
- Przykład: Pierwszy token w dowolnej sekwencji ma to samo osadzenie pozycyjne, drugi token ma inne, i tak dalej.
- Używane przez: Modele GPT OpenAI.
- Relatywne Osadzenia Pozycyjne:
- Kodują względną odległość między tokenami, a nie ich absolutne pozycje.
- Przykład: Wskazują, jak daleko od siebie są dwa tokeny, niezależnie od ich absolutnych pozycji w sekwencji.
- Używane przez: Modele takie jak Transformer-XL i niektóre warianty BERT.
Jak Osadzenia Pozycyjne Są Zintegrowane:
- Te Same Wymiary: Osadzenia pozycyjne mają tę samą wymiarowość co osadzenia tokenów.
- Dodawanie: Są dodawane do osadzeń tokenów, łącząc tożsamość tokenu z informacją pozycyjną bez zwiększania ogólnej wymiarowości.
Przykład Dodawania Osadzeń Pozycyjnych:
Załóżmy, że wektor osadzenia tokenu to [0.5, -0.2, 0.1]
, a jego wektor osadzenia pozycyjnego to [0.1, 0.3, -0.1]
. Połączone osadzenie używane przez model byłoby:
Combined Embedding = Token Embedding + Positional Embedding
= [0.5 + 0.1, -0.2 + 0.3, 0.1 + (-0.1)]
= [0.6, 0.1, 0.0]
Zalety osadzeń pozycyjnych:
- Świadomość kontekstowa: Model potrafi rozróżniać tokeny na podstawie ich pozycji.
- Zrozumienie sekwencji: Umożliwia modelowi zrozumienie gramatyki, składni i znaczeń zależnych od kontekstu.
Przykład kodu
Poniżej znajduje się przykład kodu z https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb:
# Use previous code...
# Create dimensional emdeddings
"""
BPE uses a vocabulary of 50257 words
Let's supose we want to use 256 dimensions (instead of the millions used by LLMs)
"""
vocab_size = 50257
output_dim = 256
token_embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
## Generate the dataloader like before
max_length = 4
dataloader = create_dataloader_v1(
raw_text, batch_size=8, max_length=max_length,
stride=max_length, shuffle=False
)
data_iter = iter(dataloader)
inputs, targets = next(data_iter)
# Apply embeddings
token_embeddings = token_embedding_layer(inputs)
print(token_embeddings.shape)
torch.Size([8, 4, 256]) # 8 x 4 x 256
# Generate absolute embeddings
context_length = max_length
pos_embedding_layer = torch.nn.Embedding(context_length, output_dim)
pos_embeddings = pos_embedding_layer(torch.arange(max_length))
input_embeddings = token_embeddings + pos_embeddings
print(input_embeddings.shape) # torch.Size([8, 4, 256])