CBOW模型词嵌入手算指南:从梯度计算到向量生成

1次阅读
没有评论

共计 2057 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

词嵌入的价值与 CBOW 模型

词嵌入(Word Embedding)是自然语言处理(NLP)中的一项基础技术,它将词语映射到一个低维连续向量空间,使得语义相似的词在向量空间中距离相近。CBOW(Continuous Bag of Words)模型是 Word2Vec 的一种实现方式,它通过上下文词语预测中心词,适合处理大规模语料库。

CBOW 模型词嵌入手算指南:从梯度计算到向量生成

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 编码。

梯度推导

  1. 输出层梯度:
    $\frac{\partial J}{\partial W’} = h (y – y^*)^T$

  2. 隐藏层梯度:
    $\frac{\partial J}{\partial h} = W’ (y – y^*)$

  3. 输入层梯度:
    对于每个上下文词 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

实际训练注意事项

  1. 学习率选择 :
  2. 初始学习率通常设置在 0.01-0.001 之间
  3. 可以使用学习率衰减策略提高后期训练稳定性

  4. 负采样技巧 :

  5. 原始 softmax 计算在大词汇表时计算量巨大
  6. 负采样只更新少量负例和正例的参数,大幅提升训练速度

  7. 向量维度影响 :

  8. 维度太低会导致语义信息捕获不足
  9. 维度太高会增加计算成本且可能过拟合
  10. 通常 50-300 维之间效果较好

思考题

  1. CBOW 和 Skip-gram 的梯度计算有什么主要差异?
  2. CBOW 是多个上下文词共享同一个梯度更新
  3. Skip-gram 是每个上下文词单独计算梯度

  4. 如何验证手算结果的正确性?

  5. 使用小规模词汇表人工验证前几轮参数更新
  6. 比较不同实现的结果一致性
  7. 检查损失函数是否随着训练单调下降

总结

通过本文的详细推导和代码实现,相信读者已经掌握了 CBOW 模型的核心计算流程。词嵌入技术虽然概念简单,但在实现细节上有很多需要注意的地方。建议初学者先在小规模数据上手动实现,理解每个步骤的含义,再过渡到大规模语料库的训练。

正文完
 0
评论(没有评论)