使用 one-hot 向量表示单词,虽然它们构造起来很容易,但通常并不是一个好选择。一个主要的原因是,one-hot 词向量无法准确表达不同词之间的相似度,如我们常常使用的余弦相似度。
Word2Vec 词嵌入工具的提出正是为了解决上面这个问题,它将每个词表示成一个定长的向量,并通过在语料库上的预训练使得这些向量能较好地表达不同词之间的相似和类比关系,以引入一定的语义信息。基于两种概率模型的假设,我们可以定义两种 Word2Vec 模型:
- Skip-Gram 跳字模型:假设背景词由中心词生成,即建模
,其中
为中心词,
为任一背景词;
- CBOW (continuous bag-of-words) 连续词袋模型:假设中心词由背景词生成,即建模
,其中
为背景词的集合。
在这里我们主要介绍 Skip-Gram 模型的实现,CBOW 实现与其类似。大致从以下四个部分展开:
- PTB 数据集
- Skip-Gram 跳字模型
- 负采样近似
- 训练模型
PTB 数据集
简单来说,Word2Vec 能从语料中学到如何将离散的词映射为连续空间中的向量,并保留其语义上的相似关系。那么为了训练 Word2Vec 模型,我们就需要一个自然语言语料库,模型将从中学习各个单词间的关系,这里我们使用经典的 PTB 语料库进行训练。PTB (Penn Tree Bank) 是一个常用的小型语料库,它采样自《华尔街日报》的文章,包括训练集、验证集和测试集。我们将在PTB训练集上训练词嵌入模型。
载入数据集
数据集训练文件 ptb.train.txt
示例:
aer banknote berlitz calloway centrust cluett fromstein gitano guterman ...
pierre N years old will join the board as a nonexecutive director nov. N
mr. is chairman of n.v. the dutch publishing group
...
二次采样
文本数据中一般会出现一些高频词,如英文中的“the”“a”和“in”。通常来说,在一个背景窗口中,一个词(如“chip”)和较低频词(如“microprocessor”)同时出现比和较高频词(如“the”)同时出现对训练词嵌入模型更有益。因此,训练词嵌入模型时可以对词进行二次采样。 具体来说,数据集中每个被索引词将有一定概率被丢弃,该丢弃概率为
其中 是数据集中词
的个数与总词数之比,常数 t 是一个超参数(实验中设为
)。可见,只有当
时,我们才有可能在二次采样中丢弃词
,并且越高频的词被丢弃的概率越大。
Skip-Gram 跳字模型
在跳字模型中,每个词被表示成两个 dd 维向量,用来计算条件概率。假设这个词在词典中索引为 ii ,当它为中心词时向量表示为 vi∈Rdvi∈Rd,而为背景词时向量表示为 ui∈Rdui∈Rd 。设中心词 wcwc 在词典中索引为 cc,背景词 wowo 在词典中索引为 oo,我们假设给定中心词生成背景词的条件概率满足下式:
负采样近似
由于 softmax 运算考虑了背景词可能是词典中的任一词,对于含几十万或上百万词的较大词典,就可能导致计算的开销过大。以 skip-gram 模型为例,介绍负采样 (negative sampling) 的实现来尝试解决这个问题。
负采样方法用以下公式来近似条件概率
其中 ,
为 sigmoid 函数。对于一对中心词和背景词,我们从词典中随机采样 K个噪声词(实验中设 K=5)。根据 Word2Vec 论文的建议,噪声词采样概率
设为
词频与总词频之比的 0.75 次方。
训练模型
损失函数
应用负采样方法后,我们可利用最大似然估计的对数等价形式将损失函数定义为如下
根据这个损失函数的定义,我们可以直接使用二元交叉熵损失函数进行计算。
在小规模数据集上训练了一个 Word2Vec 词嵌入模型,并通过词向量的余弦相似度搜索近义词。虽然 Word2Vec 已经能够成功地将离散的单词转换为连续的词向量,并能一定程度上地保存词与词之间的近似关系,但 Word2Vec 模型仍不是完美的,它还可以被进一步地改进:
- 子词嵌入(subword embedding):FastText 以固定大小的 n-gram 形式将单词更细致地表示为了子词的集合,而 BPE (byte pair encoding) 算法则能根据语料库的统计信息,自动且动态地生成高频子词的集合;
- GloVe 全局向量的词嵌入: 通过等价转换 Word2Vec 模型的条件概率公式,我们可以得到一个全局的损失函数表达,并在此基础上进一步优化模型。
实际中,我们常常在大规模的语料上训练这些词嵌入模型,并将预训练得到的词向量应用到下游的自然语言处理任务中。本节就将以 GloVe 模型为例,演示如何用预训练好的词向量来求近义词和类比词。
GloVe 全局向量的词嵌入
GloVe 模型
在Word2Vec 的损失函数(以 Skip-Gram 模型为例,不考虑负采样近似):
是 为中心词,
为背景词时 Skip-Gram 模型所假设的条件概率计算公式,我们将其简写为
。
注意到此时我们的损失函数中包含两个求和符号,它们分别枚举了语料库中的每个中心词和其对应的每个背景词。实际上我们还可以采用另一种计数方式,那就是直接枚举每个词分别作为中心词和背景词的情况:
其中 表示整个数据集中
作为
的背景词的次数总和。
该式进一步地改写为交叉熵 (cross-entropy) 的形式如下:
其中 是
的背景词窗大小总和,
是
在
的背景词窗中所占的比例。
从这里可以看出,我们的词嵌入方法实际上就是想让模型学出 有多大概率是
的背景词,而真实的标签则是语料库上的统计数据。同时,语料库中的每个词根据
的不同,在损失函数中所占的比重也不同。
注意到目前为止,我们只是改写了 Skip-Gram 模型损失函数的表面形式,还没有对模型做任何实质上的改动。而在 Word2Vec 之后提出的 GloVe 模型,则是在之前的基础上做出了以下几点改动:
- 使用非概率分布的变量
和
,并对它们取对数;
- 为每个词 wiwi 增加两个标量模型参数:中心词偏差项
和背景词偏差项
,松弛了概率定义中的规范性;
- 将每个损失项的权重
替换成函数
,权重函数
是值域在 [0,1] 上的单调递增函数,松弛了中心词重要性与
线性相关的隐含假设;
- 用平方损失函数替代了交叉熵损失函数。
综上,我们获得了 GloVe 模型的损失函数表达式:
由于这些非零 xijxij 是预先基于整个数据集计算得到的,包含了数据集的全局统计信息,因此 GloVe 模型的命名取“全局向量”(Global Vectors)之意。
载入预训练的 GloVe 向量
GloVe 官方 提供了多种规格的预训练词向量,语料库分别采用了维基百科、CommonCrawl和推特等,语料库中词语总数也涵盖了从60亿到8,400亿的不同规模,同时还提供了多种词向量维度供下游模型使用。
torchtext.vocab
中已经支持了 GloVe, FastText, CharNGram 等常用的预训练词向量,我们可以通过声明 torchtext.vocab.GloVe
类的实例来加载预训练好的 GloVe 词向量。
求近义词和类比词
求近义词
由于词向量空间中的余弦相似性可以衡量词语含义的相似性(为什么?),我们可以通过寻找空间中的 k 近邻,来查询单词的近义词。
求类比词
除了求近义词以外,我们还可以使用预训练词向量求词与词之间的类比关系,例如“man”之于“woman”相当于“son”之于“daughter”。求类比词问题可以定义为:对于类比关系中的4个词“a 之于 b 相当于 cc之于 d”,给定前3个词 a,b,c求 d。求类比词的思路是,搜索与 vec(c)+vec(b)−vec(a)的结果向量最相似的词向量,其中 vec(w)为 w的词向量。