NLP 笔记(四):语言模型

知识讲解

自然语言处理的两种基本方法:

  1. 基于规则的分析方法

    • 规则库开发
    • 推导方法设计

    理论基础:形式语言与自动机理论

  2. 基于语料库的统计方法

    • 语料库建设
    • 统计模型建立

    理论基础:数理统计、信息论、语料库

预备知识

概率论基础

熟悉以下概念及定理:

  • 概率
  • 最大似然估计(Maximization likelihood estimation, MLE)
  • 条件概率 (conditional probability)
  • 全概率公式
  • 贝叶斯法则(Bayes’ theorem)
  • 贝叶斯决策理论 (Bayesian decision theory )
  • 二项式分布 (binomial distribution)
  • 期望 (expectation)和方差 (variance)

信息论基础

熟悉以下概念:

  • 熵 (entropy)
  • 联合熵 (joint entropy)
  • 条件熵 (conditional entropy)
  • 相对熵 (relative entropy or Kullback-Leibler divergence)
  • 交叉熵 (cross entropy)
  • 困惑度(perplexity)

熵是信息论中重要的基本概念。 熵又称为自信息(self-information),表示信源 X 每发一个符号(不论发什么符号)所提供的平均信息量。熵也可以被视为描述一个随机变量的不确定性的数量。一个随机变量的熵越大,它的不确定性越大。那么,正确估计其值的可能性就越小。越不确定的随机变量越需要大的信息量用以确定其值。

语料库

语料库(corpus) 就是存放语言材料的仓库(语言数据库)
语料库一词在语言学上意指大量的文本,通常经过整理,具有既定格式与标记。其具备三个显著的特点:

  • 语料库中存放的是在语言的实际使用中真实出现过的语言材料
  • 语料库以电子计算机为载体承载语言知识的基础资源,但并不等于语言知识
  • 真实语料需要经过加工(分析和处理),才能成为有用的资源(生语料库是指收集之后未加工的语料库,相对而言,熟语料库是经过加工的语料库)

语料库语言学(corpus linguistics) 是基于语料库进行语言学研究。

研究的内容:

  • 语料库的建设与编纂
  • 语料库的加工和管理技术
  • 语言研究中语料库的使用
  • 语料库语言学在计算语言学中的应用

语料库的类型

按内容构成和目的划分

  • 异质的(heterogeneous):最简单的语料收集方法,没有特定的语料收集原则,广泛收集并原样存储各种语料
  • 同质的(homogeneous):只收集同一类内容的语料
  • 系统的(Systematic):根据预先确定的原则和比例收集语料,使语料具有平衡性和系统性,能够代表某一范围内的语言事实
  • 专用的(specialized):只收集用于某一特定用途的语料

按语言种类划分

  • 单语的(Monolingual)
  • 双语的(Bilingual)
  • 多语的(Multilingual),多语种语料库又可以再分为比较语料库(comparable corpora)和平行语料库(parallel corpora)。比较语料库的目的侧重于特定语言现象的对比,而平行语料库的目的侧重于获取对应的翻译实例。

按语料选取的时间划分

  • 共时语料库(syn-chronic corpus):为了对语言进行共时研究而建立的语料库
  • 历时语料库(diachronic corpus):为了对语言进行历时研究而建立的语料库

语料库设计需要考虑的问题

  • 代表性:一个语料库具有代表性,是指在该语料库上获得的分析结果可以概括成为这种语言整体或其指定部分的特性
  • 规模性:一般而言,在保证质量的前提下应足够大;随着语料库的增大,垃圾语料越来越多,语料达到一定规模以后,语料库功能不能随之增长
  • 结构性:目的地收集语料的集合,必须以电子形式存在,计算机可读的语料集合结构性体现在语料库中语料记录的代码、元数据项、数据类型、数据宽度、取值范围、完整性约束。
  • 平衡性:理想的情况是收入语料库的文本在题材、语体、时间跨度等方面有一个合理的平衡
  • 语料库的管理与维护:错误修正或改善版本升级语料库的检索系统、分析和处理工具的维护等

