主要介绍Word2Vec中的Skip-Gram模型和CBOW模型。总结来说,skip-gram是用中心词预测周围词,预测的时候是一对word pair,等于对每一个中心词都有K个词作为output,对于一个词的预测有K次,所以能够更有效的从context中学习信息,但是总共预测K*V词。CBOW模型中input是context(周围词),而output是中心词。因此,skip gram的训练时间更长,但是对于一些出现频率不高的词,在CBOW中的学习效果就不日skip-gram。
假如我们有一个句子“The dog barked at the mailman”。
选定句子“The quick brown fox jumps over lazy dog”,设定我们的窗口大小为2(window_size=2),也就是说我们仅选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。
模型细节
我们如何来表示这些单词呢?
首先,我们都知道神经网络只能接受数值输入,我们不可能把一个单词字符串作为输入,因此我们得想个办法来表示这些单词。最常用的办法就是基于训练文档来构建我们自己的词汇表(vocabulary)再对单词进行one-hot编码。
假设从我们的训练文档中抽取出10000个唯一不重复的单词组成词汇表。我们对这10000个单词进行one-hot编码,得到的每个单词都是一个10000维的向量,向量每个维度的值只有0或者1,假如单词ants在词汇表中的出现位置为第3个,那么ants的向量就是一个第三维度取值为1,其他维都为0的10000维的向量(ants=[0,0, 1, 0, …, 0])。还是上面的例子,“The dog barked at the mailman”,那么我们基于这个句子,可以构建一个大小为5的词汇表(忽略大小写和标点符号):(“the”, “dog”, “barked”, “at”, “mailman”),我们对这个词汇表的单词进行编号0-4。那么”dog“就可以被表示为一个5维向量[0,1, 0, 0, 0]。
模型的输入如果为一个10000维的向量,那么输出也是一个10000维度(词汇表的大小)的向量,它包含了10000个概率,每一个概率代表着当前词是输入样本中output word的概率大小。
隐层
隐层没有使用任何激活函数,但是输出层使用了sotfmax。我们基于成对的单词来对神经网络进行训练,训练样本是 ( input word,output word ) 这样的单词对,input word和output word都是one-hot编码的向量。最终模型的输出是一个概率分布。
如果我们现在想用300个特征来表示一个单词(即每个词可以被表示为300维的向量)。那么隐层的权重矩阵应该为10000行,300列(隐层有300个结点)。Google在最新发布的基于Google news数据集训练的模型中使用的就是300个特征的词向量。词向量的维度是一个可以调节的超参数(在Python的gensim包中封装的Word2Vec接口默认的词向量大小为100, window_size为5)。
看下面的图片,左右两张图分别从不同角度代表了输入层-隐层的权重矩阵。左图中每一列代表一个10000维的词向量和隐层单个神经元连接的权重向量。从右边的图来看,每一行实际上代表了每个单词的词向量。
所以我们最终的目标就是学习这个隐层的权重矩阵。我们现在回来接着通过模型的定义来训练我们的这个模型。上面我们提到,input word和output word都会被我们进行one-hot编码。仔细想一下,我们的输入被one-hot编码以后大多数维度上都是0(实际上仅有一个位置为1),所以这个向量相当稀疏,那么会造成什么结果呢。如果我们将一个1 x 10000的向量和10000 x 300的矩阵相乘,它会消耗相当大的计算资源,为了高效计算,它仅仅会选择矩阵中对应的向量中维度值为1的索引行(这句话很绕),看图就明白。我们来看一下上图中的矩阵运算,左边分别是1 x 5和5 x 3的矩阵,结果应该是1 x 3的矩阵,按照矩阵乘法的规则,结果的第一行第一列元素为0 x 17 + 0 x 23 + 0 x 4 + 1 x 10 + 0 x 11 = 10,同理可得其余两个元素为12,19。如果10000个维度的矩阵采用这样的计算方式是十分低效的。
为了有效地进行计算,这种稀疏状态下不会进行矩阵乘法计算,可以看到矩阵的计算的结果实际上是矩阵对应的向量中值为1的索引,上面的例子中,左边向量中取值为1的对应维度为3(下标从0开始),那么计算结果就是矩阵的第3行(下标从0开始)—— [10, 12, 19],这样模型中的隐层权重矩阵便成了一个”查找表“(lookup table),进行矩阵计算时,直接去查输入向量中取值为1的维度下对应的那些权重值。隐层的输出就是每个输入单词的“嵌入词向量”。
输出层
经过神经网络隐层的计算,ants这个词会从一个1 x 10000的向量变成1 x 300的向量,再被输入到输出层。输出层是一个softmax回归分类器,它的每个结点将会输出一个0-1之间的值(概率),这些所有输出层神经元结点的概率之和为1。下面是一个例子,训练样本为 (input word: “ants”, output word: “car”) 的计算示意图。
CBOW的神经网络模型与skip-gram的神经网络模型也是互为镜像的。
现在的Corpus是这一个简单的只有四个单词的document:{I drink coffee everyday},我们选coffee作为中心词,window size设为2。也就是说,我们要根据单词"I","drink"和"everyday"来预测一个单词,并且我们希望这个单词是coffee。
输入层:上下文单词的onehot14维3个词
输出层:1*4维的向量(概率表示)
ram模型的输入输出是相反的。这里输入层是由one-hot编码的输入上下文{ x 1 , … , x C {x_1,…,x_C} x1,…,xC}组成,其中窗口大小为 C C C,词汇表大小为 V V V。隐藏层是 N N N维的向量。最后输出层是也被one-hot编码的输出单词 y y y。被one-hot编码的输入向量通过一个 V × N V×N V×N维的权重矩阵 W W W连接到隐藏层;隐藏层通过一个 N × V N×V N×V的权重矩阵 W ′ W′ W′连接到输出层。
接下来,我们假设我们知道输入与输出权重矩阵的大小。
第一步就是去计算隐藏层 h h h的输出。如下:
( 1 ) h = 1 C W ⋅ ( ∑ i = 1 C x i ) h = \frac{1}{C}W\cdot (\sum_{i=1}^C x_i)\tag{$1$} h=C1W⋅(i=1∑Cxi)(1)
该输出就是输入向量的加权平均。这里的隐藏层与skip-gram的隐藏层明显不同。
第二部就是计算在输出层每个结点的输入。如下:
( 2 ) u j = v w j ′ T ⋅ h u_{j}=v^{'T}_{wj}\cdot h\tag{$2$} uj=vwj′T⋅h(2)
其中 v w j ′ T v^{'T}_{wj} vwj′T是输出矩阵 W ′ W^′ W′的第 j j j列。
最后我们计算输出层的输出,输出 y j y_j yj如下:
( 3 ) y c , j = p ( w y , j ∣ w 1 , . . . , w c ) = e x p ( u j ) ∑ j ′ = 1 V e x p ( u ′ j ) y_{c,j} =p(w_{y,j}|w_1,...,w_c) = \frac{exp(u_{j})}{\sum^V_{j^{'}=1}exp(u^{'}j)}\tag{$3$} yc,j=p(wy,j∣w1,...,wc)=∑j′=1Vexp(u′j)exp(uj)(3)
通过BP(反向传播)算法及随机梯度下降来学习权重
在学习权重矩阵 W W W与 W ′ W′ W′过程中,我们可以给这些权重赋一个随机值来初始化。然后按序训练样本,逐个观察输出与真实值之间的误差,并计算这些误差的梯度。并在梯度方向纠正权重矩阵。这种方法被称为随机梯度下降。但这个衍生出来的方法叫做反向传播误差算法。
后面将会介绍Word2Vec之基于Negative Sampling的 CBOW 和 skip-gram 模型。