Language Model and Recurrent Neural Networks (一)

本文是我去年十月份在公司的团队技术分享会里面分享过的内容,分享这个内容的初衷是我发现自己对RNN(本文均指Recurrent Neural Networks而非Recursive Neural Networks)比较陌生,想找个时间攻克一下,以便以后有此类工作需求可以快速上手。另外本文加入了CS224N关于语言模型和RNN的课堂内容。因此本文属于科普性质的文章,基本上RNN的细节会涉及到,但并非每个细节都会深入去研究。
因为篇幅较长,所以重新编辑了一下,把标题提到的两部分分开了,本文是语言模型部分。


语言模型

语言模型的应用很常见,例如输入法会自动计算你接下来可能要输入的字或词,搜索引擎会自动补全你可能要搜索的内容等等。所以一句话描述,语言模型就是一个用来预测接下来的词的模型,可以描述为P(x(t)x(t1),x(t2),...,x(0))P(x^{(t)}|x^{(t-1)},x^{(t-2)},...,x^{(0)})。这样我们就可以根据这个概率来估计或者采样出下面将要出现的词。

1.基于统计的语言模型

传统的语言模型是根据词频来预测的,那么上面那个式可以写成
P(x(t)x(t1),x(t2),...,x(0))=P(x(t),x(t1),x(t2),...,x(0))P(x(t1),x(t2),...,x(0)) P(x^{(t)}|x^{(t-1)},x^{(t-2)},...,x^{(0)})=\frac{P(x^{(t)},x^{(t-1)},x^{(t-2)},...,x^{(0)})}{P(x^{(t-1)},x^{(t-2)},...,x^{(0)})}

对于给定的一个语料库,我们只需要统计一下出现次数就行
P(x(t)x(t1),x(t2),...,x(0))=count(x(t),x(t1),x(t2),...,x(0))count(x(t1),x(t2),...,x(0))P(x^{(t)}|x^{(t-1)},x^{(t-2)},...,x^{(0)}) = \frac{count(x^{(t)},x^{(t-1)},x^{(t-2)},...,x^{(0)})}{count(x^{(t-1)},x^{(t-2)},...,x^{(0)})}

但是文本序列越长,需要统计的序列也越长,计算量就越大,这显然不合理的,于是聪明的人们就想,要不就简化一下吧,假定后面出现的这个次只跟前面的k=n-1个词有关就好了,于是乎,n-gram就出现了
P(x(t)x(t1),...,x(tn+1))P(x^{(t)}|x^{(t-1)},...,x^{(t-n+1)})

所以n=1时的语言模型就等于各个词的出现频率,n=2时的语言模型就是只用前一个词预测后一个词的条件概率……

基于统计的方式去计算语言模型会有一些问题。假设我要计算的某个词的概率P(x(t)P(x(t1)))P(x^{(t)}|P(x^{(t-1)})),但尴尬的是我语料库里面[x(t1),x(t)][x^{(t-1)},x^{(t)}]这个组合从来没出现过,那计算这个语言模型的时候概率就为0咯?

这显然是不科学的,不能因为没见过这个词组而说它的条件概率就为0,于是人们想到可以对它进行一些平滑操作。至于详细的平滑操作到底有哪些,咱就不深入讨论了,方法有很多,也各有利弊,可以理解成最简单的办法就是把所有出现的可能性都加上一个固定的很小的常数,那么就可以把未出现过的变成一个小概率。

平滑操作虽然可以避免分子或分母为0时带来的问题,但它根本上并没有办法解决统计上n-gram的稀疏性问题。即便语料库再大,也总有不出现的配对,而且n越大就越稀疏,存储空间的需求也是越大,搜索起来就越慢。

2.基于固定滑动窗口的语言模型

上面我们一直讨论的是语言模型是用词频来计算的,但如果这个P(y|x)是个神经网络,那上面提到的困难就迎刃而解了。

与n-gram一样,我们假设输入是前面n-1个词,那么我们就可以通过一个神经网络,以这n-1个词的词向量作为输入,输出下一个词的概率,假设这里我们n=5:

图一 使用滑动窗口的语言模型

这个过程乍一看跟CBOW算法很像,但CBOW预测的是中间词,这里预测的是下一个词。还有个细节是CBOW的输入层到隐藏层之间的权值其实是词向量,这个过程除了词向量之外是没有额外权值的,但这里图中蓝色那层已经是词嵌入向量e,这里到隐藏层h之间还有一个权值矩阵W。更多详细细节就不继续讨论了,关于CBOW的详细描述请戳这里

然而,固定滑动窗口这个办法也有些问题。首先,窗口大小我们不好取,取小了,距离较远的词不能加入计算,哪怕它们相互之间是有关系的;取大了,模型参数的shape也会变大,计算量也就越大了。其次,每个词的处理是与位置有关的,也就是e(1)e^{(1)}~e(4)e^{(4)}它们是各自与一个权重相乘,而非权值共享的。最后,也就是它最天然的问题了,窗口是固定的,窗口以外的词就没办法加入训练,这个模型是没有记忆的

CS224n课堂上提出第二点的这个问题,我个人其实认为并不是很严重的问题,不同的词序确实是会导致产生不同的上下文环境,所以权值共享是不是十分必要,我个人持保留意见,只不过这个顺序关系是基于权值来确定那就确实过分生硬了。

3.基于循环神经网络的语言模型

用RNN来建立语言模型可以比较好的解决上面提到的问题,因为RNN的循环结构使得语言模型可以以任意的长度作为输入而不会引起模型参数的增加,而且RNN在每次计算的时候会使用当期的input以及上期的output统一作为输入,这样可以使得模型具备一定的“记忆”能力。

图二 使用RNN的语言模型

关于RNN的技术细节,我们接下来将详细讨论。

4.语言模型的采样过程

在inference过程,我们可能需要使用语言模型生成文本,这就涉及到采样(sampling)。所谓采样,就是根据模型输出的各个词概率分布,抽取适当的词作为估计值。所谓“适当”,其实是可以根据实际情况选取适合的策略,例如可以采用贪心策略,即每一步都采取最大概率的预测词作为结果,又或者更为灵活一点,按照概率分布进行加权抽样。更鲁棒一点,可以采用Beam Search采样出更好的结果。那么怎么样的结果才算好呢?

5.语言模型的性能评估

我们知道分类模型可以用精准率、召回率或者准确率之类的评估指标来衡量性能,回归模型可以则用MAE、MSE、SSE之类的指标。对语言模型而言,它本质上是一个分类模型,并且使用交叉熵作为损失函数,但用准确率作为语言模型的评估未免太过苛刻,毕竟自然语言是十分灵活的,同样的意思可以有多种表述方式。这里我们介绍使用困惑度(perplexity)作为评估指标,CS224N课堂说这是语言模型的标准评估指标。
perplexity=t=1T(1PLM(x(t+1)x(t),...,x(1)))1T perplexity = \prod_{t=1}^{T}{(\frac{1}{P_{LM}{(x^{(t+1)}|x^{(t)},...,x^{(1)})}})^{\frac{1}{T}}}

细心的朋友可能会发现,如果我们对困惑度取对数,那么就等于交叉熵。所以不难反推出,困惑度就是exp(交叉熵)。e为底的指数函数是单调递增的,那么困惑度与交叉熵一样,都是越数值越小越好。

参考资料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章