NLP.TM[36] | NLP之源:n-gram语言模型
【NLP.TM】
本人有关自然语言处理和文本挖掘方面的学习和笔记,欢迎大家关注。
往期回顾
不知道现在大家学NLP还有没有从n-gram语言模型开始学起了。但是n-gram语言模型应该是当前所有高端的bert、gpt之类的飞机大炮的绝对根基,我们来看看,最经典的n元语法到底是什么操作的。当然,本文篇章有限,文末给了不少参考文献,大家可以根据文章进行深入学习和尝试。
n元语法——条件概率的盛宴
n元语法,最初就来源于简单的条件概率,来看看吧。
对一个长为的句子,按照这些词汇构成句子的概率记作,按照、条件概率概念,可以得到:
第一行,其实就是形容整个句子按照顺序出现的概率。 第二行,按照条件概率,就能够拆分成,条件概率的连乘,这种连乘其实就能理解为,“每一个词的出现依赖于前面所有词的出现”,这就是根据历史的条件概率。 第三行,就是简单的抽象连乘。
那么我们需要估计的,其实就是上面第二行的每个p,但问题来了,这么多的词汇组合,要估计出所有来,几乎是不可能的,因此我们对他进行了简化,我们只考虑n个词,如n=1(即1-gram),则只考虑当前词,n=2(即2-gram),则考虑该词汇和前一位的词,在这种假设下,上面的(1)式可以简化为
如果考虑句子的开头和结尾,则是这样的:
在随机过程的视角看其实就是“一阶马尔科夫性”,第一位提出的应该是对条件概率和状态转移的理解非常深了。
平滑——没出现不代表不可能
问题继续来,在绝大部分情况下,我们的语料一直是不足的,NLP里面有一个特色,我把这种现象成为“尖峰厚尾性”,概念取自于时间序列领域,但在NLP里是适用的,一个NLP的语言模型要尽可能覆盖所有情况,长尾的场景次数虽少但不至于不出现,甚至相对频次会更高,而高频的内容会更少但是频次会更高,因此整个词频的分布就应该是高频词汇的词频会更高但是这样的词汇会更少,低频的词汇词频没有那么低,和传统地正态分布相比,就是尖峰厚尾了。
说的有一些玄乎,直接举个例子吧。我们常见的句子都是ABC,ADC之类的组合,但是有没有AEC的可能?再具体点,我想吃西瓜,我想吃苹果,我想吃XX,这里有很多可能,我们训练样本有限,不能遍历每一种情况,假设我们的样本里没有“我想是叉烧”,那条件概率就会出现0的情况,那整个句子就不可能出现了吗?答案当然是否定的,因此,这里就要用到——平滑。
我们仍然以2-gram为例来分析,先把条件概率拆分,看看没平滑参数估计是怎么做的:
其实就是简单的计数,然后我们就要在这里去做平滑,其实就是减少“0”的出现。
最简单的一种平滑就是加性平滑,也被称为拉普拉斯平滑:
一般地,取,表示词典库的大小。
kenlm——语言模型的实现
说完了基本的原理,我们就要看看怎么去实现。这块实现其实已经有非常成熟的包来做了,可惜不是python,而是一个c++的,就是kenlm,这个虽然有Python的API接口,但是接口里没有训练的,训练只能用c++打成的二进制文件才可用。
linux环境下把项目拉下来:https://github.com/kpu/kenlm,按照教程进行编译(cmake),当然过程中需要配置对应的包(boost、zlib等),
编译完成后,还需要对语料进行处理,这个训练过程本身不涉及具体任务,因此只需要语料即可,但对于中文,需要注意下面几个细节:
标点符号去掉。 繁简体转化、大小写转化、 分词或者单字都需要用空格分开。
然后就可以开始训练了。
bin/lmplz -o 3 --verbose header --text train_corpus.txt --arpa lm_model_20200816.arpa
训练以后,如果是python,其实可以转化为二进制,这样加载更快:
bin/build_binary -s lm_model_20200816.arpa lm_model_20200816.bin
这样就可以python执行了:
import kenlm
model = kenlm.Model('lm_model_20200816.arpa')
print(model.score('我 有 一 只 小 毛 驴 我 从 来 也 不 骑'))
有了语言模型,我们是可以评判句子出现的概率,从而可以用在判全、纠错等方面,虽然后续语言模型在持续发展,但是这种比较基础的细节的领域,n-gram语言模型还是有一定的优势。
指的注意的一个细节是,kenlm中使用的平滑方法,是Modified Kneser-ney smoothing方法(Heafield K, Pouzyrevsky I, Clark J H, et al. Scalable Modified Kneser-Ney Language Model Estimation[C]// Meeting of the Association for Computational Linguistics. 2013:690-696.),简单聊一聊他的思路,具体可以看看这篇论文:
对于搭配比较固定的词,可能他的词频比较高但实际上只有一两个特定搭配,如new york中的york,这种应该给他的1-gram一个比较低的分数。 将超高频的词的一些计数给到低频词,毕竟高频词其实已经有比较好的估计了。 预测阶段的回退,衡量词汇后面接不同词的能力,针对没有出现的情况,可以将高阶的n-gram降级为低阶的n-gram。
小结
n-gram语言模型本身作为始祖级别的存在,已经被后浪打翻,但是他对词汇的出现的理解,还是非常具有参考意义,通知,在一些特定任务上,还是可以作为一个比较强的baseline,因此,仍然非常推荐大家学习和理解。
参考文献
语言模型的原理:
统计自然语言处理。NLP经典书籍,没什么好说的。 https://blog.csdn.net/a635661820/article/details/43906731
kenlm的安装和使用,自己踩的坑比较多,根据这些基本能把坑都填了。
https://github.com/kpu/kenlm https://www.cnblogs.com/zidiancao/p/6067147.html https://blog.csdn.net/Nicholas_Wong/article/details/80013547 https://blog.csdn.net/benbenls/article/details/102898960 https://blog.csdn.net/bonjourdeutsch/article/details/107767307
有关kenlm下的原理讨论:
Heafield K, Pouzyrevsky I, Clark J H, et al. Scalable Modified Kneser-Ney Language Model Estimation[C]// Meeting of the Association for Computational Linguistics. 2013:690-696. https://blog.csdn.net/visionfans/article/details/50131397 https://blog.csdn.net/a635661820/article/details/43906731 https://blog.csdn.net/xueyuxueyuxueyu/article/details/104207249 https://blog.csdn.net/asrgreek/article/details/81979194