同学可以移步到这位同学的blog去看更细致的分析
这个答案是两年前写的,当时只读了下代码没有深入的去使用和研究,有些地方理解的还不深,写的很不好。当时自己主要是搞不明白h-softmax,所以大部分时间在想如何解释h-softmax,但现在看来,许多同学还不明白这个模型本身,我这个note里连模型的优化目标都没贴。
最近我又重新回顾了下word representation的文献,等有时间,会写个note(放在这了writing/word_representation_note_en.pdf at master · placebokkk/writing · GitHub),但不会细讲word2vec, 这个东西已经被讲烂了,代码也在那,很好读。这里稍微再说下word2vec里的SGNS(Skip-gram negative sampling):
1.一个词word会有两个向量,一般是用做上下文时的C,一个使用做次本身表示的W。
2.SG的优化目标很简单,参考mikolov论文中的公式(1)(2).不需要跟神经网络扯上关系,直接优化这个目标,梯度下降。
3.mikolov给的图跟nn的惯用法不太一致,所以每次看我都看不懂。如果要把公式(1)(2)用nn来表示,Skip gram是这样的:
- 输入层, one hot表示,当前词是1,其他是0
- 输入层到隐层的连接矩阵,即前面说的W
- 隐层的激活函数很特殊,是f(x) =x. 所以隐层的输出值其实就是当前词的表示Wi
- 隐层到输出层的链接矩阵,即前面说的C
- 输出层是个softmax
如果用neg sampling,相当于把输出层改为含V个节点(当前的上下文词的label是1,其他是0),激活函数变为sigmoid,但只选部分的负样本(即公式(4))。这样不能保证输出是归一化的,但是因为目的不是train LM,所以不必要求输出归一化。
原答案最后随口说了句 "作者的还用到了一些其他的trick,比如每个句子都做采样,根据词频随机删掉一些单词,上下文窗口的大小是随机的。" 今年的ACL上,levy的一篇论文说,SGNS之所以比PPMI-SVD效果好,是因为这些"trick"(客气的称为hyper-parameter),如果给PPMI引入这些,基于count matrix和SGNS效果都差不多。
levy的oral里最后讲到:
如果你想发paper,好好tune hyper-parameter
如果你想做研究,好好tune你的baseline的hyper-parameter
原话忘了,大概是这个意思。
-----------------------------------------------------------------------原文----------------------------------------------------------
我之前写的一篇笔记:
Word2Vec的一些理解
最近几位google的研究人员发布了一个工具包叫word2vec,利用神经网络为单词寻找一个连续向量空间中的表示。这里整理一下思路,供有兴趣的同学参考。
这里先回顾一下大家比较熟悉的N-gram语言模型。
在自然语言任务里我们经常要计算一句话的概率。比如语音识别中对于一个语音段O,需要找到一个使P(W|O)最大的文本段W。利用贝叶斯公式:
P(W|O) = P(O|W)*P(W)/P(O)
其中 P(O|W)是用hmm-gmm或者hmm-dnn建模的声学模型,而P(W)称为语言模型。
P(W)可以分解如下:
其中:
为了简化问题,可以做一个n-1阶Markov假设,即每个单词只和其前n-1个单词相关,则:
这样,只要算出这些条件概率,任意的句子的概率我们都可以计算,而这些条件概率可以通过对语料建立一个多项分布模型用最大似然法求得。
这种方法我们称为N-gram语言模型。
但是N-gram存在一个问题,若训练语料里面有些n元组没有出现过,其对应的条件概率就是0,这会导致计算一整句话的概率为0。
解决这个问题有两种常用方法:
一种是平滑法,最简单的是将出现k次的某个n元组看做出现了k+1次,这样出现0次的n元组就变成了出现1次。或者用更复杂的其他平滑方法。其本质都是将模型编程贝叶斯模型,为参数引入了先验分布,从而解决最大似然法的问题。
另一种是回退法,即如果n元的概率不到,那就用n-1元的概率乘上一个权重来模拟。
一.神经网络语言模型
在Bengio03的文章中,他们提到除了上面说的问题之外,n-gram还存在其他问题:
1.n-gram语言模型无法建模更远的关系,语料的不足使得无法训练更高阶的语言模型。(这篇文章发表时,基本都是trigram,还没有高阶的模型,不过几年后,互联网的海量数据使得可以训练10几阶的语言模型)
2.这种模型无法建模出词之间的相似度,有时候两个具有某种相似性的词,如果一个词经常出现在某段词之后,那么也许另一个词出现在这段词后面的概率也比较大,比如
The cat is walking in the bedroom
A dog was running in a room
如果第一句话里的元组在语料中出现的很多,训练的很充分,第二句话中的元组在语料中出现的少,训练的不充分,那么使用语言模型计算第一句话的概率就比较高,而第二句话的概率就低。
如果有一种方法,能知道The和a相似,cat和dog相似等等,并且会给相似的词类似的语言模型概率,那么第二句话也可以得到高概率。
另一种模型
既然我们想要的就是P(wt|Wp),那么可以用一个神经网络直接去建模这个概率,网络的输入是前N-1单词,输出是V个节点,其中第i个节点的输出值就是P(wi|Wp).本文用Wp表示wt前面的N-1的个词.
具体的模型如下:
1.每个单词i表示成一个向量Ci
2.把单词的向量表示Ci排列成一个更大的向量作为神经网络输入,输出是第n个单词。
3.训练出C和神经网络的参数
<img src="https://i-blog.csdnimg.cn/blog_migrate/170effb0b9470b019dc095543dc52bdf.png" data-rawwidth="871" data-rawheight="690" class="origin_image zh-lightbox-thumb" width="871" data-original="https://pic3.zhimg.com/61f31ca272dcd40ad0a3d05efc3172a2_r.jpg">![](https://i-blog.csdnimg.cn/blog_migrate/170effb0b9470b019dc095543dc52bdf.png)
如果不考虑虚线(输入跳过隐层直接到输出的连接),那么红框内是个标准的三层前向神经网络,输入层是单词的向量表示,隐层使用tanh激活函数,输出层用softmax做概率归一化。
这个模型也可以看做是4层的网络,输入层每个节点到层C之间的权重就是这个节点对应的单词i的向量表示Ci。
对于每个样本,输入是Wp,输出是一个1 of V的向量,即V个输出节点,只有wt对应的那个节点为1,其他都为0.
使用BP算法训练这个网络即可得到所有的参数值。
虽然这篇文章的方法是为了计算语言模型,但也同时获得一种单词在向量空间上的表示。而这个副作用才是google的word2vec的真正目标。
二.层次soft-max
虽然基于神经网络的语言模型在效果上表现的很不错,但是其训练和预测的时间较长,影响实际的应用。
这个模型的输出节点个数是V,在训练时,对于一个样本(Wp,wt),我们的目标是最大化P(wt|Wp),模型的输出是V个没有归一化的值,所以需要对其做soft-max,得到P(wt|Wp).
这就需要前向时计算所有节点的输出,并且要更新隐层到所有输出节点的权重。隐层个数是H,则这一计算量是正比于H*V的。
可不可以每次只对到标注是1的节点的权重进行更新,而不管那些标注是0的节点呢,这样每次训练时计算量不就是正比于H了吗?这是可以的,只是训练会起来很慢。
为什么呢,用一种直观但不严谨的说法来解释:
“只对到输出是1的节点的权重进行更新,而不管那些输出是0的节点”这种做法,是让模型在每次迭代时只增加我看到的东西的概率,并没有降低我没看到东西的概率(这里说的概率是非归一化概率,即没做soft-max之前输出节点的输出值,按照归一化概率而言,只要增加了看到东西的概率,就必然会降低没看到的东西的概率)。而如果让模型在每次迭代都能在增加我看到的东西的概率的同时,降低我没看到东西的概率,那么模型会更快的收敛,所以训练时隐层到输出是0的节点间的权重也要做更新。
这里提前说一下,在word2vec里面,除了h soft-max,还有另一种方法叫negative sampling,就是这种思路,他每次并不对所有的标注是0的节点的权重进行更新,而随机选取一些标注是0的节点更新权重,随机选取的量是n,这样每次就把H*V降到H*n。
现在回到上面的问题,我希望增大的不是标注为1的节点的输出概率,而是该输出在和其他节点一同进行归一化后的概率,是否有什么可以减少计算量的方法呢?
我们可以考虑这么一个问题,如果我们有100个数组成的分布,但我们只有他们的非归一化概率,这样想计算出某个数的归一概率,这样我们需要计算100次(把他们加起来)。而如果我们把他们分为10类,每类10个数。如果我们知道这10个类的非归一化的概率,以及每类的10个数的非归一化概率,那么我们计算某个数的归一化概率就只要10+10次,这就提高了5倍的速度。
更极致的方法是分更多的层,每次用2分法,这样可以把效率提高的极致。这可以等价于对输出节点进行某种2进制的编码,第i位表示这个词在第i个层次上面的分类。
这样模型的输出层就如下图所示:
<img src="https://i-blog.csdnimg.cn/blog_migrate/f7c43a9564c3bb19486983064f58c24c.png" data-rawwidth="937" data-rawheight="625" class="origin_image zh-lightbox-thumb" width="937" data-original="https://pic3.zhimg.com/c8bb89a9b12cfb0969cf496555b67122_r.jpg">![](https://i-blog.csdnimg.cn/blog_migrate/f7c43a9564c3bb19486983064f58c24c.png)
同学可能会有疑问:这样做和每次只对输出是1的节点更新有什么区别呢?后者只需要H的复杂度,这样还需要H*Log2(V)呢?
这是有区别的,但这里需要每一层分类的法则具有一定的意义,不能是随机划分。
那么在训练时,每次训练,设样本里的输出单词为wt,你增加了p(wt|Wp)的概率,本质上是增加了他在每个层次上的他属于的类别的概率,由于在每个层次上每个类别节点都是2分的,那么增加wt属于的类别概率,就是降低wt不属于的另一类别的概率。这样那些V中和wt同类的节点概率被增加,不同类的节点概率被降低,如果类别是用真是意义的,那么每次训练,那些不同类的单词就相当于负例了,而做n=0的negative sampling是没有这个效果的,他只会增加wt的输出概率。
另外在进行预测时,这种分层soft-max方法也可以降低时间复杂度,但前提是只预测一个P(wt|Wp)或者一部分(比如语音识别只要计算候选集里的即可),但如果想要预测此表中所有的V,那么分层soft-max的复杂度和直接soft-max是一样的.
三.Google的word2vec
<img src="https://i-blog.csdnimg.cn/blog_migrate/1193b2889b53bd59d284eece6d9daaba.png" data-rawwidth="811" data-rawheight="477" class="origin_image zh-lightbox-thumb" width="811" data-original="https://pic3.zhimg.com/ac3d96ff826948aa5542ac2a76fd020e_r.jpg">![](https://i-blog.csdnimg.cn/blog_migrate/1193b2889b53bd59d284eece6d9daaba.png)
有了上面的知识,word2Vec就很好理解,只是对上面的框架上做了些小改动。
其中的改动有:
1.映射层不再是将输入单词的向量表示按顺序排列,而是将他们相加。减少计算量。
2.去掉了隐层,减少计算量,同时效果并不差。
3.由于这里的目的是寻找单词的向量表示,而不是语言模型,所以可以利用一些其他的判别准则,比如,两个模型分别对应上图的CBOW和Skip-gram.这样真的是利用了上下文而不是上文了。
4.对于分层softmax,并没有wordnet进行编码,而是根据词频用huffman编码。我们提到过,使用分层softmax,要求分类是有一定意义的,如果说用人类的先验知识如wordnet或者某些无监督学习的方法去做分层,倒还有道理,但是huffman编码只用到了词频特征去分层,这样的分层为什么有效呢?作者说:
<img src="https://i-blog.csdnimg.cn/blog_migrate/06b2b2b04b52957477add8d6b118603e.png" data-rawwidth="848" data-rawheight="69" class="origin_image zh-lightbox-thumb" width="848" data-original="https://pic4.zhimg.com/c3dc9fdfcd7fa47e2624966363e24dbb_r.jpg">![](https://i-blog.csdnimg.cn/blog_migrate/06b2b2b04b52957477add8d6b118603e.png)
Ok,实践是检验真理的唯一标准。
5.除了分层softmax,作者还使用了negative sampling的方法。
作者的还用到了一些其他的trick,比如每个句子都做采样,根据词频随机删掉一些单词,上下文窗口的大小是随机的。
如果仍不理解,请阅读论文和作者的源代码。