全局向量的词嵌入(GloVe)

pij=P(wjwi)p_{ij} = P(w_j \mid w_i) 即词 wjw_j 在出现在词 wiw_i 上下文中的概率(用大型语料库中共现的频率来估计)。一种想法是选取一些背景词 wwP(wwc)P(w \mid w_c) 构成的向量来刻画 wcw_c,用一个在教材上的例子:

w=w = solid gas water fashion
p1=P(wice)p_1 = P(w\mid ice) 0.00019 0.000066 0.003 0.000017
p2=P(wsteam)p_2 = P(w\mid steam) 0.000022 0.00078 0.0022 0.000018
p1p2\frac{p_1}{p_2} 8.9 0.085 1.36 0.96
  • ice,steamice, steam 同时无关以及同时有关的词作为上下文词,两个条件概率的比值接近于 1。
  • 如果上下文词仅与其中一个相关,但与另一个不太相关,那么两个条件概率要么很大,要么很小。

条件概率的比值能很好地刻画词与词之间的关系。因此我们希望找一个关于词向量的函数来拟合这样的概率比值。设 wiw_i 为中心词,wjw_jwkw_k 为上下文词,vi\vec{v_i} 表示 wiw_i 的中心词向量,uj\vec{u_j}uk\vec{u_k} 分别表示 wjw_jwkw_k 的上下文词向量。那么我们希望寻找函数 ff,使得它能够估计 pijpik\frac{p_{ij}}{p_{ik}}

f(uj,uk,vi)pijpikf(\vec{u_j}, \vec{u_k}, \vec{v_i}) \approx \frac{p_{ij}}{p_{ik}}

我们希望 ff 能满足一些性质:

  • ff 的值为标量
  • f(uk,uj,vi)f(uj,uk,vi)=1f(\vec{u_k}, \vec{u_{j}}, \vec{v_i}) \cdot f(\vec{u_j}, \vec{u_k}, \vec{v_i}) = 1,即交换 j,kj, k 的位置满足某种 “对称性”

一种想法是令 f(uj,uk,vi)=g((ujuk)vi)f(\vec{u_j}, \vec{u_k}, \vec{v_i}) = g((\vec{u_j} - \vec{u_k})\cdot \vec{v_i}) 再由第二条性质选取 g(x)=exg(x) = e^{x},然后得到:

f(uj,uk,vi)=exp(ujvi)exp(ukvi)pijpikf(\vec{u_j}, \vec{u_k}, \vec{v_i}) = \frac{\exp(\vec{u_j}\cdot \vec{v_i})}{\exp(\vec{u_k}\cdot \vec{v_i})} \approx \frac{p_{ij}}{p_{ik}}

然后有 exp(ujvi)αpij\exp(\vec{u_j} \cdot \vec{v_i}) \approx \alpha p_{ij},其中 α\alpha 是某个与 i,j,ki, j, k 有关的常数。对两边取对数,然后将 pijp_{ij} 替换为频率的比值,得到 ujvilogα+logxijlogxi\vec{u_j} \cdot \vec{v_i} \approx \log \alpha + \log {x_{ij}} - \log {x_i}。我们添加中心词偏置 bib_i 以及上下文偏置 cjc_j 来拟合 logαlogxi\log \alpha - \log x_i。于是得到最终的式子:

ujvi+bi+cjlogxij\vec{u_{j}}\cdot \vec{v_i} + b_i + c_j \approx \log x_{ij}

对于损失函数,我们使用加权平方损失函数:

L=iVjVh(xij)(ujvi+bi+cjlogxij)2L = \sum_{i \in \mathcal{V}} \sum_{j \in \mathcal{V}}h(x_{ij})(\vec{u_j}\cdot \vec{v_i} +b_i + c_j - \log x_{ij})^2

其中 h:N[0,1]h: \N \mapsto [0, 1] 为权重函数,用于防止高频词占据较大的权重,一种例子是:

h(x)={0(x=0)(x/100)0.75(x<100)1(x100)h(x) = \begin{cases}0 & (x= 0) \\ (x/100)^{0.75} & (x < 100) \\ 1 & (x \geqslant 100)\end{cases}

使用预训练的 GloVe

torchtext 包中可以找到预训练的嵌入模型:

1
2
3
4
import torch
import torchtext.vocab as vocab

print(vocab.pretrained_aliases.keys())

输出:

1
dict_keys(['charngram.100d', 'fasttext.en.300d', 'fasttext.simple.300d', 'glove.42B.300d', 'glove.840B.300d', 'glove.twitter.27B.25d', 'glove.twitter.27B.50d', 'glove.twitter.27B.100d', 'glove.twitter.27B.200d', 'glove.6B.50d', 'glove.6B.100d', 'glove.6B.200d', 'glove.6B.300d'])

GloVe 的命名大致规范为 模型.(数据集.)数据集词数.词向量维度

通过

1
glove = vocab.GloVe(name = '6B', dim = 50, cache = './.vector_cache')

可以获取 glove.6B.50 的嵌入模型。如果不指定 cache 的值的话,默认缓存在 ./.vector_cache。返回的实例主要有三个属性:

  • stoi:词到索引的字典
  • itos:索引到词的列表
  • vectors:词向量

不知道为什么我电脑上一直 HTTP Error 404。可以在这个地方找到国内的服务器下载的地址,或者也可以直接从这个地方下载。然后把压缩包改名为 glove.6B.zip,里面放 glove.6B.50d.txt 就可以用了。

看了一下那个文件,其实它每行前面的先是词(也可能是标点),后面是对应的向量。感觉其实自己写一个嵌入的字典也不是很麻烦。

子词嵌入(fastText )

在英语中,例如 helpshelped 都是 help 的变形,语言的变形和含义有着类似的关系,但是 word2vec 和 GloVe 都没有考虑词内部的结构进行探讨。为了使用形态信息,fastText 模型提出一种子词嵌入的方法,其中每个子词是由 nn 个字符组成的子串。

例如,单词 where 以及 n=3n = 3,首先在 where 首尾添加特殊字符得到 <where>,然后再拆分得到:<whwhehererere>。首尾添加的特殊字符使得可以区分整词 <her> 和子词 her

在 fastText 中,对于任意一个词 ww,用 Gw\mathcal G_w 表示 ww 所有长度在 [3,6][3, 6] 之间的字词构成的集合,为所有子词分配一个向量 z\vec{z},词 ww 的中心词向量被定义为其子词向量的和:

vw=gGwzg\vec{v_{w}} = \sum_{g\in \mathcal G_{w}}\vec{z_g}

fastText 中剩余部分与跳元模型相同。但是与跳元模型相比,fastText 的词量更大,参数也更多,也有着更高的计算复杂度。但是一些罕见词能在 fastText 中获得更好的向量表示。