Фаза 05 · урок 03
Векторные представления слов — Word2Vec с нуля
Цель урока: TF-IDF знает, что dog и puppy — разные слова. Он не знает, что они почти одинаковы по смыслу. Классификатор, обученный на dog , не может обобщиться на отзыв о puppy . Можно попытаться обойти это, перечислив синонимы, но такой подход…
Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.
Содержание урока
- Проблема
- Концепция
- Соберите это
- Шаг 1: обучающие пары из корпуса
- Шаг 2: таблицы векторных представлений
- Шаг 3: целевая функция негативного семплирования
- Шаг 4: обучение на игрушечном корпусе
- Шаг 5: приём с аналогиями
- Примените это
- Когда Word2Vec по-прежнему выигрывает в 2026 году
- Где Word2Vec не справляется
- Подготовьте к поставке
- Упражнения
- Ключевые термины
- Дополнительное чтение
О слове судят по компании, которую оно держит. Обучите неглубокую сеть на этой идее — и появится геометрия.
Тип: Сборка Языки: Python Предварительные требования: Фаза 5 · 02 (BoW + TF-IDF), фаза 3 · 03 (Обратное распространение ошибки с нуля) Время: ~75 минут
Проблема
TF-IDF знает, что dog и puppy — разные слова. Он не знает, что они почти одинаковы по смыслу. Классификатор, обученный на dog, не может обобщиться на отзыв о puppy. Можно попытаться обойти это, перечислив синонимы, но такой подход ломается на редких терминах, профессиональном жаргоне и на любом языке, которого вы не предусмотрели.
Нужно представление, в котором dog и puppy располагаются близко в пространстве. В котором king - man + woman оказывается рядом с queen. В котором модель, обученная на dog, бесплатно переносит часть сигнала на puppy.
Word2Vec дал нам такое пространство. Двухслойная нейронная сеть, запуски обучения на триллионах токенов, публикация в 2013 году. Архитектура почти до неловкости проста. Результаты на десятилетие изменили NLP.
Концепция
Дистрибутивная гипотеза (distributional hypothesis) (Firth, 1957): «Вы узнаете слово по компании, которую оно держит». Если два слова встречаются в похожих контекстах, то, вероятно, они имеют сходный смысл.
Word2Vec существует в двух вариантах, и оба используют эту идею.
- Skip-gram. По центральному слову предсказывает окружающие слова.
cat -> (the, sat, on)при размере окна 2. - CBOW (continuous bag of words, непрерывный мешок слов). По окружающим словам предсказывает центральное слово.
(the, sat, on) -> cat.
Skip-gram обучается медленнее, но лучше работает с редкими словами. Он стал вариантом по умолчанию.
У сети один скрытый слой без нелинейности. Вход — one-hot-вектор по словарю. Выход — softmax по словарю. После обучения выходной слой отбрасывается. Веса скрытого слоя и есть векторные представления.
one-hot(center) ── W ──▶ hidden (d-dim) ── W' ──▶ softmax(vocab)
^
this is the embedding
Сложность в том, что softmax по 100k словам непомерно дорог. Word2Vec использует negative sampling (негативное семплирование), чтобы превратить задачу в бинарную классификацию. Нужно предсказать: «встречалось ли это контекстное слово рядом с центральным — да или нет». Вместо вычисления softmax по всему словарю для каждой обучающей пары выбирается несколько отрицательных (не встречающихся совместно) слов.
word-vector-arithmetic
Соберите это
Шаг 1: обучающие пары из корпуса
def skipgram_pairs(docs, window=2):
pairs = []
for doc in docs:
for i, center in enumerate(doc):
for j in range(max(0, i - window), min(len(doc), i + window + 1)):
if i == j:
continue
pairs.append((center, doc[j]))
return pairs
>>> skipgram_pairs("the", "cat", "sat", "on", "mat", window=2)
[('the', 'cat'), ('the', 'sat'),
('cat', 'the'), ('cat', 'sat'), ('cat', 'on'),
('sat', 'the'), ('sat', 'cat'), ('sat', 'on'), ('sat', 'mat'),
...]
Каждая пара (центр, контекст) в окне — положительный обучающий пример.
Шаг 2: таблицы векторных представлений
Две матрицы. W — таблица представлений центральных слов (её вы сохраняете). W' — таблица контекстных слов (часто отбрасывается, иногда усредняется с W).
import numpy as np
def init_embeddings(vocab_size, dim, seed=0):
rng = np.random.default_rng(seed)
W = rng.normal(0, 0.1, size=(vocab_size, dim))
W_prime = rng.normal(0, 0.1, size=(vocab_size, dim))
return W, W_prime
Небольшая случайная инициализация. Размер словаря 10k и размерность 100 реалистичны; для обучения достаточно словаря из 50 слов и размерности 16, чтобы увидеть геометрию.
Шаг 3: целевая функция негативного семплирования
Для каждой положительной пары (center, context) выберите k случайных слов из словаря как отрицательные. Обучайте модель так, чтобы скалярное произведение W[center] · W'[context] было большим для положительных пар и малым для отрицательных.
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-np.clip(x, -20, 20)))
def train_pair(W, W_prime, center_idx, context_idx, negative_indices, lr):
v_c = W[center_idx]
u_pos = W_prime[context_idx]
u_negs = W_prime[negative_indices]
pos_score = sigmoid(v_c @ u_pos)
neg_scores = sigmoid(u_negs @ v_c)
grad_center = (pos_score - 1) * u_pos
for i, u in enumerate(u_negs):
grad_center += neg_scores[i] * u
W[context_idx] = W[context_idx]
W_prime[context_idx] -= lr * (pos_score - 1) * v_c
for i, neg_idx in enumerate(negative_indices):
W_prime[neg_idx] -= lr * neg_scores[i] * v_c
W[center_idx] -= lr * grad_center
Главная формула: логистическая потеря на положительной паре (нужно, чтобы sigmoid был близок к 1) плюс логистическая потеря на отрицательных парах (нужно, чтобы sigmoid был близок к 0). Градиенты проходят к обеим таблицам. Полный вывод приведён в исходной статье; пройдите его один раз с карандашом и бумагой, если хотите по-настоящему усвоить материал.
Шаг 4: обучение на игрушечном корпусе
def train(docs, dim=16, window=2, k_neg=5, epochs=100, lr=0.05, seed=0):
vocab = build_vocab(docs)
vocab_size = len(vocab)
rng = np.random.default_rng(seed)
W, W_prime = init_embeddings(vocab_size, dim, seed=seed)
pairs = skipgram_pairs(docs, window=window)
for epoch in range(epochs):
rng.shuffle(pairs)
for center, context in pairs:
c_idx = vocab[center]
ctx_idx = vocab[context]
negs = rng.integers(0, vocab_size, size=k_neg)
negs = [n for n in negs if n != ctx_idx and n != c_idx]
train_pair(W, W_prime, c_idx, ctx_idx, negs, lr)
return vocab, W
После достаточного числа эпох на большом корпусе слова с общими контекстами получают похожие представления центральных слов. На игрушечном корпусе эффект едва заметен. На миллиардах токенов он проявляется впечатляюще.
Шаг 5: приём с аналогиями
def nearest(vocab, W, target_vec, topk=5, exclude=None):
exclude = exclude or set()
inv_vocab = {i: w for w, i in vocab.items()}
norms = np.linalg.norm(W, axis=1, keepdims=True) + 1e-9
W_norm = W / norms
target = target_vec / (np.linalg.norm(target_vec) + 1e-9)
sims = W_norm @ target
order = np.argsort(-sims)
out = []
for i in order:
if i in exclude:
continue
out.append((inv_vocab[i], float(sims[i])))
if len(out) == topk:
break
return out
def analogy(vocab, W, a, b, c, topk=5):
v = W[vocab[b]] - W[vocab[a]] + W[vocab[c]]
return nearest(vocab, W, v, topk=topk, exclude={vocab[a], vocab[b], vocab[c]})
На предобученных 300-мерных векторах Google News:
>>> analogy(vocab, W, "man", "king", "woman")
[('queen', 0.71), ('monarch', 0.62), ('princess', 0.59), ...]
king - man + woman = queen. Не потому, что модель знает, что такое монархия. Вектор (king - man) выражает нечто вроде «королевский», и добавление его к woman переносит в область королевских женщин.
Примените это
Писать Word2Vec с нуля полезно для обучения. В промышленном NLP используют gensim.
from gensim.models import Word2Vec
sentences = [
["the", "cat", "sat", "on", "the", "mat"],
["the", "dog", "ran", "across", "the", "room"],
]
model = Word2Vec(
sentences,
vector_size=100,
window=5,
min_count=1,
sg=1,
negative=5,
workers=4,
epochs=30,
)
print(model.wv["cat"])
print(model.wv.most_similar("cat", topn=3))
В реальной работе вы почти никогда не обучаете Word2Vec самостоятельно. Вместо этого загружаете предобученные векторы.
- GloVe — подход Стэнфорда с факторизацией матрицы совместной встречаемости. Контрольные точки 50d, 100d, 200d, 300d. Хорошее покрытие общего языка. Урок 04 посвящён GloVe отдельно.
- fastText — расширение Word2Vec от Facebook, которое встраивает n-граммы символов. Обрабатывает слова вне словаря, составляя их из подслов. Урок 04.
- Предобученный Word2Vec на Google News — 300d, словарь из 3M слов, опубликован в 2013 году. Его до сих пор ежедневно скачивают.
Когда Word2Vec по-прежнему выигрывает в 2026 году
- Лёгкий предметно-специфический поиск. Обучите на медицинских аннотациях за час на ноутбуке — и получите специализированные векторы, которых не даёт ни одна общая модель.
- Конструирование признаков в стиле аналогий.
gender_vector = mean(man - woman pairs). Вычтите его из других слов, чтобы получить гендерно-нейтральную ось. Это всё ещё применяют в исследованиях справедливости. - Интерпретируемость. 100d достаточно малы, чтобы построить визуализацию через PCA или t-SNE и действительно увидеть формирующиеся кластеры.
- Любой случай, где инференс должен выполняться на устройстве без GPU. Поиск Word2Vec — это извлечение одной строки.
Где Word2Vec не справляется
Стена многозначности. У bank один вектор. river bank и financial bank используют его совместно. То же относится к table (электронная таблица и предмет мебели). Классификатор далее по конвейеру не может различить значения по вектору.
Контекстные представления (ELMo, BERT, каждый трансформер после них) решили это, создавая разный вектор для каждого употребления слова на основе окружающего контекста. Это переход от Word2Vec к BERT: от статических представлений к контекстным. Фаза 7 посвящена половине с трансформерами.
Другая проблема — слова вне словаря. Word2Vec никогда не видел Zoomer-approved, если его не было в обучающих данных. Запасного варианта нет. fastText исправляет это композиционным построением из подслов (урок 04).
Подготовьте к поставке
Сохраните как outputs/skill-embedding-probe.md:
---
name: embedding-probe
description: Inspect a word2vec model. Run analogies, find neighbors, diagnose quality.
version: 1.0.0
phase: 5
lesson: 03
tags: [nlp, embeddings, debugging]
---
You probe trained word embeddings to verify they are working. Given a `gensim.models.KeyedVectors` object and a vocabulary, you run:
1. Three canonical analogy tests. `king : man :: queen : woman`. `paris : france :: tokyo : japan`. `walking : walked :: swimming : ?`. Report the top-1 result and its cosine.
2. Five nearest-neighbor tests on domain-specific words the user supplies. Print top-5 neighbors with cosines.
3. One symmetry check. `similarity(a, b) == similarity(b, a)` to within float precision.
4. One degenerate check. If any embedding has a norm below 0.01 or above 100, the model has a training bug. Flag it.
Refuse to declare a model good on analogy accuracy alone. Analogy benchmarks are gameable and do not transfer to downstream tasks. Recommend intrinsic + downstream evaluation together.
Упражнения
- Легко. Запустите цикл обучения на маленьком корпусе (20 предложений о кошках и собаках). После 200 эпох убедитесь, что
nearest(vocab, W, W[vocab["cat"]])возвращаетdogв первой тройке. Если нет, увеличьте число эпох или словарь. - Средне. Добавьте подвыборку частых слов. Слова с частотой выше
10^-5исключаются из обучающих пар с вероятностью, пропорциональной их частоте. Измерьте влияние на сходство редких слов. - Сложно. Обучите модель на корпусе 20 Newsgroups. Вычислите две оси смещения:
he - sheиdoctor - nurse. Спроецируйте слова профессий на обе оси. Сообщите, у каких профессий наибольший разрыв смещения. Именно такой зонд используют исследователи справедливости.
Ключевые термины
| Термин | Как обычно говорят | Что это на самом деле означает |
|---|---|---|
| Векторное представление слова | Слово как вектор | Плотное низкоразмерное (обычно 100–300) представление, изученное из контекста. |
| Skip-gram | Приём Word2Vec | Предсказывает контекстные слова по центральному слову. Медленнее CBOW, лучше для редких слов. |
| Негативное семплирование | Ускорение обучения | Заменяет softmax по полному словарю бинарной классификацией против k случайных слов. |
| Статическое представление | Один вектор на слово | Один и тот же вектор независимо от контекста. Не справляется с многозначностью. |
| Контекстное представление | Вектор, чувствительный к контексту | Разный вектор для каждого употребления на основе окружающего контекста. Именно это создают трансформеры. |
| OOV | Out of vocabulary | Слово, не встречавшееся при обучении. Word2Vec не может выдать для него вектор. |
Дополнительное чтение
- Mikolov et al. (2013). Distributed Representations of Words and Phrases and their Compositionality — статья о негативном семплировании. Короткая и доступная.
- Rong, X. (2014). word2vec Parameter Learning Explained — наиболее ясный вывод градиентов, если математика исходной статьи кажется слишком плотной.
- Учебник gensim по Word2Vec — производственные настройки обучения, которые действительно работают.
Источник: Word Embeddings — Word2Vec from Scratch 05.02 — Bag of Words и TF-IDF · Фаза 5 — Основы и продвинутые темы NLP · Полный каталог · 05.04 — GloVe, fastText и подслова