Embeddings: как LLM понимает смысл слов
Введение: что такое embeddings
Embeddings — это векторные представления слов, предложений или документов. Каждая единица текста отображается в точку в многомерном пространстве, где близость означает семантическую близость.
"король" → [0.23, -0.45, 0.89, ..., 0.12] (1536 измерений для GPT-4)
"queen" → [0.21, -0.43, 0.91, ..., 0.14] (очень близко к "король")
"банан" → [0.87, 0.34, -0.12, ..., -0.56] (далеко от "король")
Почему это важно для LLM?
Embeddings — это первый слой любой нейросети, работающей с текстом:
Текст → Токенизация → Embedding Layer → Transformer Layers → Output
[числа] [векторы D] [обработка] [вероятности]
Без качественных embeddings transformer не сможет ничего понять.
Уровень 1: Словарные embeddings
One-Hot Encoding (самый простой подход)
Словарь: ["cat", "dog", "bird", "fish"]
"cat" → [1, 0, 0, 0]
"dog" → [0, 1, 0, 0]
"bird" → [0, 0, 1, 0]
"fish" → [0, 0, 0, 1]
Проблемы:
- Нет семантики: "cat" и "dog" так же далеки, как "cat" и "bird"
- Огромное пространство: словарь 50K токенов → вектор из 50K измерений
- Редкость: редкие слова имеют мало данных для обучения
Уровень 2: Dense Embeddings (Word2Vec, GloVe)
Идея: обучить векторы
Вместо hand-crafted one-hot, мы обучаем векторы так, чтобы близкие по смыслу слова были близки в пространстве.
Word2Vec (Google, 2013)
Два подхода:
Skip-gram: предсказывать контекст по слову
Контекст: "the cat sits on the mat"
Для слова "cat":
Вход: "cat"
Цель: предсказать "the", "sits", "on", "mat"
Для слова "dog":
Вход: "dog"
Цель: предсказать "the", "runs", "on", "grass"
→ "cat" и "dog" будут иметь похожие векторы!
CBOW (Continuous Bag of Words): предсказывать слово по контексту
Контекст: "the ___ sits on the mat"
Вход: ["the", "on", "the", "mat"]
Цель: предсказать "cat"
Результат обучения Word2Vec
Аналогии работают!
"король" - "мужчина" + "женщина" ≈ "королева"
Векторная арифметика:
king - man + woman = [0.23, -0.45, 0.89, ...] - [0.12, 0.34, -0.23, ...] + [0.10, 0.36, -0.21, ...]
= [0.21, -0.43, 0.91, ...] ≈ queen
GloVe (Stanford, 2014)
GloVe использует матрицу совместной встречаемости слов:
Матрица совместности:
| cat | dog | runs | sits | food
--------|-----|-----|------|------|------
cat | 0 | 3 | 1 | 15 | 8
dog | 3 | 0 | 12 | 2 | 5
runs | 1 | 12 | 0 | 1 | 0
sits | 15 | 2 | 1 | 0 | 0
food | 8 | 5 | 0 | 0 | 0
GloVe обучает embeddings так, чтобы:
w_i^T * w_j + b_j = log(P(j|i))
где w_i — вектор слова i, P(j|i) — вероятность слова j в контексте i
Размерности embeddings
Модель | Размерность | Словарь
-----------------|-------------|----------
Word2Vec | 100-300 | ~1M
GloVe | 50-843 | ~400K
FastText | 100-300 | ~1M (+ подсловные n-граммы)
Уровень 3: Контекстуальные embeddings (BERT и далее)
Проблема статических embeddings
Word2Vec даёт один вектор на слово:
"bank" в "river bank" → [0.23, -0.45, ...]
"bank" в "bank account" → [0.23, -0.45, ...] ← тот же вектор!
Но это разные значения!
BERT: контекстуальные embeddings
BERT генерирует разные векторы для одного слова в разном контексте:
"Banking on this bank's stability"
BERT token 1 "Banking": [0.12, -0.34, 0.56, ...] (финансовый контекст)
BERT token 4 "bank": [0.23, -0.45, 0.67, ...] (финансовый контекст)
BERT token 5 "'s": [0.01, 0.12, 0.23, ...]
BERT token 6 "stability": [0.45, 0.23, -0.12, ...]
BERT token 1 "Banking": [0.34, -0.12, 0.78, ...] (другой контекст — другой вектор!)
Как BERT создаёт embeddings
Вход: "[CLS] cat sits on [MASK] mat [SEP]"
Компоненты:
1. Token embeddings: [cat, sits, on, mask, mat] → векторы
2. Segment embeddings: [0, 0, 0, 0, 0] (один сегмент)
3. Position embeddings: [0, 1, 2, 3, 4] (позиции)
Сумма: token + segment + position → вход в transformer
Выход: 768-мерные векторы для каждого токена (BERT-base)
Уровень 4: Embeddings в LLM (Transformer)
Embedding Layer в GPT/Llama
В LLM embedding layer — это просто таблица поиска:
class EmbeddingLayer(nn.Module):
def __init__(self, vocab_size=128256, hidden_size=4096):
# Таблица: токен_id → вектор
self.weight = nn.Embedding(vocab_size, hidden_size)
# Вес: [128256, 4096] = ~512M параметров только для эмбеддинга!
def forward(self, token_ids):
# token_ids: [batch_size, seq_len]
# output: [batch_size, seq_len, hidden_size]
return self.weight(token_ids)
# Пример:
# Токены: [122, 353, 749, 0]
# → Векторы: [4096, 4096, 4096, 4096] (4 вектора по 4096 чисел)
Масштаб embedding layer
Модель | Vocab Size | Hidden Size | Параметры
----------------|------------|-------------|----------
GPT-2 | 50,257 | 768 | ~38M
GPT-3 125M | 50,257 | 768 | ~38M
Llama 3 8B | 128,256 | 4,096 | ~512M
Llama 3 70B | 128,256 | 8,192 | ~1B
GPT-4 (оценка) | ~150,000 | ~16,384 | ~2.4B
Embedding layer может составлять 10-20% всех параметров модели!
Визуализация embeddings
t-SNE и PCA
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
# Получаем embeddings
embeddings = model.token_embedding.weight.detach().numpy() # [50257, 768]
# Снижаем до 2D
tsne = TSNE(n_components=2, random_state=42)
embeddings_2d = tsne.fit_transform(embeddings)
# Строим
plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1], alpha=0.1)
plt.show()
Что видно на графике?
Кластеры формируются по:
1. Части речи: существительные, глаголы, прилагательные
2. Тематике: животные, еда, технологии, география
3. Грамматике: единственное/множественное число, прошедшее/будущее время
Пример кластера "животные":
cat, dog, bird, fish, horse, cow, sheep, elephant, ...
Пример кластера "глаголы действия":
run, walk, jump, fly, swim, climb, crawl, ...
Специализированные embeddings
Sentence embeddings
Для сравнения предложений и поиска похожих:
Model | Размер | Описание
-------------------|--------|----------
sentence-transformers | 768 | BERT-based для предложений
OpenAI text-embedding-3-small | 1536 | GPT-based
OpenAI text-embedding-3-large | 3072 | Более точный
Cohere embed-multilingual-v3 | 1024 | 100+ языков
Использование
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = [
"Кошка сидит на ковре",
"Кот находится на коврике",
"Собака бегает по парку",
"The cat sits on the mat"
]
embeddings = model.encode(sentences)
# Поиск похожих
from sklearn.metrics.pairwise import cosine_similarity
print(cosine_similarity([embeddings[0]], embeddings))
# [[0.92, 0.89, 0.23, 0.78]]
# "Кошка сидит на ковре" ближе к "Кот находится на коврике" (0.89)
Embeddings для RAG и векторных баз данных
Как embeddings используются в RAG
1. Индексация:
Документ → Чанки → Embeddings → Векторная БД
2. Поиск:
Запрос → Embedding → Поиск похожих → Контекст для LLM
3. Генерация:
LLM + контекст → Ответ
Векторные БД
Система | Тип | Особенности
------------------|------------|---------------------------
FAISS | ANN | Быстрый, от Meta
Pinecone | Managed | Полностью управляемый
Weaviate | Hybrid | Векторы + графы + фильтры
Milvus | Distributed| Масштабируемый
Chroma | Local | Простой, для разработки
Qdrant | Hybrid | Сэмплирование + фильтры
Cosine Similarity — метрика близости
cosine_similarity(a, b) = (a · b) / (||a|| × ||b||)
Результат: [-1, 1]
1 = идентичные направления
0 = ортогональные (независимые)
-1 = противоположные направления
Пример:
a = [1, 0, 0]
b = [0.9, 0.1, 0]
cosine = (0.9) / (1 × 1) = 0.9 ← очень похожи
Практические аспекты
Выбор размерности
Меньше размерность:
+ Быстрее вычисления
+ Меньше память
- Меньше информации
Больше размерность:
+ Больше информации
+ Лучше для сложных задач
- Медленнее, больше память
Нормализация embeddings
# L2 нормализация (для cosine similarity)
import torch
def normalize(embeddings):
norms = torch.norm(embeddings, p=2, dim=-1, keepdim=True)
return embeddings / norms
# После нормализации:
# ||embedding|| = 1
# cosine_similarity(a, b) = a · b (проецирование)
Квантование embeddings
Float32: 4 байта на измерение
Float16: 2 байта на измерение
Int8: 1 байт на измерение
Int4: 0.5 байта на измерение
Embedding 4096 dim:
Float32: 16 KB
Float16: 8 KB
Int8: 4 KB
Int4: 2 KB
Будущее embeddings
Мультимодальные embeddings
CLIP (OpenAI):
Текст: "кошка на ковре" → [0.23, -0.45, ...] (768 dim)
Изображение: [фото кошки] → [0.25, -0.43, ...] (768 dim)
Cosine similarity = 0.92 ← текст и изображение близки!
DALL-E, Stable Diffusion:
Текст → embedding → генерация изображения
Dynamic embeddings
Вместо фиксированных векторов — генерация на лету:
Традиционный подход:
lookup[token_id] → fixed_vector
Dynamic подход:
small_network(token_id, context) → adaptive_vector
→ Вектор адаптируется под контекст, но без полного transformer
Итоги
- Embeddings — это векторные представления текста в многомерном пространстве
- Word2Vec/GloVe — статические, один вектор на слово
- BERT — контекстуальные, разные векторы для одного слова
- В LLM embedding layer — это таблица поиска, до 20% параметров модели
- Sentence embeddings используются в RAG и векторных БД
- Cosine similarity — основная метрика близости
- Мультимодальные embeddings объединяют текст, изображения, аудио
- Квантование embeddings экономит память без значительной потери качества