GloVe

GloVe(Global Vector),是一种结合全局矩阵分解和本地上下文窗口的方法。LSA(latent semantic analysis)虽然能够有效的统计信息,但在词汇类比任务中表现很差。而Skip-gram虽然在词汇类比任务中表现很好,但依赖于窗口的移动,而不能有效统计全局的计数。论文作者认为,全局计数的对数加上双线性回归方法会非常合适。

算法

首先,我们需要对整个语料库建立一个统计全局词频的矩阵,一般称为共现矩阵(Co-ocurrence Matrix)

假设,语料库有三句话:

  1. \(I \hspace{0.15cm} enjoy \hspace{0.15cm} flying\)
  2. \(I \hspace{0.15cm} like \hspace{0.15cm} NLP\)
  3. \(I \hspace{0.15cm} like \hspace{0.15cm} deep \hspace{0.15cm} learning\)

那他们的共现矩阵就是:

\(\large X_{ij}\) 表示单词 \(\large i\) 在单词 \(\large j\) 上下文出现的次数。你会发现,共现矩阵是对称的(symmetric),因为他同时取左右两边的单词,若只选取左边或右边的任一边,则是非对称的(asymmetric)。

构建词向量(Word Vector)和共现矩阵(Co-ocurrence Matrix)之间的近似关系,论文的作者提出以下的公式可以近似地表达两者之间的关系:

\[ u_i^Tv_j + b_i + \widetilde b_j \approx log(X_{ij}) \tag{1} \]

这里的 \(\large u_i, v_j\) 与Skip-Grams模型中的意义不同,它仅表示的是单词 \(\large i,j\) 之间的联系,没有中心与周围的区分。(对于公式(1),论文中做了相关推导,因为推导过程有些复杂,我把这部分放到文章后面。)

他的代价函数是:

\[ J = \sum_{i,j=1}^V f(X_{ij})(u_i^Tv_j + b_i + \widetilde b_j - log(X_{ij})) \]

其中 \(\large log(X_{ij}) \rightarrow log(1 + X_{ij})\),这么做的目的是防止出现 \(\large log0\) 的情况。 \(\large f(X_{ij})\) 是一个加权函数(weighting function),有些单词对经常同时出现,那么会导致 \(\large X_{ij}\) 会很大,应该衰减它们;而有些单词对从不一起出现,这些单词对就不应该对代价函数产生任何贡献,它们没必要去计算损失值,这时使用 \(\large f(0) = 0\) 来避免计算。

我们可以采用分段函数:

\[ f(x) = \begin{cases} (\frac{x}{x_{max}})^\alpha & if \; x < x_{max} \\ 1 & otherwise \end{cases} \]

函数图像如下:

作者实验 \(\large \alpha\) 取值 0.75,\(\large x_{max}\) 取值 100。

\(\large u_i, v_j\) 的初始化可以不同,也可以相同。当训练得足够之后,\(\large u_i, v_j\)\(\large X_{ij}\) 相似,而 \(\large X_{ij}\) 是对称的,所以 \(\large u_i, v_j\) 也是对称或接近对称。因为都是学习得到的参数,在我们看来没有本质区别。当初始化不同时,相当于加入了噪音,你可以选择把 \(\large e = u + v\) 作为最终向量,这样有助于提高鲁棒性。

实验结果

论文中展示了glove与word2vec等方法的对比,但其实大家都旗鼓相当。

下图是glove在不同参数的情况下的表现:

(图a 展示了向量维度对准确度的影响。图b和图c分别使用对称上下文和非对称上下文在不同窗口大小下的表现。以上所有测试都使用6B (billion) 的语料库)

可以发现最好的参数是300维度,窗口大小为5。再往上增加虽然能提高准确度,但也提高了计算代价,没有必要为了提升一点准确度而产生巨大的计算代价。

公式推导

这里对上面公式(1)进行推导。

论文中举了一个简单的例子:

试问单词 \(\large k\) 应该是哪个单词?我们可以通过 \(\large P(k \vert ice)/P(k \vert stream)\) 来判断。当 \(\large P_{ik}/P_{jk}\) 很大的时候,说明 \(\large i\)\(\large k\) 更接近一些。

然后,作者就想到,我们可以用参数去近似这个比值。

\[ F(w_i, w_j, w_k) = \frac{P_{ik}}{P_{jk}} \]

\(\large F\) 这里暂且设为未知函数,因为它包含了三个参数,所以我们需要削减一下参数,因为参数是线性的,所以自然可以构造成相减的形式。

\[ F(w_i - w_j, w_k) = \frac{P_{ik}}{P_{jk}} \]

然后我们用内积来表现两个单词向量的近似程度,

\[ F((w_i - w_j)^Tw_k) = \frac{F(w_i^Tw_k)}{F(w_j^Tw_k)} \]

提取其中一个单词的 \(\large F\)

\[ F(w_i^Tw_k) = P_{ik} = \frac{X_{ik}}{X_i} \]

我们设 \(\large F\) 为指数函数,则,

\[ w_i^Tw_k = log(P_{ik}) = log(X_{ik}) - log({X_i}) \]

然后,我们用偏置项来替代 \(\large log(X_i)\),即

\[ w_i^Tw_k + b_i + b_k = log(X_{ik}) \]

就这样,这个公式就被奇异地构造出来了。

代码实现

我自己用python实现了一下,但跑起来后发现这没什么意义。GloVe与Skip-gram不同,他构造共现矩阵需要花费大量时间和内存,这部分极度需要计算性能,用python跑半天都跑不完。 论文作者是使用纯C语言实现的,提供了不少预训练的模型,以及词向量的评估脚本,详见Github

参考文献

循环神经机器翻译 负采样的Skip-Gram模型

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×