查看全课大纲(第 20 / 69 节)
  1. 1.NLP 的研究领域、应用与学习路径
  2. 2.自然语言的基本术语
  3. 3.信息论基础:信息熵与交叉熵
  4. 4.语言的进化与语义变迁
  5. 5.主观概率、客观概率与条件概率
  6. 6.贝叶斯原理与推理
  7. 7.随机变量:二项式、期望方差与联合概率
  8. 8.常见概率分布
  9. 9.辛普森悖论与贝叶斯解题实例
  10. 10.语言模型:语言概率与词袋模型
  11. 11.n-gram 语言模型(二元 / 三元 / N-Gram)
  12. 12.语言模型评价:交叉熵、困惑度与插值
  13. 13.朴素贝叶斯文本分类:垃圾邮件
  14. 14.拼写检查
  15. 15.中文分词:构词法、最大匹配、最优路径与 Jieba
  16. 16.词表示问题与语义相似度
  17. 17.TF-IDF 权重处理
  18. 18.One-Hot 表达与词表示的局限
  19. 19.Word2Vec 核心思想
  20. 20.Word2Vec 训练过程
  21. 21.Skip-gram 模型详解
  22. 22.词性标注任务与标注方法(规则 + N-Gram)
  23. 23.EM 算法与混合模型
  24. 24.高斯混合模型 GMM
  25. 25.从混合模型到隐马尔可夫模型
  26. 26.隐马尔可夫模型(HMM)推导与维特比解码
  27. 27.线性链条件随机场 CRF
  28. 28.贝叶斯网络与图模型
  29. 29.隐马尔可夫链与 HMM 推导
  30. 30.PLSA 主题模型
  31. 31.LSI 与 SVD 向量表达
  32. 32.LDA 与狄利克雷分布
  33. 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
  34. 34.上下文无关文法(CFG)与句法分析
  35. 35.概率上下文无关文法 PCFG
  36. 36.统计机器翻译:词对齐与翻译模型
  37. 37.统计机器翻译中的对齐模型与参数估计
  38. 38.从统计机器翻译到神经机器翻译
  39. 39.神经元与反向传播速览(指向深度学习课)
  40. 40.CNN 原理及其在 NLP 中的应用
  41. 41.循环神经网络的基本原理与 Elman / Jordan 网络
  42. 42.LSTM:长短期记忆网络的核心思想与分步实现
  43. 43.Encoder-Decoder 框架
  44. 44.Seq2Seq 与早期注意力机制
  45. 45.注意力机制的背景与动机
  46. 46.注意力模型实现原理与匹配函数
  47. 47.记忆网络:组成与计算
  48. 48.注意力机制的应用与历史延展
  49. 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
  50. 50.Multi-Head Attention、FFNN 与位置编码
  51. 51.层归一化、损失函数与 Universal Transformer 延展
  52. 52.预训练范式:从 ELMo 到 BERT
  53. 53.BERT 与掩码语言模型
  54. 54.BERT 下游任务微调与 GLUE 口径
  55. 55.GPT 与自回归语言建模
  56. 56.GPT-1 到 GPT-4 的演进
  57. 57.指令微调与 RLHF
  58. 58.分词器:BPE、WordPiece 与中文词表
  59. 59.上下文窗口与长文本处理
  60. 60.能力边界、成本与适用场景
  61. 61.文本分类实战:从朴素贝叶斯到逻辑回归
  62. 62.命名实体识别实战
  63. 63.文本生成与摘要实战
  64. 64.语义检索与向量库实战
  65. 65.RAG 知识库问答
  66. 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
  67. 67.幻觉与事实性问题
  68. 68.文本与知识结合(知识图谱压缩)
  69. 69.跨媒体信息检索与未来展望

Word2Vec 训练过程

小象实战讲义 · 自然语言处理

在上一节我们了解了 Word2Vec 的核心思想与两种模型架构(CBOW 和 Skip-gram)。本节我们将深入 CBOW 模型的训练过程,从数学公式推导到损失函数构建,完整揭示 Word2Vec 如何通过神经网络学习到高质量的分布式词向量。掌握这一过程,你将能真正理解词向量生成的原理,并为后续学习更复杂的神经网络模型打下坚实基础。

