天然语言处理中的N-Gram模型

N-Gram(有时也称为N元模型)是天然语言处理中一个很是重要的概念,一般在NLP中,人们基于必定的语料库,能够利用N-Gram来预计或者评估一个句子是否合理。另一方面,N-Gram的另一个做用是用来评估两个字符串之间的差别程度。这是模糊匹配中经常使用的一种手段。本文将今后开始,进而向读者展现N-Gram在天然语言处理中的各类powerful的应用。java

  • 基于N-Gram模型定义的字符串距离
  • 利用N-Gram模型评估语句是否合理
  • 使用N-Gram模型时的数据平滑算法

欢迎关注白马负金羁的博客 http://blog.csdn.net/baimafujinji,为保证公式、图表得以正确显示,强烈建议你从该地址上查看原版博文。本博客主要关注方向包括:数字图像处理、算法设计与分析、数据结构、机器学习、数据挖掘、统计分析方法、天然语言处理。算法


基于N-Gram模型定义的字符串距离

在天然语言处理时,最经常使用也最基础的一个操做是就是“模式匹配”,或者称为“字符串查找”。而模式匹配(字符串查找)又分为精确匹配和模糊匹配两种。apache

所谓精确匹配,你们应该并不陌生,好比咱们要统计一篇文章中关键词 “information” 出现的次数,这时所使用的方法就是精确的模式匹配。这方面的算法也比较多,并且应该是计算机相关专业必修的基础课中都会涉及到的内容,例如KMP算法、BM算法和BMH算法等等。markdown

另一种匹配就是所谓的模糊匹配,它的应用也随处可见。例如,通常的文字处理软件(例如,Microsoft Word等)都会提供拼写检查功能。当你输入一个错误的单词,例如 “ informtaion” 时,系统会提示你是否要输入的词实际上是 “information” 。将一个可能错拼单词映射到一个推荐的正确拼写上所采用的技术就是模糊匹配。数据结构

模糊匹配的关键在于如何衡量两个长得很像的单词(或字符串)之间的“差别”。这种差别一般又称为“距离”。这方面的具体算法有不少,例如基于编辑距离的概念,人们设计出了 Smith-Waterman 算法和Needleman-Wunsch 算法,其中后者仍是历史上最先的应用动态规划思想设计的算法之一。如今Smith-Waterman 算法和Needleman-Wunsch 算法在生物信息学领域也有重要应用,研究人员经常用它们来计算两个DNA序列片断之间的“差别”(或称“距离”)。甚至于在LeetCode上也有一道“No.72 Edit Distance”,其本质就是在考察上述两种算法的实现。可见相关问题离咱们并不遥远。机器学习

N-Gram在模糊匹配中的应用

事实上,笔者在新出版的《算法之美——隐匿在数据结构背后的原理》一书中已经详细介绍了包括Needleman-Wunsch算法、Smith-Waterman算法、N-Gram算法、Soundex算法、Phonix算法等在内的多种距离定义算法(或模糊匹配算法)。而今天为了引出N-Gram模型在NLP中的其余应用,咱们首先来介绍一下如何利用N-Gram来定义字符串之间的距离。函数

咱们除了能够定义两个字符串之间的编辑距离(一般利用Needleman-Wunsch算法或Smith-Waterman算法)以外,还能够定义它们之间的N-Gram距离。N-Gram(有时也称为N元模型)是天然语言处理中一个很是重要的概念。假设有一个字符串 ss,那么该字符串的N-Gram就表示按长度 N 切分原词获得的词段,也就是 ss 中全部长度为 N 的子字符串。设想若是有两个字符串,而后分别求它们的N-Gram,那么就能够从它们的共有子串的数量这个角度去定义两个字符串间的N-Gram距离。可是仅仅是简单地对共有子串进行计数显然也存在不足,这种方案显然忽略了两个字符串长度差别可能致使的问题。好比字符串 girl 和 girlfriend,两者所拥有的公共子串数量显然与 girl 和其自身所拥有的公共子串数量相等,可是咱们并不能据此认为 girl 和girlfriend 是两个等同的匹配。工具

