Як працює шар ембедингів: від тексту до ID токенів і векторів у LLM
Зрозуміле для початківців пояснення шару ембедингів: токенізація, таблиці пошуку, навчувані вектори, приклад на 205 мільйонів параметрів і коротке демо на PyTorch з nn.Embedding.
Введіть у LLM фразу "I love cats" — і модель так і не побачить трьох англійських слів. Нейромережі працюють із числами. Як же ця коротка фраза перетворюється на те, що може обробити трансформер? Відповідь починається з шару ембедингів.
1. Від тексту до ID токенів
Спершу йде токенізація. Токенізатор розбиває текст на шматочки, які звуться токенами, і присвоює кожному ID. Для простоти візьмімо ось такий крихітний словник:
"I" → 10
"love" → 25
"cats" → 47
Тоді речення перетворюється на короткий список цілих чисел:
"I love cats"
↓
[10, 25, 47]
Це ID токенів, і самі по собі вони нічого не означають. Число 47 не каже, що "cats" варті сорока семи чогось. Це лише індекс, мітка, що вказує на один запис у словнику. Перш ніж модель зможе зробити щось корисне, потрібен ще один крок.
2. Від ID токенів до ембедингів
Цей крок виконує шар ембедингів. Уявіть його як велику таблицю, де на кожен токен словника припадає один рядок — вектор.
Припустімо, у словнику лише 50 токенів і кожен токен описується 4 числами. Кожен рядок — це вектор ембединга:
Embedding vector
Початок таблиці може виглядати так:
Token 0 → [ 0.2, -0.4, 0.7, 0.1 ]
Token 1 → [ 0.6, 0.1, -0.3, 0.8 ]
...
Token 10 → [ 0.3, -0.2, 0.9, 0.4 ]
...
Token 25 → [-0.5, 0.7, 0.2, -0.1 ]
...
Token 47 → [ 0.8, 0.1, -0.6, 0.5 ]
...
Тепер візьмімо вхідну послідовність:
[10, 25, 47]
Шар просто знаходить ці рядки. Токен 10 отримує свій вектор:
10 → [ 0.3, -0.2, 0.9, 0.4 ]
а два інші — так само:
25 → [-0.5, 0.7, 0.2, -0.1 ]47 → [ 0.8, 0.1, -0.6, 0.5 ]
Отже, вихідний текст проходить такі етапи:
"I love cats"
↓ Tokenizer[10, 25, 47] ↓ Embedding lookup[
[ 0.3, -0.2, 0.9, 0.4 ],
[-0.5, 0.7, 0.2, -0.1 ],
[ 0.8, 0.1, -0.6, 0.5 ]
] ↓Transformer
Ось і вся робота коротко: шар ембедингів перетворює ID токенів на вектори.
3. Чому вектори, а не просто ID?
Можна спитати, чому трансформерові не можна подати ID напряму. Річ у тім, що ID — лише мітки. Погляньте на три значення:
10
25
47
Різниця між 25 і 47 нічого не каже про те, як "love" пов'язане з "cats". Вектори ембедингів інші, бо це навчені представлення. Під час навчання модель підлаштовує їхні значення, доки вони не почнуть допомагати трансформерові працювати з мовою.
4. Звідки беруться числа
Ніхто не вписує значення матриці ембедингів вручну. Вони вивчаються під час навчання. На початку вектори містять значення без жодної мовної інформації. Потім модель читає величезні обсяги тексту й тренується передбачати наступний токен. Отримавши промпт на кшталт "I love cats because they are ___", вона робить здогад.
Якщо здогад хибний, помилка йде назад мережею через зворотне поширення. Вектори ембедингів оновлюються разом з усіма іншими вагами. Після навчання в матриці лежать представлення, на які модель навчилася спиратися.
5. Що представляє ембединг
Візьмімо вектор для "cats":
cats → [0.8, 0.1, -0.6, 0.5]
Так і кортить прочитати кожне число як іменовану властивість, приблизно так:
0.8 → animal
0.1 → size
-0.6 → shape
0.5 → plural
Загалом ембединги LLM влаштовані інакше. Зміст розподілений по всіх вимірах одночасно. Один вимір рідко відповідає окремому поняттю, яке людина могла б назвати. Представленням є весь вектор цілком.
6. Справжні матриці ембедингів величезні
Тепер перейдімо від іграшкового прикладу до реалістичної моделі з такими розмірами:
Vocabulary size = 50,000
Embedding dimension = 4,096
Тоді в матриці ембедингів близько 205 мільйонів параметрів, а це чимало. Те, що виглядає як проста таблиця пошуку, може вміщати напрочуд велику частку ваг моделі.
7. Що відбувається далі
Коли "I love cats" перетворилася на набір векторів ембедингів, вони надходять у трансформер. Ось як іде потік:
"I love cats"
│
▼
Tokenizer
│
▼
[10, 25, 47]
│
▼
Embedding Layer
│
▼
[vector, vector, vector]
│
▼
Transformer
│
▼
Next-token prediction
Ембединг — це міст між дискретними токенами й неперервними числовими представленнями, з якими працює нейромережа.
8. Та сама ідея на Python
Щоб це показати, вистачить кількох рядків на PyTorch:
import torch
import torch.nn as nn
# Vocabulary contains 50 tokens
# Each token is represented by 4 numbers
embedding = nn.Embedding(
num_embeddings=50,
embedding_dim=4
)
# Token IDs for "I love cats"
tokens = torch.tensor([10, 25, 47])
# Look up their embeddings
vectors = embedding(tokens)
print(vectors)
print(vectors.shape)
Виведений результат виглядає приблизно так:
tensor([
[ 0.31, -0.21, 0.91, 0.42],
[-0.52, 0.73, 0.19, -0.12],
[ 0.79, 0.08, -0.61, 0.54]
])
А форма підтверджує: три токени по чотири числа:
torch.Size([3, 4])
По суті, шар ембедингів — це навчувана таблиця пошуку. Якщо скласти все докупи, конвеєр виглядає так:
Text
↓
Tokenizer
↓
Token IDs
↓
Embedding lookup
↓
Vectors
Transformer
Токенізатор визначає, які токени присутні. Шар ембедингів перетворює їхні ID на навчені числові представлення. Трансформер бере ці представлення, розбирається в контексті й робить передбачення.
Наступного разу, побачивши у визначенні моделі такий рядок:
nn.Embedding(vocab_size, embedding_dim)
читайте його як вказівку побудувати таблицю з vocab_size рядків, де кожен токен отримує власний навчуваний вектор завдовжки embedding_dim.