Skip to content

Latest commit

 

History

History
210 lines (104 loc) · 23.4 KB

NLP面试03:word2vec.md

File metadata and controls

210 lines (104 loc) · 23.4 KB

考点!考点!考点!

如果你准备NLP面试,skip-gram模型的目标函数真的是哪怕死记硬背也得记下来!程序员onsite白板算法不意外,对于算法岗,白板这个目标函数也是不意外的。

img

看着上面的公示,正好脑补一个skip-gram的训练过程,问下自己这些字母符号都是什么含义:假设训练文本有T个词,第一个求和就像一个指针从第一个词一直指到最后一个词,每个被指到的词都是就是当前词(或称中心词);再看第二个求和符合,c是一个固定的参数,代表中心指针向前或向后能取到多少个词,换句话说,c划定了上下文的范围。那么把前两个求和符合连在一起,就有了一个浮窗(window)从第一个词一直滑到最后一个词,浮窗正中间自然是当前词,前后各c个词就是其对应的上下文;回想一下上一篇文章提到的skip-gram是怎样训练的,通过中心词来预测它的上下文中的词,那么很显然求和符号后面就是这个预测概率。整个目标函数就是为了最大化log预测概率的平均值。

标配版的skip-gram用softmax函数来计算该预测概率:

img

此词典非彼词典

那么问题来了,上方公示中的字母W代表什么?

W是整个词典的大小。简单解释下word2vec(也是整个词嵌入)中词典的意思:词嵌入训练的目的是为了给词分配向量,到底是给哪些词分配呢?肯定不会是所有的词,因为你的训练文本里可能根本没见到某些词(如何计算没见过的词的词向量是个有趣的问题,这个坑我很快会在FastText的文章里填一部分),还有些词即使见到,它们出现的次数也不多,或者有些词你根本不关心。词典的目的就是用来规定到底关心哪些词,最终要算出哪些词的词向量。

说到这里突然忍不住题外话一段。。。(这里是假装读过代码环节)。。。你在用Gensim等训练word2vec时,会发现其实程序跑了两遍,第一遍非常迅速,因为它啥都没训练(dry run),这一遍的目的就是为了建立词典,通常词典的建立受一些参数影响,如词典大小的上限,词频下限等。如果你对自己的词典有更加明确的要求,其实可以跳过第一遍训练,并替换自己的词典。

Negative sampling

为什么要专门讲W?因为标配版的skip-gram在实际应用中并不实用,问题就出在字母W。我们已经知道了W是整个词典的大小,带入softmax公式中会发现每一次计算都要把整个词典过一遍,计算量非常大。因此才有了negative sampling,用以减少计算量的同时期望达到近似效果。

img

是的,这个公式也是考点。注意一点它替代的是第一个公式中的log预测概率,不单是预测概率,还有log。

如果negative sampling是skip-gram至尊版的话,就像所有汽车、或者显卡一样,标配版和至尊版之间还有各种高配版、精英版等等(如Hierarchical Softmax或Noise Contrastive Estimation),这里不展开讲,知乎有很多精彩的硬核文章。

简单来说,negative sampling是简化版的Noise Contrastive Estimation,NCE核心想法是一个好的模型能在噪声中分辨出正确的数据。因此可以看到上方公式有两部分组成:第一个部分代表“正确的数据”,即通过中心词预测正确上下文词的概率;第二部分是“噪声”,即通过中心词预测k个噪声词的概率,这个概率当然越小越好。因此可以看到一个负号。

那么对于negative sampling,核心问题自然是如何定义噪声,也就是上方公式的P(分布)是如何定义的。作者的答案是通过试验,发现噪声分布基于词频统计,再用指数函数3/4稍微调整一下效果就很好。并没有理论上的解释。

此处感性的解释是:我给你一个训练文本,指着其中一个词,很显然我们知道正确的上下文应当是什么(附近的词),那么错误的噪声呢?既然是噪声,意思就是也不能太明显不是,要有干扰的,也经常出现的。一个词在整篇文本中出现的频率越高,它出现在训练词周围的概率自然也相对较高(此处我们不考虑太多搭配、顺序,简单地将训练文本想成一个词袋),那它就是很好的干扰项,即噪声。

