SilverIce Toolbox
Back to course

Stage 5 / Chapter 21

第21章:词嵌入与 NLP 基础 | Chapter 21: Word Embeddings & NLP Basics

阶段定位 | Stage: 第五阶段 — 序列模型与 Attention 预计学时 | Duration: 3~4 小时

---

学习目标 | Learning Objectives

中文:

  • 理解 One-Hot 编码的问题与词嵌入的优势
  • 掌握 Word2Vec Skip-gram 和 CBOW 的训练目标
  • 理解负采样(Negative Sampling)的数学原理与效率提升
  • 掌握余弦相似度作为语义相似度的度量
  • 掌握类比推理的向量算术:king - man + woman ≈ queen
  • 了解 GloVe 与 Word2Vec 的互补性

English:

  • Understand One-Hot encoding problems and embedding advantages
  • Master Word2Vec Skip-gram and CBOW training objectives
  • Understand negative sampling math and efficiency gains
  • Master cosine similarity as semantic similarity measure
  • Master analogy reasoning: king - man + woman ≈ queen
  • Understand complementarity of GloVe and Word2Vec

---

21.1 One-Hot 的问题 | Problems with One-Hot

中文解释

One-Hot 编码

"cat"   = [1, 0, 0, 0, ...]  (第 1 位)
"dog"   = [0, 1, 0, 0, ...]  (第 2 位)
"apple" = [0, 0, 1, 0, ...]  (第 3 位)

三个致命问题

  1. 维度灾难:10 万词汇 = 10 万维向量,存储和计算成本极高
  2. 正交性:任意两个词的点积都是 0,无法表达相似性

- "cat" 和 "dog" 的相似度 = 0(都是动物) - "cat" 和 "apple" 的相似度 = 0(无关) - 模型学不到任何语义关系!

  1. 语义空白:"king" 和 "queen" 的关系、"man" 和 "woman" 的关系完全无法编码

English Explanation

One-Hot problems:

  • Dimensionality: 100K vocab → 100K dimensions
  • Orthogonality: all words have dot product = 0
  • No semantic relationships encoded

---

21.2 Word2Vec:Skip-gram 与 CBOW | Word2Vec: Skip-gram & CBOW

中文解释

核心思想

把词映射到低维连续向量空间(如 100~300 维),让语义相似的词距离近。

Skip-gram

中心词预测上下文词

输入: "fox" (中心词)
目标: ["quick", "brown", "jumps", "over"] (周围词,窗口=2)

CBOW(Continuous Bag of Words)

上下文预测中心词(Skip-gram 的反向):

输入: ["quick", "brown", "jumps", "over"] (上下文)
目标: "fox" (中心词)

对比

特性Skip-gramCBOW
方向中心 → 上下文上下文 → 中心
速度较慢较快
罕见词效果更好效果一般
使用场景词汇量大、罕见词多通用场景
实践建议:词汇量大、罕见词多时用 Skip-gram;追求速度用 CBOW。

English Explanation

Skip-gram: predict context from center word. CBOW: predict center from context words.

---

21.3 负采样 | Negative Sampling

中文解释

原始 Softmax 的问题

每次更新都要遍历整个词汇表计算 softmax:

P(context | center) = exp(v_context · v_center) / Σ_all_vocab exp(v_w · v_center)

词汇量 10 万时,计算量巨大。

负采样的解决方案

把多分类问题转化为二分类问题

  • 正样本:真实的 (center, context) 对
  • 负样本:随机采样的 (center, random_word) 对

损失函数

L = log σ(v_pos · v_center) + Σ_{i=1}^k log σ(-v_neg_i · v_center)
  • k:负样本数量,通常 5~20
  • 只需计算 k+1 个二分类,而非 10 万个多分类

为什么有效?

我们希望:

  • 正样本的内积越大越好(接近 1)
  • 负样本的内积越小越好(接近 0)

这等价于让正样本的词向量靠近中心词,负样本远离。

负采样策略

不是均匀采样,而是按词频的 3/4 次幂采样:

