在之前的文章《自然语言处理中的N-Gram模型详解》里,我们介绍了NLP中的模型。最后谈到,为了解决使用N-Gram模型时可能引入的稀疏数据问题,人们设计了多种平滑算法,本文将讨论其中最为重要的几种。
欢迎关注白马负金羁的博客 http://blog.csdn.net/baimafujinji,为保证公式、图表得以正确显示,强烈建议你从该地址上查看原版博文。本博客主要关注方向包括:数字图像处理、算法设计与分析、数据结构、机器学习、数据挖掘、统计分析方法、自然语言处理。
Add-one是最简单、最直观的一种平滑算法。既然希望没有出现过的N-Gram的概率不再是0,那就不妨规定任何一个N-Gram在训练语料至少出现一次(即规定没有出现过的N-Gram在训练语料中出现了一次),则: countnew(n-gram)=countold(n-gram)+1
于是,对于unigram模型而言,会有
同理,对于bigram模型而言,可得
如此一来,训练语料中未出现的n-Gram的概率不再为 0,而是一个大于 0 的较小的概率值。Add-one 平滑算法确实解决了我们的问题,但显然它也并不完美。由于训练语料中未出现n-Gram数量太多,平滑后,所有未出现的n-Gram占据了整个概率分布中的一个很大的比例。因此,在NLP中,Add-one给训练语料中没有出现过的 n-Gram 分配了太多的概率空间。此外,认为所有未出现的n-Gram概率相等是否合理其实也值得商榷。而且,对于出现在训练语料中的那些n-Gram,都增加同样的频度值,这是否欠妥,我们并不能给出一个明确的答案。
由Add-one衍生出来的另外一种算法就是 Add-k。(根据上文中红色字体部分)既然我们认为加1有点过了,不然选择一个小于1的正数 k。此时,概率计算公式就变成了
通常我们会认为高阶模型更加可靠,我们之前博文中给出的例子也表明,当能够获知更多历史信息时,其实就获得了当前推测的更多约束,这样就更容易得出正确的结论。所以在高阶模型可靠时,尽可能的使用高阶模型。但是有时候高级模型的计数结果可能为0,这时我们就转而使用低阶模型来避免稀疏数据的问题。
如果用公式来定义,即
插值和回退的思想其实非常相像。设想对于一个trigram的模型,我们要统计语料库中 “like chinese food” 出现的次数,结果发现它没出现过,则计数为0。在回退策略中,将会试着用低阶gram来进行替代,也就是用 “chinese food” 出现的次数来替代。
在使用插值算法时,我们把不同阶别的n-Gram模型线形加权组合后再来使用。简单线性插值(Simple Linear Interpolation)可以用下面的公式来定义:
在简单单线形插值法中,权值 λi 是常量。显然,它的问题在于不管高阶模型的估计是否可靠(毕竟有些时候高阶的Gram计数可能并无为 0),低阶模型均以同样的权重被加入模型,这并不合理。一个可以想到的解决办法是让 λi 成为历史的函数。如果用递归的形式重写插值法的公式,则有
想想之前的Add-one,以及Add-k算法。我们的策略,本质上说其实是将一些频繁出现的 N-Gram 的概率匀出了一部分,分给那些没有出现的 N-Gram 上。因为所有可能性的概率之和等于1,所以我们只能在各种可能的情况之间相互腾挪这些概率。
既然我们打算把经常出现的一些N-Gram的概率分一些出来,其实也就等同于将它们出现的次数减去(discount)一部分,那到底该discount多少呢?Church & Gale (1991) 设计了一种非常巧妙的方法。首先他们在一个留存语料库(held-out corpus)考察那些在训练集中出现了4次的bigrams出现的次数。具体来说,他们首先在一个有2200万词的留存语料库中检索出所有出现了4次的bigrams (例如: “chinese food”,“good boy”,“want to”等),然后再从一个同样有2200万词的训练集中,分别统计这些bigrams出现的次数(例如:C(“chinese food”)=4,C(“good boy”)=3,C(“want to”)=3)。最终,平均下来,他们发现:在第一个2200万词的语料中出现4次的bigrams,在第二个2200万词的语料中出现了3.23次。下面这张表给出了 c 从0到9取值时(也就是出现了 c 次),统计的bigrams在留存集和训练集中出现的次数。
基于上面这个实验结果所诱发的直觉,Absolute discounting 会从每一个计数中减去一个(绝对的)数值 d 。这样做的道理就在于,对于出现次数比较多的计数我们其实已经得到了一个相对比较好的估计,那么当我们从这个计数值中减去一个较小的数值 d 后应该影响不大。上面的实验结果暗示在实践中,我们通常会对从2到9的计数进行处理。
这种算法目前是一种标准的,而且是非常先进的平滑算法,它其实相当于是前面讲过的几种算法的综合。由于这个算法比较复杂,我们从一个直观上的例子来开始。
假设我们使用 bigram 和 unigram 的插值模型来预测下面这个句子中空缺的一个词该填什么
直觉上你一定能猜到这个地方应该填 chopsticks(筷子)。但是有一种情况是训练语料库中,Zealand 这个词出现的频率非常高,因为 New Zealand 是语料库中高频词。如果你采用标准的 unigram 模型,那么显然 Zealand 会比 chopsticks 具有更高的权重,所以最终计算机会选择Zealand这个词(而非chopsticks)填入上面的空格,尽管这个结果看起来相当不合理。这其实就暗示我们应该调整一下策略,最好仅当前一个词是 New 时,我们才给 Zealand 赋一个较高的权值,否则尽管在语料库中 Zealand 也是高频词,但我们并不打算单独使用它。
如果说 P(w) 衡量了 w 这个词出现的可能性,那么我们现在想创造一个新的 unigram 模型,叫做 Pcontinuation ,它的意思是将 w 这个词作为一个新的接续的可能性。注意这其实暗示我们要考虑前面一个词(即历史)的影响。或者说,为了评估 Pcontinuation (注意这是一个 unigram 模型),我们其实需要考察使用了 w 这个词来生成的不同 bigram 的数量。注意这里说使用了 w 这个词来生成的不同类型 bigram 的数量,是指当前词为 w ,而前面一个词不同时,就产生了不同的类型。例如:w = “food”, 那么不同的 bigram 类型就可能包括 “chinese food”,“english food”,“japanese food”等。每一个 bigram 类型,当我们第一次遇到时,就视为一个新的接续(novel continuation)。
也就是说 Pcontinuation 应该同所有新的接续(novel continuation)构成的集合之势(cardinality)成比例。所以,可知
然后,为了把上面这个数变成一个概率,我们需要将其除以一个值,这个值就是所有 bigram 类型的数量,即 ∣{(wj−1,wj):C(wj−1wj)>0}∣ ,这里大于0的意思就是“出现过”。于是有
如此一来,一个仅出现在 New 后面的高频词 Zealand 只能获得一个较低的接续概率(continuation probability)。由此,再结合前面给出的Absolute Discounting 的概率计算公式,就可以得出插值的 Kneser-Ney Smoothing 的公式,即
我们前面提到Kneser-Ney Smoothing 是当前一个标准的、广泛采用的、先进的平滑算法。这里我们所说的先进的平滑算法,其实是包含了其他以 Kneser-Ney 为基础改进、衍生而来的算法。其中,效果最好的Kneser-Ney Smoothing 算法是由Chen & Goodman(1998)提出的modified Kneser-Ney Smoothing 算法。很多NLP的开发包和算法库中提供有原始的Kneser-Ney Smoothing(也就是我们前面介绍的),以及modified Kneser-Ney Smoothing 算法的实现。有兴趣的读者可以查阅相关资料以了解更多。
[1] Speech and Language Processing. Daniel Jurafsky & James H. Martin, 3rd. Chapter 4
[2] 本文中的一些例子和描述来自 北京大学 常宝宝 以及 The University of Melbourne “Web Search and Text Analysis” 课程的幻灯片素材
[3] 同时推荐美国斯坦福大学 Dan Jurafsky & Christopher Manning 在Coursera上主讲的自然语言处理公开课(https://class.coursera.org/nlp/lecture)