为了解决该问题,有学者便提出以非重复的N-Gram分词为基础来定义 N-Gram距离这一律念,能够用下面的公式来表述: post

 
|GN(s)|+|GN(t)|2×|GN(s)GN(t)||GN(s)|+|GN(t)|−2×|GN(s)∩GN(t)|

此处, |GN(s)||GN(s)| 是字符串 ss 的 N-Gram集合,N 值通常取2或者3。以 N = 2 为例对字符串Gorbachev和Gorbechyov进行分段,可得以下结果(咱们用下画线标出了其中的公共子串)。 

 


 

结合上面的公式,便可算得两个字符串之间的距离是8 + 9 − 2 × 4 = 9。显然,字符串之间的距离越小,它们就越接近。当两个字符串彻底相等的时候,它们之间的距离就是0。

 

利用N-Gram计算字符串间距离的Java实例

《算法之美——隐匿在数据结构背后的原理》一书中,咱们给出了在C++下实现的计算两个字符串间N-Gram距离的函数,鉴于全书代码已经在本博客中发布,这里再也不重复列出。事实上,不少语言的函数库或者工具箱中都已经提供了封装好的计算 N-Gram 距离的函数,下面这个例子演示了在Java中使用N-Gram 距离的方法。学习

针对这个例子,这里须要说明的是:

  • 调用函数须要引用lucene的JAR包,我所使用的是lucene-suggest-5.0.0.jar
  • 前面咱们所给出的算法计算所得为一个绝对性的距离分值。而Java中所给出的函数在此基础上进行了归一化,也就是说所得之结果是一个介于0~1之间的浮点数,即0的时候表示两个字符串彻底不一样,而1则表示两个字符串彻底相同。
import org.apache.lucene.search.spell.*;

public class NGram_distance {

    public static void main(String[] args) {

        NGramDistance ng = new NGramDistance();
        float score1 = ng.getDistance("Gorbachev", "Gorbechyov");
        System.out.println(score1);
        float score2 = ng.getDistance("girl", "girlfriend");
        System.out.println(score2);
    }
}

有兴趣的读者能够在引用相关JAR包以后在Eclipse中执行上述Java程序,你会发现,和咱们预期的同样,字符串Gorbachev和Gorbechyov所得之距离评分较高(=0.7),说明两者很接近;而girl和girlfriend所得之距离评分并不高(=0.3999),说明两者并不很接近。


利用N-Gram模型评估语句是否合理

从如今开始,咱们所讨论的N-Gram模型跟前面讲过N-Gram模型从外在来看已经大不相同,可是请注意它们内在的联系(或者说本质上它们仍然是统一的概念)。

为了引入N-Gram的这个应用,咱们从几个例子开始。 
首先,从统计的角度来看,天然语言中的一个句子 ss 能够由任何词串构成,不过几率 P(s)P(s) 有大有小。例如:

  • s1s1 = 我刚吃过晚饭
  • s2s2 = 刚我过晚饭吃

显然,对于中文而言 s1s1 是一个通顺而有意义的句子,而s2s2 则不是,因此对于中文来讲,P(s1)>P(s2)P(s1)>P(s2) 。但不一样语言来讲,这两个几率值的大小可能会反转。

其次,另一个例子是,若是咱们给出了某个句子的一个节选,咱们其实能够可以猜想后续的词应该是什么,例如

  • the large green __ . Possible answer may be “mountain” or “tree” ?
  • Kate swallowed the large green __ . Possible answer may be “pill” or “broccoli” ?

显然,若是咱们知道这个句子片断更多前面的内容的状况下,咱们会获得一个更加准确的答案。这就告诉咱们,前面的(历史)信息越多,对后面未知信息的约束就越强。

若是咱们有一个由 mm 个词组成的序列(或者说一个句子),咱们但愿算得几率 P(w1,w2,,wm)P(w1,w2,⋯,wm) ,根据链式规则,可得 

 
P(w1,w2,,wm)=P(w1)P(w2|w1)P(w3|w1,w2)P(wm|w1,,wm1)P(w1,w2,⋯,wm)=P(w1)P(w2|w1)P(w3|w1,w2)⋯P(wm|w1,⋯,wm−1)

