2.1 统计语言模型简介
2.1.1 语言模型任务
原始语句:南京市长江大桥
Sentence_1:南京;市长;江大桥
Sentence_2:南京市;长江大桥
很明显,第二种更合理。
模型指的是对事物的数学抽象,那么语言模型指的就是对语言现象的数学抽象
定义:语言模型任务是给句子一个分配概率的任务,也就是计算 P ( w ) P(w) P(w), w w w 是一句话。此外,语言模型也对给定单词在多个单词也就是一个词序列之后的可能性进行分析,并分配概率。
语料库:采样世界语句集合,得到的子集,就是样本空间。
贝叶斯公式: P ( A B ) = P ( A ∣ B ) P ( B ) = P ( B ∣ A ) P ( A ) P(AB) = P(A|B)P(B)=P(B|A)P(A) P(AB)=P(A∣B)P(B)=P(B∣A)P(A)
定义的数学解释:分配一个词在序列之后的概率(条件概率),给任意词序列分配概率。
语言模型就是给任何词序列分配一个概率。
P
(
w
i
∣
w
1
,
w
2
,
.
.
.
,
w
i
−
1
)
=
P
(
w
1
,
w
2
,
.
.
.
,
w
i
−
1
,
w
i
)
P
(
w
1
,
w
2
,
.
.
.
,
w
i
−
1
)
(2.1.1)
P(w_i|w_1,w_2,...,w_{i-1})=\frac{P(w_1,w_2,...,w_{i-1},w_i)}{P(w_1,w_2,...,w_{i-1})}\tag{2.1.1}
P(wi∣w1,w2,...,wi−1)=P(w1,w2,...,wi−1)P(w1,w2,...,wi−1,wi)(2.1.1)
我们定义一个统计模型如下:
P
(
w
1
;
n
)
=
P
(
w
1
)
P
(
w
2
∣
w
1
)
P
(
w
3
∣
w
1
:
2
)
P
(
w
4
∣
w
1
:
3
)
.
.
.
P
(
w
n
∣
w
1
:
n
−
1
)
(2.1.2)
P(w_{1;n})=P(w_1)P(w_2|w_1)P(w_3|w_{1:2})P(w_4|w_{1:3})...P(w_n|w_{1:n-1})\tag{2.1.2}
P(w1;n)=P(w1)P(w2∣w1)P(w3∣w1:2)P(w4∣w1:3)...P(wn∣w1:n−1)(2.1.2)
总结:语言模型任务的完美表现是预测序列中的下一个单词具有与人类参与者所需的相同或更低的猜测数目,这是人类智能的体现。此外,语言模型还可以用于对机器翻译和语音识别的结果进行打分。语言模型是NLP、人工智能、机器学习研究的主要角色。
缺点:
- 数据稀疏,越长的句子概率越低。
- 计算代价大,句子越长,要根据句子来索引计算和储存的 p 就越多。
- 最后一个词取决于这句话前面的所有词,是的高效建模整句话的难度大。
2.1.2 改进方法
最经典和传统的改进方法,就是n阶马尔可夫模型,也叫n元语法或者N-Gram。
一个k阶马尔可夫假设,假设序列中下一个词的概率只依赖于其前k个词。用公式表示如下( $i $ 为要预测的词的位置或索引,
w
w
w 代表词):
P
(
w
i
+
1
∣
w
1
,
i
)
≈
P
(
w
i
+
1
∣
w
i
−
k
i
i
)
(2.1.3)
P(w_{i+1}|w_{1,i}) \approx P(w_{i+1}|w_{i-k_ii})\tag{2.1.3}
P(wi+1∣w1,i)≈P(wi+1∣wi−kii)(2.1.3)
那句子的概率估计就变为:
P
(
w
1
:
n
)
≈
∏
i
=
1
n
P
(
w
i
∣
w
i
−
k
:
i
−
1
)
(2.1.4)
P(w_{1:n}) \approx \prod_{i=1}^{n} P(w_i|w_{i-k:i-1})\tag{2.1.4}
P(w1:n)≈i=1∏nP(wi∣wi−k:i−1)(2.1.4)
2.1.3 案例实现
【1】语料预处理
from collections import Counter
import numpy as np
"""语料"""
corpus = '''她的菜很好 她的菜很香 她的他很好 他的菜很香 他的她很好
很香的菜 很好的她 很菜的他 她的好 菜的香 他的菜 她很好 他很菜 菜很好'''.split() # 以空格为分隔符,将语料分开
"""语料预处理"""
counter = Counter() # 词频统计
for sentence in corpus:
for word in sentence:
counter[word] += 1
counter = counter.most_common()
lec = len(counter)
word2id = {counter[i][0]: i for i in range(lec)}
id2word = {i: w for w, i in word2id.items()}
print(word2id)
print(id2word)
【2】N-Gram建模训练
"""N-gram建模训练"""
unigram = np.array([i[1] for i in counter]) / sum(i[1] for i in counter)
print(unigram)
print("-------------------字到序号的转变-------------------")
bigram = np.zeros((lec, lec)) # + 1e-8
for sentence in corpus:
sentence = [word2id[w] for w in sentence]
print(sentence)
for i in range(1, len(sentence)):
bigram[[sentence[i - 1]], [sentence[i]]] += 1 # 对应词表位置词频加一(第一个词是3并且第二个词是0的情况词频加一)
print("-------------------相邻两个词出现的数量-------------------")
print(bigram)
print("-------------------相邻两个词出现的数量归一化-------------------")
for i in range(lec):
bigram[i] /= bigram[i].sum() # 对词频归一化,变为概率
print(bigram)
注意:
【3】句子概率
P ( w 1 : n ) ≈ ∏ i = 1 n P ( w i ∣ w i − k : i − 1 ) P(w_{1:n}) \approx \prod_{i=1}^{n} P(w_i|w_{i-k:i-1}) P(w1:n)≈∏i=1nP(wi∣wi−k:i−1)
"""句子概率"""
def prob(sentence):
s = [word2id[w] for w in sentence]
les = len(s)
if les < 1:
return 0
p = unigram[s[0]]
if les < 2:
return p
for i in range(1, les):
p *= bigram[s[i - 1], s[i]] #根据上边的概率矩阵计算得到
return p
print('很好的菜', prob('很好的菜'))
print('菜很好的', prob('菜很好的'))
print('菜好的很', prob('菜好的很'))
是根据上边的概率矩阵计算得到的, P ( w 1 , w 2 , w 3 , w 4 ) = P ( w 1 ) P ( w 2 ∣ w 1 ) P ( w 3 ∣ w 2 ) P ( w 4 ∣ w 3 ) P(w_1,w_2,w_3,w_4) = P(w_1)P(w_2|w_1)P(w_3|w_2)P(w_4|w_3) P(w1,w2,w3,w4)=P(w1)P(w2∣w1)P(w3∣w2)P(w4∣w3) ,是基于2-Gram计算的。
【4】生成排列组合
"""排列组合"""
def permutation_and_combination(ls_ori, ls_all=None):
ls_all = ls_all or [[]]
le = len(ls_ori)
if le == 1:
ls_all[-1].append(ls_ori[0])
ls_all.append(ls_all[-1][: -2])
return ls_all
for i in range(le):
ls, lsi = ls_ori[:i] + ls_ori[i + 1:], ls_ori[i]
ls_all[-1].append(lsi)
ls_all = permutation_and_combination(ls, ls_all)
if ls_all[-1]:
ls_all[-1].pop()
else:
ls_all.pop()
return ls_all
print('123排列组合', permutation_and_combination([1, 2, 3]))
"""给定词组,返回最大概率组合的句子"""
def max_prob(words):
pc = permutation_and_combination(words) # 生成排列组合
p, w = max((prob(s), s) for s in pc)
return p, ''.join(w)
print(*max_prob(list('香很的菜')))
print(*max_prob(list('她的菜香很')))
print(*max_prob(list('好很的他菜')))
print(*max_prob(list('好很的的她菜')))
传统语言模型的限制:
- 如果采用n元语法,长距离依赖无法解决
- 最大似然估计应用于n元语法,稀疏性无法解决
- 泛化性差,black car, blue car的出现,并不能有助于我们预测red car