如何计算给定一个unigram语言模型_N-Gram语言模型初探

本文介绍了n-gram语言模型的基本概念,包括n-gram模型的概率计算公式和n-gram的产生。文章还探讨了二元语法模型的计算示例,并介绍了kenlm工具的安装、训练模型和应用方法,展示了如何使用kenlm进行query纠错,通过计算ngram_score和ppl_score评估查询的通顺度。
摘要由CSDN通过智能技术生成

语言模型(Language Model,LM)在自然语言处理中占有十分重要的地位,尤其在基于统计的语音识别、机器翻译、分词和 query纠错等相关应用中有着广泛的应用。目前主要采用的是 n 元语法模型(n-gram model)。笔者在工作用中应用到了 query改写和 query 的纠错,均起到了不错的应用效果,本文将从一下几点介绍 n-gram 语言模型。

n-gram 语言模型基本概念

n-gram 语言模型的工具 — kenlm

n-gram 语言模型的应用

一、n-gram 语言模型基本概念

1.1、n-gram 概念

语言模型通常是一句话或者一个词 \(s\) 的概念分布 \(p(s)\),这里 \(p(s)\) 试图反映的是 \(s\) 出现的概率。例如,一个人说话的每 \(100\) 个句子中大约有 \(10\)句是 “这个需求什么时候上线?”(大概是产品经理👊),则可以认为:

\[

p(这个需求什么时候上线?)=0.1 \tag{1.1}

\]

而对于句子 “这个需求马上上线!”(大概是程序员🙄),几乎没有程序员说这样的话,我们认为程序员说这句话的概率为 \(0%=\)。所以需要注意的是,语言模型与句子是否合法是没有相关性的,即使一个句子符合语法逻辑,我们仍然可以认为它出现的概率为 \(0\),这个概率和你训练模型的数据是相关的。

对于由 \(m\) 个词(这个词可以是字、词或短语等)组成的句子 \(s=w_{1}w_{2}...w_{m}\) ,其概率计算公式为:

\[

\begin{align}

p(s)

&=p(w_{1})p(w_{2}|w_{1})p(w_{3}|w_{1}w_{2})...p(w_{m}|w_{1}...w_{m-1}) \tag{1.2} \\

&=\prod_{i=1}^{m} p(w_{i}|w_{1}...w_{i-1}) \tag{1.3}

\end{align}

\]

1.2、n-gram 的产生

上式中,产生第 \(i\) 个词的概率由已经产生的 \(i-1\) 个词 \(w_{1}w_{2}...w_{i-1}\) 序列 (称作 history)决定的。这种方法计算概率有一个弊端,如果history的长度为 \(i-1\),那么就有\(L^{l-1}\) 种不同的history(\(L\) 为词表的长度),我们要计算所有 \(L^{l-1}\) 种不同history情况下产生第 \(i\) 个词的概率,这样的情况下,模型中就有 \(L^{l}\) 个自由参数 \(p(w_{i}|w_{1}...w_{i-1})\),假设 \(L=5000\),\(i=3\) 自由参数的数目就是 \(1250\)亿个,这对于我们训练模型和应用模型来说是几乎不可能实现的。为了解决这个问题,可以将history \(w_{1}w_{2}...w_{i-1}\) 序列按照某种法则映射到等价类 \(E(w_{1}w_{2}...w_{i-1})\),而等价类的数目远远小于不同history的数目,假定:

\[

\begin{align}

p(w_{i}|w_{1},w_{2},...,w_{i-1})=p(w_{i}|E(w_{1},w_{2},...,w_{i-1})) \tag{1.4}

\end{align}

\]

那么,自由参数的数目将会大大减少。有很多种方法可以将history划分为等价类,一种比较实际的做法是,将两个history \(w_{i-n+2}...w_{i-1}w_{i}\) 序列和\(v_{k-n+2}...v_{k-1}v_{k}\) 映射到同一个等价类,当且仅当这两个history最近的 \(n-1\)(\(1\leq{n}\leq{m}\))个词相同,即如果 \(E(w_{1}w_{2}...w_{i-1}w_{i})\) \(=\) \(E(v_{1}v_{2}...v_{i-1}v_{i})\),当且仅当 \(w_{i-n+2}...w_{i-1}w_{i}\) \(=\) \(v_{i-n+2}...v_{i-1}v_{i}\)。