💡 核心导读

  • CBOW 模型的目标:根据上下文词预测中心词,通过调整网络参数使预测概率最大化。
  • 训练流程六步走:从 One-Hot 向量到词向量映射,再到上下文向量聚合、得分计算、概率转换,最终构建交叉熵损失函数。
  • 核心参数:嵌入矩阵 EE(输入层到隐藏层的权重)和输出权重矩阵 UU(隐藏层到输出层的权重),两者共同决定了词向量的质量。
  • 损失函数的意义:使用交叉熵来衡量模型预测的概率分布与真实 One-Hot 分布之间的差距,通过最小化损失来优化参数。

从 One-Hot 到词向量:嵌入矩阵 EE

在 CBOW 模型中,输入是中心词 wcw_c 的上下文窗口内的 2m2m 个词。首先,每个上下文词 wc−m,...,wc−1,wc+1,...,wc+mw_{c-m}, ..., w_{c-1}, w_{c+1}, ..., w_{c+m} 都表示为一个 VV 维的 One-Hot 向量,其中 VV 是词汇表大小。

我们有一个可学习的嵌入矩阵 E∈RV×KE \in \mathbb{R}^{V \times K},其中 KK 是词向量的维度(通常远小于 VV)。将 One-Hot 向量 xix_i 与嵌入矩阵 EE 相乘,即可得到该词的 KK 维稠密词向量 viv_i: vi=ETxiv_i = E^T x_i 由于 xix_i 是 One-Hot 向量,这个乘法操作本质上就是选取 EE 矩阵中对应于该词索引的那一行。因此,嵌入矩阵 EE 的每一行就是一个词的词向量。

# 示例:从 One-Hot 到词向量
import numpy as np

# 假设词汇表 V = 5,词向量维度 K = 3
V = 5
K = 3
np.random.seed(42) # 固定随机种子,确保输出可复现

# 嵌入矩阵 E,形状 (V, K)
E = np.random.randn(V, K).round(4)
print("Embedding matrix E (shape V x K):")
print(E)
print()

# 假设词汇表为 {0:'我', 1:'爱', 2:'北京', 3:'天安门', 4:'的'}
# 词"北京"的索引是2,其 One-Hot 向量为 [0, 0, 1, 0, 0]
x_beijing = np.array([0, 0, 1, 0, 0])
# 计算词向量 v_beijing = E^T * x_beijing
v_beijing = np.dot(x_beijing, E) # 等价于 E[2, :]
print(f"One-Hot vector for '北京': {x_beijing}")
print(f"Word vector for '北京' (v_beijing): {v_beijing.round(4)}")
print(f"Directly from row 2 of E: {E[2, :].round(4)}")
Embedding matrix E (shape V x K):
[[ 0.4967 -0.1383  0.6477]
 [ 1.523  -0.2342 -0.2341]
 [ 1.5792  0.7674 -0.4695]
 [ 0.5426 -0.4634 -0.4657]
 [ 0.242  -1.9133 -1.7249]]

One-Hot vector for '北京': [0 0 1 0 0]
Word vector for '北京' (v_beijing): [ 1.5792  0.7674 -0.4695]
Directly from row 2 of E: [ 1.5792  0.7674 -0.4695]

上下文向量的聚合:求平均得到 v^\hat{v}

对于给定的上下文窗口,我们得到了 2m2m 个上下文词向量 vc−m,...,vc−1,vc+1,...,vc+mv_{c-m}, ..., v_{c-1}, v_{c+1}, ..., v_{c+m}(不含中心词 vcv_c)。CBOW 模型将这些上下文向量进行平均,得到一个聚合后的上下文表示 v^\hat{v}: v^=12m∑i=−m,i≠0mvc+i\hat{v} = \frac{1}{2m} \sum_{i=-m, i \neq 0}^{m} v_{c+i} 这个 v^\hat{v} 是一个 KK 维向量,它代表了整个上下文窗口的“中心”语义信息。模型的目标不是让 v^\hat{v} 与输入矩阵 EE 中中心词的行向量做最近邻,而是让 v^\hat{v} 经输出矩阵 UU 打分后,真实中心词在 UU 对应列上的得分最高。


# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
np.random.seed(42)  # 固定随机种子,确保输出可复现
V = 5
K = 3
E = np.random.randn(V, K).round(4)

# 示例:上下文向量聚合
# 假设上下文窗口 m=1,即用前一个和后一个词预测中心词。
# 句子:"我 爱 北京 天安门",中心词是"北京",上下文是"爱"和"天安门"。
# 假设词汇表索引:我:0, 爱:1, 北京:2, 天安门:3
# 上下文词"爱"和"天安门"的词向量就是 E 的第1行和第3行
v_ai = E[1, :]  # "爱"的词向量
v_tiananmen = E[3, :] # "天安门"的词向量
print(f"Vector for '爱': {v_ai.round(4)}")
print(f"Vector for '天安门': {v_tiananmen.round(4)}")

# 求平均得到聚合向量 hat_v
hat_v = (v_ai + v_tiananmen) / 2
print(f"Aggregated context vector hat_v: {hat_v.round(4)}")
Vector for '爱': [ 1.523  -0.2342 -0.2341]
Vector for '天安门': [ 0.5426 -0.4634 -0.4657]
Aggregated context vector hat_v: [ 1.0328 -0.3488 -0.3499]

从隐藏层到输出层:得分计算与 Softmax

接下来,我们需要根据聚合向量 v^\hat{v} 来预测中心词。这通过另一个可学习的权重矩阵 U∈RK×VU \in \mathbb{R}^{K \times V} 来实现。将 v^\hat{v} 与 UU 相乘,得到一个 VV 维的得分向量 zz: z=v^TUz = \hat{v}^T U zz 中的每一个元素 zjz_j 代表了聚合上下文 v^\hat{v} 与词汇表中第 jj 个词(通过 UU 矩阵的第 jj 列表示)的关联程度。点积值越大,表示相关性越强。

然而,zz 的值范围不确定(可能为任意实数),我们需要将其转换为一个概率分布,即词汇表中每个词作为中心词出现的概率。这里使用 Softmax 函数: yhat,j=P(wj∣context)=exp⁡(zj)∑k=1Vexp⁡(zk)y_{\text{hat}, j} = P(w_j | \text{context}) = \frac{\exp(z_j)}{\sum_{k=1}^{V} \exp(z_k)} 这样,yhaty_{\text{hat}} 就是一个 VV 维的概率向量,所有元素和为1。


# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
np.random.seed(42)  # 固定随机种子,确保输出可复现
V = 5
K = 3
E = np.random.randn(V, K).round(4)
v_ai = E[1, :]  # "爱"的词向量
v_tiananmen = E[3, :] # "天安门"的词向量
hat_v = (v_ai + v_tiananmen) / 2

# 示例:得分计算与 Softmax
# 输出权重矩阵 U,形状 (K, V)
U = np.random.randn(K, V).round(4)
print("Output weight matrix U (shape K x V):")
print(U)
print()

# 计算得分向量 z
z = np.dot(hat_v, U) # 等价于 hat_v^T * U
print(f"Score vector z (shape V): {z.round(4)}")

# 应用 Softmax 得到预测概率 y_hat
def softmax(x):
    exp_x = np.exp(x - np.max(x)) # 减去最大值防止数值溢出
    return exp_x / exp_x.sum()

y_hat = softmax(z)
print(f"Predicted probability distribution y_hat: {y_hat.round(4)}")
print(f"Sum of y_hat: {y_hat.sum():.4f}")
Output weight matrix U (shape K x V):
[[-0.5623 -1.0128  0.3142 -0.908  -1.4123]
 [ 1.4656 -0.2258  0.0675 -1.4247 -0.5444]
 [ 0.1109 -1.151   0.3757 -0.6006 -0.2917]]

Score vector z (shape V): [-1.1307 -0.5645  0.1695 -0.2307 -1.1667]
Predicted probability distribution y_hat: [0.1015 0.1787 0.3724 0.2496 0.0979]
Sum of y_hat: 1.0000

构建损失函数:交叉熵

现在,我们有了模型的预测概率分布 yhaty_{\text{hat}}。真实的标签是什么?对于给定的上下文,真实的中心词是 wcw_c,我们可以用一个 VV 维的 One-Hot 向量 yy 来表示它,其中只有在 wcw_c 索引位置为1,其余为0。

