深入解析CBOW模型手算词嵌入步骤:从数学原理到Python实现

1次阅读
没有评论

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

image.webp

词嵌入是 NLP 任务中的基础技术,它将词语映射到低维连续向量空间,使得语义相似的词在向量空间中也相近。CBOW(Continuous Bag of Words)模型是词嵌入的经典方法之一,通过上下文词预测目标词来学习词向量。本文将从数学原理到 Python 实现,逐步拆解 CBOW 模型的手算过程。

深入解析 CBOW 模型手算词嵌入步骤:从数学原理到 Python 实现

1. CBOW 模型的数学计算步骤

CBOW 模型的核心思想是利用上下文词预测目标词。假设我们的词汇表大小为 $V$,词向量维度为 $N$,上下文窗口大小为 $C$。以下是 CBOW 模型的主要计算步骤:

1.1 输入层到隐藏层

  1. 上下文词表示:对于每个上下文词,我们使用 one-hot 编码表示,记为 $\mathbf{x}_c$,其中 $c$ 是上下文词的索引。

  2. 词嵌入矩阵:我们有一个词嵌入矩阵 $\mathbf{W}_{V \times N}$,其中每一行对应一个词的词向量。通过矩阵乘法,我们可以得到上下文词的词向量:
    $$
    \mathbf{v}_c = \mathbf{W}^T \mathbf{x}_c
    $$

  3. 隐藏层向量:将上下文词的词向量求平均,得到隐藏层向量 $\mathbf{h}$:
    $$
    \mathbf{h} = \frac{1}{C} \sum_{c=1}^C \mathbf{v}_c
    $$

1.2 隐藏层到输出层

  1. 输出矩阵:我们还有一个输出矩阵 $\mathbf{W}’_{N \times V}$,用于将隐藏层向量映射到输出层。

  2. 得分计算:通过矩阵乘法计算每个词的得分:
    $$
    \mathbf{u} = \mathbf{W}’^T \mathbf{h}
    $$

  3. Softmax 归一化:对得分进行 Softmax 归一化,得到每个词的概率分布:
    $$
    p(w_j | w_{c1}, …, w_{cC}) = \frac{\exp(u_j)}{\sum_{k=1}^V \exp(u_k)}
    $$

  4. 损失函数:使用交叉熵损失函数计算预测误差:
    $$
    E = -\log p(w_O | w_{c1}, …, w_{cC})
    $$

2. Python 实现

以下是 CBOW 模型的 Python 实现代码,使用 NumPy 库完成矩阵运算:

import numpy as np

# 参数初始化
V = 10000  # 词汇表大小
N = 300    # 词向量维度
C = 4      # 上下文窗口大小

# 初始化词嵌入矩阵和输出矩阵
W = np.random.randn(V, N) * 0.01
W_prime = np.random.randn(N, V) * 0.01

# 前向传播
def forward(context_indices, target_index):
    # 上下文词的 one-hot 编码
    x = np.zeros((C, V))
    for i, idx in enumerate(context_indices):
        x[i, idx] = 1

    # 输入层到隐藏层
    v = np.dot(x, W)  # (C, N)
    h = np.mean(v, axis=0)  # (N,)

    # 隐藏层到输出层
    u = np.dot(W_prime.T, h)  # (V,)

    # Softmax
    exp_u = np.exp(u - np.max(u))  # 数值稳定性
    p = exp_u / np.sum(exp_u)

    # 损失
    loss = -np.log(p[target_index])

    return h, u, p, loss

# 反向传播
def backward(context_indices, target_index, h, u, p):
    # 计算输出层的梯度
    grad_u = p.copy()
    grad_u[target_index] -= 1

    # 更新输出矩阵
    grad_W_prime = np.outer(h, grad_u)  # (N, V)

    # 隐藏层的梯度
    grad_h = np.dot(W_prime, grad_u)  # (N,)

    # 更新词嵌入矩阵
    grad_v = grad_h / C  # (N,)
    grad_W = np.zeros((V, N))
    for idx in context_indices:
        grad_W[idx] += grad_v

    return grad_W, grad_W_prime

# 梯度检查
def gradient_check(context_indices, target_index):
    epsilon = 1e-7
    h, u, p, loss = forward(context_indices, target_index)
    grad_W, grad_W_prime = backward(context_indices, target_index, h, u, p)

    # 检查词嵌入矩阵的梯度
    for i in range(V):
        for j in range(N):
            W[i, j] += epsilon
            _, _, _, loss_plus = forward(context_indices, target_index)
            W[i, j] -= 2 * epsilon
            _, _, _, loss_minus = forward(context_indices, target_index)
            W[i, j] += epsilon

            numeric_grad = (loss_plus - loss_minus) / (2 * epsilon)
            assert np.abs(grad_W[i, j] - numeric_grad) < 1e-5, "Gradient check failed for W"

    # 检查输出矩阵的梯度
    for i in range(N):
        for j in range(V):
            W_prime[i, j] += epsilon
            _, _, _, loss_plus = forward(context_indices, target_index)
            W_prime[i, j] -= 2 * epsilon
            _, _, _, loss_minus = forward(context_indices, target_index)
            W_prime[i, j] += epsilon

            numeric_grad = (loss_plus - loss_minus) / (2 * epsilon)
            assert np.abs(grad_W_prime[i, j] - numeric_grad) < 1e-5, "Gradient check failed for W'"

    print("Gradient check passed!")

3. 对比 Skip-gram 模型

CBOW 和 Skip-gram 是 Word2Vec 的两种主要模型,它们的区别在于:

  1. 目标函数:CBOW 通过上下文词预测目标词,而 Skip-gram 通过目标词预测上下文词。

  2. 计算复杂度:CBOW 对上下文词求平均,适合处理高频词;Skip-gram 对每个上下文词单独处理,适合处理低频词。

  3. 性能:在小数据集上,CBOW 通常表现更好;在大数据集上,Skip-gram 更能捕捉词之间的复杂关系。

4. 工程实践优化

4.1 上下文窗口大小的选择

  • 小窗口(2-5):捕捉语法关系(如动词和宾语的搭配)。
  • 大窗口(5-10):捕捉语义关系(如“中国”和“北京”)。

4.2 词频统计与 Subsampling

  • 高频词降采样:对高频词(如“the”、“and”)按概率丢弃,避免它们主导训练过程。
  • 采样概率公式:
    $$
    P(w_i) = 1 – \sqrt{\frac{t}{f(w_i)}}
    $$
    其中 $t$ 是阈值(如 $10^{-5}$),$f(w_i)$ 是词的频率。

4.3 负采样加速训练

  • 负采样:随机采样 $K$ 个负样本(非目标词),代替计算全词汇表的 Softmax。
  • 损失函数:
    $$
    E = -\log \sigma(\mathbf{u}O^T \mathbf{h}) – \sum)
    $$
    其中 $\sigma$ 是 Sigmoid 函数。}^K \log \sigma(-\mathbf{u}_k^T \mathbf{h

5. 避坑指南

5.1 稀疏矩阵的内存优化

  • 使用稀疏矩阵 :对于大型词汇表,使用scipy.sparse 存储 one-hot 向量。
  • 哈希技巧:用哈希函数映射词到固定大小的向量。

5.2 学习率衰减策略

  • 线性衰减:
    $$
    \eta_t = \eta_0 \cdot (1 – t / T)
    $$
    其中 $T$ 是总迭代次数。
  • 指数衰减:
    $$
    \eta_t = \eta_0 \cdot \gamma^t
    $$
    其中 $\gamma$ 是衰减率(如 0.95)。

5.3 词向量可视化技巧

  • PCA 降维:用 PCA 将词向量降到 2D 或 3D 后绘图。
  • t-SNE:对高维数据更有效,但计算成本较高。

6. 开放问题

  1. 生僻词处理:CBOW 对低频词的表现较差,如何改进?
  2. 与 BERT 对比:BERT 等预训练模型是否完全替代了 Word2Vec?

CBOW 模型虽然简单,但仍然是理解词嵌入的基础。通过手算和代码实现,我们可以更深入地掌握其原理和优化技巧。

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