赞
踩
声明:
1)该博文是Google专家以及多位博主所无私奉献的论文资料整理的。具体引用的资料请看参考文献。具体的版本声明也参考原文献
2)本文仅供学术交流,非商用。所以每一部分具体的参考资料并没有详细对应,更有些部分本来就是直接从其他博客复制过来的。如果某部分不小心侵犯了大家的利益,还望海涵,并联系老衲删除或修改,直到相关人士满意为止。
3)本人才疏学浅,整理总结的时候难免出错,还望各位前辈不吝指正,谢谢。
4)阅读本文需要机器学习、概率统计算法等等基础(如果没有也没关系了,没有就看看,当做跟同学们吹牛的本钱),基础篇url:http://blog.csdn.net/mytestmy/article/details/26961315 。
5)此属于第一版本,若有错误,还需继续修正与增删。还望大家多多指点。请直接回帖,本人来想办法处理。
6)word版的和pdf版的文档已上传到csdn,下载url:http://download.csdn.NET/detail/mytestmy/8565955,或者http://download.csdn.net/detail/mytestmy/8565959,资源分1分,评论后据说可以返还的,就有劳各位帮忙攒点分吧。如果有必要可以回复或者发邮件到邮箱:beiliude@163.com。
当然如果已经了解了,就随便看看得了。
怎么得到这些词向量更加是一个重要的过程,也是word2vec这整个算法最重要的东西,后面会认真介绍。
至于霍夫曼树,其实是一个优化的解法,后面再提。
其中d是一个映射函数,把Y里面的元素映射到词的类别D里面的元素。还有个证明
看到这务必想明白,因为开始要讨论怎么训练了。
解法选用的是SGD,博文《在线学习算法FTRL》中说过SGD算法的一些情况。具体说来就是对每一个样本都进行迭代,但是每个样本只影响其相关的参数,跟它无关的参数不影响。对于上面来说,第j个样本的第ij个词的负对数似然是
第j个样本的第ij个词的在遇到第kij个非叶节点时的负对数似然是
计算的梯度,注意参数包括和,其中的梯度是用来计算的时候用到。另外需要注意的是logσ(x)的梯度是1-σ(x),log(1-σ(x))的梯度是-σ(x),
和
上面的Fq和Fc只是简写,有了梯度就可以对每个参数进行迭代了
同时,每个词的词向量也可以进行迭代了
注意第二个迭代的wI是代表所有的输入词的,也就是假如输入了4个词,这四个词都要根据这个方式进行迭代(注意是上下文的词才是输入词,才根据梯度更新了,但是wij这个词本身不更新的,就是轮到它自己在中间的时候就不更新了)。第二个迭代式确实不好理解,因为这里的意思是所有非叶节点上的对上下文的梯度全部加和就得到了这个词的上下文的梯度,看起来这个就是BP神经网络的误差反向传播。
论文《Hierarchical Probabilistic Neural Network Language Model》和《Three New Graphical Models for Statistical Language Modelling》中看起来也是这么样的解释,人家都是Context的几个词首尾连接得到的一个向量,对这个长向量有一个梯度,或者一个超大的V*m矩阵(m是词向量的维度),对这个矩阵每个元素有一个梯度,这些梯度自然也包括了输入词的梯度。
如果有人发现了这个做法的解释请告知。
为啥要抽样呢?目的跟(二)中的霍夫曼树其实是一样的,都是为了节省计算量,这个计算量就是式(2.1.2)中计算 p(A|C)的概率,因为这个概率实在不好算。论文《Distributed Representations of Words and Phrases and their Compositionality》中提到有一个叫NCE的方法可以来代替上面的那个hierarchical softmax方法(就是使用霍夫曼树的方法),但是由于word2vec只关心怎么学到高质量的词向量,所以就用了一种简单的NCE方法,称为NEG,方法的本质就是在第j个句子的第ij个词wij处使用下面的式子代替
其中T_j表示第j个句子的词个数,w_(u_ij+i_j )表示词w_(i_j )左右的各c_ij个词的其中一个,注意c_ij对每个w_(i_j )都不一样的。极大似然要对整个语料库去做的。
但是,Google这公司,为了代码风格的统一,用了一个trick,我举例说明吧。
对于一开始的那句话:大家 喜欢 吃 好吃 的 苹果,总共6个词,假设每次的cij都抽到了2,按照上面的公式中的部分按每个词作为条件w_(i_j )展开,看到得到什么吧。
大家:P(喜欢|大家)*p(吃|大家)
喜欢:p(大家|喜欢)*p(吃|喜欢)*p(好吃|喜欢)
吃: p(大家|吃) *p(喜欢|吃)*p(好吃|吃)*p(的|吃)
好吃:p(喜欢|好吃)*p(吃|好吃)*p(的|好吃)*p(苹果|好吃)
的: p(吃|的) *p(好吃|的)*p(苹果|的)
苹果:p(好吃|苹果)*p(的|苹果)
把结果重新组合一下,得到下面的组合方式。
大家:p(大家|喜欢)*p(大家|吃)
喜欢:P(喜欢|大家)*p(喜欢|吃)*p(喜欢|好吃)
吃: p(吃|大家) *p(吃|喜欢)*p(吃|好吃)*p(吃|的)
好吃:p(好吃|喜欢)*p(好吃|吃)*p(好吃|的)*p(好吃|苹果)
的: p(的|吃) *p(的|好吃)*p(的|苹果)
苹果:p(苹果|好吃)* p(苹果|的)
不证明,不推导,直接得到下面的结论
这个是网易有道的那个资料给出的结论,这里是变成了本文的方式来描述,具体参考网易有道的原文。
这个变化倒是莫名其妙的,不过也能理解,Google公司的代码是要求很优美的,这样做能把代码极大地统一起来。
对数似然就会是下面的样子
再利用这个式子替换掉(5.2.2)中的就能得到总的对数似然函数,也就是目标函数,剩下的就是怎么解了。
可以注意的是,计算每个词(例中的“吃”)上下文概率的时候都要计算好几个条件概率的(例子中p(大家|吃),p(喜欢|吃),p(好吃|吃)和p(的|吃)),这每个条件概率又是需要在huffman树上走好几个非叶节点的,每走到一个非叶节点,都需要计算一个的。可以看到,走到每一个非叶节点,在总的对数似然函数中,都类似logistic regression的一个样本,为了方便描述,就用样本和label的方式在称呼这些东西。
跟一般的logistic regression一样,每走到一个非叶节点,如果是向左走的,就定义label为1,为正样本;否则label就是0,是负样本。这样label=1-dk,每一个非叶节点都为整个问题产生了一个样本。
对输入词I的更新是走完整个huffman树后对整个误差一起计算的,这个误差保存在neu1e这个数组里面。
其中480-483行是计算的值,保存在f中,vocab[word].code[d]表示的就是dk的值,neu1e就保存了从根节点到叶子节点的路径上的所有非叶节点的累积误差。
这个误差反向传播就简单多了,每次都是对一个词进行更新的,就是p(w|I)中的那个w。
这个就简单说说吧,不清楚的看上面的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。