从零实现CBOW模型:词嵌入矩阵调整算法实例与最佳实践

1次阅读
没有评论

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

image.webp

1. 背景介绍

CBOW(Continuous Bag of Words)模型是 Word2Vec 的两种经典实现之一,主要作用是通过上下文预测中心词来学习词向量表示。它的核心优势在于对高频词的处理效率和训练速度,特别适合处理大规模语料库。在实际应用中,CBOW 常用于文本分类、信息检索等需要稠密词向量的场景。

从零实现 CBOW 模型:词嵌入矩阵调整算法实例与最佳实践

2. CBOW vs Skip-gram

  • CBOW 特点
  • 用上下文词预测中心词
  • 训练速度更快(尤其对高频词)
  • 对小数据集表现更好

  • Skip-gram 特点

  • 用中心词预测上下文词
  • 对低频词效果更好
  • 适合大数据集

3. 核心实现

3.1 词嵌入矩阵初始化

词嵌入矩阵 W∈ℝ^(V×d)和上下文矩阵 W ’∈ℝ^(d×V)通常随机初始化:

import numpy as np

vocab_size = 10000  # 词汇表大小
embedding_dim = 300  # 词向量维度

# Xavier 初始化
W = np.random.uniform(-1/np.sqrt(embedding_dim), 
                     1/np.sqrt(embedding_dim), 
                     (vocab_size, embedding_dim))
W_prime = np.random.uniform(-1/np.sqrt(embedding_dim),
                          1/np.sqrt(embedding_dim),
                          (embedding_dim, vocab_size))

3.2 前向传播

  1. 将上下文词的 one-hot 向量与 W 相乘得到词向量
  2. 对上下文词向量求平均
  3. 与 W ’ 相乘得到输出分数
  4. 通过 softmax 得到概率分布
def forward(context_words_indices, W, W_prime):
    # context_words_indices: 上下文词索引列表
    h = np.mean(W[context_words_indices], axis=0)
    u = np.dot(h, W_prime)
    y_pred = softmax(u)
    return h, u, y_pred

3.3 损失函数

使用交叉熵损失函数:

def cross_entropy(y_pred, y_true):
    return -np.sum(y_true * np.log(y_pred + 1e-10))

3.4 反向传播

关键梯度计算:

def backward(context_indices, center_index, h, y_pred, W, W_prime, learning_rate):
    # 计算梯度
    e = y_pred.copy()
    e[center_index] -= 1

    # 更新 W'
    dW_prime = np.outer(h, e)
    W_prime -= learning_rate * dW_prime

    # 更新 W
    dh = np.dot(W_prime, e)
    for i in context_indices:
        W[i] -= learning_rate * dh / len(context_indices)

4. 完整实现代码

import numpy as np
from collections import defaultdict

class CBOW:
    def __init__(self, vocab_size, embedding_dim):
        self.vocab_size = vocab_size
        self.embedding_dim = embedding_dim

        # 初始化参数
        self.W = np.random.uniform(-1/np.sqrt(embedding_dim),
                                 1/np.sqrt(embedding_dim),
                                 (vocab_size, embedding_dim))
        self.W_prime = np.random.uniform(-1/np.sqrt(embedding_dim),
                                      1/np.sqrt(embedding_dim),
                                      (embedding_dim, vocab_size))

    @staticmethod
    def softmax(x):
        exp_x = np.exp(x - np.max(x))
        return exp_x / exp_x.sum()

    def forward(self, context_indices):
        h = np.mean(self.W[context_indices], axis=0)
        u = np.dot(h, self.W_prime)
        y_pred = self.softmax(u)
        return h, u, y_pred

    def train(self, context_indices, center_index, learning_rate=0.01):
        # 前向传播
        h, u, y_pred = self.forward(context_indices)

        # 计算误差
        e = y_pred.copy()
        e[center_index] -= 1

        # 反向传播
        dW_prime = np.outer(h, e)
        dh = np.dot(self.W_prime, e)

        # 更新参数
        self.W_prime -= learning_rate * dW_prime
        for i in context_indices:
            self.W[i] -= learning_rate * dh / len(context_indices)

        # 返回损失
        loss = -np.log(y_pred[center_index] + 1e-10)
        return loss

5. 性能优化建议

  • 负采样:用负采样替代 softmax,大幅减少计算量
  • 层次 softmax:使用霍夫曼树加速 softmax 计算
  • mini-batch 训练:批量处理数据提高并行效率
  • 词频处理:对高频词进行下采样

6. 避坑指南

  • 数值稳定性
  • 对 softmax 做 max 归一化
  • 添加微小值 (1e-10) 防止 log(0)

  • 学习率选择

  • 初始学习率 0.01-0.1
  • 使用学习率衰减策略

  • 评估方法

  • 相似词任务
  • 类比推理任务(king – man + woman ≈ queen)
  • 下游任务评估

7. 进阶思考

  • 实际应用
  • 作为文本分类的特征输入
  • 用于信息检索的 query 扩展

  • 改进方向

  • 加入 subword 信息
  • 结合上下文位置信息
  • 多任务联合训练

通过这个完整实现,我们不仅掌握了 CBOW 的核心算法,还了解了词嵌入矩阵调整的关键技术。建议读者尝试在真实数据集上训练,并观察不同超参数对结果的影响。

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