这个几率显然并很差算,不妨利用马尔科夫链的假设,即当前这个词仅仅跟前面几个有限的词相关,所以也就没必要追溯到最开始的那个词,这样即可以大幅缩减上诉算式的长度。即 
 
P(wi|w1,,wi1)=P(wi|win+1,,wi1)P(wi|w1,⋯,wi−1)=P(wi|wi−n+1,⋯,wi−1)

特别地,对于 nn 取得较小值的状况 
当 n=1n=1, 一个一元模型(unigram model)即为 
 
P(w1,w2,,wm)=i=1mP(wi)P(w1,w2,⋯,wm)=∏i=1mP(wi)

当 n=2n=2, 一个二元模型(bigram model)即为 
 
P(w1,w2,,wm)=i=1mP(wi|wi1)P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−1)

当 n=3n=3, 一个三元模型(trigram model)即为 
 
P(w1,w2,,wm)=i=1mP(wi|wi2wi1)P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−2wi−1)

接下来的思路就比较明确了,能够利用最大似然法来求出一组参数,使得训练样本的几率取得最大值。

 

  • 对于unigram model而言,其中c(w1,..,wn)c(w1,..,wn) 表示 n-gram w1,..,wnw1,..,wn 在训练语料中出现的次数,MM 是语料库中的总字数(例如对于 yes no no no yes 而言,M=5M=5) 
     
    P(wi)=C(wi)MP(wi)=C(wi)M
  • 对于bigram model而言, 
     
    P(wi|wi1)=C(wi1wi)C(wi1)P(wi|wi−1)=C(wi−1wi)C(wi−1)
  • 对于nn-gram model而言, 
     
    P(wi|win1,,wi1)=C(win1,,wi)C(win1,,wi1)P(wi|wi−n−1,⋯,wi−1)=C(wi−n−1,⋯,wi)C(wi−n−1,⋯,wi−1)

来看一个具体的例子,假设咱们如今有一个语料库以下,其中<s1><s2><s1><s2> 是句首标记,</s2></s1></s2></s1> 是句尾标记: 

 
<s1><s2>yesnonononoyes</s2></s1><s1><s2>nononoyesyesyesno</s2></s1><s1><s2>yesnonononoyes</s2></s1><s1><s2>nononoyesyesyesno</s2></s1>

下面咱们的任务是来评估以下这个句子的几率: 
 
<s1><s2>yesnonoyes</s2></s1><s1><s2>yesnonoyes</s2></s1>

咱们来演示利用trigram模型来计算几率的结果 
 
P(yes|<s1><s2>)=12,P(no|<s2>yes)=1P(no|yesno)=12,P(yes|nono)=25P(</s2>|noyes)=12,P(</s1>|yes</s2>)=1P(yes|<s1><s2>)=12,P(no|<s2>yes)=1P(no|yesno)=12,P(yes|nono)=25P(</s2>|noyes)=12,P(</s1>|yes</s2>)=1

因此咱们要求的几率就等于: 
 
12×1×12×25×12×1=0.0512×1×12×25×12×1=0.05

 

再举一个来自文献[1]的例子,假设如今有一个语料库,咱们统计了下面一些词出现的数量

 


 

下面这个几率做为其余一些已知条件给出: 
 
P(i|<s>)=0.25P(english|want)=0.0011P(food|english)=0.5P(</s>|food)=0.68P(i|<s>)=0.25P(english|want)=0.0011P(food|english)=0.5P(</s>|food)=0.68

下面这个表给出的是基于Bigram模型进行计数之结果 

 

例如,其中第一行,第二列 表示给定前一个词是 “i” 时,当前词为“want”的状况一共出现了827次。据此,咱们即可以算得相应的频率分布表以下。 

 

由于咱们从表1中知道 “i” 一共出现了2533次,而其后出现 “want” 的状况一共有827次,因此P(want|i)=827/25330.33P(want|i)=827/2533≈0.33 
如今设s1=<s>iwantenglishfood</s>s1=“<s>iwantenglishfood</s>” ,则能够算得 
 