我们的目标是让预测分布 yhaty_{\text{hat}} 尽可能接近真实分布 yy。衡量两个概率分布之间差异的常用方法是交叉熵(Cross-Entropy)。对于单个训练样本,损失函数 LL 定义为: L=−∑j=1Vyjlog⁡(yhat,j)L = -\sum_{j=1}^{V} y_j \log(y_{\text{hat}, j}) 由于 yy 是 One-Hot 向量,只有索引 cc 处 yc=1y_c=1,其他 yj=0y_j=0,因此上式简化为: L=−log⁡(yhat,c)L = -\log(y_{\text{hat}, c}) 最小化这个损失函数,就等价于最大化真实中心词 wcw_c 的预测对数概率。


# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
np.random.seed(42)  # 固定随机种子,确保输出可复现
V = 5
K = 3
E = np.random.randn(V, K).round(4)
v_ai = E[1, :]  # "爱"的词向量
v_tiananmen = E[3, :] # "天安门"的词向量
hat_v = (v_ai + v_tiananmen) / 2
U = np.random.randn(K, V).round(4)
z = np.dot(hat_v, U) # 等价于 hat_v^T * U
def softmax(x):
    exp_x = np.exp(x - np.max(x)) # 减去最大值防止数值溢出
    return exp_x / exp_x.sum()
y_hat = softmax(z)

# 示例:计算交叉熵损失
# 假设真实的中心词是"北京",索引为2
true_index = 2
y_true = np.zeros(V)
y_true[true_index] = 1

# 计算交叉熵损失
loss = -np.log(y_hat[true_index])
print(f"True label vector y: {y_true.astype(int)}")
print(f"Predicted probability for '北京' (index {true_index}): {y_hat[true_index]:.6f}")
print(f"Cross-entropy loss L: {loss:.6f}")
True label vector y: [0 0 1 0 0]
Predicted probability for '北京' (index 2): 0.372375
Cross-entropy loss L: 0.987855

参数更新与训练

我们的模型有两个主要的参数集合需要学习:

  1. 嵌入矩阵 EE:它直接定义了每个词的词向量。
  2. 输出权重矩阵 UU:它定义了从隐藏层(上下文表示)到每个输出词的概率映射关系。

训练过程就是使用大量的(上下文,中心词)样本对,通过反向传播算法和梯度下降优化方法(如 SGD、Adam),不断计算损失 LL 对 EE 和 UU 的梯度,并更新这两个矩阵,使得总损失不断减小。

训练伪代码流程(示意):
1. 初始化嵌入矩阵 E 和输出矩阵 U。
2. For epoch in range(num_epochs):
3.    For each (context_words, center_word) in training_data:
4.        # 前向传播
5.        context_vectors = lookup(E, context_words) # 查找词向量
6.        hat_v = average(context_vectors)           # 求平均
7.        z = dot(hat_v, U)                          # 计算得分
8.        y_hat = softmax(z)                         # 得到概率
9.        loss = -log(y_hat[center_word_index])      # 计算损失
10.       
11.       # 反向传播
12.       grad_U, grad_E_context = compute_gradients(loss, U, E, context_words)
13.       
14.       # 参数更新
15.       U = U - learning_rate * grad_U
16.       # 注意:只更新上下文词对应的 E 中的行
17.       for word in context_words:
18.           E[word_index] = E[word_index] - learning_rate * grad_E_context[word]

训练完成后,嵌入矩阵 EE 就是我们最终得到的词向量查找表。若只是提取已训练好的词向量用于下游任务,通常只使用 EE;但如果要运行中心词预测(对候选词打分、计算 Softmax 概率),输出矩阵 UU 仍是必需参数。在一些改进方法(如负采样)中,UU 的列向量也被视为词的另一种“输出向量”,有时会与 EE 中的向量结合使用。

📝 动手练一练

  1. 概念辨析:在 CBOW 模型的损失函数 L=−log⁡(yhat,c)L = -\log(y_{\text{hat}, c}) 中,如果模型预测完全正确,即 yhat,c=1y_{\text{hat}, c} = 1,那么损失 LL 是多少?这说明了交叉熵损失的什么性质?
👉 点击查看参考答案

