在之前的连载中我们用代码实现了word2vec,但并没有详细讲解CBOW的原理,本文手把手带你走入CBOW的算法原理。
word2vec是一种将word转为向量的方法,其包含两种算法,分别是skip-gram和CBOW,它们的最大区别是skip-gram是通过中心词去预测中心词周围的词,而CBOW是通过周围的词去预测中心词。
这个word2vec的方法是在的论文《Efficient Estimation of Word Representations in Vector Space》中提出的,作者来自google,文章下载链接:/pdf/1301.3781.pdf
文章提出了这两种方法如下图所示:
你现在看这张图可能一头雾水,不知所措,没关系,我们慢慢来学习。
在处理自然语言时,通常将词语或者字做向量化,例如独热编码,例如我们有一句话为:“关注数据科学杂谈公众号”,我们分词后对其进行独热编码,结果可以是:
“关注”: 【1,0,0,0】
“数据科学”:【0,1,0,0】
“杂谈”: 【0,0,1,0】
“公众号”: 【0,0,0,1】
这样,我们就可以将每个词用一个向量表示了。
但是独热编码在大量数据的情况下会出现维度灾难,通过观察我们可以知道上面的独热编码例子中,如果不同的词语不是4个而是n个,则独热编码的向量维度为1*n,也就是说,任何一个词的独热编码中,有一位为1,其他n-1位为0,这会导致数据非常稀疏(0特别多,1很少),存储开销也很大(n很大的情况下)。
那有什么办法可以解决这个问题呢?
于是,分布式表示被提出来了。什么是分布式表示?
它的思路是通过训练,将每个词都映射到一个较短的词向量上来。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。现在很常见的例如300维。
例如下面图展示了四个不同的单词,可以用一个可变化的维度长度表示(图中只画出了前4维),其实可以是多少维由你指定。假设为4维。
大家如果细心,会发现在展示的这些维度中的数字已经不是1和0了,而是一些其他的浮点数。
这种将高维度的词表示转换为低维度的词表示的方法,我们称之为词嵌入(word embedding)。
上图是将一个3维词向量表示转为2维词向量表示。
有个有意思的发现是,当我们使用词嵌入后,词之间可以存在一些关系,例如:
king的词向量减去man的词向量,再加上woman的词向量会等于queen的词向量!
出现这种神奇现象的原因是,我们使用的分布式表示的词向量包含有词语上下文信息。
怎么理解上下文信息呢?
其实很简单,我们在上学时,做阅读理解经常会提到联系上下文,所谓的上下文信息无非是当前内容在文本中前后的其他内容信息。
如下图所示,learning这个词的上下文信息可以是它左右两边的content标记的内容。
试想一下,如果这里的learning换成studying,是不是这句话仍然很合适呢?毕竟这两个单词都是学习的意思。
再转换一下思维,由于在当前上下文信息中,learning和studying都可以出现,是不是learning和studying是近义词了呢?没错,在当前的CBOW下确实是这样,甚至man和woman,cat和dog都可能是近义词。
所以大家是否理解了呢?
其实就是拥有相似或者相同的上下文的多个词可能是近义词或者同义词。
这里慢慢将CBOW的算法思想透露出来了,因为CBOW就是通过当前中心词的上下文单词信息预测当前中心词。
此时再来看CBOW这张示意图,是不是有点感觉了?
接下来进入具体的算法模型部分!
首先我们需要训练CBOW模型,该模型的结构如下图:
这张图略微复杂,我们需要从最左边开始看,最左边的一列是当前词的上下文词语,例如当前词的前两个词和后两个词,一共4个上下文词。
这些上下文词即为图中的x1k、x2k。。。xck。
这些词是独热编码表示,维度为1*V(虽然图上画得像列向量V*1,这图画的容易理解错误,其中V为词空间的大小,也就是有多少个不同的词,则独热编码的维度为多少,也就是V个不同的词)。
然后刚说的每个上下文的词向量都需要乘以一个共享的矩阵W,由于整个模型是一个神经网络结构,我们将这个存在于输入层和隐藏层之间的矩阵称为W1,该矩阵的维度为V*N,其中V如前所述,N为我们自己定义的一个维度。
学过线性代数的矩阵乘法我们知道,这里的独热编码向量1*V乘上维度为V*N的矩阵W1,结果是1*N的向量。
这里因为一个中心词会有多个上下文词,而每个上下文词都会计算得到一个1*N向量,我们将这些上下文词的1*N向量相加取平均,得到中间层(隐藏层)的向量,这个向量也为1*N,之后,这个向量需要乘以一个N*V的矩阵W2,最终得到的输出层维度为1*V。
然后将1*V的向量softmax归一化处理得到新的1*V向量,在V个取值中概率值最大的数字对应的位置所表示的词就是预测结果。如果对softmax的概念陌生,可以搜索学习一下。
而这个输出的结果1*V就是预测出的中心词的分布式表示。
别急,我们只是讲通了这个CBOW模型的前向计算过程。
我们接下来说说模型的训练过程。
(1)当前词的上下文词语的独热编码输入到输入层。
(2)这些词分别乘以同一个矩阵W1后分别得到各自的1*N向量。
(3)将这些1*N向量取平均为一个1*N向量。
(4)将这个1*N向量乘矩阵W2,变成一个1*V向量。
(5)将1*V向量softmax归一化后输出取每个词的概率向量1*V。
(6)将概率值最大的数对应的词作为预测词。
(7)将预测的结果1*V向量和真实标签1*V向量(真实标签中的V个值中有一个是1,其他是0)计算误差,一般是交叉熵。
(8)在每次前向传播之后反向传播误差,不断调整W1和W2矩阵的值。
预测的时候,做一次前向传播即可得到预测的中心词结果。
你可能会想,word2vec不是要将词转为分布式表示的词嵌入么?怎么变成预测中心词了?
这个问题问得好!其实我们在做CBOW时,最终要的是W1这个V*N矩阵,想想这是为什么呢?
因为我们是要将词转换为分布式表示的词嵌入,我们先将词进行独热编码,每个词的向量表示是1*V的,经过乘以W1后,根据矩阵乘法的理解,假设1*V向量中第n位是1,其他是0,则矩阵乘法结果是得到了W1矩阵中的第n行结果,也就是将词表示为了一个1*N的向量,一般N远小于V,这也就将长度为V的独热编码稀疏词向量表示转为了稠密的长度为N的词向量表示。
如果还没啥感觉,看下面这张图帮助你理解:
所以,当我们下次要查某个词的词向量时,只需要和矩阵W1相乘就能得到结果。常用的词向量长度有300,大家想想300是不是远小于我们词表里所有不重复词的数量呢?
扫码下图关注我们不会让你失望!