语言模型

在实际应用中,我们需要解决一类问题,即一个句子出现的概率(一个句子是否合理可以通过它出现的可能性大小来判定),例如:

  • 机器翻译: P(hign winds tonite) > P(large winds tonite)
  • 拼写纠错 :P(about fifteen minutes from) > P(about fifteen minuets from)
  • 语音识别 :P(I saw a van) >> P(eye awe of an)
  • 音字转换:P(你现在干什么 | nixianzaiganshenme) > P(你西安在干什么 | nixianzaiganshenme)
  • 自动文摘、问答系统等

语言模型的定义:

一个语言模型通常构建为字符串 s 的概率分布 p(s),这里 p(s) 试图反映的是字符串 s作为一个句子出现的频率,对于一个由n个基元构成的句子 s = w1, w2, … , wn,其概率计算公式可以表示为:
$$
P(s)=P(w_1)P(w_2|w_1)P(w_3|w_1 ,w_2)…P(w_n |w_1 ,w_2 … w_{n-1})=\prod_{i=1}^{n}P(w_i |w_1 …w_{i-1})
$$
当 i=1 时,
$$
P(s)=P(w_1|w_0)=P(w_1)
$$
说明:

  • wi 可以是字、词、短语或者词类等,称为统计基元
  • wi (1 ≤ i ≤ n) 的概率由已产生的 i-1 个词 w1, … , wi-1 决定;一般得,我们把前 i-1个词构成的一个序列,称为 wi 的历史 (history)

计算 P 最简单的方法是直接计数做除法
$$
P(w_i |w_1 ,w_2 …w_{i-1})=\frac{count(w_1 …w_{i-1},w_i)}{count(w_1 …w_{i-1})}
$$
比如,对于句子 NLP is so interesting 有:

P(“NLP is so interesting”) = P(NLP)✖P(is | NLP)✖P(so | NLP is) ✖P(interesting | NLP is so)

那么,这些概率的计算方法如下:
$$
P(interesting|NLP,is,so)=\frac{count(NLP,is,so,interesting)}{count(NLP,is,so)}
$$
这里,我们面临两个重要问题:

  1. 参数空间过大:随着历史基元数量的增加,不同的“历史” 按指数级增长。假设词汇表有 L 个不同的基元,那么i 基元就有 Li-1 种不同的历史情况,按照公式,我们必须考虑在所有 Li-1 种不同历史情况下产生第 i 个基元的概率。那么模型中有 Li 个自由参数。假如L=5000,i=3,那么自由参数的数目就是1250亿个!这使我们几乎不可能从训练数据中正确地估计出这些参数。

问题的解决:马尔可夫假设(Markov Assumption)
为了解决第2个问题,基于马尔可夫假设提出:下一个词的出现仅依赖于它前面的一个或者几个词。那么,假设下一个词依赖于前 k 个词,那么我们的 ,计算简化如下:
$$
P(s)\approx \prod_{i=1}^{n}P(w_i |w_{i-k} …w_{i-1})
$$
也就是说:
$$
P(w_i |w_1 …w_{i-1})\approx P(w_i |w_{i-k} …w_{i-1})
$$
这种情况下的语言模型称为 n 元文法(n-gram)

  • 当 k = 0时,即基元 wi 不与任何词相关,每个词之间相互独立;此时对应的模型叫做一元模型(Unigram model), n-gram 被称为一阶马尔可夫链(uni-gram 或 monogram)
  • 当 k = 1时, 即基元 wi 仅依赖与它前面的一个词,此时对应的模型称为二元模型(Bigram model)或者2阶马尔可夫链(bi-gram)
  • 当 k = 2时, 即基元 wi 仅依赖与它前面的两个词,n-gram 被称为3阶马尔可夫链(tri-gram)