如果模型预测完全正确,即预测中心词的概率为 1,则 L=−log⁡(1)=0L = -\log(1) = 0。这说明交叉熵损失的下界是 0,当预测分布与真实分布完全一致时,损失达到最小值 0。损失值越小,表示模型预测得越好。

  1. 手算练习:假设一个极简词汇表 V=3V=3,词向量维度 K=2K=2。上下文聚合向量 v^=[0.5,−0.2]\hat{v} = [0.5, -0.2],输出矩阵 U=[[0.1,0.4,−0.3],[0.2,−0.1,0.5]]U = [[0.1, 0.4, -0.3], [0.2, -0.1, 0.5]]。请计算得分向量 zz,并手动计算 Softmax 后的概率分布 yhaty_{\text{hat}}(保留4位小数)。如果真实中心词索引为 1(从0开始),此时的交叉熵损失是多少?
👉 点击查看参考答案

首先计算得分向量 zz: z=v^TU=[0.5,−0.2]⋅[[0.1,0.4,−0.3],[0.2,−0.1,0.5]]=[0.5∗0.1+(−0.2)∗0.2,0.5∗0.4+(−0.2)∗(−0.1),0.5∗(−0.3)+(−0.2)∗0.5]=[0.05−0.04,0.2+0.02,−0.15−0.1]=[0.01,0.22,−0.25]z = \hat{v}^T U = [0.5, -0.2] \cdot [[0.1, 0.4, -0.3], [0.2, -0.1, 0.5]] = [0.5*0.1 + (-0.2)*0.2, 0.5*0.4 + (-0.2)*(-0.1), 0.5*(-0.3) + (-0.2)*0.5] = [0.05 - 0.04, 0.2 + 0.02, -0.15 - 0.1] = [0.01, 0.22, -0.25]

接着计算 Softmax。先计算指数:e0.01≈1.0101,e0.22≈1.2461,e−0.25≈0.7788e^{0.01} \approx 1.0101, e^{0.22} \approx 1.2461, e^{-0.25} \approx 0.7788。总和为 1.0101+1.2461+0.7788=3.03501.0101 + 1.2461 + 0.7788 = 3.0350。

则 yhat=[1.0101/3.0350,1.2461/3.0350,0.7788/3.0350]≈[0.3328,0.4106,0.2566]y_{\text{hat}} = [1.0101/3.0350, 1.2461/3.0350, 0.7788/3.0350] \approx [0.3328, 0.4106, 0.2566]。

真实中心词索引为 1,其预测概率为 0.41060.4106,交叉熵损失 L=−log⁡(0.4106)≈0.8901L = -\log(0.4106) \approx 0.8901。

本章小结

本节我们深入剖析了 Word2Vec 中 CBOW 模型的完整训练过程。我们从上下文词的 One-Hot 表示出发,通过嵌入矩阵 EE 得到词向量,聚合后得到上下文表示 v^\hat{v}。然后利用输出矩阵 UU 计算与每个词的得分,并通过 Softmax 转换为概率分布。最终,我们使用交叉熵损失函数来衡量预测与真实中心词的差距,并通过优化 EE 和 UU 来最小化这个损失,从而学习到能够捕捉词语语义关系的词向量。

值得注意的是,在整个词表上做完整 Softmax 是高开销的基线方案,计算成本与词汇表大小 VV 成正比;它并非原始 Word2Vec 论文的实现方式。为在大规模语料上训练,Word2Vec 采用了高效近似:负采样(Negative Sampling)由 Mikolov 等人提出;层次 Softmax(Hierarchical Softmax)则早由 Morin 和 Bengio(2005)引入,后被 Word2Vec 采用。这些优化技术我们将在后续章节中介绍。

📋 行动清单

  • 理解 CBOW 模型前向传播的六个步骤:One-Hot -> 词向量 -> 平均 -> 得分 -> Softmax -> 损失。
  • 掌握交叉熵损失函数在 One-Hot 标签下的简化形式 L=−log⁡(yhat,c)L = -\log(y_{\text{hat}, c}) 及其含义。
  • 尝试在小规模数据上(如自己构造的几句话)手动推导一遍 CBOW 的计算过程,加深理解。

—— 小象教研组