word embedding算法中最为经典的算法就是今天要介绍的word2vec,最早来源于Google的Mikolov的:
1、Distributed Representations of Sentences and Documents
2、Efficient estimation of word representations in vector space
也是开创了无监督词嵌入的新局面,让大量之后的NLP工作基于word2vec(或类似的改进工作)的词嵌入再做其他任务。推荐看[1]的论文,相对比较容易看懂。也推荐看下[4],是本文中的公式和基本原理讲解来源,因为我只是用于笔记,不作为商用,所以很多话如果我觉得可以理解的话我就不改了,但是中间会穿插一些自己的理解。希望通过本篇可以把word2vec再巩固一遍。
先讲一讲one-hot词向量和distributed representation分布式词向量
one hot的表示形式:word vector = [0,1,0,…,0],其中向量维数为词典的个数|V|,当前词对应的位置为1,其他位置为0。
distributed的表示形式:word vector = [0.171,-0.589,-0.346,…,0.863],其中向量维数需要自己指定(比如设定256维等),每个维度的数值需要通过训练学习获得。
虽然one-hot词向量构造起来很容易,但通常并不是一个好选择。一个主要的原因是,one-hot词向量无法准确表达不同词之间的相似度,如我们常常使用的余弦相似度。对于向量 x , y ∈ R d x,y \in R^d x,y∈Rd ,它们的余弦相似度是它们之间夹角的余弦值
x ⊤ y ∥ x ∥ ∥ y ∥ ∈ [ − 1 , 1 ] . \frac{\boldsymbol{x}^\top \boldsymbol{y}}{\|\boldsymbol{x}\| \|\boldsymbol{y}\|} \in [-1, 1]. ∥x∥∥y∥x⊤y∈[−1,1].
word2vec工具的提出正是为了解决上面这个问题 。它将每个词表示成一个定长的向量,并使得这些向量能较好地表达不同词之间的相似和类比关系。word2vec工具包含了两个模型,即跳字模型(skip-gram) 和连续词袋模型(continuous bag of words,CBOW)。接下来让我们分别介绍这两个模型以及它们的训练方法。
注解:看到这里,就引出了word2vec的核心方法,其实就是认为每个词相互独立,用连乘来估计最大似然函数,求解目标函数就是最大化似然函数。上面公式涉及到一个中心词向量v,以及北京词向量u,因此呢很有趣的是,可以用一个input-hidden-output的三层神经网络来建模上面的skip-model。
Skip-gram可以表示为由输入层(Input)、映射层(Projection)和输出层(Output)组成的神经网络(示意图如下,来自[2]):
图1
注解:上面的公式每一步都推荐推到一下,都很基础。上面只求了对中心词向量v的梯度,同理对背景词向量的梯度,也很容易计算出来。然后就采用传统的梯度下降(一般采用sgd)来训练词向量(其实我们最后关心的是中心词向量来作为词的表征。)
这里有一个key point说下,也许大家也想到了:词向量到底在哪里呢?回看下前面图1,有两层神经网络,第一层是input层到hidden层,这个中间的weight矩阵就是词向量!!!
假设input的词汇表N长度是10000,hidden层的长度K=300,左乘以一个10000长度one-hot编码,实际上就是在做一个查表!因此,这个weight矩阵的行就是10000个词的词向量。很有创意是不是?再来看神经网络的第二层,hidden到out层,中间hidden层有没有激活函数呢?从前面建模看到,u和v是直接相乘的,没有激活层,所以hidden是一个线性层。out层就是建模了v和u相乘,结果过一个softmax,那么loss函数是最大似然怎么办呢?其实就是接多个只有一个true label(背景词)的cross entropy loss,把这些loss求和。因为交叉熵就是最大似然估计,如果这点不清楚的可以去网上搜一下,很容易知道。
所以呢,我们即可用前面常规的最大似然建模来理解如何对u和v的进行优化求解;也完全可以把skip-model套到上面图1这样的一个简单神经网络中,然后就让工具自己来完成weight的训练,就得到了我们想要的中心词向量。
下图是对这个过程的简单可视化过程示意图。左边矩阵为词汇表中第四个单词的one-hot表示,右边矩阵表示包含3个神经元的隐藏层的权重矩阵,做矩阵乘法的结果就是从权重矩阵中选取了第四行的权重。因此,这个隐藏层的权重矩阵就是我们最终想要获得的词向量字典[2]
获取训练样本:按照上下文窗口的大小从训练文本中提取出词汇对,下面以句子The quick brown fox jumps over the lazy dog为例提取用于训练的词汇对,然后将词汇对的两个单词使用one-hot编码就得到了训练用的train_data和target_data。 下面的图中给出了一些我们的训练样本的例子。我们选定句子“The quick brown fox jumps over lazy dog”,设定我们的窗口大小为2(window_size=2),也就是说我们仅选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。Training Samples(输入, 输出)示意图如下:
如果使用随机梯度下降,那么在每一次迭代里我们随机采样一个较短的子序列来计算有关该子序列的损失,然后计算梯度来更新模型参数。
CBOW就倒过来,用多个背景词来预测一个中心词,CBOW对小型数据库比较合适,而Skip-Gram在大型语料中表现更好[2]。但是方法和上面是很像的,因此这里我就放下图。推导的方法是一样的。
我们发现,不论是跳字模型(skip-gram) 还是连续词袋模型(CBOW),我们实际上都是取得input-hidden这个词向量(weight矩阵),而不是后面带着loss那一部分,这样我们也很容易可以对loss(训练方法)进行修改,这个也是下一篇要说的内容:
问:每次梯度的计算复杂度是多少?当词典很大时,会有什么问题?
word2vec作者很创造性地提出了2种近似训练方法(分层softmax(hierarchical softmax)和负采样(negative sampling)),得益于此,可以训练大规模语料库。
参考资料:
[1] word2vec Parameter Learning Explained,《word2vec Parameter Learning Explained》论文学习笔记
[2] Word2Vector之详解Skip-gram(1toN)
[3] 探索:word2vec
[4] http://zh.gluon.ai/chapter_natural-language-processing/word2vec.html