在自然语言处理领域中,本文向量化是文本表示的一种重要方式。在当前阶段,对文本的大部分研究都是通过词向量化实现的,但同时也有一部分研究将句子作为文本处理的基本单元,也就是doc2vec和str2vec技术。
大家很熟悉的词袋(bag of words)模型是最早的以词语为基本处理单元的文本向量化算法,所谓的词袋模型就是借助于词典把文本转化为一组向量,下面是两个简单的文本示例:
现假设词典如下:
{"john":1,"likes":2,"to":3,"watch":4, "movies":5,"also":6,"football":7,"games":8,"mary":9 "too":10}
在这个自己构建的词典中,每个单词都有一个唯一的索引,那么上述的两个文本就可以基于这个暂时的词典来构建其文本的向量表示,如下:
[1,2,1,1,1,0,0,0,0,1,1]
[1,1,1,1,0,1,1,1,0,0]
由此可以看出此向量的构建是根据该词在词典出现的次数而构成的,比如第一条文本中的”likes”,这个词在文本中出现了2次,所以基于词袋的文本向量是根据词出现的饿次数构建的。但是此向量与文本中单词出现的顺序没有关系,只是一种频率的表示,该方法容易实现,但是有很大的问题:
例如:关于数据稀疏的问题
自然语言处理经常把字词转为离散的单独的符号,也就是One-Hot Encoder。
杭州 [0,0,0,0,0,0,0,1,0,……,0,0,0,0,0,0,0]
上海 [0,0,0,0,1,0,0,0,0,……,0,0,0,0,0,0,0]
宁波 [0,0,0,1,0,0,0,0,0,……,0,0,0,0,0,0,0]
北京 [0,0,0,0,0,0,0,0,0,……,1,0,0,0,0,0,0]
比如上面的这个例子,在语料库中,杭州、上海、宁波、北京各对应一个向量,向量中只有一个值为1,其余都为0。但是使用One-Hot Encoder有以下问题。一方面,城市编码是随机的,向量之间相互独立,看不出城市之间可能存在的关联关系。其次,向量维度的大小取决于语料库中字词的多少。如果将世界所有城市名称对应的向量合为一个矩阵的话,那这个矩阵过于稀疏,并且会造成维度灾难。
现在随着互联网的发展,大量的无标注数据产生,此时的word2vec技术即是利用神经网络从大量的无标注的文本中提取有用的信息而产生的。
为什么说word2vec能提取有用的信息呢?
我们知道词语是表达语义的基本单元,而词袋模型只是简单的将词语符号化,举个不太恰当的比喻就是:现在有”一麻袋”的词语,而我们要处理的文本就像是从一个麻袋中无序得(不分先后顺序)抽出麻袋中所有的词,再查看文本中出现的个数,注意这里的从麻袋中抽取词的过程是无序的,也就是只是简单的统计文本中有没有出现该词和该词出现了几次,所以对于词袋模型,文本的语序特征就丧失了,也就丧失了语义的信息。
此时我们需要一个模型就是能在使文本向量化的同时也保留了词序的信息。分布式假说的提出就是解决了语义信息的问题。该方法的思想是:上下文相似的词,其语义也相似,随后就有了基于上下文分布表示词义的方法,这就是“词空间模型“。Word2Vec可以将One-Hot Encoder转化为低维度的连续值,也就是稠密向量,并且其中意思相近的词将被映射到向量空间中相近的位置。而使用神经网络可以灵活的对上下文进行建模,也因此成为用的比较多的方法。
one-hot向量作为word2vec的输入,通过word2vec训练低维词向量(word embedding)
输入层:One-Hot Vector
隐藏层:没有激活函数,也就是线性的单元。
输出层:维度跟输入层的维度一样,用的是Softmax回归。
我们要获取的dense vector其实就是Hidden Layer的输出单元。有的地方定为Input Layer和Hidden Layer之间的权重,其实说的是一回事。
可以看出:
输入层:5个神经元
隐藏层:3个神经元
所以权重矩阵是5x3的大小,可以看出权重矩阵中的[10,12,19]和前向传播后[10,12,19]是一样的。
word2vec主要分为CBOW(Continuous Bag of Words)和Skip-Gram两种模式。CBOW是从原始语句推测目标字词;而Skip-Gram正好相反,是从目标字词推测出原始语句。CBOW对小型数据库比较合适,而Skip-Gram在大型语料中表现更好。
所以,需要定义loss function(一般为交叉熵代价函数),采用梯度下降算法更新W和 W′ W ′ 。训练完毕后,输入层的每个单词与矩阵W相乘得到的向量的就是我们想要的词向量(word embedding),这个矩阵(所有单词的word embedding)也叫做look up table(其实这个look up table就是矩阵W自身),也就是说,任何一个单词的onehot乘以这个矩阵都将得到自己的词向量。有了look up table就可以免去训练过程直接查表得到单词的词向量了。
从直观上理解,Skip-Gram是给定input word来预测上下文。
接下来我们来看看如何训练我们的神经网络。假如我们有一个句子“The dog barked at the mailman”。
首先我们选句子中间的一个词作为我们的输入词,例如我们选取“dog”作为input word;
有了input word以后,我们再定义一个叫做skip_window的参数,它代表着我们从当前input word的一侧(左边或右边)选取词的数量。如果我们设置skip_window=2,那么我们最终获得窗口中的词(包括input word在内)就是[‘The’, ‘dog’,’barked’, ‘at’]。skip_window=2代表着选取左input word左侧2个词和右侧2个词进入我们的窗口,所以整个窗口大小span=2x2=4。另一个参数叫num_skips,它代表着我们从整个窗口中选取多少个不同的词作为我们的output word,当skip_window=2,num_skips=2时,我们将会得到两组 (input word, output word) 形式的训练数据,即 (‘dog’, ‘barked’),(‘dog’, ‘the’)。
神经网络基于这些训练数据将会输出一个概率分布,这个概率代表着我们的词典中的每个词是output word的可能性。这句话有点绕,我们来看个栗子。第二步中我们在设置skip_window和num_skips=2的情况下获得了两组训练数据。假如我们先拿一组数据 (‘dog’, ‘barked’) 来训练神经网络,那么模型通过学习这个训练样本,会告诉我们词汇表中每个单词是“barked”的概率大小。
模型的输出概率代表着到我们词典中每个词有多大可能性跟input word同时出现。举个栗子,如果我们向神经网络模型中输入一个单词“中国“,那么最终模型的输出概率中,像“英国”, ”俄罗斯“这种相关词的概率将远高于像”苹果“,”蝈蝈“非相关词的概率。因为”英国“,”俄罗斯“在文本中更大可能在”中国“的窗口中出现。我们将通过给神经网络输入文本中成对的单词来训练它完成上面所说的概率计算。
面的图中给出了一些我们的训练样本的例子。我们选定句子“The quick brown fox jumps over lazy dog”,设定我们的窗口大小为2(window_size=2),也就是说我们仅选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。Training Samples(输入, 输出)
我们的模型将会从每对单词出现的次数中习得统计结果。例如,我们的神经网络可能会得到更多类似(“中国“,”英国“)这样的训练样本对,而对于(”英国“,”蝈蝈“)这样的组合却看到的很少。因此,当我们的模型完成训练后,给定一个单词”中国“作为输入,输出的结果中”英国“或者”俄罗斯“要比”蝈蝈“被赋予更高的概率。
再次提醒,最终我们需要的是训练出来的权重矩阵。
此时注意到,这个训练过程的参数规模非常巨大。
假设语料库中有30000个不同的单词,hidden layer取128,word2vec两个权值矩阵维度都是[30000,128],在使用SGD对庞大的神经网络进行学习时,将是十分缓慢的。而且,你需要大量的训练数据来调整许多权重,避免过度拟合。数以百万计的重量数十亿倍的训练样本意味着训练这个模型将是一个野兽。
一般来说,有两种加速算法:Hierarchical Softmax、Negative Sampling等方式来解决。
参考:
https://blog.csdn.net/mylove0414/article/details/61616617
https://blog.csdn.net/free356/article/details/79445895