满足以上条件的语言模型称为 \(n\)元语法或者 \(n\)元文法(n-gram)。通常情况下,\(n\)的取值不能太大,否则等价类太多,自由参数过多的问题仍然不能解决,因此在实际应用中,\(n\) 取值为 \(3\) 的情况较多。

当 \(n=1\)时,即出现在第\(i\)位上出现的词 \(w_{i}\) 独立于history。一元文法计作 unigram或者uni-gram。

当 \(n=2\)时,即出现在第\(i\)位上出现的词 \(w_{i}\) 仅与前面一个history词 \(w_{i_1}\) 有关。二元文法计作 bigram或者bi-gram。

当 \(n=3\)时,即出现在第\(i\)位上出现的词 \(w_{i}\) 仅与前面两个history词 \(w_{i-2}w_{i-1}\) 有关。三元文法计作 trigram或者tri-gram。

1.3、n-gram 计算示例

以二元语法模型为例,根据前面的解释,我们可以近似地认为,一个词的概率只依赖于它前面的一个词,那么,

\[

\begin{align}

p(s)

&=\prod_{i=1}^{m} p(w_{i}|w_{1}...w_{i-1}) \tag{1.5}\\

&\approx \prod_{i=1}^{m} p(w_{i}|w_{i-1}) \tag{1.6}

\end{align}

\]

另外,有两点值得注意:

为了使得 \(p(w_{i}|w_{i-1})\) 对于 \(i=1\)有意义,在句子的开头加上句首标记 \(\),即假设 \(w_{0}\) 就是 \(\)。

为了使得所有句子的概率之和 \(\sum_{i} p(s)\) 等于 1,需要在句子结尾加上一个句尾标记 \(\),并包含在概率计算公式(5-3)的乘积中。

例如,我们计算概率 \(p(我爱工作)\),可以这样计算:

\[

\begin{align}

p(我爱读书)=p(我|)p(爱|我)p(读|爱)p(书|读)p(|书) \tag{1.7}

\end{align}

\]

为了估计 \(p(w_{i}|w_{i-1})\) 条件概率,可以简单的计算二元语法 \(w_{i-1}w_{i}\) 在训练语料中出现的概率,然后归一化。如果用 \(c(w_{i-1}w_{i})\) 表示二元语法 \(w_{i-1}w_{i}\) 在给定文本中出现的次数,我们采用最大似然估计计算条件概率的公式如下:

\[

\begin{align}

p(w_{i}|w_{i_1})= \frac{c(w_{i-1}w_{i})}{\sum_{w_{i}}c(w_{i-1}w_{i})} \tag{1.8}

\end{align}

\]

对于 \(n>2\) 的 \(n\)元语法模型,条件概率中要考虑前面 \(n-1\)个词的概率,为了使得公式 (1.8) 对于 \(n>2\) 成立,取:

\[

\begin{align}

p(s)= \prod_{i=1}^{m+1} p(w_{i}|w_{i-n+1}^{i-1}) \tag{1.9}

\end{align}

\]

其中,\(w_{i}^{j}\) 表示词 \(w_{i}...w_{j}\),那么 \(w_{-n+2}\) 到 \(w_{0}\) 为 \(\),\(w_{m+1}\) 为 \(\)。同样的用最大似然估计计算条件概率:

\[

\begin{align}

p(w_{i}|w_{i-n+1}^{i-1})= \frac{c(w_{i-n+1}^{i})}{\sum_{w_{i}}c(w_{i-n+1}^{i})} \tag{1.10}

\end{align}

\]

具体的计算实例可以参考宗成庆老师著《统计自然语言处理(第2版)》

二、n-gram 语言模型的工具 — kenlm

限于篇幅,这里我们仅仅介绍如何安装和使用kenlm,详细信息参考kenlm官网,后续另开一文详细介绍这个工具中n-gram分数和ppl_socre(语句通顺度)的计算过程。

2.1、安装

笔者成功在macOS和centos上成功安装并使用了kenlm,windows下使用需要用cygwin 64模拟linux环境使用。需要安装Boost 和zlib以及gcc。

