标签:1.4 过程 neu nbsp 机器学习 参考 需要 构建 否则
word2vec有两个模型,分别是CBOW和Skip-gram模型。这两个模型又都可以有两种优化方法。分别是
Hierarchical Softmax与Negative Sampling 。所以实现word2vec有四种方式:
2013年末,Google发布的word2vec引起了一帮人的热捧。在大量赞叹word2vec的微博或者短文中,几乎都认为它是深度学习在自然语言领域的一项了不起的应用,各种欢呼“深度学习在自然语言领域开始发力了”。但实际上,简单看看代码就知道它实际上只是一个三层网络,压根算不上所谓的深层网络,学习过程也很简单,并未用太玄妙的东西,以至于在了解完整以后对它的简单叹为观止。
首先,它的结构就是一个三层网络——输入层、隐层(也可称为映射层),输出层。
其次,代码中让人费解的主要是hierarchical softmax。得同事J指导,和同事S讨论,终于弄明白其网络结果,如下图所示:
输入层读入窗口内的词,将它们的向量(K维,初始随机)加和在一起,形成隐藏层K个节点。输出层是一个巨大的二叉树,叶节点代表语料里所有的词(语料含有V个独立的词,则二叉树有|V|个叶节点)。而这整颗二叉树构建的算法就是Huffman树。这样,对于叶节点的每一个词,就会有一个全局唯一的编码,形如”010011″。我们可以记左子树为1,右子树为0。接下来,隐层的每一个节点都会跟二叉树的内节点有连边,于是对于二叉树的每一个内节点都会有K条连边,每条边上也会有权值。
这样,整体的结构就清晰了。在训练阶段,当给定一个上下文,要预测后面的词(Wn)的时候(word2vec的CBOW和Skip-gram都不是预测后面的词,都是在中间的词上做文章,但是本文这么写并不影响理解),实际上我们知道要的是哪个词(Wn),而Wn是肯定存在于二叉树的叶子节点的,因此它必然有一个二进制编号,如”010011″,那么接下来我们就从二叉树的根节点一个个地去便利,而这里的目标就是预测这个词的二进制编号的每一位!即对于给定的上下文,我们的目标是使得预测词的二进制编码概率最大。形象地说,我们希望在根节点,词向量和与根节点相连经过logistic计算得到的概率尽量接近0(即预测目标是bit=1);在第二层,希望其bit是1,即概率尽量接近1……这么一直下去,我们把一路上计算得到的概率相乘,即得到目标词Wn在当前网络下的概率(P(Wn)),那么对于当前这个sample的残差就是1-P(Wn)。于是就可以SGD优化各种权值了。
那么hs(hierarchical softmax)如何保证叶节点输出的概率值(即我们一路沿二进制编号乘下去的概率)是归一化的呢(否则,所谓的残差1-P(Wn)就没什么意义了)?这点其实很简单,请看下图:
从根节点开始,对于一个sample而言,目标词是W2,二进制编码是”110″。我们在根节点计算得到它的第一位是’1’的概率是P,那么它第一位是’0’的概率就是1-P;在左子树里,第二位是”1″的概率是P’,那么第二位是”0″的概率就是1-P’,而在右子树里,第二位是”1″的概率是P”,那么第二位是”0″的概率就是1-P”;第三位亦如此。为方便表示记,我们只写到第二层。这样,在第二层,整个概率之和就是
(P*(P’) + P*(1-P’)) + ((1-P)*(P”) + (1-P)*(1-P”)) = P + (1-P) = 1
即按照目标词的二进制编码计算到最后的概率值就是归一化的,这也是为啥它被称作hierarchical softmax的原因。
如果没有使用这种二叉树,而是直接从隐层直接计算每一个输出的概率——即传统的softmax,就需要对|V|中的每一个词都算一遍,这个过程时间复杂度是O(|V|)的。而使用了二叉树(如word2vec中的Huffman树),其时间复杂度就降到了O(log2(|V|)),速度大大地加快了。
不过虽然hierarchical softmax一般被认为只是用于加速,但是仍然可以感性地理解一下为啥它会奏效:二叉树里面的每一个内节点实际上是一种隐含概念的分类器(二元分类器,因为二进制编码就是0/1),它的输出值的大小预示着当前上下文能够表达该隐含概念的概率,而一个词的编码实际上是一堆隐含概念的表达(注意,这个隐含概念的表达和词向量的维度所表达的隐含概念是不一样的)。我们的目标就在于找到这些当前上下文对于这些概念分类的最准确的那个表达(即目标词向量)。由于概念之间实际上是有互斥关系的(二叉树保证),即在根节点如果是”1″,即可以表达某一概念,那么该上下文是绝对不会再有表达根节点是”0″的其他情况的概念了,因此就不需要继续考虑根节点是”0″的情况了。因此,整个hierarchical softmax可以被看作完全不同于传统softmax的一套。
本文的理论部分大量参考《word2vec中的数学原理详解》,按照我这种初学者方便理解的顺序重新编排、重新叙述。题图来自siegfang的博客。我提出的Java方案基于kojisekig,我们还在跟进准确率的问题。
在统计自然语言处理中,语言模型指的是计算一个句子的概率模型。
传统的语言模型中词的表示是原始的、面向字符串的。两个语义相似的词的字符串可能完全不同,比如“番茄”和“西红柿”。这给所有NLP任务都带来了挑战——字符串本身无法储存语义信息。该挑战突出表现在模型的平滑问题上:标注语料是有限的,而语言整体是无限的,传统模型无法借力未标注的海量语料,只能靠人工设计平滑算法,而这些算法往往效果甚微。
神经概率语言模型(Neural Probabilistic Language Model)中词的表示是向量形式、面向语义的。两个语义相似的词对应的向量也是相似的,具体反映在夹角或距离上。甚至一些语义相似的二元词组中的词语对应的向量做线性减法之后得到的向量依然是相似的。词的向量表示可以显著提高传统NLP任务的性能,例如《基于神经网络的高性能依存句法分析器》中介绍的词、词性、依存关系的向量化对正确率的提升等。
从向量的角度来看,字符串形式的词语其实是更高维、更稀疏的向量。若词汇表大小为N,每个字符串形式的词语字典序为i,则其被表示为一个N维向量,该向量的第i维为1,其他维都为0。汉语的词汇量大约在十万这个量级,十万维的向量对计算来讲绝对是个维度灾难。而word2vec得到的词的向量形式(下文简称“词向量”,更学术化的翻译是“词嵌入”)则可以自由控制维度,一般是100左右。
word2vec作为神经概率语言模型的输入,其本身其实是神经概率模型的副产品,是为了通过神经网络学习某个语言模型而产生的中间结果。具体来说,“某个语言模型”指的是“CBOW”和“Skip-gram”。具体学习过程会用到两个降低复杂度的近似方法——Hierarchical Softmax或Negative Sampling。两个模型乘以两种方法,一共有四种实现。这些内容就是本文理论部分要详细阐明的全部了。
无论是哪种模型,其基本网络结构都是在下图的基础上,省略掉hidden layer:
为什么要去掉这一层呢?据说是因为word2vec的作者嫌从hidden layer到output layer的矩阵运算太多了。于是两种模型的网络结构是:
其中w(t)代表当前词语位于句子的位置t,同理定义其他记号。在窗口内(上图为窗口大小为5),除了当前词语之外的其他词语共同构成上下文。
CBOW 是 Continuous Bag-of-Words Model 的缩写,是一种根据上下文的词语预测当前词语的出现概率的模型。其图示如上图左。
CBOW是已知上下文,估算当前词语的语言模型。其学习目标是最大化对数似然函数:
其中,w表示语料库C中任意一个词。从上图可以看出,对于CBOW,
输入层是上下文的词语的词向量(什么!我们不是在训练词向量吗?不不不,我们是在训练CBOW模型,词向量只是个副产品,确切来说,是CBOW模型的一个参数。训练开始的时候,词向量是个随机值,随着训练的进行不断被更新)。
投影层对其求和,所谓求和,就是简单的向量加法。
输出层输出最可能的w。由于语料库中词汇量是固定的|C|个,所以上述过程其实可以看做一个多分类问题。给定特征,从|C|个分类中挑一个。
对于神经网络模型多分类,最朴素的做法是softmax回归:
softmax回归需要对语料库中每个词语(类)都计算一遍输出概率并进行归一化,在几十万词汇量的语料上无疑是令人头疼的。
不用softmax怎么样?比如SVM中的多分类,我们都知道其多分类是由二分类组合而来的:
这是一种二叉树结构,应用到word2vec中被作者称为Hierarchical Softmax:
上图输出层的树形结构即为Hierarchical Softmax。
非叶子节点相当于一个神经元(感知机,我认为逻辑斯谛回归就是感知机的输出代入f(x)=1/(1+e^x)),二分类决策输出1或0,分别代表向下左转或向下右转;每个叶子节点代表语料库中的一个词语,于是每个词语都可以被01唯一地编码,并且其编码序列对应一个事件序列,于是我们可以计算条件概率。
在开始计算之前,还是得引入一些符号:
从根结点出发到达w对应叶子结点的路径.
路径中包含结点的个数
路径中的各个节点
词w的编码,表示路径第j个节点对应的编码(根节点无编码)
路径中非叶节点对应的参数向量
于是可以给出w的条件概率:
这是个简单明了的式子,从根节点到叶节点经过了-1个节点,编码从下标2开始(根节点无编码),对应的参数向量下标从1开始(根节点为1)。
其中,每一项是一个逻辑斯谛回归:
考虑到d只有0和1两种取值,我们可以用指数形式方便地将其写到一起:
我们的目标函数取对数似然:
将代入上式,有
这也很直白,连乘的对数换成求和。不过还是有点长,我们把每一项简记为:
怎么最大化对数似然函数呢?分别最大化每一项即可(这应该是一种近似,最大化某一项不一定使整体增大,具体收敛的证明还不清楚)。怎么最大化每一项呢?先求函数对每个变量的偏导数,对每一个样本,代入偏导数表达式得到函数在该维度的增长梯度,然后让对应参数加上这个梯度,函数在这个维度上就增长了。这种白话描述的算法在学术上叫随机梯度上升法,详见更规范的描述。
每一项有两个参数,一个是每个节点的参数向量,另一个是输出层的输入,我们分别对其求偏导数:
因为sigmoid函数的导数有个很棒的形式:
于是代入上上式得到:
合并同类项得到:
于是的更新表达式就得到了:
其中,是机器学习的老相好——学习率,通常取0-1之间的一个值。学习率越大训练速度越快,但目标函数容易在局部区域来回抖动。
再来的偏导数,注意到中和是对称的,所有直接将的偏导数中的替换为,得到关于的偏导数:
于是的更新表达式也得到了。
不过是上下文的词向量的和,不是上下文单个词的词向量。怎么把这个更新量应用到单个词的词向量上去呢?word2vec采取的是直接将的更新量整个应用到每个单词的词向量上去:
其中,代表上下文中某一个单词的词向量。我认为应该也可以将其平均后更新到每个词向量上去,无非是学习率的不同,欢迎指正。
于是就可以得到两个参数更新的伪码:
在原版C代码中的对应关系是:
标签:1.4 过程 neu nbsp 机器学习 参考 需要 构建 否则
原文地址:http://www.cnblogs.com/www-caiyin-com/p/7360503.html