什么是Word2Vec和Embeddings?
Word2Vec是从大量文本语料中以无监督的方式学习语义知识的一种模型,它被大量地用在自然语言处理(NLP)中。那么它是如何帮助我们做自然语言处理呢?Word2Vec其实就是通过学习文本来用词向量的方式表征词的语义信息,即通过一个嵌入空间使得语义上相似的单词在该空间内距离很近。Embedding其实就是一个映射,将单词从原先所属的空间映射到新的多维空间中,也就是把原先词所在空间嵌入到一个新的空间中去。
我们从直观角度上来理解一下,cat这个单词和kitten属于语义上很相近的词,而dog和kitten则不是那么相近,iphone这个单词和kitten的语义就差的更远了。通过对词汇表中单词进行这种数值表示方式的学习(也就是将单词转换为词向量),能够让我们基于这样的数值进行向量化的操作从而得到一些有趣的结论。比如说,如果我们对词向量kitten、cat以及dog执行这样的操作:kitten - cat + dog,那么最终得到的嵌入向量(embedded vector)将与puppy这个词向量十分相近。
模型介绍
skip-gram神经网络模型其最基本的形式实际上是惊人的简单; Word2Vec使用了一个你可能在机器学习中看到过的技巧。我们将训练一个带有单个隐藏层的简单的神经网络来完成某个任务,但是实际上我们并没有将这个神经网络用于我们训练的任务。相反,目标实际上只是为了学习隐藏层的权重 - 我们会看到这些权重实际上是我们试图学习的“单词向量”。
另一个你可能已经看到这个技巧的地方是无监督的特征学习,在这里你训练一个自动编码器来压缩隐藏层中的输入向量,并在输出层将其解压缩回原始数据。训练完成后,您将剥离输出图层(解压缩步骤),然后使用隐藏层 -这是学习良好图像特征而不标记训练数据的一种技巧。
假任务
我们要训练神经网络做以下事情。给定一个句子中的特定单词(输入单词),查看附近的单词并随机选择一个单词。网络将告诉我们,我们词汇表中每个单词是我们选择的“邻近单词”的概率。
当说“附近”时,算法实际上有一个“window size”参数。一个典型的窗口大小可能是5,即5个字在前,5个字在后(共10个)。
输出概率将和我们查找到字典单词在我们输入词附近的可能性有关。例如,如果你给训练有素的网络输入单词“苏联”,输出是比如“联盟”和“俄罗斯”的概率将比“西瓜”和“袋鼠”这样无关的单词高得多。
我们将通过在训练文档中找到单词对来训练神经网络。下面的例子显示了一些训练样本(单词对),这些单词对将从“The quick brown fox jumps over the lazy dog.”获取。这里采用了一个小的窗口大小2,。标为高亮蓝色的是我们的输入单词。
该神经网络会学习每一对单词出现频率的统计信息。例如,该神经网络获得的(““Soviet”, “Union”)训练样本数,远多于(“Soviet”, “Sasquatch”)。训练结束后,如果输入“苏联”一词作为输入,则输出““Union””或“Russia””的概率比“Sasquatch”的概率高得多。
模型细节
我们如何来表示这些单词呢?首先,我们都知道神经网络只能接受数值输入,我们不可能把一个单词字符串作为输入,因此我们得想个办法来表示这些单词。最常用的办法就是基于训练文档来构建我们自己的词汇表(vocabulary)再对单词进行one-hot编码。
假设从我们的训练文档中抽取出10000个唯一不重复的单词组成词汇表。我们对这10000个单词进行one-hot编码,得到的每个单词都是一个10000维的向量,向量每个维度的值只有0或者1,假如单词ants在词汇表中的出现位置为第3个,那么ants的向量就是一个第三维度取值为1,其他维都为0的10000维的向量(ants=[0, 0, 1, 0, ..., 0])。
还是上面的例子,“The dog barked at the mailman”,那么我们基于这个句子,可以构建一个大小为5的词汇表(忽略大小写和标点符号):("the", "dog", "barked", "at", "mailman"),我们对这个词汇表的单词进行编号0-4。那么”dog“就可以被表示为一个5维向量[0, 1, 0, 0, 0]。
模型的输入如果为一个10000维的向量,那么输出也是一个10000维度(词汇表的大小)的向量,它包含了10000个概率,每一个概率代表着当前词是输入样本中output word的概率大小。
下图是我们神经网络的结构:
隐层没有使用任何激活函数,但是输出层使用了sotfmax。
我们基于成对的单词来对神经网络进行训练,训练样本是 ( input word, output word ) 这样的单词对,input word和output word都是one-hot编码的向量。最终模型的输出是一个概率分布。
隐层
说完单词的编码和训练样本的选取,我们来看下我们的隐层。如果我们现在想用300个特征来表示一个单词(即每个词可以被表示为300维的向量)。那么隐层的权重矩阵应该为10000行,300列(隐层有300个结点)。
所以我们最终的目标就是学习这个隐层的权重矩阵。
如果10000个维度的矩阵采用这样的计算方式是十分低效的。
上面的例子中,左边向量中取值为1的对应维度为3(下标从0开始),那么计算结果就是矩阵的第3行(下标从0开始)—— [10, 12, 19],这样模型中的隐层权重矩阵便成了一个”查找表“(lookup table),进行矩阵计算时,直接去查输入向量中取值为1的维度下对应的那些权重值。隐层的输出就是每个输入单词的“嵌入词向量”。
输出层
经过神经网络隐层的计算,ants这个词会从一个1 x 10000的向量变成1 x 300的向量,再被输入到输出层。输出层是一个softmax回归分类器,它的每个结点将会输出一个0-1之间的值(概率),这些所有输出层神经元结点的概率之和为1。
下面是一个例子,训练样本为 (input word: “ants”, output word: “car”) 的计算示意图。
实际上,这种方法实际上也可以帮助你进行词干化(stemming),例如,神经网络对”ant“和”ants”两个单词会习得相似的词向量。词干化(stemming)就是去除词缀得到词根的过程。
在建模过程中,训练文本中会出现很多“the”、“a”之类的常用词(也叫停用词),这些词对于我们的训练会带来很多噪音。剔除高频的停用词来减少模型的噪音,并加速训练。但不能直接全部删除,否则当输入‘the‘的时候找不到配对。
抽样率
ωi 是一个单词,Z(ωi) 是 ωi 这个单词在所有语料中出现的频次。举个栗子,如果单词“peanut”在10亿规模大小的语料中出现了1000次,那么 Z(peanut) = 1000/1000000000 = 1e - 6。
在代码中还有一个参数叫“sample”,这个参数代表一个阈值,默认值为0.001(在gensim包中的Word2Vec类说明中,这个参数默认为0.001,文档中对这个参数的解释为“ threshold for configuring which higher-frequency words are randomly downsampled”)。这个值越小意味着这个单词被保留下来的概率越小(即有越大的概率被我们删除)。
P(ωi) 代表着保留某个单词的概率:
图中x轴代表着 Z(ωi) ,即单词 ωi 在语料中出现频率,y轴代表某个单词被保留的概率。对于一个庞大的语料来说,单个单词的出现频率不会很大,即使是常用词,也不可能特别大。
从这个图中,我们可以看到,随着单词出现频率的增高,它被采样保留的概率越来越小,我们还可以看到一些有趣的结论:
● 当 Z(ωi) <= 0.0026 时,P(ωi) = 1.0 。当单词在语料中出现的频率小于 0.0026 时,它是 100% 被保留的,这意味着只有那些在语料中出现频率超过 0.26% 的单词才会被采样。
● 当时 Z(ωi) = 0.00746 时,P(ωi) = 0.5,意味着这一部分的单词有 50% 的概率被保留。
● 当 Z(ωi) = 1.0 时,P(ωi) = 0.033,意味着这部分单词以 3.3% 的概率被保留。
负采样(negative sampling)
当我们用训练样本 ( input word: "fox",output word: "quick") 来训练我们的神经网络时,“ fox”和“quick”都是经过one-hot编码的。如果我们的vocabulary大小为10000时,在输出层,我们期望对应“quick”单词的那个神经元结点输出1,其余9999个都应该输出0。在这里,这9999个我们期望输出为0的神经元结点所对应的单词我们称为“negative” word。
一个单词被选作negative sample的概率跟它出现的频次有关,出现频次越高的单词越容易被选作negative words。
每次去我们进行负采样时,只需要在0-1亿范围内生成一个随机数,然后选择表中索引号为这个随机数的那个单词作为我们的negative word即可。一个单词的负采样概率越大,那么它在这个表中出现的次数就越多,它被选中的概率就越大。
相关资料: