2013年,Google开源了一款用于词向量计算的工具—word2vec,引起了工业界和学术界的关注。首先,word2vec可以在百万数量级的词典和上亿的数据集上进行高效地训练;其次,该工具得到的训练结果——词向量(word embedding),可以很好地度量词与词之间的相似性。随着深度学习在自然语言处理中应用的普及,很多人误以为word2vec是一种深度学习算法。其实word2vec算法的背后是一个浅层神经网络。另外需要强调的一点是,word2vec是一个计算word vector的开源工具。当我们在说word2vec算法或模型的时候,其实指的是其背后用于计算word vector的CBoW模型和Skip-gram模型。
什么是统计语言模型呢?
简单来说,统计语言模型就是用来计算一个句子的概率的概率模型,它通常基于一个语料库来构建。通俗来说就是让整条语句看起来是一句人话。
如何计算一段文本序列在某种语言下出现的概率?
例如,在机器翻译的问题中,如果我们知道了目标语言中每句话的概率,就可以从候选集合中挑选出最合理的句子做为翻译结果返回。
统计语言模型给出了这一类问题的一个基本解决框架。对于一段文本序列 S = w 1 , w 2 , . . . , w T S=w_1, w_2, ... , w_T S=w1,w2,...,wT , 它的概率可以表示为:
P ( S ) = P ( w 1 , w 2 , . . . , w T ) = ∏ t = 1 T p ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) P(S)=P(w_1, w_2, ..., w_T)=\prod_{t=1}^Tp(w_t|w_1, w_2, ..., w_{t-1}) P(S)=P(w1,w2,...,wT)=t=1∏Tp(wt∣w1,w2,...,wt−1)
即将序列的联合概率转化为一系列条件概率的乘积。问题变成了如何去预测这些给定previous words下的条件概率: p ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) p(w_t|w_1,w_2,...,w_{t-1}) p(wt∣w1,w2,...,wt−1)。 一般来说,语言模型都是为了使得条件概率 p ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) p(w_t|w_1,w_2,...,w_{t-1}) p(wt∣w1,w2,...,wt−1) 最大化,不过考虑到近因效应,当前词只与距离它比较近的n个词更加相关(一般n不超过5),而非前面所有的词都有关。 上述公式可以近似为:
p ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) ≈ p ( w t ∣ w t − ( n + 1 ) , . . . , w t − 1 ) p(w_t|w_1, w_2, ..., w_{t-1}) \approx p(w_t|w_{t-(n+1)}, ..., w_{t-1}) p(wt∣w1,w2,...,wt−1)≈p(wt∣wt−(n+1),...,wt−1)
N-gram模型仍有其局限性。
首先,由于参数空间的爆炸式增长,它无法处理更长程的context( N > 3 N>3 N>3)。
其次,它没有考虑词与词之间内在的联系性。
然而, Ngram模型做不到。 因为Ngram本质上是将词当做一个个孤立的原子单元(atomic unit)去处理的。这种处理方式对应到数学上的形式是一个个离散的one-hot向量(除了一个词典索引的下标对应的方向上是1,其余方向上都是0)。 例如:对于一个大小为5的词典:{“I”, “love”, “nature”, “luaguage”, “processing”},其中的“nature”对应的one-hot向量为: [ 0 , 0 , 1 , 0 , 0 ] [0,0,1,0,0] [0,0,1,0,0]。显然,one-hot向量的维度等于词典的大小。 在动辄上万甚至百万词典的实际应用中,面临着巨大的维度灾难问题(The Curse of Dimensionality)。
NNLM最初由Bengio在2003年发表的一篇论文《A Neural Probabilistic Language Mode》中提出来的,word2vec便是从其中简化训练而来。
NNLM模型的基本思想可以概括如下:
将整个模型拆分成两部分加以理解:
通过最小化一个cross-entropy的正则化损失函数来调整模型的参数 θ \ \theta θ:
L ( θ ) = 1 T ∑ t log f ( w t , w t − 1 , . . . , w t − n + 1 ) + R ( θ ) L(\theta)=\frac{1}{T}\sum_t{\log{f(w_t, w_{t-1}, ..., w_{t-n+1})}}+R(\theta) L(θ)=T1t∑logf(wt,wt−1,...,wt−n+1)+R(θ)
其中,模型的参数 θ \theta θ 包括了Embedding层矩阵 C C C 的元素,和前向反馈神经网络模型 g g g 里的权重。这是一个巨大的参数空间。
在用SGD学习更新模型的参数时,并不是所有的参数都需要调整(例如未在输入的context中出现的词对应的词向量)。计算的瓶颈主要是在softmax层的归一化函数上(需要对词典中所有的word计算一遍条件概率)。
神经语言模型解决了两个问题:
通过引入连续的词向量和平滑的概率模型,使得可以在一个连续空间里对序列概率进行建模,从而从根本上缓解数据稀疏性和维度灾难的问题。另一方面,以条件概率 p ( w t ∣ c o n t e x t ) p(w_t|context) p(wt∣context)为学习目标去更新词向量的权重,具有更强的导向性。
NNLM存在的几个问题:
这时Mikolov注意到,原始的NNLM模型的训练其实可以拆分成两个步骤:
而NNLM模型的计算瓶颈主要是在第二步。
如果我们只是想得到word的连续特征向量,是不是可以对第二步里的神经网络模型进行简化呢?
Mikolov是这么想的,也是这么做的。他在2013年一口气推出了两篇paper,并开源了一款计算词向量的工具——至此,word2vec横空出世,主角闪亮登场。
在word2vec中提出了两个模型(假设上下文窗口为3,图来自2013年Mikolov的原始论文,注意这里没有隐藏层,只有输入层、投影层、输出层,且输入层到投影层不带权重,投影层到输出层带权重)
CBOW(Continuous Bag-of-Word):以上下文词汇预测当前词,即用 w t − 2 、 w t − 1 、 w t + 1 、 w t + 2 w_{t-2}、w_{t-1}、w_{t+1}、w_{t+2} wt−2、wt−1、wt+1、wt+2去预测 w t w_{t} wt。
SkipGram:以当前词预测其上下文词汇,即用 w t w_{t} wt去预测 w t − 2 、 w t − 1 、 w t + 1 、 w t + 2 w_{t-2}、w_{t-1}、w_{t+1}、w_{t+2} wt−2、wt−1、wt+1、wt+2。
传统的神经网络词向量语言模型,里面一般有三层,输入层(词向量),隐藏层和输出层(softmax层)。最大的问题在于从隐藏层到输出的softmax层的计算量很大,因为要计算所有词的softmax概率,再去找概率最大的值。这个模型如下图所示。其中 V V V是词汇表的大小,
word2vec对这个模型做了改进,
首先,对于从输入层到隐藏层的映射,没有采取神经网络的线性变换加激活函数的方法,而是采用简单的对所有输入词向量求和并取平均的方法。
从隐藏层到输出的softmax层这里的计算量做改进。
为了避免要计算所有词的softmax概率,word2vec采样了霍夫曼树来代替从隐藏层到输出softmax层的映射。
Q: 如何映射呢?
由于把之前所有都要计算的从输出softmax层的概率计算变成了一颗二叉霍夫曼树,那么softmax概率计算只需要沿着树形结构进行就可以了。如下图所示,沿着霍夫曼树从根节点一直走到叶子节点的词 w 2 w_2 w2。
与神经网络语言模型相比,霍夫曼树的所有内部节点就类似之前神经网络隐藏层的神经元, 其中,根节点的词向量对应投影后的词向量,而所有叶子节点就类似于神经网络softmax输出层的神经元,叶子节点的个数就是词汇表的大小。在霍夫曼树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着霍夫曼树一步步完成的,因此这种softmax取名为"Hierarchical Softmax"。
Q: 如何“沿着霍夫曼树一步步完成”呢?
在word2vec中,采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:
P ( + ) = σ ( x w T θ ) = 1 1 + e − x w T θ P(+) = \sigma(x_w^T\theta) = \frac{1}{1+e^{-x_w^T\theta}} P(+)=σ(xwTθ)=1+e−xwTθ1
其中 x w x_w xw 是当前内部节点的词向量,而 θ \theta θ 则是需要从训练样本求出的逻辑回归的模型参数。
Q: 使用霍夫曼树有什么好处呢?
容易理解,被划分为左子树而成为负类的概率为 P ( − ) = 1 − P ( + ) P(−)=1−P(+) P(−)=1−P(+)。在某一个内部节点,要判断是沿左子树还是右子树走的标准就是看 P ( − ) , P ( + ) P(−),P(+) P(−),P(+)谁的概率值大。 而控制 P ( − ) , P ( + ) P(−),P(+) P(−),P(+)谁的概率值大的因素一个是当前节点的词向量,另一个是当前节点的模型参数 θ \theta θ(从公式里就能看出来)。
对于上图中的 w 2 w_2 w2,如果它是一个训练样本的输出,那么我们期望对于里面的隐藏节点 n ( w 2 , 1 ) n(w_2,1) n(w2,1) 的 P ( − ) P(−) P(−) 概率大, n ( w 2 , 2 ) ) n(w_2,2)) n(w2,2)) 的 P ( − ) P(−) P(−) 概率大, n ( w 2 , 3 ) n(w_2,3) n(w2,3) 的 P ( + ) P(+) P(+) 概率大。
目标就是找到合适的所有节点的词向量和所有内部节点 θ \theta θ, 使训练样本达到最大似然。那么如何达到最大似然呢?
使用最大似然法来寻找所有节点的词向量和所有内部节点 θ \theta θ。先拿上面的 w 2 w_2 w2 例子来看,我们期望最大化下面的似然函数:
∏ i = 1 3 P ( n ( w i ) , i ) = ( 1 − 1 1 + e − x w T θ 1 ) ( 1 − 1 1 + e − x w T θ 2 ) 1 1 + e − x w T θ 3 \prod_{i=1}^3P(n(w_i),i) = (1- \frac{1}{1+e^{-x_w^T\theta_1}})(1- \frac{1}{1+e^{-x_w^T\theta_2}})\frac{1}{1+e^{-x_w^T\theta_3}} i=1∏3P(n(wi),i)=(1−1+e−xwTθ11)(1−1+e−xwTθ21)1+e−xwTθ31
对于所有的训练样本,我们期望最大化所有样本的似然函数乘积。
为了方便,我们定义:
定义 w w w经过的霍夫曼树某一个节点 j j j的逻辑回归概率为 P ( d j w ∣ x w , θ j − 1 w ) P(d_j^w|x_w, \theta_{j-1}^w) P(djw∣xw,θj−1w),其表达式为:
P ( d j w ∣ x w , θ j − 1 w ) = { σ ( x w T θ j − 1 w ) d j w = 0 1 − σ ( x w T θ j − 1 w ) d j w = 1 P(d_j^w|x_w, \theta_{j-1}^w)= \begin{cases} \sigma(x_w^T\theta_{j-1}^w)& {d_j^w=0}\\[2ex] 1- \sigma(x_w^T\theta_{j-1}^w) & {d_j^w = 1} \end{cases} P(djw∣xw,θj−1w)=⎩ ⎨ ⎧σ(xwTθj−1w)1−σ(xwTθj−1w)djw=0djw=1
那么对于某一个目标输出词 w w w,其最大似然为:
∏ j = 2 l w P ( d j w ∣ x w , θ j − 1 w ) = ∏ j = 2 l w [ σ ( x w T θ j − 1 w ) ] 1 − d j w [ 1 − σ ( x w T θ j − 1 w ) ] d j w \prod_{j=2}^{l_w}P(d_j^w|x_w, \theta_{j-1}^w) = \prod_{j=2}^{l_w} [\sigma(x_w^T\theta_{j-1}^w)] ^{1-d_j^w}[1-\sigma(x_w^T\theta_{j-1}^w)]^{d_j^w} j=2∏lwP(djw∣xw,θj−1w)=j=2∏lw[σ(xwTθj−1w)]1−djw[1−σ(xwTθj−1w)]djw
在word2vec中,由于使用的是随机梯度上升法,所以并没有把所有样本的似然乘起来得到真正的训练集最大似然,仅仅每次只用一个样本更新梯度,这样做的目的是减少梯度计算量。这样我们可以得到 w w w的对数似然函数 L L L如下:
L = l o g ∏ j = 2 l w P ( d j w ∣ x w , θ j − 1 w ) = ∑ j = 2 l w ( ( 1 − d j w ) l o g [ σ ( x w T θ j − 1 w ) ] + d j w l o g [ 1 − σ ( x w T θ j − 1 w ) ] ) L= log \prod_{j=2}^{l_w}P(d_j^w|x_w, \theta_{j-1}^w) = \sum\limits_{j=2}^{l_w} \left((1-d_j^w) log \left[\sigma(x_w^T\theta_{j-1}^w)\right] + d_j^w log\left[1-\sigma(x_w^T\theta_{j-1}^w)\right]\right) L=logj=2∏lwP(djw∣xw,θj−1w)=j=2∑lw((1−djw)log[σ(xwTθj−1w)]+djwlog[1−σ(xwTθj−1w)])
要得到模型中 w w w 词向量和内部节点的模型参数 θ \theta θ, 使用梯度上升法即可。首先我们求模型参数 θ j − 1 w \theta^w_{j-1} θj−1w 的梯度:
∂ L ∂ θ j − 1 w = ( 1 − d j w ) ( σ ( x w T θ j − 1 w ) ( 1 − σ ( x w T θ j − 1 w ) σ ( x w T θ j − 1 w ) x w − d j w ( σ ( x w T θ j − 1 w ) ( 1 − σ ( x w T θ j − 1 w ) 1 − σ ( x w T θ j − 1 w ) x w = ( 1 − d j w ) ( 1 − σ ( x w T θ j − 1 w ) ) x w − d j w σ ( x w T θ j − 1 w ) x w = ( 1 − d j w − σ ( x w T θ j − 1 w ) ) x w \begin{align} \frac{\partial L}{\partial \theta_{j-1}^w} & = (1-d_j^w)\frac{(\sigma(x_w^T\theta_{j-1}^w)(1-\sigma(x_w^T\theta_{j-1}^w)}{\sigma(x_w^T\theta_{j-1}^w)}x_w - d_j^w \frac{(\sigma(x_w^T\theta_{j-1}^w)(1-\sigma(x_w^T\theta_{j-1}^w)}{1- \sigma(x_w^T\theta_{j-1}^w)}x_w \\[3ex] & = (1-d_j^w)(1-\sigma(x_w^T\theta_{j-1}^w))x_w - d_j^w\sigma(x_w^T\theta_{j-1}^w)x_w \\[2ex] & = (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))x_w \end{align} ∂θj−1w∂L=(1−djw)σ(xwTθj−1w)(σ(xwTθj−1w)(1−σ(xwTθj−1w)xw−djw1−σ(xwTθj−1w)(σ(xwTθj−1w)(1−σ(xwTθj−1w)xw=(1−djw)(1−σ(xwTθj−1w))xw−djwσ(xwTθj−1w)xw=(1−djw−σ(xwTθj−1w))xw
θ j − 1 w \theta^w_{j-1} θj−1w的更新公式为:
θ j − 1 w : = θ j − 1 w + η [ 1 − d j w − σ ( x w T θ j − 1 w ) ] x w \theta^w_{j-1}:=\theta^w_{j-1}+\eta\left[1-d_j^w-\sigma(x_w^T\theta_{j-1}^w)\right]x_w θj−1w:=θj−1w+η[1−djw−σ(xwTθj−1w)]xw
η \eta η表示学习率。
同样的方法,可以求出 x w x_w xw的梯度表达式如下:
∂ L ∂ x w = ∑ j = 2 l w ( 1 − d j w − σ ( x w T θ j − 1 w ) ) θ j − 1 w \frac{\partial L}{\partial x_w} = \sum\limits_{j=2}^{l_w}(1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))\theta_{j-1}^w ∂xw∂L=j=2∑lw(1−djw−σ(xwTθj−1w))θj−1w
有了梯度表达式,我们就可以用梯度上升法进行迭代来一步步的求解我们需要的所有的 θ j − 1 w \theta^w_{j-1} θj−1w和 x w x_w xw。
CBOW模型(上下文词预测当前词)的网络结构,包括三层:输入层、投影层和输出层。
定义词向量的维度大小 M M M,以及CBOW的上下文大小 2 c 2c 2c,这样对于训练样本中的每一个词,其前面的 c c c 个词和后面的 c c c 个词作为了CBOW模型的输入,该词本身作为样本的输出,期望softmax概率最大。
输入层:包含上下文中 2 c 2c 2c个词的词向量
投影层:将输入层的 2 c 2c 2c个两个做求和积累
x w = 1 2 c ∑ i = 1 2 c x i x_w = \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i xw=2c1i=1∑2cxi
输出层:对应一棵二叉树(Huffman树),由语料库中出现过的词当作叶子结点,各个词的频率当作权值构建的一棵Huffman树。(叶子结点的个数: N N N(词典的所有词),非叶子结点: N − 1 N-1 N−1)。
构建完Huffman树之后, 通过梯度上升法来更新的 θ j − 1 w \theta^w_{j−1} θj−1w和 x w x_w xw,注意这里的 x w x_w xw是由 2 c 2c 2c个词向量相加而成,梯度更新完毕后会用梯度项直接更新原始的各个 x i ( i = 1 , 2 , . . . , 2 c ) x_i(i=1,2,...,2c) xi(i=1,2,...,2c),即:
θ j − 1 w = θ j − 1 w + η ( 1 − d j w − σ ( x w T θ j − 1 w ) ) x w x i = x i + η ∑ j = 2 l w ( 1 − d j w − σ ( x w T θ j − 1 w ) ) θ j − 1 w ( i = 1 , 2.. , 2 c ) \theta_{j-1}^w = \theta_{j-1}^w + \eta (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))x_w\\[2ex] x_i= x_i +\eta \sum\limits_{j=2}^{l_w}(1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))\theta_{j-1}^w \;(i =1,2..,2c) θj−1w=θj−1w+η(1−djw−σ(xwTθj−1w))xwxi=xi+ηj=2∑lw(1−djw−σ(xwTθj−1w))θj−1w(i=1,2..,2c)
其中 η \eta η 为梯度上升法的步长。
基于Hierarchical Softmax的CBOW模型算法流程,梯度迭代使用了随机梯度上升法:
输入:基于CBOW的语料训练样本,词向量的维度大小 M M M,CBOW的上下文大小 2 c 2c 2c,步长 η \eta η
输出:霍夫曼树的内部节点模型参数 θ \theta θ,所有的词向量 w w w
基于语料训练样本建立霍夫曼树。
随机初始化所有的模型参数 θ \theta θ,所有的词向量 w w w
进行梯度上升迭代过程,对于训练集中的每一个样本 $(context(w),w) 做如下处理:
a) e = 0 e=0 e=0,计算 x w = 1 2 c ∑ i = 1 2 c x i x_w= \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i xw=2c1i=1∑2cxi
b) for j = 2 j=2 j=2 to l w l_w lw:
f = σ ( x w T θ j − 1 w ) g = ( 1 − d j w − f ) η e = e + g θ j − 1 w θ j − 1 w = θ j − 1 w + g x w f = \sigma(x_w^T\theta_{j-1}^w)\\[2ex] g = (1-d_j^w-f)\eta\\[2ex] e = e + g\theta_{j-1}^w\\[2ex] \theta_{j-1}^w= \theta_{j-1}^w + gx_w f=σ(xwTθj−1w)g=(1−djw−f)ηe=e+gθj−1wθj−1w=θj−1w+gxw
c) 对于 c o n t e x t ( w ) context(w) context(w)中的每一个词向量 x i x_i xi(共 2 c 2c 2c个)进行更新:
x i = x i + e x_i = x_i + e xi=xi+e
d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。
Skip-Gram模型(当前词预测上下文词)的网络结构,包括三层:输入层、投影层和输出层。
定义词向量的维度大小 M M M,以及CBOW的上下文大小 2 c 2c 2c, 对于训练样本中的每一个词,该词本身作为样本的输入, 其前面的 c c c 个词和后面的 c c c 个词作为了Skip-Gram模型的输出,期望这些词的softmax概率比其他的词大。
首先 需要先将词汇表建立成一颗霍夫曼树。
构建完Huffman树之后, 通过梯度上升法来更新我们的 θ j − 1 w \theta^w_{j−1} θj−1w和 x w x_w xw,注意这里的 x w x_w xw周围有 2 c 2c 2c个词向量, 此时期望 P ( x i ∣ x w ) , i = 1 , 2...2 c P(x_i|x_w),i=1,2...2c P(xi∣xw),i=1,2...2c最大。 由于上下文是相互的,在期望 P ( x i ∣ x w ) , i = 1 , 2...2 c P(x_i|x_w),i=1,2...2c P(xi∣xw),i=1,2...2c最大化的同时,反过来也期望 P ( x w ∣ x i ) , i = 1 , 2...2 c P(x_w|x_i),i=1,2...2c P(xw∣xi),i=1,2...2c最大。那么是使用 P ( x i ∣ x w ) ) P(x_i|x_w)) P(xi∣xw))好还是 P ( x w ∣ x i ) P(x_w|x_i) P(xw∣xi)好?
word2vec使用了后者,好处就是在一个迭代窗口内,不是只更新 x w x_w xw 一个词,而是 x i ( i = 1 , 2...2 c ) x_i\ (i=1,2...2c\ ) xi (i=1,2...2c ) 共 2 c 2c 2c 个词。这样整体的迭代会更加的均衡。因此,Skip-Gram模型并没有和CBOW模型一样对输入进行迭代更新,而是对 2 c 2c 2c 个输出进行迭代更新。
基于Hierarchical Softmax的Skip-Gram模型算法流程,梯度迭代使用了随机梯度上升法:
输入:基于 Skip-Gram 的语料训练样本,词向量的维度大小 M M M, Skip-Gram 的上下文大小 2 c 2c 2c,步长 η \eta η
输出:霍夫曼树的内部节点模型参数 θ \theta θ,所有的词向量 w w w
基于语料训练样本建立霍夫曼树。
随机初始化所有的模型参数 θ \theta θ,所有的词向量 w w w
进行梯度上升迭代过程,对于训练集中的每一个样本 ( c o n t e x t ( w ) , w ) (context(w),w) (context(w),w)做如下处理:
a) for i = 1 i=1 i=1 to 2 c 2c 2c:
i) e = 0 e=0 e=0
ii) for j = 2 j=2 j=2 to l w l_w lw:
f = σ ( x w T θ j − 1 w ) g = ( 1 − d j w − f ) η e = e + g θ j − 1 w θ j − 1 w = θ j − 1 w + g x w f = \sigma(x_w^T\theta_{j-1}^w)\\[2ex] g = (1-d_j^w-f)\eta\\[2ex] e = e + g\theta_{j-1}^w\\[2ex] \theta_{j-1}^w= \theta_{j-1}^w + gx_w f=σ(xwTθj−1w)g=(1−djw−f)ηe=e+gθj−1wθj−1w=θj−1w+gxw
iii)
x i = x i + e x_i = x_i + e xi=xi+e
b) 如果梯度收敛,则结束梯度迭代,否则回到步骤a继续迭代。
假设2014年世界杯期间,从新浪微博中抓取了若干条与足球相关的微博,经统计,“我”、“喜欢”、“观看”、“巴西”、“足球”、“世界杯”这个六个词出现的次数分别为15,8,6,5,3,1。这6个词的Huffman编码,其中约定(词频大的)左孩子结点编码为1,(词频较小)右孩子结点编码为0。“我”、“喜欢”、“观看”、“巴西”、“足球”、“世界杯”这个六个词的Huffman编码为:0,111,110,101,1001,1000。
现在考虑词 w = " 足球 " w="足球" w="足球"的情形。
从根结点到“足球”叶子结点,中间经过了4个分支(每条红色的边对应一次分支),而每一次分支可以视为一次二分类。Huffman编码为1的结点定义为负类,编码为0的结点定义为正类(这只是一个约定)。
对于从根结点到“足球”叶子结点,经过了4次二分类,每次分类的结果的概率如下:
所以要求的 p ( 足球 ∣ C o n t e x t ( 足球 ) ) p(足球|Context(足球)) p(足球∣Context(足球))等于上述4个概率的乘积
p ( 足球 ∣ C o n t e x t ( 足球 ) ) = ∏ 2 5 p ( d j w ∣ x w , θ j − 1 w ) p(足球|Context(足球))=\prod\limits^5\limits_2p(d^w_j|x_w,\theta^w_{j-1}) p(足球∣Context(足球))=2∏5p(djw∣xw,θj−1w)
Hierachical Softmax的基本思想就是首先将词典中的每个词按照词频大小构建出一棵Huffman树,保证词频较大的词处于相对比较浅的层,词频较低的词相应的处于Huffman树较深层的叶子节点,每一个词都处于这棵Huffman树上的某个叶子节点。
Hierachical Softmax 将原本的一个 ∣ V ∣ |V| ∣V∣分类问题变成了 l o g ∣ V ∣ log|V| log∣V∣次的二分类问题。
原先要计算 P ( w t ∣ c t ) P(w_t|c_t) P(wt∣ct)的时候,因为使用的是普通的softmax,势必要求词典中的每一个词的概率大小 ,那么如何减少计算量呢?
计算当前词 w t w_t wt 在其上下文中的概率大小,只需要把它变成在Huffman树中的路径预测问题就可以了,因为当前词 w t w_t wt 在Huffman树中对应到一条路径,这条路径由这棵二叉树中从根节点开始,经过一系列中间的父节点,最终到达当前这个词的叶子节点而组成,那么在每一个父节点上,都对应的是一个二分类问题(本质上就是一个LR分类器),而Huffman树的构造过程保证了树的深度为 l o g ∣ V ∣ log|V| log∣V∣,所以也就只需要做 l o g ∣ V ∣ log|V| log∣V∣次二分类便可以求得 P ( w t ∣ c t ) P(w_t|c_t) P(wt∣ct) 的大小。
层次化softmax改变了原来的模型结构。
原来是 1 ∗ V 1*V 1∗V(输入one-hot),经过 V ∗ D V*D V∗D的矩阵(input-embedding) ,再经过 D ∗ V D*V D∗V矩阵(output-embedding),即一系列矩阵乘法 ( 1 , V ) ∗ ( V , D ) ∗ ( D , V ) = ( 1 , V ) (1,V)*(V,D)*(D,V)=(1,V) (1,V)∗(V,D)∗(D,V)=(1,V)【其中的第一次乘法,一般用查表的方式直接读取,不用相乘】,再softmax,得到目标词是词典中每个词的概率。
使用层次化softmax时,output-embedding被取消了,查表后直接用(1,D)的向量与每个中间路径节点对应的一个向量 γ n γ_n γn【形状为 ( D , 1 ) (D,1) (D,1)】,进行相乘,得到一个scalar,再经过sigmoid,转为一个0到1之间的小数,此数从就是从树(树是二叉树,树的每个叶子节点都对应一个单词)的根节点到某个叶子节点的路径上每次在中间节点选择向左(也可定为向右)走的概率。
路径上每条边的概率进行连乘,就是从根节点到叶子节点的总的概率,也就是模型的预测值为该叶子节点(目标单词)的概率。层次化softmax的目标函数就是最大化目标词的路径概率,换句话说,我们只需要关注目标词的路径概率即可,而目标词是已知的,完全不需要计算其他的词的路径概率。这点是后续优化措施的关键。
1.层次化softmax的优点体现在哪里?
计算预测输出值为每个单词的概率时,由于概率是从根节点到叶子节点的路径上的边进行连乘,也就是计算量与目标词(叶子节点)的路径长度成正比,原来的softmax的目标函数在反向传播时需要求导,而求导时,由于分母是e的指数的累加和,导致必须算出所有单词的概率,计算量是与词表大小V成正比。
2.层次化softmax的树是二叉树,路径长度也就是树的高度,假设是满二叉树,则树高= V V V 取2为底的对数值,远远小于 V V V,节省了很多计算过程。比如一个大小为 1024 1024 1024 的词典,原来softmax是 1024 1024 1024 个e的指数计算,现在是 l o g 1024 = 10 log1024=10 log1024=10 次sigmoid值再连乘,共节省了 1024 − 10 = 1014 1024-10=1014 1024−10=1014 次计算。是否还可以进一步优化?
可以。以上表述中层次化的softmax用到的树,一般认为是满的二叉树,也就是树的高度为 l o g V logV logV,其实还有优化的空间。因为目标词的概率仅与根节点到目标词的路径长度有关,如果我们能够优化根节点到目标词的路径长度,就可以进一步节省计算。什么二叉树的根节点到叶子节点的路径长度最短?答案是哈夫曼树。构建哈夫曼树时,比较节点的权重大小是依据该节点(也就是单词)的词频,词频大的放在离根节点近的地方,词频小的远离根节点。如此词频大的词只需要很少的比较次数,路径就结束了,计算次数比较少。
使用霍夫曼树来代替传统的神经网络,可以提高模型训练的效率。但是如果我们的训练样本里的中心词 w w w是一个很生僻的词,那么就得在霍夫曼树中辛苦的向下走很久了。能不能不用搞这么复杂的一棵霍夫曼树,将模型变的更加简单呢?
Negative Sampling就是这么一种求解word2vec模型的方法,它摒弃了霍夫曼树,采用了Negative Sampling(负采样)的方法来求解。
对于文本“”Thou shalt not make a machine in the likeness of a human mind“而言:如何使用它来训练一个能预测相邻词汇的自然语言模型。
训练过程,用not预测thou
将特征输入到未经训练的模型,让它预测一个可能的相邻单词。该模型会执行三个步骤并输入预测向量(对应于单词表中每个单词的概率)。由于模型未经训练,该阶段的预测肯定是错误的。
如上图一样,预测结果极度不靠谱,not本该预测thou,但其却将 0.4 0.4 0.4 的概率预测成了taco,预测错了,如何解决?计算损失loss,利用梯度
现在误差向量可以被用于更新模型了,所以在下一轮预测中,如果用not作为输入,我们更有可能得到thou作为输出了。接下来继续对数据集内下一份样本进行同样的操作,直到遍历所有的样本。这就是一轮(epoch)了。我们再多做几轮(epoch),得到训练过的模型,于是就可以从中提取嵌入矩阵来用于其他应用了。
如果按照上述训练方法,则not是不是要把整个词表的所有的都要预测一遍?
回想一下这个神经语言模型计算预测值的三个步骤:
从计算的角度来看,第三步非常昂贵 - 尤其是当我们将需要在数据集中为每个训练样本都做一遍(很容易就多达数千万次)。我们需要寻找一些提高表现的方法。
一种方法是将目标分为两个步骤:
1.生成高质量的词嵌入(不要担心下一个单词预测)。
2.使用这些高质量的嵌入来训练语言模型(进行下一个单词预测)。
在本文中我们将专注于第1步(因为这篇文章专注于嵌入)。要使用高性能模型生成高质量嵌入,我们可以改变一下预测相邻单词这一任务:
将其切换到一个提取输入与输出单词的模型,并输出一个表明它们是否是邻居的分数(0表示“不是邻居”,1表示“邻居”)。
比如有一个训练样本,中心词是 w w w,它周围上下文共有 2 c 2c 2c 个词,记为 c o n t e x t ( w ) context(w) context(w)。由于这个中心词 w w w 的确和 c o n t e x t ( w ) context(w) context(w) 相关存在,因此它是一个真实的正例。通过Negative Sampling采样,得到neg个和 w w w不同的词 w i , i = 1 , 2 , . . n e g \ w_i,i=1,2,..neg wi,i=1,2,..neg, 这样 c o n t e x t ( w ) context(w) context(w)和 w i w_i wi就组成了neg个并不真实存在的负例 作为负例。利用这一个正例和neg个负例,进行二元逻辑回归,得到负采样对应每个词 w i w_i wi对应的模型参数 θ i \theta_i θi,和每个词的词向量。
训练流程:
- 在训练过程开始之前,预先处理正在训练模型的文本。确定词典的大小(vocab_size,比如说10,000)以及哪些词被它包含在内。
- 在训练阶段的开始,创建两个矩阵——Embedding矩阵和Context矩阵。
- 在训练过程开始时,用 “随机值初始化” 这些矩阵。开始训练过程。在每个训练步骤中,采取一个相邻的例子及其相关的非相邻例子。
- 输入单词not和输出/上下文单词: thou(实际邻居词),aaron和taco(负面例子)。
对于输入词,查看Embedding矩阵。对于上下文单词,查看Context矩阵
- 计算输入嵌入与每个上下文嵌入的点积。在每种情况下,结果都将是表示输入和上下文嵌入的相似性的数字。计算误差,使用梯度下降法更新参数。
Negative Sampling也是采用了二元逻辑回归来求解模型参数,通过负采样得到了neg个负例 ( c o n t e x t ( w ) , w i ) i = 1 , 2 , . . n e g (context(w),w_i)i=1,2,..neg (context(w),wi)i=1,2,..neg为了统一描述将正例定义为 w 0 w_0 w0。
在逻辑回归中,正例应该期望满足: P ( c o n t e x t ( w 0 ) , w i ) = σ ( x w 0 T θ w i ) , y i = 1 , i = 0 P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta_{w_i}) ,y_i=1, i=0 P(context(w0),wi)=σ(xw0Tθwi),yi=1,i=0
负例期望满足: P ( c o n t e x t ( w 0 ) , w i ) = 1 − σ ( x w 0 T θ w i ) , y i = 0 , i = 1 , 2 , . . n e g P(context(w_0), w_i) =1- \sigma(x_{w_0}^T\theta_{w_i}), y_i = 0, i=1,2,..neg P(context(w0),wi)=1−σ(xw0Tθwi),yi=0,i=1,2,..neg
期望可以最大化: ∏ i = 0 n e g P ( c o n t e x t ( w 0 ) , w i ) = σ ( x w 0 T θ w 0 ) ∏ i = 1 n e g ( 1 − σ ( x w 0 T θ w i ) ) \prod\limits_{i=0}\limits^{neg}P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta_{w_0})\prod\limits_{i=1}\limits^{neg}(1- \sigma(x_{w_0}^T\theta_{w_i})) i=0∏negP(context(w0),wi)=σ(xw0Tθw0)i=1∏neg(1−σ(xw0Tθwi))
似然函数为: ∏ i = 0 n e g σ ( x w 0 T θ w i ) y i ( 1 − σ ( x w 0 T θ w i ) ) 1 − y i \prod\limits_{i=0}\limits^{neg} \sigma(x_{w_0}^T\theta_{w_i})^{y_i}(1- \sigma(x_{w_0}^T\theta_{w_i}))^{1-y_i} i=0∏negσ(xw0Tθwi)yi(1−σ(xw0Tθwi))1−yi
对数似然: L = ∑ i = 0 n e g y i l o g ( σ ( x w 0 T θ w i ) ) + ( 1 − y i ) l o g ( 1 − σ ( x w 0 T θ w i ) ) L = \sum\limits_{i=0}^{neg}y_i log(\sigma(x_{w_0}^T\theta_{w_i})) + (1-y_i) log(1- \sigma(x_{w_0}^T\theta_{w_i})) L=i=0∑negyilog(σ(xw0Tθwi))+(1−yi)log(1−σ(xw0Tθwi))
梯度上升法来更新我们的 θ w i \theta_{w_i} θwi和 x w i , i = 0 , 1 , . . n e g x_{wi},i=0,1,..neg xwi,i=0,1,..neg,分别求解梯度:
∂ L ∂ θ w i = y i ( 1 − σ ( x w 0 T θ w i ) ) x w 0 − ( 1 − y i ) σ ( x w 0 T θ w i ) x w 0 = ( y i − σ ( x w 0 T θ w i ) ) x w 0 ∂ L ∂ x w 0 = ∑ i = 0 n e g ( y i − σ ( x w 0 T θ w i ) ) θ w i \begin{align} \frac{\partial L}{\partial \theta_{w_i} } &= y_i(1- \sigma(x_{w_0}^T\theta_{w_i}))x_{w_0}-(1-y_i)\sigma(x_{w_0}^T\theta_{w_i})x_{w_0} \\[2ex] & = (y_i -\sigma(x_{w_0}^T\theta_{w_i})) x_{w_0} \\[4ex] \frac{\partial L}{\partial x_{w_0} } &= \sum\limits_{i=0}^{neg}(y_i -\sigma(x_{w_0}^T\theta_{w_i}))\theta_{w_i} \end{align} ∂θwi∂L∂xw0∂L=yi(1−σ(xw0Tθwi))xw0−(1−yi)σ(xw0Tθwi)xw0=(yi−σ(xw0Tθwi))xw0=i=0∑neg(yi−σ(xw0Tθwi))θwi
如果词汇表的大小为 V V V,那么将一段长度为1的线段分成 V V V份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词 w w w的线段长度由下式决定:
l e n ( w ) = c o u n t ( w ) ∑ u ∈ v o c a b c o u n t ( u ) len(w) = \frac{count(w)}{\sum\limits_{u \in vocab} count(u)} len(w)=u∈vocab∑count(u)count(w)
在word2vec中,分子和分母都取了 3 / 4 3/4 3/4 次幂如下:
l e n ( w ) = c o u n t ( w ) 3 / 4 ∑ u ∈ v o c a b c o u n t ( u ) 3 / 4 len(w) = \frac{count(w)^{3/4}}{\sum\limits_{u \in vocab} count(u)^{3/4}} len(w)=u∈vocab∑count(u)3/4count(w)3/4
在采样前,将这段长度为1的线段划分成 M M M等份,这里 M > > V M>>V M>>V,这样可以保证每个词对应的线段都会划分成对应的小块。而 M M M份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从 M M M个位置中采样出 n e g neg neg个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。
在word2vec中, M M M取值默认为 1 0 8 10^8 108。
总的来说:非等距切分+等距切分
例如: 选定句子 “The quick brown fox jumps over lazy dog” ,设定窗口大小为2 ( w i n d o w _ s i z e = 2 window\_size=2 window_size=2) 选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。
对高频词进行抽样
但是对于“the”这种常用高频单词,这样的处理方式会存在下面两个问题:
Word2Vec通过“抽样”模式来解决这种高频词问题。它的基本思想如下:对于在训练原始文本中遇到的每一个单词,它们都有一定概率从文本中被删掉,而这个被删除的概率与单词的频率有关。
如果设置窗口大小 s p a n = 10 span=10 span=10,并且从文本中删除所有的“the”,那么会有下面的结果:
Q:如何选择抽样率呢?
word2vec的C语言代码实现了一个计算在词汇表中保留某个词概率的公式。
P ( w i ) = ( Z ( w i ) 0.001 + 1 ) × 0.001 Z ( w i ) P(w_i)=(\sqrt{\frac{Z(w_i)}{0.001}}+1)×\frac{0.001}{Z(w_i)} P(wi)=(0.001Z(wi)+1)×Z(wi)0.001
P ( w i ) P(w_i) P(wi)表示某个单词被保留的概率。 w i w_i wi是一个单词, Z ( w i ) Z_(w_i) Z(wi)是 w i w_i wi这个单词在所有语料中出现的频次。
举个栗子:如果单词“peanut”在10亿规模大小的语料库中出现了1000次,则有 Z ( " p e a n u t " ) = 1000 / 1000000000 = 1 e − 6 Z("peanut")=1000/1000000000=1e-6 Z("peanut")=1000/1000000000=1e−6
在代码中还有一个参数叫“sample”,这个参数代表一个阈值,默认值为0.001 (在gensim包中的Word2Vec类说明中,这个参数默认为0.001,文档中对这个参数的解释为“ threshold for configuring which higher-frequency words are randomly downsampled”)。这个值越小意味着这个单词被保留下来的概率越小(即有越大的概率被我们删除)。
负采样每遍历到一个目标词,为了使得目标词的概率 P ( w t ∣ c t ) P(w_t|c_t) P(wt∣ct) 最大,根据softmax函数的概率公式,也就是让分子中的 e ′ ( w t ) T x e′(w_t)^Tx e′(wt)Tx 最大,而分母中其他非目标词的 e ′ ( w i ) T x e′(w_i)^Tx e′(wi)Tx 最小,普通softmax的计算量太大就是因为它把词典中所有其他非目标词都当做负例了,而负采样的思想特别简单,就是每次按照一定概率随机采样一些词当做负例,从而就只需要计算这些负采样出来的负例了,那么概率公式便相应变为:
P ( w t ∣ c t ) = e x p ( e ´ ( w t ) T ) x ∑ i = 1 K e x p ( e ´ ( w i ) T ) x , x = ∑ i ∈ c e ( w i ) P(w_t|c_t)=\frac{exp(e´(w_t)^T)x}{\sum\limits_{i=1}\limits^Kexp(e´(w_i)^T)x}, \quad x=\sum\limits_{i \in c}e(w_i) P(wt∣ct)=i=1∑Kexp(e´(wi)T)xexp(e´(wt)T)x,x=i∈c∑e(wi)
仔细和普通softmax进行比较便会发现,将原来的|V|分类问题变成了K分类问题,这便把词典大小对时间复杂度的影响变成了一个常数项,而改动又非常的微小,不可谓不巧妙。
不同于one_hot这种暴力映射,无法衡量字词的语义空间距离。Word2vec采用的是低维稠密矩阵去表征字词的语义关系。
对于泛化能力的理解:假设现有四个特征值woman girl man boy,经过onet-hot编码之后得到
[1,0,0,0]–>woman [0,1,0,0]–>girl 每个特征之间是独立的,放进网络里训练彼此之间是没有任何关系的。如果将特征进行embedding操作为2维度:[性别,年龄]==> 女 40(通过性别和年龄能学习出woman)—>woman 可以学习出泛化性。
本文仅仅作为个人学习记录所用,不作为商业用途,谢谢理解。
参考:
1.https://zhuanlan.zhihu.com/p/88874759
2.https://www.cnblogs.com/peghoty/p/3857839.html
3.https://www.cnblogs.com/pinard/p/7160330.html