把短语一视同仁

Word2vec的论文前前后后读过5遍以上,每次都能有些新的理解或发现。之前对于短语的提取和训练部分,我一直把它当作一个文本预处理的小trick,直到最近一次读时才对这部分有了些新的思考。(其实本应该更早发现短语在这篇论文中的重要性,毕竟标题就有“短语”,abstract更是单独用了一段来讲。。。)

之前做学术研究时,“词嵌入训练”对我来说就像它的名字一样,我基本只关注“词”级别,文本来了简单预处理后就喂给模型训练了,因为大部分常用的直接评价词嵌入质量(intrinsic evaluation)的数据集都是基于词(word)的。而到了实际工业应用中,突然发现有太多短语、词组等需要关注。

获得短语的词嵌入并不难,其实就是修改训练前的词典部分(“此词典非彼词典”中已经提过)把短语添加进去。如果你完全清楚要关注的短语有哪些,直接全部加到词典就行了。因此文章实际讨论的问题是在不知道文本中有哪些短语的情况下,如何在文本中找出来。

img

作者给出的解决方案非常简单,就是基于词频的统计,找出那些在一起经常出现而在其他上下文中不常出现的词组。

该方法在文本上过第一次时可以找到两个词(word)组成的词组,在此基础上过第二遍就能找到三个词组成的词组,以此类推。

A+B=?

“国王相对于男人相当于女王相对于女人”的例子大家应该都不陌生(陌生的话可以出门左拐看我关于word2vec的另一篇文章),有了词组嵌入的加持(倒不一定必须有词组),作者提出了一个类似的想法:“中国+货币 约等于 人民币”、“中国+航空公司 约等于 中国南方航空”等等。作者通过也列举一些符合这种观察的例子,他给出的解释是:词向量可以被看作是其上下文分布的表征;a与b相加得到向量可以看作是一个feature,只有与a和b在相同上下文经常出现的词才会接近该feature。我个人总觉得作者是受了短语识别思路的影响。

我个人对该现象持保留意见,不过它至少可以说明,好的词嵌入空间,不仅关乎两个词之间的“距离”(词相似度)、也关乎词在整个空间的相对位置(“a+b=c+d” 和 “a+b=e”)。

如果word2vec模型架构师食材的话,上面提到的种种改进就是佐料。对于word2vec的成功,为什么佐料比食材还重要?因为一个简洁有效的模型,背后反而是更多的细节做支撑的。在应用时我们或许感觉不到,因为这些细节已经有了足够好的默认设置。但是仔细去探究这些默认设置、了解公式中每一个字母的含义后,会发现处处都有选择,最终这些好选择被匹配到一起并隐藏起来后才让一个模型真正实用、好用起来。

对于研究者,反而可以深究一下这些选择是不是最好的、为什么?比如噪声的分布,连托老师也只是说实验所得,那会不会有更好的噪声分布?上下文的选择会不会有更好的方法?词向量的维度多少最好?等等。word2vec的相关研究已经很多了,模型本身也老了,但是这种探求方法对于其他模型也是适用的。如果拿不出新食材、那就在佐料上下下功夫。

什么是word2vec

关于word2vec,首先需要弄清楚它并不是一个模型或者DL算法,而是描述从自然语言到词向量转换的技术。 Word2Vec本质上就是一个只有一个hidden layer的全连接神经网络,它可以将所有的词向量化,这样词与词就可以定量的去度量他们之间的关系.

在NLP中,把x看做一个句子里的一个词语,y是这个词语的上下文词语,那么这里$f$的便是nlp中经常出现的语言模型,这个模型的目的就是判断样本$(x,y)$是否符合自然语言语法规则. word2vec并不在乎$f$训练的有多完美,而是只关心模型训练结束后模型参数(这里就是神经网络的参数),并将这些参数作为输入x的向量化表示,也就是词向量. 后面会介绍为什么是这样.