P(s1)=P(i|<s>)P(want|i)P(english|want)P(food|english)P(</s>|food)=0.25×0.33×0.0011×0.5×0.68=0.000031P(s1)=P(i|<s>)P(want|i)P(english|want)P(food|english)P(</s>|food)=0.25×0.33×0.0011×0.5×0.68=0.000031

 


使用N-Gram模型时的数据平滑算法

有研究人员用150万词的训练语料来训练 trigram 模型,而后用一样来源的测试语料来作验证,结果发现23%的 trigram 没有在训练语料中出现过。这其实就意味着上一节咱们所计算的那些几率有空为 0,这就致使了数据稀疏的可能性,咱们的表3中也确实有些为0的状况。对语言而言,因为数据稀疏的存在,极大似然法不是一种很好的参数估计办法。

这时的解决办法,咱们称之为“平滑技术”(Smoothing)或者 “减值” (Discounting)。其主要策略是把在训练样本中出现过的事件的几率适当减少,而后把减少获得的几率密度分配给训练语料中没有出现过的事件。实际中平滑算法有不少种,例如: 
  ▸ Laplacian (add-one) smoothing 
  ▸ Add-k smoothing 
  ▸ Jelinek-Mercer interpolation 
  ▸ Katz backoff 
  ▸ Absolute discounting 
  ▸ Kneser-Ney

对于这些算法的详细介绍,咱们将在后续的文章中结合一些实例再来进行讨论。


A Final Word

若是你能从前面那些繁冗、复杂的概念和公式中挺过来,恭喜你,你对N-Gram模型已经有所认识了。尽管,咱们还没来得及探讨平滑算法(但它即将出如今个人下一篇博文里,若是你以为还未过瘾的话),可是其实你已经掌握了一个相对powerful的工具。你能够能会问,在实践中N-Gram模型有哪些具体应用,做为本文的结束,主页君便在此补充几个你曾见过的或者曾经好奇它是如何实现的例子。

Eg.1 
搜索引擎(Google或者Baidu)、或者输入法的猜测或者提示。你在用百度时,输入一个或几个词,搜索框一般会如下拉菜单的形式给出几个像下图同样的备选,这些备选实际上是在猜测你想要搜索的那个词串。再者,当你用输入法输入一个汉字的时候,输入法一般能够联系出一个完整的词,例如我输入一个“刘”字,一般输入法会提示我是否要输入的是“刘备”。经过上面的介绍,你应该可以很敏锐的发觉,这实际上是以N-Gram模型为基础来实现的,若是你能有这种觉悟或者想法,那我不得不恭喜你,都学会抢答了!

 


 

 

Eg.2 
某某做家或者语料库风格的文本自动生成。这是一个至关有趣的话题。来看下面这段话(该例子取材自文献【1】):

“You are uniformly charming!” cried he, with a smile of associating and now and then I bowed and they perceived a chaise and four to wish for.

你应该尚未感受到它有什么异样吧。但事实上这并非由人类写出的句子,而是计算机根据Jane Austen的语料库利用trigram模型自动生成的文段。(Jane Austen是英国著名女做家,表明做有《傲慢与偏见》等)

再来看两个例子,你是否能看出它们是按照哪位文豪(或者语料库)的风格生成的吗?

  • This shall forbid it should be branded, if renown made it empty.
  • They also point to ninety nine point six billion dollars from two hundred four oh three percent of the rates of interest stores as Mexico and Brazil on market conditions.

答案是第一个是莎士比亚,第二个是华尔街日报。最后一个问题留给读者思考,你以为上面两个文段所运用的n-gram模型中,n应该等于多少?


推荐阅读和参考文献:

[1] Speech and Language Processing. Daniel Jurafsky & James H. Martin, 3rd. Chapter 4 
[2] 本文中的一些例子和描述来自 北京大学 常宝宝 以及 The University of Melbourne “Web Search and Text Analysis” 课程的幻灯片素材

来自 https://blog.csdn.net/baimafujinji/article/details/51281816
相关文章
相关标签/搜索