宏观理解
文本表示是一个很重要很重要的领域和步骤,在任何模型训练之前都要找到一个匹配的文本表示方法。本文介绍一种有全局泛化能力的分布式表示模型–word2vec。但其实word2vec是两个模型的统称,分别是skip-gram和CBOW。这两种模型及其类似,介绍完一种另一种就无师自通了,所以下面我们只详细介绍一下skip-gram。
微观分析
我们认为在一句话中,挨在一起的单词相似度是比离得远的单词相似度更高的。所以我们是不是可以通过一个词周边的词去预测这个词呢?或者说可不可以通过一个词去预测周边的单词呢?
这就是skip-gram和cbow的解题思路!
Skip-gram模型
通过一个词去预测周边的单词!
我们现在有一个预料:We are learning NLP, it is interesting.我们假定window size为2的话,也就是说通过每一个单词,我们要去预测左边的2个和右边的2个词,比如
- We _ _ NLP, it is interesting.
- _ are _ _, it is interesting.
- _ _ learning _, _ is interesting.
- We _ _ NLP, _ _ interesting.
- We are _ _, it _ _.
- We are learning _, _ is _.
那么我们需要的就是使得P(are, learning|we)和P(we, learning, NLP|are)和P(we, are, NLP, it|learning)和P(are, learning,it, it|NLP)和 P(learning, NLP, is, interesting|it)和P(NLP, it, interesting|is)他们的概率都要最大化。
所以我们可以把目标函数写成:
因为是连乘,我们把它转换为连加的方式:
其中w为循环文本中的每一个单词,然后c为循环到的每一个单词再次循环window size大小的上下文单词,然后我们要最大化他们的log概率连加。只要我们找出可以让这个目标函数最大化的参数theta,也就达到了我们的目的。
但是可以注意到文本中的每一个词,都有机会作为中心词去预测周边的词,也有机会作为周边的词去被预测。所以我们为了更好的去学习每个词的表示,我们分别用两个矩阵u,v来代表当它是中心词和上下文词时的编码。所以我们可以把theta写作是一个(u,v)的组合。每个词向量矩阵都是一个(v,k)大小的矩阵,其中v = 词表长度,k = 我们想要的词向量维度
那么我们怎么求得这个P呢?首先因为它是概率,所以它的值必须是在0-1的范围内。并且它还是一个条件概率,所以。那么我们确定了这两点,就可以用softmax来 表示这个概率P。其中因为我们希望对每个词来说,和上下文词的相似度变大,也就是他们词向量的内积变大,所以在分子中当他们内积变大时,e的值会变大,e的值变大了概率也就变大了。分母是归一化的操作确保满足我们上面说的两个条件。
注意此时我们用的c‘是循环所有词表vocab的单词,而不是所有文本text的单词了。为了区分所以加了prime标示。所以我们最终要学习的变成了:
那么最后循环词表的log sum也太慢了O(|v|)的复杂度,那怎么办呢?
换个思路吧!
负采样
我们以前的思路是如果两个词挨得近,在已知他们的位置关系后,他们的概率P(w1|w2)和P(w2|w1)都会很大,要最大化他们的概率。我们现在变成用多个二分类预测w1,w2是不是在一起,如果在一起的话就是P(D=1|w1,w2),不在一起的话就是P(D=0|w1,w2)。D代表label,指示是不是上下文单词。
所以问题变成了类似逻辑回归,给定两个词和label D,然后学习这两个词是不是在一起。那我们就可以用sigmoid来解决了啊。
那么我们只要把他们的概率乘起来然后最大化不就好了吗?
不包含之前的log sum了!很好。
但是遇到了一个新的问题:如果我们的整个词表有10的5次方个单词,那么我们可以从词表中生成的正样本(也就是D = 1)的样本的个数 + 生成的负样本(也就是D = 0)的个数是10的10次方个。这也太大了吧。
但其实我们知道的是,正样本的个数要远远小于负样本的个数,那么我们就干脆从负样本中进行采样,对于每个正样本,随机抽取5-10个负样本。
这就是所谓的负采样,在源代码中作者还加入了一些哈夫曼树的技巧来加速负采样。随后我们就可以按照逻辑回归的思路进行梯度下降求解两个矩阵的值。
总结
这两种模型中运用最多的还是skip-gram,原因在于两点:
- skip-gram比cbow更加难学习,所以学出来的编码会更优秀
- 在训练之前样本可以构造的更多。