词向量

自然语言处理系统通常将词汇作为离散的单一符号,例如"cat"一词也可以表示为ID666,而"dog"一词表示为ID888.但是我们可以发现这些符号编码毫无规律,无法提供不同词汇之间可能存在的关联信息.也就是"dog"和"cat"都是动物,四条腿啊..等等,但是通过我们的单一符号却根本无法体现出他们之间的关系.可见,将词汇表达为上述的独立离散符号将进一步导致数据稀疏,使我们在训练统计模型时不得不寻求更多的数据。而词汇的向量表示将克服上述的难题。 在word2vec出现之前,最简单常用的词向量技术就是one-hot encoder,它使用的词向量维度为整个词汇表大小,将所有的词按照特定顺序固定,对于每个词汇表中的单词,其对应位置为1其余为0.如图:

img

可以发现这种方法是很稀疏的,其次词汇表一般很大,这也导致我们向量的维度很大,带来内存灾难.于是就产生了连续向量表示,也就是用实数向量来表示一个单词啥的,比如100维,这就大大降低了维度,同时还能蕴含语义信息,词之间的相似度等等... 举个例子,给出一个文档,文档就是一个单词序列比如 “A B A C B F G”, 希望对文档中每个不同的单词都得到一个对应的向量(往往是低维向量)表示。比如,对于这样的“A B A C B F G”的一个序列,也许我们最后能得到:A对应的向量为[0.1 0.6 -0.5],B对应的向量为[-0.2 0.9 0.7] (此处的数值只用于示意),这个向量就是我们说的word embedding。 这里需要说明下word2vec和word embedding的区别和联系: word embedding是一个将词向量化的概念,而word2vec是谷歌提出的一种基于word embedding的工具或算法集合.

Word2Vec

word2vec是基于分布假设,认为上下文环境相同的词语其语义也相似.,主要的方法有两种:CBOW和skip-gram. Word2vec能将one-hot Encoder转化为低维度的连续值,也就是稠密向量,具有相似语义的单词的向量之间距离会比较小,部分词语之间的关系能够用向量的运算表示.

Word2Vec的优势

1.低维稠密 一般来说分布式词向量的维度设置成100-500就足够使用,而one-hot的词向量维度与词表的大小成正比,是一种高维稀疏的表示方法,这种表示方法导致其在计算上有比较低的效率.

img

2.蕴含语义信息 one-hot这种表示方式使得每一个词映射到高维空间都是相互正交的,也就是说one-hot向量空间中词与词之间没有任何联系,或者说我们无法通过词向量来判断出他们之间是否有联系.word2vec利用"分布假设,具有相同上下文信息的词语语义相近",使得语义相近的词映射在欧式空间后具有较高的余弦相似度.(至于为什么是余弦相似度,下面你了解一下网络结构就会发现本质是网络中存在向量间的內积)

img

Word2Vec的结构

上面我们提到过word2vec本质上是只有一个隐藏层的全连接神经网络,如图:

img

1.输入为One-Hot Encoder 2.隐藏层激活函数为线性的 3.输出层使用的是softmax

CBOW(Continuous Bag-Of-Words)

CBOW模型是通过中心词的上下文来预测该中心词. 那么这个模型的输入输出是如何得到的呢? CBOW模型的训练**输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量.**比如下面这段话,我们上下文大小为4,特定的这个词是"Learning",也就是我们需要的输出向量.那么上下文词应该有8个,前后4个,这8个词就是我们模型的输入. 由于CBOW使用的是词袋模型,因此这8个词都是平等的,也就是不考虑他们和我们关注的词之间的距离大小,只要在我们上下文之内即可 如图:

img

这样我们就得到了我们模型的输入输出,输入是8个上下文词向量,输出是所有词的softmax概率(我们希望我们的特定中心词的概率最大),对应的CBOW模型输入层有8个神经元,输出有词汇表大小个神经元,隐藏层个数我们自己指定.

CBOW的结构

假设这里我们将词embedding到n维,设词汇表维度为$|V|$ 1.输入层: $2c$个节点,上下文共 $2c$个词的n维向量,注意一开始为随机初始化 2.隐藏层: n个节点,上下文共$2c$个词的词向量相加后取平均值 3.隐藏层到输出层的权重$W_0$: 维度$|V| \times n$ 4.输出层:$|V|$个节点.采用softmax回归. 对于这里来说输入层的就是输入的词向量,我们看到,输入层每个周围词用n个节点表示,n就是词向量的大小,一开始随机初始化,在之后作为模型参数学习更新,最终得到合适的词向量。在gensim 和 google的 word2vec实现中,也是这样的结构,就是输入层初始化的时候直接为每个词随机生成一个n维的向量,并且把这个n维向量作为模型参数学习,最终得到该词向量。

另一种结构

1.输入层: $2c$个词的one-hot 编码,每个的维度为$|V|$ 输入层到隐藏层的权重$W_0$的维度: $|V|\times n$2.隐藏层: $n$个节点,上下围共$2c$个词的one-hot编码和$W_0$相乘得到的词向量求和再取平均值. 隐藏层到输入层的权重$W_1$的维度$|V| \times n$ 3.输出层$|V|$个节点,采用softmax.

img

然后词向量就是神经网络的权重,生成词向量的过程就是使用训练数据使模型变优的一个参数更新的过程. 某个词的one-hot编码可以看成是$|V| \times 1$的向量与$n \times |V|$的权重矩阵$W_0$相乘,因为one-hot中只有一个1,所以可以看成它只是激活了$W_0$中的特定列,那么该列就可以看成是该词的一个词向量.对于隐藏层到输出层的权重$W_1$同理,所以这里$W_0$和$W_1$我们分别叫做输入向量和输出向量,它们两个本质上没什么区别,我们一般使用输入向量.

img

skip-gram模型

skip-gram模型和CBOW的思路是相反的,CBOW是根据上下为来预测中心词,skip-gram模型是根据中心词来预测上下文.即输入是特定词,输出是softmax概率排前8的几个词,对应的skip-gram神经网络模型的输入层有1个神经元,输出层有词汇大小个神经元.隐藏层的个数还是我们自己指定. 例如: 句子为“the quick brown fox jumped over the lazy dog”(实际语料库中单词的数量会很非常大),当上下文窗口为1时,skip-gram的任务是从"quick"预测"the"和"brown",从"brown"预测"quick"和"fox",从"fox"预测...因此训练skip-gram的输入输出对(input,output)为:(quick,the),(quick,brown),(brown,quick),(brown,fox)... 在结构上和CBOW一样,也是两种角度,其中对应CBOW的第二种:

img

我们知道,Word2vec 本质上是一个语言模型,它的输出节点数是 V 个,对应了 V 个词语,也是一个多分类问题,但实际当中,词语的个数非常非常多,直接softmax来计算会给计算造成很大困难,所以需要用技巧来加速训练,下面就介绍word2vec对应的两个加速技巧hierarchical softmax和negative sampling。注意:这两个技巧只是加速训练的技巧

基于Hierarchical Softmax的模型概述

对于原始的模型来说,里面最大的问题在于从隐藏层到输出的softmax层的计算量很大,因为要计算所有词的softmax概率,再去找概率最大的值.为了避免要计算所有词的softmax概率,word2vec采用了哈夫曼树(关于什么是哈夫曼树,如何构建哈夫曼树,这里不做介绍)来代替从隐藏层到输出softmax层的映射.由于我们把之前所有都要计算的输出层softnax层的概率计算变成了一个二叉哈夫曼树,那么我们的softmax概率计算只需要沿着树形结构进行就可以了,如下图,我们可以沿着哈夫曼树从根节点一直走到我们的叶子节点的词$w_2$.

img

和之前的模型相比,我们的哈夫曼树的所有内部节点就类似之前隐藏层神经元,其中根节点的词向量对应我们的投影后的词向量,而所有叶子节点就类似于之前神经网络softmax输出层的神经元,叶子节点的个数就是词汇表的大小.在哈夫曼树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着哈夫曼树一步步完成的,因此这种softmax取名为"Hierarchical Softmax"。

