语言模型就是要预测由一些单词组成的一个序列的概率,我们希望通过语言模型使得那些更加接近人类自然语言的序列能够得到更高的预测概率。
在机器翻译领域,由相同的词组成的不同语序的两句话对应的概率可能不同。比如对于组成词相同的两句话,“the cat is small"的概率一定要比"small is the cat"要高,因为前者才是通顺的一句话。另一种情况,对于同一语义的两句话来说,可能句子中的一些词的不同也会对应不同的概率。比如对于“放学后走回家”这句话,可能对应两个翻译"walking home after school"和"walking house after school”,那么前者在英文中就更加自然,因此也应该对应更高的概率。
在一个句子中,每个词都取决于之前的词,那么一个句子对应的概率可以表示为: p ( w 1 , w 2 , w 3 , . . . , w m ) = ∏ i = 1 m p ( w i ∣ w i − 1 , . . . , w 1 ) ≈ ∏ i = 1 m p ( w i ∣ w i − 1 , . . . , w i − n + 1 ) p(w_1,w_2,w_3,...,w_m)=\prod_{i=1}^{m}p(w_i|w_{i-1},...,w_1)\approx\prod_{i=1}^{m}p(w_i|w_{i-1},...,w_{i-n+1}) p(w1,w2,w3,...,wm)=i=1∏mp(wi∣wi−1,...,w1)≈i=1∏mp(wi∣wi−1,...,wi−n+1) 为了简化问题,可以只考虑当前词前面一个包含n个词的窗口,也就是只考虑前面的n个词(n-gram model)。
这个假设并不正确,但这是传统的语言模型中必不可少的一部分。
n-gram正是应用马尔可夫假设的一个模型,它是一种基于统计的模型,所以我们通过统计词频来计算相应的条件概率。比如 p ( w 2 ∣ w 1 ) = c o u n t ( w 1 , w 2 ) c o u n t ( w 1 ) p ( w 3 ∣ w 1 , w 2 ) = c o u n t ( w 1 , w 2 , w 3 ) c o u n t ( w 1 , w 2 ) p(w_2|w_1)=\frac{count(w_1,w_2)}{count(w_1)}\qquad p(w_3|w_1,w_2)=\frac{count(w_1,w_2,w_3)}{count(w_1,w_2)} p(w2∣w1)=count(w1)count(w1,w2)p(w3∣w1,w2)=count(w1,w2)count(w1,w2,w3) 根据n-gram模型,我们可以得到unigram(一元模型)、bigram(二元模型)、trigram(三元模型),理论上我们考虑的n越大,模型的效果应该越好,但是n越大,计算的时间代价和内存需求就越高,因此实际使用还是以上面三种模型为主。
RNN的优点:
每步计算所用的权重是同一个能够考虑到之前所有出现过的词内存需求只和次的数量有关RNN的每一步计算过程如下所示: 第一步开始前的前序向量 h 0 h_0 h0 可以是一个初始化的向量。
每个时刻输出的 y y y 都是词汇的概率分布,所以损失可以用交叉熵函数来计算。 最后整个序列的整体误差 J J J 是每个时刻误差 J t J_t Jt 之和。 整个序列总的误差的偏导是各个时间步长的偏导之和,那么对于某一时间 t t t 的偏导如下图所示的链式求导法所得。 梯度的链式求导过程: 隐藏层随时序前向传播的公式: h t = W f ( h t − 1 ) + W ( h x ) x [ t ] h_t=Wf(h_{t-1})+W^{(hx)}x_{[t]} ht=Wf(ht−1)+W(hx)x[t] 由于RNN的隐藏层的计算过程中是参数共享的,所以 h t h_t ht 和 h t − 1 h_{t-1} ht−1 以及之后的 h k h_k hk 中都包含同一个参数矩阵 W W W,那么根据复合函数的链式求导可得: ∂ h t ∂ W = ∂ + h t ∂ W + ∂ h t ∂ f ( h t − 1 ) ∂ f ( h t − 1 ) ∂ h t − 1 ∂ h t − 1 ∂ W = ∂ + h t ∂ W + ∂ h t ∂ f ( h t − 1 ) ∂ f ( h t − 1 ) ∂ h t − 1 [ f ( h t − 2 ) + ∂ h t − 1 ∂ f ( h t − 2 ) ∂ f ( h t − 2 ) ∂ h t − 2 ∂ h t − 2 ∂ W ] = ∂ + h t ∂ W + ∂ h t ∂ h t − 1 ∂ + h t − 1 ∂ W + ∂ h t ∂ h t − 2 ∂ h t − 2 ∂ W = ∂ h t ∂ h t ∂ + h t ∂ W + ∂ h t ∂ h t − 1 ∂ + h t − 1 ∂ W + ∂ h t ∂ h t − 2 ∂ + h t − 2 ∂ W + . . . + ∂ h t ∂ h 1 ∂ + h 1 ∂ W = ∑ k = 1 t ∂ h t ∂ h k ∂ + h k ∂ W \begin{aligned} \frac{\partial h_t}{\partial W}&=\frac{\partial^+ h_t}{\partial W}+\frac{\partial h_{t}}{\partial f(h_{t-1})}\frac{\partial f(h_{t-1})}{\partial h_{t-1}}\frac{\partial h_{t-1}}{\partial W}\\ &=\frac{\partial^+ h_t}{\partial W}+\frac{\partial h_{t}}{\partial f(h_{t-1})}\frac{\partial f(h_{t-1})}{\partial h_{t-1}}[f(h_{t-2})+\frac{\partial h_{t-1}}{\partial f(h_{t-2})}\frac{\partial f(h_{t-2})}{\partial h_{t-2}}\frac{\partial h_{t-2}}{\partial W}]\\ &=\frac{\partial^+ h_t}{\partial W}+\frac{\partial h_{t}}{\partial h_{t-1}}\frac{\partial^+ h_{t-1}}{\partial W}+\frac{\partial h_{t}}{\partial h_{t-2}}\frac{\partial h_{t-2}}{\partial W}\\ &=\frac{\partial h_{t}}{\partial h_{t}}\frac{\partial^+ h_t}{\partial W}+\frac{\partial h_{t}}{\partial h_{t-1}}\frac{\partial^+ h_{t-1}}{\partial W}+\frac{\partial h_{t}}{\partial h_{t-2}}\frac{\partial^+ h_{t-2}}{\partial W}+...+\frac{\partial h_{t}}{\partial h_{1}}\frac{\partial^+ h_{1}}{\partial W}\\ &=\sum^t_{k=1}\frac{\partial h_{t}}{\partial h_{k}}\frac{\partial^+ h_k}{\partial W} \end{aligned} ∂W∂ht=∂W∂+ht+∂f(ht−1)∂ht∂ht−1∂f(ht−1)∂W∂ht−1=∂W∂+ht+∂f(ht−1)∂ht∂ht−1∂f(ht−1)[f(ht−2)+∂f(ht−2)∂ht−1∂ht−2∂f(ht−2)∂W∂ht−2]=∂W∂+ht+∂ht−1∂ht∂W∂+ht−1+∂ht−2∂ht∂W∂ht−2=∂ht∂ht∂W∂+ht+∂ht−1∂ht∂W∂+ht−1+∂ht−2∂ht∂W∂+ht−2+...+∂h1∂ht∂W∂+h1=k=1∑t∂hk∂ht∂W∂+hk其中 ∂ + h t ∂ W \frac{\partial^+ h_t}{\partial W} ∂W∂+ht 是指 h t h_t ht 直接对 W W W 求导的结果,也就是 f ( h t − 1 ) f(h_{t-1}) f(ht−1)。
所以最后 t t t 时刻的误差 E t E_t Et 对 W W W 的偏导为: ∂ E t ∂ W = ∑ k = 1 t ∂ E t ∂ y t ∂ y t ∂ h t ∂ h t ∂ h k ∂ + h k ∂ W \frac{\partial E_t}{\partial W}=\sum^t_{k=1}\frac{\partial E_t}{\partial y_t}\frac{\partial y_t}{\partial h_t}\frac{\partial h_{t}}{\partial h_{k}}\frac{\partial^+ h_k}{\partial W} ∂W∂Et=k=1∑t∂yt∂Et∂ht∂yt∂hk∂ht∂W∂+hk 而整个计算过程中的误差又是从始至终每个时刻误差的和,所以最后 W W W 总的梯度为: ∂ E ∂ W = ∑ t = 1 T ∑ k = 1 t ∂ E t ∂ y t ∂ y t ∂ h t ∂ h t ∂ h k ∂ + h k ∂ W \frac{\partial E}{\partial W}=\sum^T_{t=1}\sum^t_{k=1}\frac{\partial E_t}{\partial y_t}\frac{\partial y_t}{\partial h_t}\frac{\partial h_{t}}{\partial h_{k}}\frac{\partial^+ h_k}{\partial W} ∂W∂E=t=1∑Tk=1∑t∂yt∂Et∂ht∂yt∂hk∂ht∂W∂+hk 这种将整个序列完全计算一遍然后再一次性的进行反向传播的方法叫做BPTT(BackwardPropagation Through Time)。
梯度消失和梯度爆炸是RNN训练过程中的一个常见的问题,这种问题可能会导致RNN无法捕捉长距离的信息。 从前面的公式中可以看出 ∂ h t ∂ h t − 1 = W T f ′ ( h t − 1 ) \frac{\partial h_t}{\partial h_{t-1}}=W^Tf^{'}(h_{t-1}) ∂ht−1∂ht=WTf′(ht−1)那么 ∂ h t ∂ h k = ∏ j = k + 1 t W T f ′ ( h j − 1 ) \frac{\partial h_t}{\partial h_{k}}=\prod_{j=k+1}^tW^Tf^{'}(h_{j-1}) ∂hk∂ht=j=k+1∏tWTf′(hj−1)可以看到在这个计算过程中会产生参数的多次连乘。 由于这种链式求导过程中过多的求导次数累乘,导数的值可能会随参数初始化的值变得过大或者过小,这样前面距离较远的一些词对序列后面的词的影响就无法正常捕捉。
Thomas Mikolov提出,为梯度设定一个上限值,当梯度增大到一定程度时,就让梯度限定在这个较小的上限值上。这种方法听上去感觉缺少数学依据,但是在实际应用中效果很好。
如图所示为RNN训练过程中的误差平面,可以看到如果不加限制,当参数撞到梯度爆炸的“曲率墙”时,参数的值会迅速偏离到一个很远的区域(实线),而如果加以限制,就会一定程度上缓和这种现象(虚线)。 当然,这种方法不适用于解决梯度消失,因为我们可以通过设定梯度的上限来避免参数变化太大,但不能设定梯度的下限,否则无法在训练中达到收敛,与梯度下降的思想相违背。
除了使用RNN的改良版模型LSTM、GRU,还有一种比较简单的方法,就是调整参数的初始化值。 在初始化阶段,可以将两个参数矩阵 W ( h h ) W^{(hh)} W(hh) 和 W ( h x ) W^{(hx)} W(hx) 都设为单位矩阵,并都在前面乘 1 2 \frac{1}{2} 21,也就是两个矩阵都是 d i a g ( 1 2 , 1 2 , . . . , 1 2 ) diag(\frac{1}{2},\frac{1}{2},...,\frac{1}{2}) diag(21,21,...,21)(假设两个矩阵有相同的尺寸)。除此之外,隐藏层之间的激活函数使用ReLU。那么在实际的运算中,当前时刻隐藏层的状态等于上一时刻隐藏层状态的一半加上此刻输入信息的一半。即: h t = 1 2 h t − 1 + 1 2 x t h_t=\frac{1}{2}h_{t-1}+\frac{1}{2}x_t ht=21ht−1+21xt 这种方法的思想就是在初始化时不去随意取值,而是简单的在第一趟时做平均,然后再进行误差的反向传播。 实验结果也表明,这种方法在MNIST数据集上的分类表现远好于普通的RNN模型。
在每次的预测过程中,我们都需要将隐藏层的向量乘上一个参数矩阵 W ( S ) W^{(S)} W(S),然后将得到的向量用 s o f t m a x softmax softmax 函数计算概率分布。如果隐藏层的向量维度是1000维,而词汇总数为100000个,那么这个参数矩阵 W ( S ) W^{(S)} W(S) 的维度就是 R 100000 × 1000 \R^{100000\times1000} R100000×1000,参数矩阵的维度巨大,而且每一步的计算都要用隐藏层状态乘上这个参数矩阵,所以在计算中会导致速度很慢。
一个解决方法是基于类的词语预测。思路就是将要预测的词先分为几个不同的类,在预测时先预测这个词属于哪一类,在从这个类中去预测对应的词。这样分两步去预测就可以大大减少参数的数量。在这种方法中,类别数越多,最后预测的困惑度(Perplexity)就越低,但每次迭代所需的时间也越多。
另一个经常用到的方法就是BPTT(BackwardPropagation Through Time)。也就是无须每个时刻计算得到结果之后就进行反向传播,而是每次计算时累加要更新的梯度,在整个序列计算结束后再进行一次反向传播。
常规的RNN模型在预测下一个词时只能依据之前出现过的词,无法利用之后将要出现的词,而双向的RNN模型则能够处理这个问题。 这个模型分别从两个方向做相同的时序传播计算,有两个分别代表两个传播方向的不同的隐藏层值。 不过与普通的单向RNN相同,双向RNN也会存在梯度消失和梯度爆炸的问题。
除了这种简单的单层RNN,RNN也可以在深度上进行扩充,搭建一个更深层次的网络。下图所示就是一个多层的双向RNN。
准确率 p e r c i s i o n percision percision: p e r c i s i o n = t p t p + f p percision=\frac{t_p}{t_p+f_p} percision=tp+fptp 召回率 r e c a l l recall recall: r e c a l l = t p t p + f n recall=\frac{t_p}{t_p+f_n} recall=tp+fntp F1值: F 1 = 2 ⋅ p e r c i s i o n ⋅ r e c a l l p e r c i s i o n + r e c a l l F1=2\cdot\frac{percision\cdot recall}{percision+recall} F1=2⋅percision+recallpercision⋅recall 其中 t p t_p tp 表示样本属于正类并且也正确预测为正类的数目, f p f_p fp 表示样本属于负类但预测成正类的数目, f n f_n fn 表示样本属于正类但预测成负类的数目。 总之,准确率表示预测为正类的样本中预测对的比例,召回率表示实际的正类样本中正确预测为正类的比例,F1值则是准确率和召回率的调和平均值。 这种评估指标比较适用于样本分类不均衡的情况,比如某类样本很多但某类样本很少。