共计 2062 个字符,预计需要花费 6 分钟才能阅读完成。
CBOW 模型与词嵌入矩阵的核心作用
CBOW(Continuous Bag of Words)模型是 Word2Vec 的经典实现之一,通过上下文词预测中心词来学习词向量。在实际应用中,比如搜索引擎的查询理解、推荐系统的物品表征等场景,词嵌入矩阵的质量直接影响语义捕捉能力。矩阵运算在这里扮演核心角色——从初始化词向量、计算上下文表示,到梯度更新,整个过程本质上是高维空间中的张量操作。

传统实现中的三大痛点
-
内存瓶颈:词表规模 V 和维度 d 构成 V×d 矩阵,当 V =10^5 时,单精度浮点矩阵占用约 400MB,稀疏矩阵处理不当会浪费显存
-
计算效率:传统 for 循环实现上下文窗口遍历,时间复杂度 O(V×k)(k 为窗口大小),在 Python 中可能比向量化慢 100 倍
-
数值稳定性:softmax 的分母求和可能导致数值溢出,未经处理的梯度更新易引发维度爆炸
手写矩阵实现关键步骤
1. 词向量矩阵初始化
import numpy as np
def init_embedding(vocab_size, dim):
"""
采用 Xavier 初始化,适应后续的 sigmoid 激活
:param vocab_size: 词表大小
:param dim: 向量维度
:return: (V, d)的矩阵
"""
bound = np.sqrt(6 / (vocab_size + dim))
return np.random.uniform(-bound, bound, (vocab_size, dim))
2. 向量化负采样实现
将采样过程转化为矩阵运算:
def negative_sampling(target_idx, context_vec, embedding, k=5):
"""
:param target_idx: 正样本词索引
:param context_vec: 上下文向量平均值 (d,)
:param embedding: 词嵌入矩阵 (V, d)
:param k: 负采样数
"""
# 使用 log-uniform 采样替代 np.random.choice
neg_indices = ... # 采样代码省略
# 矩阵化计算得分 (k+1, d) @ (d,) -> (k+1,)
sample_vectors = np.vstack([embedding[target_idx],
embedding[neg_indices]])
scores = np.dot(sample_vectors, context_vec)
# 使用 sigmoid 替代 softmax 避免归一化
return 1 / (1 + np.exp(-scores))
3. 梯度更新推导
损失函数对输入向量的梯度为:
$$
\frac{\partial J}{\partial \mathbf{v}_c} = (\sigma(\mathbf{v}_c^T \mathbf{v}_w) – y) \cdot \mathbf{v}_w
$$
对应代码实现:
def update_embedding(target, context, embedding, lr=0.01):
"""
:param target: 目标词索引
:param context: 上下文词索引列表
:param embedding: 词嵌入矩阵
"""
context_vec = np.mean(embedding[context], axis=0) # (d,)
# 正样本梯度
pos_score = sigmoid(np.dot(embedding[target], context_vec))
pos_grad = (pos_score - 1) * context_vec # 公式 (1) 实现
# 负样本梯度(矩阵化计算)neg_scores = sigmoid(np.dot(embedding[neg_indices], context_vec))
neg_grad = np.outer(neg_scores, context_vec) # (k,d)
# 合并更新
embedding[target] -= lr * pos_grad
embedding[neg_indices] -= lr * neg_grad
性能优化实战
耗时对比(V=10000, d=300)
| 方法 | 100 次迭代耗时 |
|---|---|
| for 循环 | 28.7s |
| 向量化实现 | 1.2s |
GPU 加速关键改动
# 原始代码
embedding = np.random.uniform(...)
# CuPy 迁移仅需替换导入
import cupy as np # 其余代码保持不变
避坑指南
-
维度与学习率:经验公式 $lr = 0.025 \times \min(1, d^{-0.5})$,当 d =300 时建议初始 lr=0.0014
-
稀疏存储选择:
- COO 格式适合频繁更新的中间结果
-
CSR 格式适合静态的词共现矩阵
-
梯度裁剪:当梯度 L2 范数超过 5.0 时进行缩放,可稳定训练过程
延伸思考
尝试修改矩阵运算公式实现 GloVe 模型训练,核心在于将 CBOW 的点积运算替换为:
$$
J = \sum_{i,j} f(X_{ij}) (\mathbf{w}i^T \tilde{\mathbf{w}}_j + b_i + \tilde{b}_j – \log X)^2
$$
其中 $X_{ij}$ 是共现频率,$f(x)$ 为加权函数。如何用矩阵运算高效实现?