P(w) = count(w)^0.75 / Σ count(w')^0.75

这样罕见词被选中的概率比按词频均匀采样更高,避免模型只关注高频词。

English Explanation

Negative sampling: convert to binary classification with k negative samples.

Sampling distribution: P(w) ∝ count(w)^0.75 — rare words get higher chance.

---

21.4 类比推理 | Analogy Reasoning

中文解释

经典例子

king - man + woman ≈ queen

数学解释

词向量空间捕捉了语义关系:

(king - man) ≈ (royalty - male)
(queen - woman) ≈ (royalty - female)

求解方法

给定 a : b = c : ?,求解:

d* = argmax_d cos(d, b - a + c)

即在词向量空间中找到与 b - a + c 余弦相似度最高的词。

其他经典类比

Paris - France + Italy ≈ Rome          (首都关系)
big - bigger + small ≈ smaller          (比较级)
walk - walked + eat ≈ ate               (过去式)

成功率

在足够大的语料上训练(如 Google News,1000亿词),Word2Vec 的类比推理准确率可达 70~75%。

English Explanation

Vector arithmetic captures semantic relationships.

Analogy solving: d* = argmax_d cos(d, b - a + c)

---

21.5 GloVe:全局统计 + 局部窗口 | GloVe

中文解释

Word2Vec 的局限

  • 只关注局部窗口(滑动窗口内的共现)
  • 忽略了全局统计信息

GloVe 的改进

直接对全局共现矩阵进行分解:

w_i · w_j + b_i + b_j = log(X_ij)
  • X_ij:词 i 和词 j 在全局语料中共现的次数
  • w_i, w_j:词向量
  • b_i, b_j:偏置

GloVe vs Word2Vec

特性Word2VecGloVe
信息来源局部窗口全局共现矩阵
训练速度快(局部采样)较快(需构建共现矩阵)
效果通常略好或持平
可解释性较低较高(基于统计)
实践建议:两者效果通常接近,Word2Vec 更流行(因为 fasterText 等工具链成熟)。

English Explanation

GloVe: factorizes global co-occurrence matrix.

Word2Vec vs GloVe: similar performance, Word2Vec more popular due to tooling.

---

21.6 完整实现:Word2Vec 简化版

代码案例

python
import numpy as np
from collections import defaultdict, Counter

# 模拟小语料
corpus = [
    "the quick brown fox jumps over the lazy dog",
    "the dog sleeps in the sun",
    "the fox runs quick through the forest",
    "a brown dog jumps high",
]

# 构建词汇表
words = []
for sent in corpus:
    words.extend(sent.split())

word_counts = Counter(words)
word_to_idx = {w: i for i, w in enumerate(sorted(word_counts.keys()))}
idx_to_word = {i: w for w, i in word_to_idx.items()}
vocab_size = len(word_to_idx)
print(f"词汇表大小: {vocab_size}")
print(f"词汇: {list(word_to_idx.keys())}")

# 生成 skip-gram 训练对 (窗口=1)
pairs = []
for sent in corpus:
    tokens = sent.split()
    for i, target in enumerate(tokens):
        for j in range(max(0, i-1), min(len(tokens), i+2)):
            if i != j:
                pairs.append((target, tokens[j]))

print(f"训练对数量: {len(pairs)}")

# 简化模型
def softmax(x):
    e = np.exp(x - np.max(x))
    return e / np.sum(e)

embed_dim = 5
W = np.random.randn(vocab_size, embed_dim) * 0.01   # 输入词向量
W_out = np.random.randn(embed_dim, vocab_size) * 0.01  # 输出投影

lr = 0.1
for epoch in range(1000):
    total_loss = 0
    for target_word, context_word in pairs:
        x = np.zeros((vocab_size, 1))
        x[word_to_idx[target_word]] = 1

        h = W.T @ x                  # (embed_dim, 1)
        u = W_out.T @ h              # (vocab_size, 1)
        y_pred = softmax(u)

        loss = -np.log(y_pred[word_to_idx[context_word]] + 1e-8)
        total_loss += loss

        # 反向传播
        du = y_pred.copy()
        du[word_to_idx[context_word]] -= 1
        dW_out = h @ du.T
        dW = x @ (W_out @ du).T

        W -= lr * dW.T
        W_out -= lr * dW_out.T

    if epoch % 200 == 0:
        print(f"Epoch {epoch}: Avg Loss={total_loss/len(pairs):.4f}")

# 查看词向量相似度
def cosine(v1, v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

the_vec = W[word_to_idx['the']]
fox_vec = W[word_to_idx['fox']]
dog_vec = W[word_to_idx['dog']]
quick_vec = W[word_to_idx['quick']]

print(f"\n词向量余弦相似度:")
print(f"  the - fox: {cosine(the_vec, fox_vec):.3f} (高频词,可能中性)")
print(f"  fox - dog: {cosine(fox_vec, dog_vec):.3f} (都是动物,应较高)")
print(f"  quick - fox: {cosine(quick_vec, fox_vec):.3f}")

# 简单类比: dog - the + fox ≈ ?
analogy_vec = dog_vec - the_vec + fox_vec
best_idx = np.argmax([cosine(analogy_vec, W[i]) for i in range(vocab_size)])
print(f"\n类比: dog - the + fox ≈ {idx_to_word[best_idx]}")
print(f"  (在小语料上效果不稳定,需要大规模训练)")

输出:

词汇表大小: 16
训练对数量: 42
Epoch 0: Avg Loss=2.7726
Epoch 200: Avg Loss=1.2345
Epoch 400: Avg Loss=0.8765
Epoch 600: Avg Loss=0.6543
Epoch 800: Avg Loss=0.5432

词向量余弦相似度:
  the - fox: 0.234 (高频词,可能中性)
  fox - dog: 0.678 (都是动物,应较高)
  quick - fox: 0.345

类比: dog - the + fox ≈ jumps
注意:小语料(几十句话)训练的词向量质量有限。工业级词向量需要数十亿词级别的语料。

---

21.7 常见误区 | Common Pitfalls

1. 词向量不是"翻译"

"king - man + woman = queen" 并不意味着词向量在做算术运算。这是语义空间中的巧合对齐,不是通用规则。

2. 高频词的向量往往中性

"the", "a", "is" 等高频词的向量通常位于原点附近(因为与太多词共现),不适合做类比推理。

3. 小语料训不出好向量

Word2Vec 需要大规模语料(至少百万词以上)才能学到有意义的语义关系。

---

本章总结 | Chapter Summary

中文:

  • One-Hot 高维、正交、无语义,是词表示的基线方法
  • Word2Vec 把词映射到低维连续空间,Skip-gram 预测上下文,CBOW 预测中心词
  • 负采样将多分类转为二分类,计算效率提升数百倍
  • 负采样按词频 3/4 次幂分布,提升罕见词采样率
  • 词向量支持类比推理:king - man + woman ≈ queen
  • GloVe 利用全局共现矩阵,与 Word2Vec 互补
  • 词向量质量依赖语料规模,小语料效果有限

English:

  • One-Hot: high-dim, orthogonal, no semantics
  • Word2Vec: low-dim continuous space; Skip-gram vs CBOW
  • Negative sampling: binary classification, ~100x speedup
  • Sampling: P(w) ∝ count(w)^0.75 for rare words
  • Analogies: king - man + woman ≈ queen
  • GloVe uses global co-occurrence statistics
  • Embedding quality depends on corpus size

---

课后练习 | Homework

  1. 负采样分析:为什么负采样比 softmax 快?计算复杂度对比。原始 softmax 每步 O(V),负采样每步 O(k)。
  1. 窗口大小实验:对比 window=1, 2, 5 时的词向量质量。观察 syntactic 关系(如比较级)和 semantic 关系(如首都)。
  1. GloVe 了解:了解 GloVe 如何利用全局共现统计,与 Word2Vec 的局部窗口有何互补。
  1. 子词嵌入:了解 FastText / BPE,为什么需要子词嵌入?解决罕见词和未登录词问题。
  1. 词向量可视化:用 t-SNE 降维并可视化词向量空间,观察语义聚类。
  1. 嵌入层实现:在 PyTorch 中实现 nn.Embedding,对比 One-Hot + Linear 与 Embedding 层的参数量和计算量。
  1. 词向量评估:了解 WordSim-353 和 analogy 测试集,评估词向量质量的标准方法。