手算KN-based ngram语言模型

什么是ngram语言模型

语言模型是NLP中最最基础的模块,从传统基于统计的ngram语言模型,再到基于深度学习的CNN,RNN语言模型,再到现在基于tranformer的预训练语言模型,每次语言模型的发展都能给整个NLP领域带来巨大推动。

由于传统的ngram语言模型具备原理简单,推断速度快等特点,所以至今依然在广泛应用在众多NLP任务中,尤其在计算资源受限的移动端。本文将系统介绍ngram语言模型的内部原理,计算方法及相关工具。

ngram语言模型计算方法

给定一句话:

\{w_{1},w_{2},w_{3},...,w_{n}\} ,这里的
w_{i} 为语言描述的最小单元,可以是字,也可以是词。语言模型是用来评价这一句话成立的概率:

P(w_{1}^{n}) = p(w_{1}) p(w_{2}|w_{1}) p(w3|w_{1}^{2})...p(w_{n}|w_{1}^{n-1})

对于其中的每一小项

p(w_{k}|w_{1}^{k-1}), 可以用频率来估计概率,即:

p({w_{k}|w_{1}^{k-1}}) = \frac{\#\{w_{1}^{k}\}}{\#\{w_{1}^{k-1}\}}

这里

\#\{​{w_{1}^{k}}\} 表示在整个语料中
\{w_{1},w_{2},w_{3},...,w_{k}\} 联合出现的次数。

理论上语料足够充足,就可以很好的用频率直接估计出概率,但实际操作中对于较长的序列

w_{1}^{k} 可能数量非常少,或者不存在。例如,计算: p(处理| {我, 爱, 自然, 语言}),可能整个语料中都没有{我, 爱, 自然, 语言, 处理}这个表达。

ngram语言模型的核心就在于一个强假设:当前词的概率分布只与前N-1个词有关,即:

p(w_{k}|w_{1}^{k-1}) = p(w_{k}|w_{k-(N-1)}^{k-1})

本质上 N-gram 模型的假设类似于马尔可夫链当中的 N-1 阶马尔可夫性假设。通常情况下n=1,2,3。对于再高阶的4-gram,5-gram就很少见,因为需要非常大的语料才能训练充分高阶的语言模型,而且模型本身的体积也会非常大(占内存)。

  • 当n=1时为unigram:当前词的概率分布与历史信息无关
  • 当n=2时为bigram:当前词的概率分布只和前一个词有关
  • 当n=3时为trigram:当前词的概率分布只和前两个词有关

所以假设n=2,p(处理| {我, 爱, 自然, 语言}) = p(处理| 语言) = #{语言, 处理} / #{语言} ,这样相对而言就可算了。

基于kenlm的ngram语言模型训练

运用ngram语言模型目前最便捷的工具就是kenlm,可快速实现语言模型的训练与应用。

首先准备一份语言模型训练语料(test_corpus.txt)注意每个词之前需要空格分割,如果训练基于字的语言模型,则每个字之前用空格分割。

模型
语言 模型
传统
模型
语言

关于kenlm的安装网上有很多教程,实际操作的过程中也确实有坑,为了避免踩坑,可直接采用docker来获得已安装了kenlm的环境。具体参见:GitHub - nghuyong/kenlm-docker: docker for kenlm 。下面采用kenlm训练一个bigram语言模型:

# 拉取镜像
docker pull nghuyong/kenlm
# 启动并进入容器
docker run -it -v $(pwd):/var nghuyong/kenlm bash
# 容器内训练kenlm
./lmplz -o 2 --verbose_header --text /var/test_corpus.txt --arpa /var/arpa.kenlm

这样就完成了语言模型的训练,并获得arpa模型文件。

下面是输出的apra文件

# Input file: /var/test_corpus.txt
# Token count: 6
# Smoothing: Modified Kneser-Ney
\data\
ngram 1=6
ngram 2=7

\1-grams:
-0.89085555     <unk>   0
0       <s>     -0.22184873
-0.89085555     </s>    0
-0.46488678     模型    0
-0.69896996     语言    -0.30103
-0.69896996     传统    -0.30103

\2-grams:
-0.89085555     模型 </s>
-0.50267535     语言 </s>
-0.24850096     传统 </s>
-0.44889864     <s> 模型
-0.37527603     语言 模型
-0.56863624     <s> 语言
-0.6575773      <s> 传统

\end\

可以看到生成的arpa文件包括ngram的统计值以及ngram的概率。基于这份arpa文件就可以计算一句话的概率分布了。

再看一下kenlm训练过程输出的日志情况:

=== 1/5 Counting and sorting n-grams ===
Reading /data/mm64/rightyonghu/code/kenlm/build/bin/test_corpus.txt
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100                                                                    
****************************************************************************************************                                                                    
Unigram tokens 6 types 6
=== 2/5 Calculating and sorting adjusted counts ===
Chain sizes: 1:72 2:33536714342
Statistics:
1 6 D1=0.5 D2=0.5 D3+=3
2 7 D1=0.5 D2=1.25 D3+=3
Memory estimate for binary LM:
type       B
probing  292 assuming -p 1.5
probing  320 assuming -r models -p 1.5
trie     226 without quantization
trie    1235 assuming -q 8 -b 8 quantization
trie     226 assuming -a 22 array pointer compression
trie    1235 assuming -a 22 -q 8 -b 8 array pointer compression and quantization
=== 3/5 Calculating and sorting initial probabilities ===
Chain sizes: 1:72 2:112
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100                                                                    
####################################################################################################                                                                    
=== 4/5 Calculating and writing order-interpolated probabilities ===
Chain sizes: 1:72 2:112
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100                                                                    
####################################################################################################                                                                    
=== 5/5 Writing ARPA model ===
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100                                                                    
****************************************************************************************************                                                                    
Name:lmplz      VmPeak:33297604 kB      VmRSS:3344 kB   RSSMax:8928344 kB       user:0.484      sys:3.636       CPU:4.12075     real:4.12281  

根据日志可以看出,kenlm在训练语言模型时候分成了5个主要步骤:统计并排序ngram,计算并排序调整就计数,计算并排序初始概率,计算并写入差值概率以及生成arpa模型文件。

下面我们就将细致拆解这个几个步骤,给定语料,硬核的手算出arpa模型。

手算基于KN的语言模型

继续以test_corpus.txt为语料,手算一个bigram的语言模型。

模型
语言 模型
传统
模型
语言

1. ngram初始统计

第一步是进行ngram的统计,因为这里训练bigram的语言模型,所以需要统计unigram以及bigram的数量。

在进行统计之前需要先给语料中每句话的开始和结束加上特殊的token:<s> 和 </s>。这样语料进一步处理成:

<s> 模型 </s>
<s> 语言 模型 </s>
<s> 传统 </s>
<s> 模型 </s>
<s> 语言 </s>

根据以上语料统计ngram的数量

unigramcount
<s>5
传统1
语言2
模型3
</s>5
bigramcount
<s> 模型2
<s> 语言2
<s> 传统1
语言 模型1
模型 </s>3
传统 </s>1
语言 </s>1

2. ngram计数调整

对于N-gram的语言模型,调整技术主要针对n<N的ngram进行计数调整。核心是将计数从原先的直接数量统计调整为可接词数量的统计。具体的计算方法如下:

a(w_{1}^{n}) = \left \{  \begin{array} {ll} & c(w_{1}^{n}) & n=N \quad or \quad w_{1}=<s> \\ & |v:c(v:w_{1}^{n}) > 0| & otherwise \end{array}  \right.

这里的

c(w_{1}^{n}) 表明对于
w_{1}^{n} 语料中的直接计数,
a(w_{1}^{n})表明调整后的计数。当n=N或者
w_{1} 为<s>时不需要调整计数;对于其他情况,需要将计数调整为
w_{1}^{n} 之前可接词的数量。

所以调整后计数的结果为:

unigramadjust countreason
<s>5w1 = <s>, a = c = 5
传统1| {<s>, 传统} | = 1
语言1| {<s>, 语言} | = 1
模型2| {<s>, 模型},
{语言, 模型} | = 2
</s>3| {模型, </s>},
{传统, </s>}, {语言, </s>} | = 3
bigramadjust countreason
<s> 模型2n = N, a = c =2
<s> 语言2n = N, a = c =2
<s> 传统1n = N, a = c =1
语言 模型1n = N, a = c =1
模型 </s>3n = N, a = c =3
传统 </s>1n = N, a = c =1
语言 </s>1n = N, a = c =1

3. 计数打折

计数打折的思想为:对于出现频率较高的ngram减少一点对最终的概率影响不会很大,可将其加到那些未出现的ngram上;对于出现频率较低的ngram则不能减少。

具体根据Chen and Goodman提出的打折公式进行计算

D_{n}(k) = \left \{ \begin{array}{} & 0 & k=0 \\ & k - \frac{(k+1)t_{n,1}t_{n,k+1}}{(t_{n,1}+2t_{n,2})t_{n,k}} & 1\leq k \leq 3 \\ & D_{n}(3) & k > 4  \end{array} \right.

这里的

t_{n,k} 表示出现了k次的ngram个数,这里的
k\in [1,4]

t_{n,k} = |w_{1}^{n}:a(w_{1}^{n} ) = k|
t_{n,k} (n=1,2; k=1,2,3,4)valuereason
t_{1,1}2n=1, |a(语言) , a(传统) | = 2
t_{1,2}1n=1, |a(模型) | = 1
t_{1,3}1n=1, |a(</s>)| = 1
t_{1,4}0n=1, 不存在a为4的unigram
t_{2,1}4n = 2, |a({<s>, 传统}), a({语言, 模型}), a({传统, </s>}), a({语言, </s>})| = 4
t_{2,2}2n = 2, |a({<s>, 模型}), a({<s>, 语言})| = 2
t_{2,3}1n = 2, |a({模型, </s>})| = 1
t_{2,4}0n=2, 不存在a为4的bigram
D_{n,k}value
D_{1}(1)1/2
D_{1}(2)1/2
D_{1}(3)3
D_{1}(4)3
D_{2}(1)1/2
D_{2}(2)5/4
D_{2}(3)3
D_{2}(4)3

4. 计算伪概率

伪概率的计算公式如下:

u(w_{n}|w_{1}^{n-1}) = \frac{a(w_{1}^{n}) - D_{n}(a(w_{1}^{n}))}{  \sum_{x}a(w_{1}^{n-1}x) }

可以看到,分子如果没有减去

D_{n}(k) 这项就是最基础的用概率估计频率,减去这个折扣项蕴含的思想是”劫富济贫“,即对那些出现次数较多的 n-gram 减少对应的次数,之后加到未出现的 n-gram 上面。
unigramu valuereason
<s>2/7a = 5, D_{1}(5) = 3,
a(传统) + a(语言) + a(模型) + a(</s>) = 7(5-3) / 7 = 2/7
传统1/14a = 1, D_{1}(1) = 1/2
(1-1/2)/7 = 1/14
语言1/14a = 1, D_{1}(1) = 1/2
(1-1/2)/7 = 1/14
模型3/14a = 2, D_{1}(2). = 1/2
(2-1/2)/7 = 3/14
</s>0a =3, D_{1}(3) = 3
(3-3) / 7 = 0
bigramu valuereason
<s> 模型3/20a = 2, D_{2}(2) = 5/4,
a({<s> 模型}) + a({<s> 语言}) + a({<s> 传统}) = 5(2-5/4)/5 = 3/20
<s> 语言3/20a = 2, D_{2}(2) = 5/4
(2-5/4)/5 = 3/20
<s> 传统1/10a = 1, D_{2}(1) = 1/2
(1-1/2)/5 = 1/10
语言 模型1/4a = 1, D_{2}(1) = 1/2
a({语言 模型}) + a({语言 </s>}) = 2(1-1/2) / 2 = 1/4
模型 </s>0a = 3, D_{2}(3) = 3
a({模型 </s>}) = 3(3-3) / 3 = 0
传统 </s>1/2a = 1, D_{2}(1) = 1/2
a({模型 </s>}) = 1(1-1/2)/1 = 1/2
语言 </s>1/4a = 1, D_{2}(1) = 1/2
a({语言 </s> }) + a({语言 模型}) = 2(1-1/2)/2= 1/4

注意,当n=1时,计算

\sum_{x}a(w_{1}^{n-1}x) 不考虑<s>, 因为<s>之前不可能再接入词

5. 回退值计算

定义回退值为接词的能力,具体回退值的计算公式如下:

b(w_{1}^{n-1} )= \frac{\sum_{i=1}^{3} D_{n}(i) |x:a(w_{1}^{n-1}x)=i| }{\sum_{x}a(w_{1}^{n-1}x)}
unigrambackoff valuereason
<s>3/5(1/2*1 + 5/4 * 2 + 3 * 0) / 5= 3/5
传统1/2(1/2*1 + 5/4*0 + 3*0) / 1 = 1/2
语言1/2(1/2*2 + 5/4 *0 + 3*0) / 2 = 1/2
模型1(1/2*0 + 5/4 *0 + 3*1) / 3 = 1
</s>00

注意</s>后面不可能接新词,所以backoff为0

6. 差值计算

差值的计算可根据递推公式:

p(w_{n}|w_{1}^{n-1}) = u(w_{1}^{n}|w_{1}^{n-1}) + b(w_{1}^{n-1})*p(w_{n}|w_{2}^{n-1})

根据此递推公式一定会递归到unigram,而unigram可直接由以下公式进行计算

p(w_{n}) = u(w_{n}) + b(\epsilon) \frac{1}{|V|}

这里的

\epsilon 为空字符串,即可以认为是
w_{1}^{0} ,所以回退值可计算为:

b(\epsilon)=\frac{D_{1}(1)*2 + D_{1}(2)*1 + D_{1}(3)*1}{\sum_{x\ne<s>} a(x)} = \frac{9}{14}

首先计算unigram插值后的概率值,注意对于<s>的概率直接置为0

unigrampreason
<s>00
传统1/51/14 + 9/14 * (1/5) = 14/70
语言1/51/14 + 9/14 * (1/5) = 14/70
模型12/353/14 + 9/14 * (1/5) = 24/70
</s>9/700 + 9/14 * (1/5) = 9/70
<unk>9/700 + 9/14 * (1/5) = 9/70

再根据递推公式,进一步计算bigram插值后的概率值

bigrampreason
<s> 模型249/7003/20 + 3/5 * 24/70 = 249/700
<s> 语言27/1003/20 + 3/5 * 1/5 = 27/100
<s> 传统11/501/10 + 3/5 * 1/5 = 11/50
语言 模型59/1401/4 + 1/2 * 24/70 = 59/140
模型 </s>9/700 + 1 * 9/70 = 9/70
传统 </s>79/1401/2 + 1/2 * 9/70 = 79/140
语言 </s>11/351/4 + 1/2 * 9/70 = 11/35

7. 生成语言模型

整理上文中计算的概率以及backoff,并计算log10

unigramplog10 pbackofflog10 backoff
<s>003/5-0.221849
传统1/5-0.6989701/2-0.301030
语言1/5-0.6989701/2-0.301030
模型12/35-0.46488710
</s>9/70-0.89085600
<unk>9/70-0.89085600
bigramplog10 p
<s> 模型249/700-0.448899
<s> 语言27/100-0.568636
<s> 传统11/50-0.657577
语言 模型59/140-0.375276
模型 </s>9/70-0.890856
传统 </s>79/140-0.248501
语言 </s>11/35-0.502675

进一步整理成arpa格式, 可以发现与之前kenlm计算的结果一致

\data\
ngram 1=6
ngram 2=7

\1-grams:
-0.890856	<unk>	0
0	<s>	-0.22184873
-0.69896996	传统	-0.30103
-0.69896996	语言	-0.30103
-0.46488678	模型	0
-0.89085555	</s>	0
-0.890856	<unk>	0

\2-grams:
-0.24850096	传统 </s>
-0.44889864	<s> 模型
-0.56863624	<s> 语言
-0.6575773	<s> 传统
-0.37527603	语言 模型
-0.89085555	模型 </s>
-0.50267535	语言 </s>

\end\


参考文献

Scalable Modified Kneser-Ney Language Model Estimation: aclanthology.org/P13-21

AI教室:传统语言模型+KenLM 实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nghuyong

您的鼓励是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值