Word2vector

Word2vector可以说是nlp的基石,但是其思想不仅仅局限在nlp,比如广告推荐中id的embedding也可以应用,本篇就来梳理一下w2v的思想。


1,基本思想

Word2vector(简称w2v),字面上理解就是“词”转化为“向量”,或者说,单词的向量的化表示,其实这里也不一定是单词,但是在nlp应用比较广泛。

其主要的方法有,基于统计的方法、基于Skip-gram、CBOW的embedding方法

2,counting

W2v最简单的方法和是counting编码的方式。比如语料库有三句话:
“I like deep learning” “I like NLP” “I enjoy flying”
利用counting编码,我们可以用如下方式表示:
比如“I”,对所对应的所有预料进行统计:I like enjoy deep learning NLP flying 分别为0 2 1 0 0 0 0,那么I的向量表示就完成了。其中2表示I与like共同出现2次。

缺陷
1)是词语数量较大时,向量维度高且稀疏,向量矩阵巨大而难以存储
2)是向量并不包含单词的语义内容,只是基于数量统计
3)是当有新的词加入语料库后,整个向量矩阵需要更新

3,Skip-gram(Continuous Skip-gram Model)

Skip-gram的思想与自编码神经网络有些类似。首先将所有词语进行one-hot编码,输入只有一个隐藏层的神经网络,定义好loss后进行训练。训练完成后,我们就可以用隐藏层的权重来作为词的向量表示,完成向量压缩。

接下来我们分析,skip-gram算法是如何构造的。

3.1 数据处理

假设我们的语料库中只有一句话:The quick brown fox jumps over the lazy dog. 这句话*有8个词,skip gram算法是怎么为这8个词生成词向量的呢?n-grim方法!如下图所示:
Word2vector
我们以词为单位扫描这句话,每扫描到一个词,都把该词左右各两个词共4个词拿出来,分别与被扫描的单词组成单词对,作为我们的训练数据。这里我们设置窗口大小为2,对于每个词,与其前后各两个单词组成一个数据组,生成训练数据,一般,我们程序的实现过程中窗口大小设置为经验值5。

很明显,这里描述的是一种上下文关系,用上下文表示当前词。用上下文信息作为输出,单词作为输如,形成一个类似于自编码的网络,这里就完成了训练数据的基本表达。

数据关系表达完成以后,必然要进行数字化表示,才能输入网络。方法很简单one-hot编码,对每个单词进行0ne-hot编码,就可以输入模型训练了,比如(the,quick)单词对([1,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0])

3.2 网络结构

数据关系与数据表示我们处理完了,接下来就是构造网络。
Word2vector
观察这个网络结构,我们可以发现,它的隐藏层并没有**函数,但是输出层却用了softmax,这是为了保证输出的向量是一个概率分布。

对于我们前面举的例子,这个网络的输入和输出时一个8维的向量,对于隐藏层(就是我们要把数据压缩成的样子),假设我们设置为3,隐藏层的权重就可以用一个8行3列的矩阵来表示,这个8行3列的矩阵的第一行,就是三个隐藏神经元对应于输入向量第一维的权重。
Word2vector
网络训练完成以后,这个8行3列的矩阵的每一行就是一个单词的词向量!如下图所示,让我们回忆一下,我们的语料库一共有8个词,这里,每个词就用一行向量表示。

下面这幅图(很多地方都有),更加直观一些:
Word2vector
我们费尽心思得到的就是这个稠密矩阵,因为单词的表示是one-hot编码,这里相当于是在查表。

我们说counting方法不能包含语义信息,而skip-gram可以。因为相似的单词,其上下文也是相近的,甚至是相同的,所以相近的单词,其向量表示也很相似。

3.3 Skip-gram的改进

但是目前的skip-gram网络仍然有一个很大的缺点:skip-gram算法构造的神经网络神经元太多了,导致权重矩阵非常大。 假设我们的语料库中的词有10000个,生成的词向量为300维。那么权重系数就有100003002那么多,训练如此巨大的网络难度很大。

1)将词组和短语看作是独立的单词
有些短语和句子是固定组合的,拆开来看意义并不大,对于这类预料,组合在一起处理要好很多。

