参考:https://zhuanlan.zhihu.com/p/22477976
http://yobobobo001.github.io/2016/05/26/%E6%88%91%E6%89%80%E7%90%86%E8%A7%A3%E7%9A%84word2vec/
http://x-algo.cn/index.php/2016/03/12/281/#i
https://www.zhihu.com/question/25269336
http://mp.weixin.qq.com/s?__biz=MzA3MDg0MjgxNQ==&mid=208116963&idx=3&sn=2a4c2eb8fbd27cad2293d8acabce52e9&mpshare=1&scene=1&srcid=0327HXI7DLeR7UUMDXHlb7H3#rd
http://www.flyml.net/2016/11/07/word2vec-basic-understanding/
word2vec就是用一个一层的神经网络(CBOW的本质)把one-hot形式的词向量映射为分布式形式的词向量,为了加快训练速度,用了Hierarchical softmax,negative sampling 等trick。训练的主要目标是获得分布式词向量,而不是神经网络的预测模型。word2vec的训练过程是有监督的神经网络学习,但是得到的结果居然是无监督的clustering的效果,就是获取词向量。分布式词向量隐含了词语的信息,分布式词向量之间的夹角可以表示词语之间的相关性,因此用作特征值比直接用词本身更方便。word2vec还规避了两大问题:词语的次序和热门词语的降权处理。与潜在语义分析(Latent Semantic Index, LSI)、潜在狄立克雷分配(Latent Dirichlet Allocation,LDA)的经典过程相比,Word2vec利用了词的上下文,语义信息更加地丰富。
word2vec涉及到很多自然语言处理的名词。首先是词向量(word vector),图像和音频等信号都可以用一个矩阵或者向量表示,所以我们也希望用一个数学方法来表达单词,这样可以方便的用于各种后续计算,这就是词向量。
1. one-hot方式。从很大的词库corpus里选V个频率最高的词(忽略其他的),V一般比较大,比如V=10W,固定这些词的顺序,然后每个词就可以用一个V维的稀疏向量表示了,这个向量只有一个位置的元素是1,其他位置的元素都是0。One hot方式其实就是简单的直接映射,所以缺点也很明显,维数很大,也没啥计算上的意义。
2. 分布式词向量(distributed word representation), 分布式词向量是一个固定大小的实数向量,事前确定它的大小比如N=300维或者N=1000维,每个元素都是一个实数,实数的具体值是词库里面每个词通过不同的贡献得来的,所以叫分布式的。而word2vec就是一种学习这个分布式词向量的算法。向量的余弦夹角可以代表词语之间的相似度。种方法相较于One-hot方式另一个区别是维数下降极多,对于一个10W的词表,我们可以用10维的实数向量来表示一个词,而One-hot得要10W维。
分布式词向量并不是word2vec的作者发明的,他只是提出了一种更快更好的方式来训练也就是:连续词袋模型Continous Bag of Words Model(CBOW)和Skip-Gram Model。这两种都是训练词向量的方法,可以选择其一,不过据论文说CBOW要更快一些(1天vs.3天的区别)。统计语言模型statistical language model就是给你几个词,在这几个词出现的前提下来计算某个词出现的(事后)概率。CBOW也是统计语言模型的一种,顾名思义就是根据某个词前面的C个词或者前后C个连续的词,来计算某个词出现的概率。Skip-Gram Model相反,是根据某个词,然后分别计算它前后出现某几个词的各个概率。
以“我爱北京天安门”这句话为例。假设我们现在关注的词是“爱”,C=2时它的上下文分别是“我”,“北京天安门”。CBOW模型就是把“我” “北京天安门” 的one hot表示方式作为输入,也就是C个1xV的向量,分别跟同一个VxN的大小的系数矩阵W1相乘得到C个1xN的隐藏层hidden layer,然后C个取平均所以只算一个隐藏层。这个过程也被称为线性激活函数(这也算激活函数?分明就是没有激活函数了)。然后再跟另一个NxV大小的系数矩阵W2相乘得到1xV的输出层,这个输出层每个元素代表的就是词库里每个词的事后概率。输出层需要跟ground truth也就是“爱”的one hot形式做比较计算loss。这里需要注意的就是V通常是一个很大的数比如几百万,计算起来相当费时间,除了“爱”那个位置的元素肯定要算在loss里面,word2vec就用基于huffman编码的Hierarchical softmax筛选掉了一部分不可能的词,然后又用nagetive samping再去掉了一些负样本的词所以时间复杂度就从O(V)变成了O(logV)。Skip gram训练过程类似,只不过输入输出刚好相反。
训练完成后对于某个词就可以拿出它的1xN的隐藏层作为词向量,就可以w2v(中国)-w2v(北京)=w2v(法国)-w2v(巴黎)了。
如何训练词向量
一般而言,一篇文章都会有个主题,比如海贼王的文章里面可能通篇都出现海贼王相关的词语,如路飞,索隆等,这些词的语义肯定比不同文章的更相近,所以作者提出了以下两种模型,把相近的单词放进模型里面训练。
cbow
把中间的词单独拎出来,让周围的词去预测这个词的概率。
skip-gram
skip-gram则是输入一个单词,把词周围的其他词作为要预测的单词。
softmax层加速
两种网络结构的最后一层都是一个大的softmax,起到对于每个词的预测概率归一化作用,可是在实际训练过程中每次迭代都要通过softmax计算每个词的概率是相当耗费时间的,能否优化这个呢?这个问题早在03年Bengio提出的语言模型[2]也遇到,后来他们提出了Hierarchical Softmax[3]来加速。
为什么要归一化
在每次迭代中把要预测的词相关权重增加,通过归一化,同时把其他的词相关权重减少。这个不难理解,总的预测概率和是1,把其中某一个词的概率增加就意味着把其他词的预测概率打压。能不能只增加其中某个词的概率呢?可以,但是收敛很慢。
Hierarchical Softmax 是如何提速的?
Hierarchical Softmax 相对于原来的softmax是把一个多分类问题转换成了多个二分类问题。通俗地说,现在仓管人员要去仓库找一个配件,按照softmax策略是把仓库里面的配件一个个的过一遍,最后确定下来是哪个,Hierarchical Softmax则是根据预先记录的表知道了要找的配件在第二个房间的第一个货架第二行第一个直接去取的。在上面提到,归一化的作用是把其他词的概率打压,那么能不能把词分好类别直接打压呢?这样就是每次打压一个类别(多个单词),而不用一个个地打压每个单词的相关权重了。原来的softmax是这样的:
现在我们构建一颗二叉树把所有单词放在叶子节点上:
现在我们要更新12号节点,沿着二叉树的路径可以看到判断是这样的:是否在1号节点的左儿子里,是否在2号节点的左儿子里,是否在5号节点的左儿子里,是否在9号儿子的左节点里。这就变成了多个二分类问题,作者采用哈弗曼树编码更是把每个单词在树中路径的长度减少到极致,并且等价于原来的softmax,因为整棵树的叶子节点和是归一化的。所以,最终的更新只需要更新一部分的权重即可。注意:最终的节点已经不是表示单词了,而是二叉树上的非叶子节点。
这样,由原来的O(N)次运算下降到了O(logN)级别,搞过ACM的同学可以意识到这个做法类似线段树的段更新。
negative sampling
不做归一化可是又想降低其他单词的预测概率,该怎么做呢?这就是negative sampling的由来。作者对其他的单词进行负采样以降低其他单词的相关权重,作者认为负采样中单词作为负样本的概率应该和其词频正相关,所以词频越高的越大概率被抽到。这种方案我认为只是一种近似方案,和Hierarchical Softmax 这种等价于原来的softmax是有明显区别的。不过这种方案在低频词上的学习会好一些,主要原因在于负采样更容易抽到其中的高频词。
训练数据的窗口选取策略
从cbow的网络结构可以看出,模型输入的单词数量是不定长的,那在构造训练样本的时候应该如何选取预测单词附近的多少个单词作为输入呢?Mikolov认为离预测单词近的词语比那些远的单词更相关,所以采用了随机窗口大小的方式来选取,每次在区间[1, window]随机一个数值K,然后在预测单词的前后各选取k个单词作为输入。
window是可选参数大小,window增大可以使向量学习更充分,但同时也增加了训练的复杂度。
exp运算打表
计算softmax的时候需要求e的x次方,一轮迭代中指数运算至少需要上亿级别的指数运算,由于sigmod函数是个长“S”型的函数,可以通过对中间部分的x查表来取代多次指数运算。
降采样
对于语料中的高频词,Mikolov选择对它们进行降采样(sub-samplig),我认为高频词在语料中的出现次数比较多,而且高频词一般而言都不是决定附近词语的词,比如“的”这种停用词。所以对高频词进行降采样既不影响模型效果,又能提升收敛速度,何乐而不为呢?
低频词的处理
对于训练预料中的低频词,源码中是对这部分进行过滤处理,我认为主要是这部分低频词的数据不充足,模型对于低频词向量训练不充分,学习的向量置信度不高,也会影响其他向量的效果。所以,过滤低频词是一个比较好的选择。
学习率调整策略
学习率过大导致模型不收敛,学习率过小则会陷入局部最优,Mikolov在实现过程中采取了学习率随着实际训练的语料增加而减少。
学习率的动态调整极为重要,在搭建网络训练过程中,跑了15轮的迭代相应的观察指标都没有降下来,我反反复复检查了数据生成以及网络实现都没有发现问题,断断续续debug了两天之后才意识到根本不是实现上的问题,而是学习率没有控制好,模型没有收敛。
caffe等框架中的实现方法
这段主要是在框架中实现源码的细节,如对框架代码不熟悉者可略过
网络结构:
在Hierarchical Softmax添加了全连接矩阵,每次需要做二分类(logistics regression)的时候就把相应列中的参数抠出来组成一个小矩阵乘以输入的x向量即可,相应的bias偏置项也一样,梯度回传的时候则根据每个二分类的loss分别回传,记得对应之前抠出来的列向量。
训练指标
如何评估模型是否符合预期地正在训练呢?最大似然,如果模型朝着正确的方向收敛,相应的单词出现时其预测概率会增大。所以,通过累加每个单词的最大似然预测求均值可以看出模型是否如预期一样收敛。贴一个自己训练过程中的最大似然(取反)
评估指标
原文中作者主要是模型学到的向量所包含的线性代数关系作为评估指标的,即判断类似
这种关系的准确率,我查了不少资料,发现很少单独针对词向量的模型,更多的是把训练出来的词向量放到实际应用场景中看是否有所提升,如语音识别,机器翻译等。
实际效果。用了wiki的数据进行训练:
word2vec适合的情况就是对于一个序列的数据,在序列局部数据间存在着很强的关联。word2vec中两个词的相似度可以直接通过余弦来衡量,巧妙的地方就是如何定义doc和word。
有一个个性化推荐的场景,给当前用户推荐他可能关注的『大V』。对一个新用户,此题基本无解,如果在已知用户关注了几个『大V』之后,相当于知道了当前用户的一些关注偏好,根据此偏好给他推荐和他关注过大V相似的大V,就是一个很不错的推荐策略。所以,如果可以求出来任何两个V用户的相似度,上面问题就可以基本得到解决。
我们知道word2vec中两个词的相似度可以直接通过余弦来衡量,接下来就是如何将每个V用户变为一个词向量的问题了。巧妙的地方就是如何定义doc和word,针对上面问题,可以将doc和word定义为:
由于用户量很大(大约4亿),可以将关注word个数少的doc删掉,因为本身大V的种类是十万级别(如果我没记错的话), 选择可以覆盖绝大多数大V的文章数量就足够了。
KDD上有一篇DeepWalk的文章,在社交网络上进行随机游走生成一组组节点的序列,然后通过word2vec训练每个节点对应的向量。但是我用这个方法在qq的社交网络上面做了一些实验,发现效果非常不理想,可能和qq社交网络的复杂性有关。
在商品推荐的场景中,竞品推荐和搭配推荐的时候都有可能需要计算任何两个商品的相似度,根据浏览/收藏/下单/App下载等行为,可以将商品看做词,将每一个用户的一类行为序看做一个文档,通过word2vec将其训练为一个向量。
同样的,在计算广告中,根据用户的点击广告的点击序列,将每一个广告变为一个向量。变为向量后,用此向量可以生成特征融入到rank模型中。
这种相识性还可以用在,物品的推荐上,根据用户购买物品的顺序,把每个物品当成一个单词,相当于一门外语了,谁也看不懂而已,但里面放映了上下文的关系,这个是很重要的,也是我们一开头那种普通算法无法做到的,同时对一些热门的物品自然有降权的处理,非常的方便。word2vec自然规避了两大问题:词语的次序和热门词语的降权处理。
1. 在nlp的任务中,可以通过将词聚类后,生成一维新的特征来使用。在CRF实体识别的任务中,聚类结果类似词性,可以作为特征来使用。
在依存句法分析的任务中,哈工大ltp的nndepparser则是将词向量直接作为输入。
具体论文『A Fast and Accurate Dependency Parser using Neural Networks』
2. 作为其它如火如荼的cnn rnn rnn-lstm 系列的初始化输入特征
当我们将一个文档变成一个向量之后,如何根据余弦/欧氏距离快速得到其最相似的topk个文章,是工程实现上不得不考虑的问题。例如线上可以允许的时间是5ms以内,如果文章数量往往上万或者更多,O(n)的方式计算明显不可接受了。
如果文章更新的速度很慢,可以通过离线的方式一天或者几天计算一次,导入redis(或者别的)提供线上快速查询。 但是如果文章实时新增,并且大量流量来自新文章,这个问题就要好好考虑一下。
一般可以通过kd-tree、simhash、聚类等方式解决,选择不同的方式和具体的推荐场景、数据分布有关。
在A公司的多个页面中,电商公司B有他们的一个主页,专门介绍他们公司一些产品促销,抢购和发布会什么的。
公司A目前有很多用户的浏览数据,如用户u浏览了公司A的页面a1,a2,a3等。
把这些数据处理一下,整合成word2vec能处理的数据,如下
U1 a1,a2,a3……
U2 a2,a3,a5,……
U3 a1,a3,a6,……
其中u1,u2,u3表示不同的用户,后面的一串表示这些用户的浏览记录,如U1 a1,a2,a3表示用户u1先浏览了页面a1,再浏览a2,然后浏览了a3,……
这些数据还不符合word2vec的输入数据格式,把第一列去掉,变成下面的样子
a1,a2,a3……
a2,a3,a5,……
a1,a3,a6,……
这些数据就可以作为word2vec的输入数据了。
就把这些数据作为word2vec的训练数据,词向量维度为3,进行训练,完成后得到下面的输出
A1 (0.3,-0.5,0.1)
A2 (0.1,0.4,0.2)
A3 (-0.3,0.7,0.8)
……
An (0.7,-0.1,0.3)
就得到了每个页面的向量。
这些向量有啥意义呢?其实单个向量的意义不大,只是用这些向量可以计算一个东西——距离,这个距离是页面之间的距离,如页面a1和a2可以用欧式距离或者cos距离计算公式来计算一个距离,这个距离是有意义的,表示的是两个网页在用户浏览的过程中的相似程度(也可以认为是这两个页面的距离越近,被同一个人浏览的概率越大)。注意这个距离的绝对值本身也是没有意义的,但是这个距离的相对大小是有意义的,意思就是说,假设页面a1跟a2、a3、a4的距离分别是0.3、0.4、0.5,这0.3、0.4、0.5没啥意义,但是相对来说,页面a2与a1的相似程度就要比a3和a4要大。
那么这里就有玄机了,如果页面a1是电商公司B的主页,页面a2、a3、a4与a1的距离在所有页面里面是最小的,其他都比这三个距离要大,那么就可以认为同一个用户u浏览a1的同时,浏览a2、a3、a4的概率也比较大,那么反过来,一个用户经常浏览a2、a3、a4,那么浏览a1的概率是不是也比较大呢?从实验看来可以这么认为的。同时还可以得到一个推论,就是用户可能会喜欢a1这个页面对应的广告主的广告。
这个在实验中实际上也出现过的。这里模拟一个例子吧,如a1是匹克体育用品公司在媒体公司A上的官网,a2是湖人队比赛数据页,a3是热火队的灌水讨论区,a4是小牛队的球员讨论区。这个结果看起来是相当激动人心的。
根据这样的一个结果,就可以在广告主下单的那个页面上增加一个条件——经常浏览的相似页面推荐,功能就是——在广告主过来选条件的时候,可以选择那些经常浏览跟自己主页相似的页面的用户。举个例子就是,当匹克体育用品公司来下单的时候,页面上给它推荐了几个经常浏览页面的粉丝:湖人队比赛数据页,热火队的灌水讨论区,小牛队的球员讨论区。意思是说,目标人群中包括了经常浏览这三个页面的人。
这个功能上线后是获得过很多广告主的好评的。