文章目录
- 现象引入:从“国王-男人+女人=女王”说起
- 提出问题:如何让计算机“理解”词语?
- 原理剖析:Word2Vec的两种精巧模型
- 1. CBOW (Continuous Bag-of-Words) 模型
- 2. Skip-gram 模型
- 源码印证:从原始论文到工程优化
- 1. 层次Softmax (Hierarchical Softmax)
- 2. 负采样 (Negative Sampling)
- 实际影响:Word2Vec的遗产与局限
现象引入:从“国王-男人+女人=女王”说起
刚接触NLP那会儿,我被一个神奇的例子震撼到了:用训练好的词向量做计算,vector(‘国王’) - vector(‘男人’) + vector(‘女人’),结果最接近的词竟然是‘女王’。这太反直觉了!文字是离散的符号,怎么能像数字一样加减,还产生有意义的语义结果?这背后就是词向量(Word Embedding)的魔力。当时我就在想,这玩意儿到底是怎么把一个个孤立的单词,变成高维空间里一个个有“位置”、有“关系”的点的?今天,我们就来彻底拆解词向量领域的里程碑——Word2Vec,看看它是如何“让文字拥有数学灵魂”的。
提出问题:如何让计算机“理解”词语?
在Word2Vec之前,表示一个词最常见的方法是独热编码(One-Hot Encoding)。比如“苹果”在词典里是第100个词,它的向量就是一个长度为词典大小的向量,只有第100维是1,其余全是0。我早期项目里用过这种方法,很快就发现了三个致命问题:
- 维度灾难:词典动辄几万、几十万维,计算和存储都是噩梦。
- 语义鸿沟:
“苹果”(水果)和“香蕉”的向量点积是0,“苹果”(水果)和“苹果”(公司)的向量点积也是0。计算机完全无法从表示上看出任何语义关联。 - 数据稀疏:向量里几乎全是0,有效信息极少。
我们需要的是一种稠密、低维、且能蕴含语义的表示方法。理想状态下,语义相近的词(如“猫”和“狗”),其向量在空间中的距离应该很近。Word2Vec就是为了解决这个核心问题而诞生的。
原理剖析:Word2Vec的两种精巧模型
Word2Vec不是一个复杂的深度神经网络,它的思想非常巧妙,核心是“用一个词的上下文来表征这个词本身”。这基于一个著名的语言学假设:分布式假设,即“出现在相似上下文中的词,其语义也相似”。
它提供了两种实现这一思想的模型架构:CBOW和Skip-gram。很多人一开始会混淆,我当初也是。记住一个简单的对比:CBOW是用上下文预测中心词,适合大数据集;Skip-gram是用中心词预测上下文,适合小数据集或稀有词。
1. CBOW (Continuous Bag-of-Words) 模型
CBOW模型的思想是:给定一个中心词(如“今天”)的上下文(如[“早上”, “我”, “吃”, “苹果”]),来预测这个中心词本身。
模型结构解析:
- 输入层:上下文词的One-Hot向量。假设窗口大小为4,就有4个
V维向量(V是词典大小)。 - 隐藏层:所有输入向量乘以同一个共享的输入权重矩阵
W_{V×N}(N是设定的词向量维度,如300)。然后对得到的多个N维向量求平均,得到一个N维的隐藏层向量。这个隐藏层向量,就是上下文的语义表示。 - 输出层:将隐藏层向量乘以输出权重矩阵
W’_{N×V},得到一个V维的向量。再通过Softmax函数,将其转换为一个概率分布,表示词典中每一个词作为中心词出现的概率。
训练目标:最大化真实中心词(“今天”)的预测概率。通过反向传播,同时更新输入矩阵W和输出矩阵W’。
关键点:训练完成后,我们通常丢弃输出层,只保留输入权重矩阵W。W的每一行,就对应词典中一个词的N维词向量!这就是我们最终要的东西。
# 一个极简的CBOW思想伪代码,帮助理解importnumpyasnp# 假设词典大小V=5,词向量维度N=3V,N=5,3W_input=np.random.randn(V,N)# 输入权重矩阵,待学习W_output=np.random.randn(N,V)# 输出权重矩阵,待学习# 假设上下文词索引为 [0, 2, 4],中心词索引为 1context_indices=[0,2,4]center_index=1# 1. 上下文词向量查找并平均 (前向传播)context_vectors=W_input[context_indices]# 形状 (3, N)h=np.mean(context_vectors,axis=0)# 隐藏层向量,形状 (N,)# 2. 输出层计算u=np.dot(h,W_output)# 形状 (V,)# 3. Softmax得到概率分布 (此处省略,实际需要)# 4. 计算损失(如交叉熵),并与真实中心词One-Hot比较# 5. 反向传播,更新 W_input 和 W_output关键:训练后,W_input[word_index] 就是词word的向量。
2. Skip-gram 模型
Skip-gram模型是CBOW的“镜像”:给定一个中心词(如“今天”),来预测它周围窗口内的所有上下文词(如[“早上”, “我”, “吃”, “苹果”])。
模型结构解析:
- 输入层:中心词的One-Hot向量。
- 隐藏层:输入向量乘以输入权重矩阵
W_{V×N},直接得到该中心词的N维词向量表示。 - 输出层:将隐藏层向量乘以输出权重矩阵
W’_{N×V},并通过Softmax,为每一个上下文位置都计算一个V维的概率分布。
训练目标:最大化所有上下文词的预测概率之和。同样,训练后我们主要使用输入矩阵W。
为什么Skip-gram对稀有词更友好?因为每个中心词-上下文词对都是一个独立的训练样本。一个稀有词作为中心词时,模型会集中精力学习它与少数几个上下文的关系。而CBOW中,稀有词被淹没在上下文的平均操作中,信息被稀释了。
源码印证:从原始论文到工程优化
理解了原理,我们来看看Google开源的原始C语言实现(word2vec.c)里的一些关键优化,这些优化让训练大规模语料成为可能。这也是我当年读源码时收获最大的部分。
1. 层次Softmax (Hierarchical Softmax)
原始Softmax需要计算词典中所有词的概率,计算复杂度是O(V),V通常上万,这是不可接受的。
层次Softmax的妙处:它把一次巨大的V分类问题,转化为约log₂(V)次二分类问题。它用一棵霍夫曼树(按词频构建)来组织所有词汇。每个词是叶子节点,每个内部节点是一个逻辑回归二分类器。
- 预测时:从根节点走到目标叶子节点,每一步判断向左还是向右,将路径上所有节点的概率相乘得到最终概率。
- 训练时:只更新路径上的节点参数,复杂度从O(V)降到O(log V)。
2. 负采样 (Negative Sampling)
这是Skip-gram模型最常用的优化,思想更直接:我们并不需要计算所有负例(非目标词)的概率,只需要采样几个负例,让模型能区分目标和噪声即可。
损失函数被改造为:最大化目标词(正样本)出现的概率,同时最小化K个随机采样的噪声词(负样本)出现的概率。
# 负采样损失(Skip-gram with Negative Sampling, SGNS)的直观理解# 对于中心词 w 和上下文词 c:loss=-log(σ(v_c · v_w))# 正样本对的得分应高foriinrange(K):# 从噪声分布(如词频的3/4次幂)中随机采样一个词 nn=sample_noise()loss-=log(σ(-v_n · v_w))# 负样本对的得分应低其中σ是sigmoid函数。负采样极大地减少了计算量,并且被证明能产生高质量的词向量,是工程实践中的绝对主流。
实际影响:Word2Vec的遗产与局限
Word2Vec的影响是革命性的。它产出的词向量可以直接作为下游任务(如文本分类、情感分析)的特征输入,开启了NLP的“预训练”时代先河。我参与的很多项目中,直接加载开源的Word2Vec预训练向量(如GoogleNews向量)作为初始化,都能带来显著的性能提升。
但它也有明显的局限,这也是后来者(如GloVe, FastText, BERT)试图改进的地方:
- 静态向量:一个词只有一个固定的向量,无法解决一词多义问题(“苹果”的公司和水果义项)。
- 上下文简单:仅使用局部窗口上下文,对长距离依赖和全局文档信息利用不足。
- 未登录词(OOV)问题:无法为训练时没见过的词生成向量。FastText通过引入子词(subword)单元部分解决了这个问题。
总结一下:Word2Vec通过“上下文预测”这一巧妙的自监督学习任务,将词语映射到了稠密的向量空间,让语义关系得以用数学(向量运算)来度量。它简洁高效的模型设计和工程优化(负采样、层次Softmax),使其成为NLP领域影响最深远的工具之一。尽管如今已被BERT等动态上下文模型超越,但理解Word2Vec是理解现代NLP模型演进脉络的基石。
如有问题欢迎评论区交流,持续更新中…