5.5 Word2vec
前几节将词表示为维度对应词表单词的稀疏长向量。现在介绍更强大的表示:短而稠密的嵌入。嵌入通常有 d=50 到 1000 个维度,而不是词表大小 V 那么多;这些维度没有清晰的人工解释。向量也是稠密的:元素不再是大多为零的计数或计数函数,而是可以为负的实数。
稠密向量在各种 NLP 任务中通常都优于稀疏向量。原因尚未完全明了,但有一些直觉:用 300 维稠密向量表示词,比用 50,000 维向量所需学习的分类器权重少得多,较小的参数空间可能有助于泛化并避免过拟合;稠密向量也更能捕捉同义关系。例如,稀疏表示中 car 和 automobile 是彼此独立、毫无关联的两个维度,因而可能无法捕捉“以 car 为邻词”和“以 automobile 为邻词”的两个词之间的相似性。
本节介绍一种计算嵌入的方法:带负采样的 skip-gram(skip-gram with negative sampling, SGNS)。skip-gram 是 word2vec 软件包中的两种算法之一,因此也常被宽泛地称为 word2vec(Mikolov et al., 2013a, 2013b)。word2vec 方法速度快、训练高效,代码和预训练嵌入也很容易获得。它学到的是静态嵌入:词表中每个词只有一个固定向量。第 9 章将介绍 BERT 表示等动态上下文嵌入,其中同一个词在不同语境中有不同向量。
word2vec 的直觉是:不再统计每个语境词 c 在 apricot 附近出现多少次,而是训练一个二元分类器,回答“词 c 是否可能出现在 apricot 附近?”我们并不真正关心这个预测任务本身,而是把分类器学到的权重当作词嵌入。
关键思想是直接把连续文本用作隐式监督训练数据:出现在目标词 apricot 附近的词 c,就是“c 是否可能出现在 apricot 附近?”这一问题的金标准正确答案。这个通常称为自监督(self-supervision)的方法不需要任何人工标注信号。Bengio et al.(2003)和 Collobert et al.(2011)最早在神经语言建模中提出这一思想:预测前文之后的下一个词,并直接用连续文本中的实际下一个词作监督信号,同时学习每个词的嵌入表示。
下一章将介绍神经网络,而 word2vec 比神经网络语言模型简单得多:第一,它把任务从词预测简化为二元分类;第二,它把架构从带隐藏层、需要复杂训练算法的多层神经网络简化为逻辑回归分类器。skip-gram 的思路是:
把目标词与相邻语境词组成正例;
从词表随机抽取其他词作为负例;
用逻辑回归训练分类器区分两种情形;
将学到的权重用作嵌入。
5.5.1 分类器¶
先考虑分类任务,再讨论训练。假设句子中目标词为 apricot,语境窗口左右各包含两个词:
... lemon, a [tablespoon of apricot jam, a] pinch ...
c1 c2 w c3 c4
目标是训练分类器:给定目标词 w 与候选语境词 c 组成的二元组 (w,c),例如 (apricot, jam) 或 (apricot, aardvark),返回 c 是真实语境词的概率——对 jam 为真,对 aardvark 为假:
P(+∣w,c)(5.11) c 不是 w 的真实语境词的概率为:
P(−∣w,c)=1−P(+∣w,c)(5.12) 分类器如何计算概率?skip-gram 假设概率以嵌入相似度为基础:若某词的嵌入与目标词嵌入相似,它就更可能出现在目标词附近。两个向量点积较高时可视为相似,因此:
Similarity(w,c)≈c⋅w(5.13) 点积只是可从负无穷到正无穷的数,并非概率。用逻辑回归核心的 sigmoid 函数将它转换到 0 与 1 之间:
σ(x)=1+exp(−x)1(5.14) 于是,c 是 w 真实语境词的概率为:
P(+∣w,c)=σ(c⋅w)=1+exp(−c⋅w)1(5.15) 为使两个事件(c 是或不是语境词)的总概率为 1,另一个概率为:
P(−∣w,c)==1−P(+∣w,c)σ(−c⋅w)=1+exp(c⋅w)1(5.16) 式 5.15 只处理一个词,但窗口中有多个语境词。skip-gram 作出简化假设,认为所有语境词彼此独立,因此可直接将概率相乘:
P(+∣w,c1:L)=i=1∏Lσ(ci⋅w)(5.17) logP(+∣w,c1:L)=i=1∑Llogσ(ci⋅w)(5.18) 总之,给定测试目标词 w 及其由 L 个词构成的语境窗口 c1:L,skip-gram 训练的概率分类器依据窗口与目标词的相似程度赋予概率;具体做法是将目标词嵌入与每个语境词嵌入的点积输入 sigmoid。为计算这个概率,只需要词表中每个目标词和语境词的嵌入。

