共计 2681 个字符,预计需要花费 7 分钟才能阅读完成。
词嵌入(Word Embedding)是自然语言处理(NLP)领域的一项基础技术,它将词语映射到低维连续向量空间,使得语义相似的词在向量空间中距离相近。CBOW(Continuous Bag-of-Words)模型是词嵌入技术中的一种经典算法,由 Mikolov 等人在 2013 年提出。与 Skip-gram 模型不同,CBOW 模型通过上下文词预测中心词,适合处理大规模语料库且训练速度较快。

1. CBOW 模型算法原理
CBOW 模型的核心思想是利用上下文词来预测当前词。假设我们有一个句子 ”I love natural language processing”,窗口大小为 2,那么对于中心词 ”natural”,其上下文词为 [“I”, “love”, “language”, “processing”]。
1.1 模型结构
CBOW 模型包含三层结构:
-
输入层 :将上下文词的 one-hot 向量作为输入。假设词汇表大小为 V,则每个词的 one-hot 向量维度为 V。
-
投影层 :将上下文词的 one-hot 向量与词嵌入矩阵 W 相乘,得到词向量表示。词嵌入矩阵 W 的维度为 V×N,其中 N 是词向量的维度。
-
输出层 :将投影层的输出(上下文词向量的平均值)与另一个矩阵 W ’ 相乘,得到输出向量,再通过 softmax 函数计算每个词作为中心词的概率。矩阵 W ’ 的维度为 N×V。
1.2 数学表达
- 输入层:上下文词的 one-hot 向量 x₁, x₂, …, x_C,其中 C 是上下文词的数量。
- 投影层:词向量 v_i = W^T x_i,上下文词向量的平均值为 v_avg = (v₁ + v₂ + … + v_C) / C。
- 输出层:输出向量 u = W’^T v_avg,概率分布 y = softmax(u)。
2. 手算示例
假设词汇表包含三个词:”I”, “love”, “NLP”,词向量维度 N =2。词嵌入矩阵 W 和 W ’ 如下:
W = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
W' = [[0.7, 0.8, 0.9], [0.1, 0.2, 0.3]]
以中心词 ”love” 为例,其上下文词为 ”I” 和 ”NLP”。计算过程如下:
- 将 ”I” 和 ”NLP” 的 one-hot 向量与 W 相乘,得到词向量:
- v_I = W^T x_I = [0.1, 0.2]
- v_NLP = W^T x_NLP = [0.5, 0.6]
- 计算平均向量:v_avg = ([0.1, 0.2] + [0.5, 0.6]) / 2 = [0.3, 0.4]
- 计算输出向量:u = W’^T v_avg = [0.70.3 + 0.10.4, 0.80.3 + 0.20.4, 0.90.3 + 0.30.4] = [0.25, 0.32, 0.39]
- 计算概率分布:y = softmax(u) ≈ [0.31, 0.34, 0.35]
3. Python 实现
以下是使用 NumPy 实现 CBOW 模型的代码:
import numpy as np
# 定义词汇表和词向量维度
vocab = ["I", "love", "NLP"]
vocab_size = len(vocab)
embedding_dim = 2
# 初始化词嵌入矩阵 W 和 W'
W = np.random.rand(vocab_size, embedding_dim)
W_prime = np.random.rand(embedding_dim, vocab_size)
# 定义 one-hot 编码函数
def one_hot(word, vocab):
vec = np.zeros(len(vocab))
vec[vocab.index(word)] = 1
return vec
# 定义 softmax 函数
def softmax(x):
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum()
# 定义 CBOW 模型
def cbow(context_words, center_word, vocab, W, W_prime, learning_rate=0.01):
# 将上下文词转换为 one-hot 向量
context_vecs = [one_hot(word, vocab) for word in context_words]
# 计算投影层输出(平均向量)v_avg = np.mean([np.dot(W.T, vec) for vec in context_vecs], axis=0)
# 计算输出层
u = np.dot(W_prime.T, v_avg)
y = softmax(u)
# 计算损失(交叉熵)target = one_hot(center_word, vocab)
loss = -np.sum(target * np.log(y))
# 反向传播更新参数
grad = y - target
dW_prime = np.outer(v_avg, grad)
dW = np.zeros_like(W)
for vec in context_vecs:
dW += np.outer(vec, np.dot(W_prime, grad)) / len(context_words)
W -= learning_rate * dW
W_prime -= learning_rate * dW_prime
return loss, W, W_prime
# 示例训练
context_words = ["I", "NLP"]
center_word = "love"
loss, W, W_prime = cbow(context_words, center_word, vocab, W, W_prime)
print("Loss:", loss)
print("Updated W:", W)
print("Updated W':", W_prime)
4. 性能优化建议
- 大规模语料处理 :
- 使用负采样(Negative Sampling)替代 softmax,减少计算量。
- 采用分层 softmax(Hierarchical Softmax)加速训练。
-
使用多线程或 GPU 加速矩阵运算。
-
内存优化 :
- 对词频低的词进行截断(Subsampling)。
- 使用稀疏矩阵存储 one-hot 向量。
- 分批训练(Mini-batch)减少内存占用。
5. 避坑指南
- 常见错误 :
- 词向量维度设置不合理:维度太小会导致信息丢失,太大会增加计算量。
-
学习率过高或过低:建议使用自适应学习率算法(如 Adam)。
-
超参数调优 :
- 窗口大小:通常设置为 5 -10。
- 词向量维度:常用值为 100-300。
- 学习率:初始值设为 0.01,根据训练效果调整。
6. 思考题
如何将 CBOW 模型扩展到更大规模的语料处理?可以考虑以下方向:
- 分布式训练:将语料库分割到多台机器上并行训练。
- 增量训练:逐步增加语料库规模,避免重新训练。
- 模型压缩:对词嵌入矩阵进行量化或剪枝,减少存储和计算开销。
通过本文的详细解析和代码实现,读者可以深入理解 CBOW 模型的原理和实现细节,为进一步探索 NLP 领域打下坚实基础。