同样地,我们能拓展到4-gram,5-gram…

  • 当 k = n-1 时,对应的模型为 n 元模型,即 N-gram

总的来说,N-gram 仍是一个不足的语言模型,因为有些句子存在长依赖关系 (long-distance dependencies)

通常来说,对于依赖词的个数

  • n 值愈大,下一个词出现的依赖条件更多,辨别力更大

  • n 值愈小,统计数据在训练语料库中出现的次数更多,统计信息更可靠

在实际使用经验中,我们常选取 bi-gram 或者 tri-gram 模型

N-gram概率的计算:极大似然估计(The Maximum Likelihood Estimate,MLE)

语言模型性能的评价:Lower perplexity(困惑度) = better model

  1. 数据稀疏(Sparse Data)严重:如果按这样计算,某些的句子中一系列词同时出现的次数是很少的,组合阶数高时尤其明显,这可能导致过多的条件概率趋于0

问题的解决:数据平滑

数据平滑(Data Smoothing) 的基本思想:调整最大似然估计的概率值,使零概率增值,使非零概率下调,“劫富济
贫”,消除零概率,改进模型的整体正确率

  • 基本约束:
    $$
    \sum P(w_i |w_1 ,w_2 …w_{i-1})=1
    $$
  • 基本目标:测试样本语言模型的困惑度越小越好

加一平滑法Add-one (Laplace) smoothing(最常用)

基本思想:每种情况出现的次数加一,即保证每个 n-gram 在训练语料中至少出现 1次

其他方法:

  • 减值法 (Discounting)
  • 古德-图灵(Good-Turing)估计法
  • Back-off(后退)方法
  • 绝对减值法
  • 插值法(Interpolation)
  • Stupid backoff

练习

了解基础的 Python NTLK 操作, 给定文本 text_en.txt,完成文本预处理:

  1. 分词、 提取词干
  2. 去停用词
  3. 标点符号过滤
  4. 低频词过滤(n <= threshold)
  5. 绘制离散图, 查看指定单词(Elizabeth, Darcy, Wickham, Bingley, Jane) 在文中的分布位置
  6. 对前 20 个有意义的高频词, 绘制频率分布图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import nltk
import string
import re

text = open(u'text_en.txt', encoding='utf-8', errors='ignore').read()

# 分词、提取词干
words=nltk.word_tokenize(text)
from nltk.stem import LancasterStemmer
stemmerlan=LancasterStemmer()
for i in range(len(words)):
words[i] = stemmerlan.stem(words[i])

# 去停用词
from nltk.corpus import stopwords
stops=set(stopwords.words('english'))
words_no_stop = [word for word in words if word.lower() not in stops]

# 标点符号过滤
def filter_punctuation(words):
new_words = []
illegal_char = string.punctuation + '【·! …() —: “”? 《》 、 ; 】 '
pattern=re.compile('[%s]' % re.escape(illegal_char))
for word in words:
new_word = pattern.sub(u'', word)
if not new_word == u'':
new_words.append(new_word)
return new_words
words_no_punc = filter_punctuation(words_no_stop)

# 低频词过滤
from nltk.probability import FreqDist
fdist = FreqDist(words_no_punc)
words_no_low_fre = [key for key in fdist if fdist[key] > 20]

# 写入文件
f = open('words.txt', 'w', encoding='utf-8')
for word in words_no_low_fre:
f.write(word+'\n')

# 绘制离散图, 查看指定单词(Elizabeth, Darcy, Wickham, Bingley, Jane) 在文中的分布位置
nltk_text = nltk.text.Text(nltk.word_tokenize(text))

check_words = ['Elizabeth', 'Darcy', 'Wickham', 'Bingley', 'Jane']
nltk_text.dispersion_plot(check_words[:])

# 对前 20 个有意义的高频词, 绘制频率分布图
fdist.plot(20)
0%