图 5.6 skip-gram 模型学到的嵌入。算法为每个词存储两个嵌入:目标嵌入(也称输入嵌入)和语境嵌入(也称输出嵌入)。因此,算法学习的参数 θ 是 2V 个 d 维向量组成的矩阵,由目标嵌入矩阵 W 和语境加噪声嵌入矩阵 C 拼接而成。
skip-gram 实际为每个词保存两个嵌入:一个用于该词充当目标时,另一个用于它充当语境时。因此需要学习两个矩阵 W 和 C,二者都为词表 V 中的每个词包含一个嵌入。
5.5.2 学习 skip-gram 嵌入¶
skip-gram 的学习算法输入文本语料和选定的词表大小 N。它先为 N 个词各随机分配一个嵌入向量,然后反复调整:使词 w 的嵌入更接近文本中邻近词的嵌入,更远离不在附近的词。仍考虑:
... lemon, a [tablespoon of apricot jam, a] pinch ...
目标词 w 是 apricot,L=±2 的窗口中有 4 个语境词,因而得到 4 个正训练实例。训练二元分类器还需要负例;SGNS 通常使用比正例更多的负例,比例由参数 k 决定。对每个正例 (w,cpos),创建 k 个负样本,每个由目标 w 和一个噪声词(noise word)cneg 组成。噪声词从词表随机抽取,但不能是目标词。下表以 k=2 为例:
正例 + | | 负例 − | | | |
w | cpos | w | cneg | w | cneg |
apricot | tablespoon | apricot | aardvark | apricot | seven |
apricot | of | apricot | my | apricot | forever |
apricot | jam | apricot | where | apricot | dear |
apricot | a | apricot | coaxial | apricot | if |
噪声词根据加权一元概率 Pα(w) 选择,其中 α 是权重。若直接按未加权概率 P(w) 抽样,the 被选中的概率就是其一元概率,aardvark 亦然。实践中常设 α=.75:
Pα(w)=∑w′count(w′)αcount(w)α(5.19) α=.75 会略微提高低频噪声词的概率,因而效果更好。设 P(a)=.99、P(b)=.01:
Pα(a)=.99.75+.01.75.99.75=.97Pα(b)=.99.75+.01.75.01.75=.03(5.20) 可见,罕见事件 b 的概率从 .01 增至 .03。
给定正、负训练实例和初始嵌入,学习算法要调整嵌入,使正例中目标词—语境词对 (w,cpos) 的相似度最大,使负例中 (w,cneg) 的相似度最小。对一个正例及其 k 个噪声词,可把两个目标写成需要最小化的损失:第一项让真实语境词有较高的“是邻词”概率,第二项让每个噪声词有较高的“不是邻词”概率;在独立性假设下将它们相乘:
L(w,cpos,cneg∗)=−log[P(+∣w,cpos)∏i=1kP(−∣w,cnegi)]=−[logP(+∣w,cpos)+∑i=1klogP(−∣w,cnegi)]=−[logσ(cpos⋅w)+∑i=1klogσ(−cnegi⋅w)](5.21) 也就是最大化目标词与真实语境词的点积,最小化它与 k 个负采样非邻词的点积。使用随机梯度下降最小化该损失。

图 5.7 一步梯度下降的直觉。skip-gram 调整嵌入,使目标嵌入(apricot)更接近邻近词的语境嵌入(jam,即点积更高),并远离附近未出现的噪声词语境嵌入(Tolstoy、matrix,即点积更低)。
对式 5.21 中不同嵌入求导,得到(证明留作章末练习):
∂cpos∂L=[σ(cpos⋅w)−1]w(5.22) ∂cnegi∂L=σ(cnegi⋅w)w(5.23) ∂w∂L=[σ(cpos⋅w)−1]cpos+i=1∑kσ(cnegi⋅w)cnegi(5.24) 因此,从时刻 t 到 t+1 的 SGD 更新为:
cpost+1=cpost−η[σ(cpost⋅wt)−1]wt(5.25) cnegit+1=cnegit−ησ(cnegit⋅wt)wt(5.26) wt+1=wt−η([σ(cpost⋅wt)−1]cpost+i=1∑kσ(cnegit⋅wt)cnegit)(5.27) 与逻辑回归一样,算法从随机初始化的 W 和 C 出发,遍历训练语料,用梯度下降按式 5.25—5.27 移动二者,使式 5.21 的损失最小。
skip-gram 为每个词 i 学习目标嵌入 wi 和语境嵌入 ci,分别存于矩阵 W 和 C。常见做法是将二者相加,用 wi+ci 表示词 i;也可以丢弃 C,只用 wi。与 tf-idf 等计数方法相同,语境窗口大小会影响 skip-gram 嵌入的表现,实验中常在开发集上调节该参数。
5.5.3 其他静态嵌入¶
静态嵌入还有许多种。word2vec 的扩展 fastText(Bojanowski et al., 2017)解决了未知词问题,即测试语料中出现训练语料从未见过的词。相关问题是词稀疏性:在形态丰富的语言中,每个名词或动词的许多形式可能都很少出现。
fastText 使用子词模型解决这些问题:把每个词表示为词本身及其组成字符 n 元语法的词袋,并在词两端加特殊边界符 < 和 >。例如 n=3 时,where 表示为 <where> 加字符三元语法:
<wh, whe, her, ere, re>
算法为每个组成 n 元语法学习 skip-gram 嵌入,并将它们的嵌入之和作为 where 的表示。未知词也可以仅用其组成 n 元语法之和表示。fastText 的开源库及 157 种语言的预训练嵌入见 https://fasttext.cc。
另一种广泛使用的静态嵌入模型是 GloVe(Global Vectors;Pennington et al., 2014)。它着眼于语料的全局统计,基于词—词共现矩阵中的概率比率。
word2vec 等稠密嵌入与基于计数的嵌入之间其实存在优雅的数学关系:word2vec 可以看作隐式优化某个采用特定 PPMI 加权的计数矩阵函数(Levy and Goldberg, 2014c)。