共计 2057 个字符,预计需要花费 6 分钟才能阅读完成。
词嵌入的价值与 CBOW 模型
词嵌入(Word Embedding)是自然语言处理(NLP)中的一项基础技术,它将词语映射到一个低维连续向量空间,使得语义相似的词在向量空间中距离相近。CBOW(Continuous Bag of Words)模型是 Word2Vec 的一种实现方式,它通过上下文词语预测中心词,适合处理大规模语料库。

CBOW 模型的计算流程
1. 输入层到隐藏层
给定一个中心词和其上下文窗口(例如窗口大小为 2),模型的输入是上下文词的 one-hot 编码向量。假设词汇表大小为 V,词向量维度为 d:
- 输入向量:$x^{(c)} \in \mathbb{R}^V$(上下文词 c 的 one-hot 向量)
- 权重矩阵:$W \in \mathbb{R}^{V \times d}$(输入到隐藏层的权重)
- 隐藏层输出:$h = \frac{1}{C} \sum_{c=1}^C W^T x^{(c)}$(C 为上下文词数量)
2. 隐藏层到输出层
- 输出权重矩阵:$W’ \in \mathbb{R}^{d \times V}$
- 得分向量:$u = W’^T h$
- 通过 softmax 计算概率分布:$y_j = \frac{\exp(u_j)}{\sum_{k=1}^V \exp(u_k)}$
梯度计算与参数更新
损失函数
使用交叉熵损失:$J = -\sum_{j=1}^V y_j^* \log(y_j)$,其中 $y_j^*$ 是真实标签的 one-hot 编码。
梯度推导
-
输出层梯度:
$\frac{\partial J}{\partial W’} = h (y – y^*)^T$ -
隐藏层梯度:
$\frac{\partial J}{\partial h} = W’ (y – y^*)$ -
输入层梯度:
对于每个上下文词 c:
$\frac{\partial J}{\partial W} = x^{(c)} \left(\frac{\partial J}{\partial h}\right)^T$
参数更新
使用随机梯度下降(SGD)更新参数:
$W = W – \eta \frac{\partial J}{\partial W}$
$W’ = W’ – \eta \frac{\partial J}{\partial W’}$
其中 $\eta$ 为学习率。
Python 代码实现
import numpy as np
# 参数设置
V = 10000 # 词汇表大小
d = 100 # 词向量维度
C = 4 # 上下文窗口大小
eta = 0.01 # 学习率
# 初始化权重
W = np.random.randn(V, d) * 0.01
W_prime = np.random.randn(d, V) * 0.01
# 模拟输入数据 (上下文词索引)
context_words = [10, 20, 30, 40] # 示例上下文词索引
target_word = 25 # 目标词索引
# 将上下文词转换为 one-hot 向量
context_vectors = np.zeros((C, V))
for i, word_idx in enumerate(context_words):
context_vectors[i, word_idx] = 1
# 前向传播
# 1. 计算隐藏层
h = np.mean([W.T.dot(x) for x in context_vectors], axis=0)
# 2. 计算输出层
u = W_prime.T.dot(h)
# softmax
exp_u = np.exp(u - np.max(u)) # 数值稳定性处理
y = exp_u / np.sum(exp_u)
# 计算损失
y_true = np.zeros(V)
y_true[target_word] = 1
loss = -np.sum(y_true * np.log(y + 1e-10)) # 添加小常数避免 log(0)
# 反向传播
# 1. 计算输出层梯度
grad_W_prime = np.outer(h, y - y_true)
# 2. 计算隐藏层梯度
grad_h = W_prime.dot(y - y_true)
# 3. 更新输入层权重
for x in context_vectors:
W[x.argmax()] -= eta * grad_h # 只更新对应词的行
# 更新输出层权重
W_prime -= eta * grad_W_prime
实际训练注意事项
- 学习率选择 :
- 初始学习率通常设置在 0.01-0.001 之间
-
可以使用学习率衰减策略提高后期训练稳定性
-
负采样技巧 :
- 原始 softmax 计算在大词汇表时计算量巨大
-
负采样只更新少量负例和正例的参数,大幅提升训练速度
-
向量维度影响 :
- 维度太低会导致语义信息捕获不足
- 维度太高会增加计算成本且可能过拟合
- 通常 50-300 维之间效果较好
思考题
- CBOW 和 Skip-gram 的梯度计算有什么主要差异?
- CBOW 是多个上下文词共享同一个梯度更新
-
Skip-gram 是每个上下文词单独计算梯度
-
如何验证手算结果的正确性?
- 使用小规模词汇表人工验证前几轮参数更新
- 比较不同实现的结果一致性
- 检查损失函数是否随着训练单调下降
总结
通过本文的详细推导和代码实现,相信读者已经掌握了 CBOW 模型的核心计算流程。词嵌入技术虽然概念简单,但在实现细节上有很多需要注意的地方。建议初学者先在小规模数据上手动实现,理解每个步骤的含义,再过渡到大规模语料库的训练。