yum -y install gcc gcc-c++ boost boost-devel zlib zlib-devel

安装好环境后就可以安装编译kenlm了:

git clone https://github.com/kpu/kenlm.git

mkdir -p build

cd build

cmake ..

make -j 4

2.2、训练模型

训练

/bin/lmplz -o 3 --verbose_header --text corpus.txt --arpa kenlm.arpa

由于.arpa文件较大,可以转化为二进制文件

bin/build_binary kenlm.arpa kenlm.klm

2.3、应用

通过安装 kenlm 的 python sdk 后我们就可以使用了。

import kenlm

strings = ["蒙 牛 纯 牛 奶", "蒙 牛 咖 啡 奶", "蒙 牛 存 牛 奶"]

kn_model = kenlm.Model('kenlm.klm')

for i in range(len(strings)):

print("query: ", strings[i])

print("ngram_score: ", kn_model.score(strings[i], bos=True, eos=True))

print("ppl_score: ", kn_model.perplexity(strings[i]))

print("========================================")

得到结果如下:

query: 蒙 牛 纯 牛 奶

ngram_score: -5.382442951202393

ppl_score: 7.889942264061641

========================================

query: 蒙 牛 咖 啡 奶

ngram_score: -10.560464859008789

ppl_score: 57.554260281408254

========================================

query: 蒙 牛 存 牛 奶

ngram_score: -15.350200653076172

ppl_score: 361.7152136927609

========================================

需要补充的是,为了方便计算条件概率的乘积过程,我们将概率取对数,那么求积就转化成求和。从结果中我们可以得到结论:

越是常见的 query ,其对应的取对数后的概率(ngram_score)就越小,对应的条件概率越大,符合第一节我们的推理过程。

同样的,越是常见的query,其对应的语言通顺度 (ppl_score)也越小,至于ppl_score计算过程依赖于ngram_score,详情可参考官网论文。

三、n-gram 语言模型的应用

正如在前沿中所述,语言模型在基于统计的语音识别、机器翻译、分词和 query纠错等应用中有广泛应用,这里我们介绍一下其在query改错中的应用。

通常的流程是:

query 分词。一般常用多种方式(分词工具 + ngram)切分出尽量多的词组。

检测疑似错别字。主要是自定义的纠错逻辑以及分词后未登录词和通过n-gram平滑得到疑似错误字词。

疑似错别字寻找其正确候选集。主要是通过同音近形字、编辑距离词以及自定义混淆词,并通过词表过滤掉不在此表中的词,最终得到候选集。

语言模型纠正。自定义混淆词可以不用这一步,直接替换;其他几种情况通过语言模型计算语义通顺度,得到得分最小的作为正确词(通常需要有一个最小分词的阈值逻辑判断是否修改正确)。

笔者这里有一些纠错案例:

origin: 月饼莲蓉 | score: 234.13980266961732 ----> corrected: 莲蓉月饼 | score: 36.40022878394902

origin: 好奇纯绵棉柔巾 | score: 122.08895880088059 ----> corrected: 好奇纯棉棉柔巾 | score: 43.72525151069142

origin: 硬毛芽刷 | score: 3071.285575052603 ----> corrected: 硬毛牙刷 | score: 87.96634329493364

origin: 伊立牛奶 | score: 275.74163855910786 ----> corrected: 伊利牛奶 | score: 18.14028069230779

origin: 利伊牛奶 | score: 1009.111608062079 ----> corrected: 伊利牛奶 | score: 18.14028069230779

origin: 同人堂洗发液 | score: 646.2303979080308 ----> corrected: 同仁堂洗发液 | score: 27.881235951440328

origin: 军乐宝酸奶 | score: 244.22472729364367 ----> corrected: 君乐宝酸奶 | score: 12.32559814126351

origin: 伊犁牛奶 | score: 167.27450985853162 ----> corrected: 伊利牛奶 | score: 18.14028069230779

origin: 酒精噴雾 | score: 8307.144176007858 ----> corrected: 酒精喷雾 | score: 55.69452559695505

origin: 猎头肉 | score: 1074.2781682994482 ----> corrected: 猪头肉 | score: 45.298965075390726

最终笔者在实际应用中,纠错准确率能达到95%以上。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值