Word2vec简单整理

参考: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

    把中间的词单独拎出来,让周围的词去预测这个词的概率。

    1. 每个单词先映射到公用词表中(大矩阵)一列对应的向量
    2. 把向量相加
    3. 把这个向量经过一个softmax得出预测相应单词的概率。
  • 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。

文本序列分析

典型的就是文本的序列了,邻近的词之间关联很强,甚至可以通过一个词的上下文大概预测出中间那个词是什么。学习到的词向量代表了词的语义,可以用来做分类、聚类、也可以做词的相似度计算。此外,Word2vec本身的层次分类器或者采样方式实际上对热门item做了很大的惩罚,所以不会像一般的矩阵分解一样,最后算出来语义接近的都是热门词,这也是word2vec很好的一个特性。
对于短文本分类,直接把文档里面所有的word对应的向量线性相加,作为文本的特征训练分类器,效果也很不错。这种方式其实在word2vec的训练过程中也有使用。另外如果换成非线性分类器,比如rbf kernel SVM,分类准确度还能再高,这个也是符合预期的。

  1. 找相关词,注意是相关词而不是同义词。例如你输入”雷军”,计算出来的相关词就会有:手机,小米,乔布斯等等。
  2. 根据上下文预测句子中缺失的单词
  3. 根据不同语种里相同单词的词向量之间的特定关系做机器翻译。

app分发平台

我非常满意的一个应用是把word2vec应用在用户app下载序列上,根据用户下载app的顺序,把app看做单词,也是可以形成这样的序列数据,进而训练处每个app对应的向量。利用这个向量计算app之间的相似度,效果非常好,能够把真正内容相关的app聚合在一起,同事规避热门app的影响。

相关词挖掘  

用在品牌词和品牌相似词挖掘中品牌词的挖掘也特别有意思。背景是淘宝打击盗版,普通商家不敢在淘宝商品详情里写品牌名。他们会进行品牌词的变种,比如Levi's会变成李家、L家,tommy 会变成汤米、汤家、T家等等来躲避打击。 我先进行文本的 统计分词, 然后用word2vec 进行模型训练、最终把这些盗版品牌找出来了。 

在社交网络中的推荐

有一个个性化推荐的场景,给当前用户推荐他可能关注的『大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、聚类等方式解决,选择不同的方式和具体的推荐场景、数据分布有关。

序列点击数据的分析

感觉自己是第一个把word2vec应用在电商上的,哈哈。现在感觉很多应用都在使用word2vec。介绍一下自己的工程实践尝试。
1、序列数据不能过长,过长会导致偏移现象,训练的词向量会变差。其实就是用户注意力的问题,用时髦的话说就是attention。现在attention在lstm里混得风生水起。我当时拿到用户log后,先根据session进行数据切割,如果一个session过长,我会进行限制,只允许最大一个长度。尽量保证一个序列的主题(attention)基本一致。这一块还有很多可以优化,session的切分好像也有相关算法。数据没清洗干净,后期也很恼火。
2、淘宝的数据特别多,用户点击数据量特别大,并且商品数也接近10亿,真是让人又爱又恨。10亿的商品如果每个都算200维的话,内存根本撑不住。淘宝的商品id特别长,占用内存多,最开始我对商品进行了重排序节约内存。但是这点优化远远不够,就开始修改源代码,只保存整数,后来发现还是不行,就切换到当时有个很初级的ps版本word2vec(我和xlab的一个同事开发),吭哧吭哧的跑了很久完成。不过效果不如原始的word2vec,有点心灰意冷,后面有其他任务,这方面老大也不是特别上心,没有支持,就停止了这方面的尝试。感觉很是遗憾。

(1) 推理、问题回答(答案基本上是一个单词或者一个短语。)
(2) Pos Tagging / NER (TensorFlow 官网)
(3) 搜索的自动提示
(4) 各种NN的输入,cnn rnn 。。。
(5) item推荐
知乎上面一个腾讯员工的回答: app 推荐
思路:
将用户下载的app当成一个word序列,然后自动推测下一个app/word 是什么
小结: 这一点思想上跟贝叶斯有点类似, 但是有一个序列/上下文的关系在里面。


扩展

word2vec提供了一种计算关联的新思路,通过转化为term向量的方式,可以计算出任何term之间的关联度,同时这种关联能够在一个很快速的时间内被计算出来,这在实际应用中就有很大的价值。上述的term还可以被替换为其他任何类型的item,比如book2vec、movie2vec、query2vec等等,只要你有足够的上下文语料去做训练,而现实中这种语料是非常多的,比如一个用户看过/买过/评价过的book、movie,用户搜索过的query。


A公司的多个页面中,电商公司B有他们的一个主页,专门介绍他们公司一些产品促销,抢购和发布会什么的。


公司A目前有很多用户的浏览数据,如用户u浏览了公司A的页面a1a2a3等。


把这些数据处理一下,整合成word2vec能处理的数据,如下

U1 a1,a2,a3……

U2 a2,a3,a5,……

U3 a1,a3,a6,……

其中u1u2u3表示不同的用户,后面的一串表示这些用户的浏览记录,如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)

就得到了每个页面的向量。


这些向量有啥意义呢?其实单个向量的意义不大,只是用这些向量可以计算一个东西——距离,这个距离是页面之间的距离,如页面a1a2可以用欧式距离或者cos距离计算公式来计算一个距离,这个距离是有意义的,表示的是两个网页在用户浏览的过程中的相似程度(也可以认为是这两个页面的距离越近,被同一个人浏览的概率越大)。注意这个距离的绝对值本身也是没有意义的,但是这个距离的相对大小是有意义的,意思就是说,假设页面a1a2a3a4的距离分别是0.30.40.5,这0.30.40.5没啥意义,但是相对来说,页面a2a1的相似程度就要比a3a4要大。


那么这里就有玄机了,如果页面a1是电商公司B的主页,页面a2a3a4a1的距离在所有页面里面是最小的,其他都比这三个距离要大,那么就可以认为同一个用户u浏览a1的同时,浏览a2a3a4的概率也比较大,那么反过来,一个用户经常浏览a2a3a4,那么浏览a1的概率是不是也比较大呢?从实验看来可以这么认为的。同时还可以得到一个推论,就是用户可能会喜欢a1这个页面对应的广告主的广告。


这个在实验中实际上也出现过的。这里模拟一个例子吧,如a1是匹克体育用品公司在媒体公司A上的官网,a2是湖人队比赛数据页,a3是热火队的灌水讨论区,a4是小牛队的球员讨论区。这个结果看起来是相当激动人心的。


根据这样的一个结果,就可以在广告主下单的那个页面上增加一个条件——经常浏览的相似页面推荐,功能就是——在广告主过来选条件的时候,可以选择那些经常浏览跟自己主页相似的页面的用户。举个例子就是,当匹克体育用品公司来下单的时候,页面上给它推荐了几个经常浏览页面的粉丝:湖人队比赛数据页,热火队的灌水讨论区,小牛队的球员讨论区。意思是说,目标人群中包括了经常浏览这三个页面的人。

这个功能上线后是获得过很多广告主的好评的。








阅读更多
个人分类: 神经网络
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