2)对高频词进行抽样
高频词汇,比如介词,几乎遍布每个句子,这种词的编码表示没有多大意义,因为它太普遍了。删除这些高频词,减小语料库,减少训练量。如何衡量一个词的概率,公式如下

P(wi)=(z(wi)0.001+1)0.001z(wi)P(w_i)=(\sqrt{\frac{z(w_i)}{0.001}+1})\frac{0.001}{z(w_i)}

P(wi)是保留单词wi的概率,z(wi)是该词在整个语料库出现的比例。

3)负抽样
负抽样技术主要解决的问题就是模型难以训练。所以,我们先来看看模型为什么难以训练。使用SGD训练神经网络的过程就是取出一条样本数据,然后据此去调整神经网络的所有权重,以便网络能够对这条数据的预测更加准确一些。这里的任务是很重的,网络所有的权重有多少,数以亿计。

负抽样解决这一问题的办法就是使得对每一条样本的每一次训练,只更新很小一部分的权重,而不是全都更新。前面的例子中我们知道,网络输出数据是one-hot编码后的数据,一个为1,其他均为0,对于一个语料库,那可是成千上万个0!所谓负抽样,就是从这些应当为0的维度中随机抽取几个,只更新这几个维度对应的神经元的权重,这既是负抽样的确切含义。当然,同时还要加上输出应当为1的维度所对应的神经元。

具体负抽样时抽几个维度的神经元,取决于具体的问题,google的论文中建议是5到20个。让我们用一个例子来具体感受一下。假设我们负抽样的维度数为5,我们的词表中有10000个单词,词向量的维度为300,也即是隐藏层有300个神经元。那么,在输出层,权重矩阵的大小将是30010000。现在我们抽取了5个负的维度(输出应当为0的维度),加上输出为1的维度,只更新这6个维度所对应的神经元。那么需要更新的权重系数是3006=1800个。这只占输出层中所有权重系数的0.06%!!

那么问题来了,负抽样,到底应该抽取哪一些呢?随机吗?当然不是,出现次数越多越容易抽到

P(wi)=f(wi)3/4j=0n(f(wj)3/4)P(w_i)=\frac{f(w_i)^{3/4}}{\sum_{j=0}^{n}(f(w_j)^{3/4})}

P(w_i)就是w_i这个单词被负抽样抽中的概率。
f(w_i)即是w_i在语料库中出现的次数。

4,CBOW(Continuous Bag-of-Words Model)

skip-gram是当前单词预测上下文,CBOW是上下文来预测当前单词。
Word2vector

CBOW的具体网络模型如下所示:
Word2vector
可能看着比较奇怪,我们一点点分解开来讲。

举个例子:
假设 Courpus = { I drik coffee everyday } ,根据 “I”“drink”“everyday”来预测“coffee”。
1)对单词进行编码,上下文信息作为输入,单词本身作为输出
Word2vector
2)初始化隐藏层模型参数,通过加和平均的方式得到隐藏层的数据表示。

Word2vector

Word2vector

3)初始化输出层模型参数,结果传到到输出层
Word2vector

为了加速训练,输出层并不是softmax,而是一个二叉树结构的哈夫曼树(Huffman Tree),其实是一种层次softmax,如下图所示,树的核心概念是 出现概率越高的符号使用较短的编码(层次越浅),出现概率低的符号则使用较长的编码(层次越深)。在一个语料库中比如‘I’出现的频率很高,放在浅层,依次排列。

Word2vector

5,总结

1)w2v就是单词的向量表示
2)CBOW与skip-gram是两种w2v思路,主要区别在于上下文与单词本身输入输出关系。
3)w2v类似于自动编码
4)w2v处理的语料库比较大,参数众多,一般通过负采样与哈夫曼树简化计算,这两种方法,都是在输出端进行的。
5)Python可以用gensim库实现w2v

参考文献:
http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/
https://www.jianshu.com/p/1405932293ea
https://www.jianshu.com/p/d8bfaae28fa9
https://blog.csdn.net/github_36235341/article/details/78607323