具体过程

在word2vec中,采用二元逻辑回归的方法,即规定沿着左子树走就是负类(哈夫曼树编码为1),沿着右子树走为正类(哈夫曼树编码为0).判断正负类的方法是sigmoid的函数,即:

[公式]

其中$x_w$是当前内部节点的词向量,而$\theta$则是我们需要从训练样本求出的逻辑回归的模型参数。 由于是二叉树,所以计算量由原来的$|V|$变为了$log |V|$,而且使用哈夫曼树使得高频的词靠近词根,这样高频词需要更少的时间会被找到. 容易理解,被划分为左子树而成为负类的概率为$P(-)=1-p(+)p(-)=1-p(+)$。在某一个内部节点,要判断是沿左子树还是右子树走的标准就是看$p(-),p(+)p(-),p(+)$谁的概率值大。而控制$p(-),p(+)p(-),p(+)$谁的概率值大的因素一个是当前节点的词向量,另一个是当前节点的模型参数$\theta$。 对于上图中的$w_2$,如果它是一个训练样本的输出,那么我们期望对于里面的隐藏节点$n(w_2,1)$的$p(-)$概率大,$n(w_2,2)$的$p(-)$概率大,$n(w_2,3)$的$p(+)$概率大。 回到基于Hierarchical Softmax的word2vec本身,我们的目标就是找到合适的所有节点的词向量和所有内部节点$\theta$, 使训练样本达到最大似然。那么如何达到最大似然呢?

梯度计算

我们使用最大似然法来寻找所有节点的词向量和所有内部节点.先拿上面的例子来说,我们期望最大化下面的似然函数:

[公式]

对于所有的训练样本,我们期望最大化所有样本的似然函数乘积。

img

基于Hierarchical Softmax的CBOW模型

首先我们要定义词向量的维度大小$M$,以及CBOW的上下文大小$2c$,这样我们对于训练的每一个词,其前面的$c$个词和后面的$c$个词作为了CBOW模型的输入,该词本身作为了输出,期望softmax概率最大. 在做CBOW模型前,我们要先将词汇表建立成一颗哈夫曼树. 对于从输出层到隐藏层的投影,这一步比较简单,就是对$w$周围的$2c$个词向量求和平均即可,即:

[公式]

第二步根据梯度下降法来更新参数$\theta _{j-1} ^w$和$x_w$,注意这里$x_w$是由$2c$个词向量相加而成,我们做梯度更新完毕后会用梯度直接更新原始的各个$x_i$,即:

[公式]

[公式]

img

基于Hierarchical Softmax的skip-gram模型

这里包括上面不清楚的都可以参考刘建平的blog

基于Negative Sampling的模型

上面所说的层级softmax虽然可以将$O(|V|)$的softmax变为$log|V|$的二分类问题,但是当我们的中心词$w$是一个很生僻的词,那么就得在哈夫曼树中辛苦向下走很久了.

什么是负采样呢?

image-20201212131715766

梯度计算

image-20201212131744702

image-20201212131808387

image-20201212131836483

img

Negative Sampling 负采样方法

image-20201212131914886

image-20201212131928348

img

image-20201212131959078

刘建平

word2vec词向量性质观察

查看某个词在embedding里的最近邻居可以看到单词间的语义接近关系,将vector构成的空间降维,可以高效地查找最近单词,但降维过程中要保持邻居关系(用哪种降维方法?),比如我们可以把学习向量映射到2维中以便我们观察,其中用到的技术可以参考 t-SNE 降纬技术。当我们第一次发现这样的诱导向量空间中,展示了一些特定的语义关系,这是非常有趣的,比如文字中 male-female,gender 甚至还有 country-capital 的关系, 如下方的图所示

img

另外,我们能得到的不仅是单词的邻接关系,由于将单词向量化,可以对单词进行计算可以通过计算进行语义加减,语法加减。