1. 简介

1.1 词表/语料

词表表示语言中包含的所有的词,比如对于中文来说:

ps: 实际上对于中文来说,词表可以分成两类:字表和词表。字表就是所有的汉字,词表指的是汉字组成的词。由于中文句子中不存在英文那种使用空格作为词与词之间的分割符的标志,因此怎样准确地进行分词也是中文自然语言处理的非常重要的工作。使用字作为句子的基本单位不存在分词的问题但是会丢失部分以词作为基本单位的语义信息。因此,以字和词作为句子的基本单位各有优劣。我们在这里讨论语言模型的时候以词作为基本单位。

令 $x = w_1w_2…w_n$ 表示一个句子,其中 $w_i \in \mathcal{V}$ 以及 $i \in \{ 1, …, (n-1)\}, n\ge 1$。令 $V$ 表示词表 $\mathcal{V}$ 的大小,写作 $|\mathcal{V}|$。

我们假设 $w_n$ 是一个特殊符号—— $STOP$ ,这个符号包含在词表中(在后面的内容中我们可以看到为什么要定义这样一个符号了),比如:

我爱北京天安门STOP

我爱吃苹果STOP

小狗好可爱STOP

令 $\mathcal{V}^+ = \{x_1, x_2, …\}$ 表示所有由词表中的词组成的句子集合。$\mathcal{V}^+$ 是一个无限集,因为句子的长度是可以任意的($\ge 1$)。

一个理想的词表应该包含索要处理的语言的所有词汇,但实际上这是不可能的,因为:

  1. 每种语言(被广泛使用的)随时随刻都在产生新的词汇,有些是从别的语言借鉴而来,有些是随着新生事务的产生而产生的;
  2. 通常在构建词表的时候是通过训练语料,训练语料的数量是有限了,不可能包含该种语言的所有句子;
  3. 一个新词可以通过一些规则排列组合,而这样的组合可以产生的新词数量是无穷无尽的;
  4. 即使是同一个字/词在不同的时代都会有不同的拼写方式。

由于以上原因的限制,我们所用到的词表通常是有限的。而一个不能包含所有词汇的词表通常会遇到一个问题,当我们处理的句子中包含一个不在词表中的词时,我们称这个问题为 OOV(Out Of Vocabulary)问题,而这个不在词表中的词称之为“未登录词”。未登录词的出现会给自然语言处理任务带来很大的困扰,对于传统的统计方法来说,未登录词就意味着我们没有关于这个词的统计信息,无法将它纳入模型中;对于神经网络来说,我们没有办法对它进行向量化,也会对后续的模型训练和推理造成问题。

面对 OOV 问题通常有三种解决方式:

  1. 在词表中添加一个特殊符号,比如 “” ,用来表示所有未登录词。在训练数据中,将一些出现频率较低的词替换成 “”。
  2. 对于形态学上比较丰富的语言,可以训练一个模型将一个词分割成几个子串可以覆盖更多的词汇,比如对于英语来说,可以把 “starting” 分成 “start” 和 “ing” 两部分,这样即使词表中没有 “starting” 这个词,也不会遇到 OOV 问题。
  3. 使用字,而不是使用词来处理,或者字和词相互结合的方式。对于任何一种语言来说,它的最基本元素是字,而字的个数通常是有限的,任何一个词都可以使用字的组合来实现。比如英文只有26个字母,中文常用字也不过万。

最常用,成本最低的方式是将未登录词替换成 ,因此这里我们以这种方式为例介绍语言模型。

1.2 语言模型的定义

语言模型是一个随机变量的概率分布,随机变量的值来源于 $\mathcal{V}^+$ 中。因此语言模型可以定义为 $p:\mathcal{V}^+ \rightarrow \mathbb{R}$:

建立一个语言模型可以分成以下几步:

  1. 收集训练语料 $x_{1:n}=\{x_1, x_2, …, x_n\}, x_i \in \mathcal{V}^+$;
  2. 从语料中构建一个词表 $\mathcal{V}$;
  3. 定义概率分布 $p$ 的参数;
  4. 利用训练语料进行参数估计。

举例说明我们如何从训练语料中学习一个语言模型:

令 $c(x_i)$ 为训练语料中句子 $x_i$ 出现的次数,$N$ 为句子总数。我们可以令:

这就是一个最简单的语言模型(ps:我们给这个模型取个名字叫 Creepy model 吧,其实这个名字是 Noah A. Smith 取的),他表示每个句子出现的概率。当然,这是一个非常简单的例子,因为对于没有出现在训练语料中的句子他会认为概率为 0,因此这个语言模型不能泛化到训练语料中不存在的句子上。而在接下来要介绍的统计语言模型就是为了尽可能的提升这种泛化性。

1.3 我们为什么要进行语言建模?

从上面的介绍我们可以大致了解到,所谓语言模型其实就是判断一个句子出现的概率。一个经常被人们使用的句子出现的概率就会高,被人们使用频率较低的句子出现概率就会低,人们几乎不使用的句子出现的概率几乎为零。这实际上就是在判断一个句子是一个正常的为人所用的句子的概率,简而言之就是判断一句话是不是人话,比如,$p(我爱北京天安门) \gt p(但是风格的歌)$” 说明前一句比后一句更像人话。

比如早期的语言建模动机来源于 “噪声通道范式” (noisy channel paradigm),该模型广泛应用于语音识别和机器翻译领域。比如语音识别先利用声学模型产生多个候选结果,然后利用语言模型判断哪一句话更像人话进行结果重排。

所谓的噪声通道是指,假设我们有两个随机变量,$O$ 和 $D$,$O$ 是系统输入(可观测量),$D$ 是我们希望系统的输出。我们可以认为 $O$ 的值是密文, $D$ 的值是 $O$ 解密后的源文本。解密过程需要:

从上式可以看出,我们可以通过先验的明文($p(\pmb{d})$)和明文对应的密文($p(\pmb{o}|\pmb{d})$)建模对密文进行解密。

首先源分布随机生成一个明文值对应 $D$,然后通过在该明文中添加噪声(channel)生成观测值 $O$ 。而解码的过程就是将这个过程反过来,利用贝叶斯公式在给定观测值 $O=o$ 时得到最有可能的 $D$。

另一个很重要的原始是在语言建模过程中使用到的参数估计方法也可以被广泛应用于其他领域。

2. 语言模型

前面我们举例介绍了一个非常简单的语言模型,但是这种语言模型几乎没有任何泛化性,为了使得我们的语言模型具有一定的泛化性,接下来介绍统计语言模型。

考虑一个随机变量序列 $ \langle W_1, W_2, …, W_l \rangle $,序列中每个随机变量可以从 $\mathcal{V}$ 中取值。我们的目标是对于任意序列 $\langle w_1, w_2, …, w_l \rangle$ 对他们的联合概率进行建模,其中 $n \ge 1, w_i \in \mathcal{V} (i=1, …, l)$:

我们令 $W_0=w_0=START$ 表示任意句子的起始符。

上式的 $l$ 并不是一个定值,任何语言中的句子都有不同的长度,在建模过程中我们怎么确定每个句子的长度呢?这个时候我们在介绍词表的时候介绍了一个特殊的符号——$STOP$ 就发挥作用了,当我们建模过程中遇到这个符号的时候就表明这个句子到此为止了,此时句子的长度就是 $l$。

以上的建模过程没有涉及任何假设,我们可以看到序列中每个词的产生都依赖于在它之前出现的所有的词。上式中的每一部分都是一个条件概率,对于条件概率我们可以用简单的统计方法进行估计:

其中 $c(\cdot)$ 表示序列出现在语料中的次数。例如:序列 $\langle 我, 爱, 北京, 天安门 \rangle$

由此可得

其中 $n$ 表示训练语料的句子数, $\pmb{x}$ 表示由 $\langle w_0, w_1, …, w_n \rangle$ 构成的句子。

到了这一步我们会遗憾的发现,这样构建起来的语言模型又回到了原点,一切都未曾改变!

2.1 Uni-Gram 模型

上述建模过程我们没有涉及到任何假设,认为句子中每个词都依赖于在它之前的所有词,造成一个很尴尬的局面。现在我们走向另一个极端,给模型加入一个非常强的假设看看模型会变成什么样子:句子中的每个词都是相互独立互不依赖的。在这个假设下:

那么

此时我们需要统计句子中每个词在语料中出现的概率:

其中 $N$ 表示语料中所有的词的数量。注意在计算频率的时候 $START$ 符号是不进行计算的,因为它作为句子的起始符并不是由模型生成的;但是需要计算 $STOP$ 符号,因为他是作为模型生成的结果出现的,它的概率通常是 $n/N$。Uni-Gram 模型的参数量为 $V$,对应每个 $w \in \mathcal{V}$。

通常把 Uni-Gram 模型称之为“词袋模型”,因为它并不关注句子的顺序,只关注句子中每个词出现的概率。也就是说,对于相同的词组成的句子的概率是相同的。比如 $p(START我爱北京天安门STOP) = p(START天安门我爱北京STOP)$。

Uni-Gram 模型非常简单易于理解,建模也非常容易。虽然存在一些问题,但是在一些任务中却非常实用(比如信息检索)。

2.2 Bi-Gram 模型

前面两种语言模型走了两个极端:一个过于精细导致模型不能泛化,另一个过于粗放导致模型缺乏准确。下面我们取个折中的假设:假设句子中每个词出现的概率只依赖于它前面的那个词。

Bi-Gram 模型又叫一阶马尔科夫模型,因为这个假设是一阶马尔科夫假设。

经过前面的讨论,不难得出:

Bi-Gram 模型参数量为词表中任意两个词的随机组合,即 $\approx V^2$。

2.3 n-Gram 模型

既然可以有一阶马尔科夫假设,那也可以有二阶马尔科夫假设,三阶马尔科夫假设等等。这些依赖马尔科夫假设的模型我们统称为 “n-Gram” 语言模型。其中 $n$ 表示当前词出现的概率依赖于前 $n-1$ 个词。

n-Gram 模型的通用形式是:

为了简化公式符号,我们令 $\pmb{h}$ 为 $w_j$ 的历史词汇,比如对于 Bi-Gram 语言模型,$\langle 我,爱,北京,天安门 \rangle$ 中”北京“ 的历史词汇是”爱“,对于 Tri-Gram 语言模型来说,”北京“的历史词汇就是”我,爱“。此时,我们可以得到模型的参数:

n-Gram 模型中的 $n$ 值越大,模型越接近 Creepy model,$n$ 值越小模型越接近 Uni-Gram 模型。通常 $n$ 值的选取取决于训练数据,这里我们可以通过一些简单的步骤进行 $n$ 值的选取:

  1. 在构建训练数据的时候留出一部分作为验证集;
  2. 令 $\mathcal{N}$ 为你期望的 $n$ 的取值范围集合,比如 $\mathcal{N} = \{1,2,3,4,5\}$;
  3. 对于任意 $g \in \mathcal{N}$:(a)在训练数据集上构建 $g$-Gram 语言模型;(b)在验证集上计算 $g$-Gram 模型的困惑度(Perplexity,用来评估语言模型好坏的指标,后面会介绍);
  4. 选择困惑度最低的 $g$-Gram 模型。

2.4 语言模型评估:困惑度(Perplexity, ppl)

现在我们知道怎么样构建一个语言模型了,但我们应该怎样对我们构建的语言模型进行评估呢?目前通用的指标是困惑度(Perplexity),定义如下:

其中 $\bar{\pmb{x}}$ 是未参与训练的验证集(验证集不能参与训练这一点非常重要),$M$ 是验证集中词的数量。这个公式看起来好吓人哦,但是别着急,我们拆开了,掰碎了一点一点来看,这到底是个什么东西。

假设我们在训练数据集之外有一个验证数据集,$\langle \bar{\pmb{x}}_1, \bar{\pmb{x}}_2, …, \bar{\pmb{x}}_m \rangle$,其中每个 $\bar{\pmb{x}}_i, i \in \{1, 2, …, m\}$ 都是一个句子,每个句子都是由 $\langle w_{i1}, w_{i2}, …, w_{in} \rangle$ 组成的序列,$w_{ij}, j \in \{1,2,…,n\}$ 表示句子 $\bar{\pmb{x}}_i$ 中的第 $j$ 个词。

对于验证集中的每一句话我们都可以计算它的概率 $p(\bar{\pmb{x}}_i)$,那么对于整个验证集的所有句子出现的概率为:

直观上来讲,语言模型的质量越高,那么 $p(\bar{\pmb{x}})$ 的值应该越大。困惑度指标就是基于这一基本假设提出的。

假设我们的验证集中包含 $N$ 个词,我们定义:

这里先回答两个问题:

  1. 为什么要取对数?

    ① 语言模型作为一种概率分布,对于每一个句子出现的概率一定是满足 $0<p(\pmb{x})<1$ 的,即使每个句子出现的概率都能达到 0.99,当我们的验证语料包含 1 万个句子的时候,$\prod_i p(\pmb{x}_i) = 0.99^{10000} \approx 2.2e^{-44}$。所以我们看到,我们直接相乘的时候很容易造成极小的数字,最后精度溢出。而如果我们取对数,最后的结果会是 $\log_2(0.99^{10000})\approx -145$,这样就不会造成精度溢出问题了 。

    ② 对于计算机来说,乘法的效率要低于加法,因此,通过取对数将计算过程从乘法变成加法,提高了困惑度的计算效率。

  2. 为什么以 2 为底?

    首先,其实以几为底并不重要,因为通过下面的介绍我们会发现,最后还是会通过取 2 的指数来进行 “还原”,2 的作用相当于被抵消了。其次,以 2 为底可以使计算曲线更加平滑,不会造成大起大落的效果。

    实际上对于这两个问题以上的解释并没有触及问题的本质,要真正理解这两个问题需要从信息熵的角度出发,具体可以看这里

有了以上定义,我们就可以定义困惑度了:

困惑度取了负指数,说明困惑度值越低,语言模型越好。为什么要这样定义呢?我们以最简单的情形加以说明。

假设我们有一个词表 $\mathcal{V}$ ,词表大小为 $V$,语言模型服从均匀分布,即对于语言模型来说词表中的每个词出现的概率是一样的,比如对于 Uni-Gram 来说 $p(w_i) = 1/V$,对于Bi-Gram 来说 $p(w_i|w_{i-1})=1/V$ 等等。我们将这样一个分布代入到上面的困惑度计算公式中会发现困惑度为 $V$,也就是词表的大小。

因此,我们可以认为,困惑度其实相当于一个等效词表,困惑度的值表明对于模型来说预测下一个词有多少种选择。比如困惑度为 120 时,当模型遇到“我爱”的时候需要从 120 个选择中准确找到“北京”这个词。(选择越多越困惑)

接下来我们一步一步解析困惑度的计算过程:

  1. 计算验证集中每个句子的概率:$p(\bar{\pmb{x}}_i), i \in \{1, 2, …m\}$;
  2. 对每个句子的概率取对数:$\log_2 p(\bar{\pmb{x}}_i), i \in \{1,2,…,m\}$;
  3. 计算每个词出现的平均概率:$\frac{1}{M} \sum_{i=1}^m \log_2p(\bar{\pmb{x}}_i), i \in \{1,2,…m\}$;
  4. 将平均概率作为指数计算:$2^{-\frac{1}{M} \sum_{i=1}^m \log_2p(\bar{\pmb{x}}_i)}, i \in \{1,2,…m\}$。

需要注意的是困惑度并不是一个完美的指标,困惑度降低并不一定意味着语言模型质量的提升,影响困惑度的因素主要有以下几个:

  1. 训练数据集越大,困惑度会下降得更低,十亿数据量的语料和十万数据量的语料训练效果是很不一样的;

  2. 数据中的标点会对模型的困惑度产生很大影响,一个句号能让困惑度波动几十,标点的预测总是不稳定;

  3. 预测语句中的“的,了”等无意义词(通用词)也对困惑度有很大影响,可能“我借你的书”比“我借你书”的指标值小几十,但从语义上分析有没有这些停用词并不能完全代表句子生成的好坏。

因此,在对比两个 模型的困惑度的时候要记住两个原则:

  1. 使用不同词表的语言模型对比困惑度没有意义;
  2. 概率和不等于 1 的语言模型困惑度没有意义。

所以,评估语言模型时可以用困惑度大致估计训练效果,作出判断和分析,但它不是完全意义上的标准,具体问题还是要具体分析。

3. 语言模型参数估计中的稀疏化问题

前面在介绍语言模型参数估计的时候是通过统计相对频率的方式,这种方式我们称之为最大似然估计。在实际情景中,我们直接用最大似然估计进行参数估计的时候会有一个问题——数据稀疏化。

随着 n-Gram 模型中 $n$ 的增大,模型参数量以 $\approx V^n$ 的指数形式增长,如此大量的参数必然造成参数的稀疏,比如词表大小 $V=10000$,$n=3$ (这也是常见的语言模型),此时模型参数量为 $\approx 10^{12}$。拥有如此大量的参数而训练集有限的情况下,必然有很多计数为零的情况出现:

  1. 当 $c(\pmb{h},w_j)=0$ 时,会导致在训练集中没有出现的句子的概率为 0,这显然是不对的,因为训练集并没有包含所有的句子;
  2. 当 $c(\pmb{h})=0$ 时,会导致参数没有意义,因为作为分母是不能为 0 的。

为了使语言模型达到可用状态,我们需要使用平滑技术(smoothing)来消除这些统计词频为零的情况。平滑的基本思想就是从高概率的句子中抽取微小的概率分配给零概率的句子,有点类似“劫富济贫”。在使用平滑技术的时候有一点要非常小心,那就是我们必须保证平滑完以后的语言模型概率和仍为 1。这里介绍三种平滑技术:线性插值平滑(Linear Interpolation Smoothing)、加性平滑(Additive Smoothing)和减值平滑(Discounting Smoothing)。

3.1 线性插值平滑

前面介绍 Creepy 模型 和 Uni-Gram 模型的时候说这两个模型走了两个极端,然后通过调整 $n$ 来控制语言模型的精细度。由此可见,不同 $n$ 对应的语言模型的优缺点是不同的,比如 Uni-Gram 一定不会出现统计频率为零的情况出现,但忽略了词与词之间的依赖关系,而 Bi-Gram 模型可能出现零词频的情况,但比 Uni-Gram 多了对词之间依赖关系的考虑,如果我们将两个模型结合起来,既可以保留 Bi-Gram 的依赖关系,又可以保留 Uni-Gram 的非零词频的优点岂不是美哉。

其中 $\alpha \in [0,1]$。利用 Uni-Gram 对 Bi-Gram 进行插值,只要 $\alpha \gt 0$,既保证了 Bi-Gram 不会出现零概率,也保证了 Uni-Gram 的词依赖。这就是线性插值的基本思想:利用低元 n-Gram 模型对高元 n-Gram 模型进行线性插值,取长补短。

将上面的例子扩展到一般情况:

那么我们如何选择 $\alpha_k$ 的值呢?一个常用的方法和前面介绍 n-Gram 模型时,介绍选取 $n$ 值的方法一样,利用验证集找到最合适的 $\alpha_k$ 值。但是无论怎么选,有一个大的原则:在高元语言模型分母遇到零词频的时候令对应的 $\alpha_k=0$。 比如:Tri-Gram

当 $c(w_{j-2}, w_{j-1})=0$ 或 $c(w_{j-1})=0$ 时,相应的令 $\alpha_1=0$ 或 $\alpha_2=0$ 。

3.2 加性平滑

加性平滑又称拉普拉斯平滑,想法非常简单:只需要对每个统计单元词频加 $\alpha$,比如:

假设 $c(我,爱)$ 在训练语料中的出现的次数是 100,经过拉普拉斯平滑以后就变成了 $100+\alpha$,这样就一定能避免任意词频出现零的情况。而 $\alpha$ 的选取仍然作为模型的超参数,通过验证集进行选取。在实际的语言模型中通常 $\alpha \lt 1$。

拉普拉斯平滑存在两个问题:

  1. 由于训练语料中零词频的词数量太多,通常会占据整个概率分布中的一个很大的比例。通过拉普拉斯平滑会给零词频的词分配了太多的概率空间。
  2. 给所有未出现的词的词频加上相同的数,即认为这些词出现的概率是相同的,这种假设是否合理其实也值得商榷。而且,对于非零词频的词都增加同样的频度值,这是否合理,我们并不能给出一个明确的答案。

3.3 减值平滑

之所以会产生零词频,是因为那些非零词频的词出现的概率被模型高估了,占用了原本属于零词频词的概率份额,所以我们可以通过直接从非零词频对应的概率上扣除一部分概率还给零词频词,这就是减值平滑的基本思想。

以 Bi-Gram 为例,$\theta(w_j|w_{j-1}) = c(w_{j-1}, w_j)/c(w_{j-1})$,对于任意 $c(w_{j-1}, w_j)>0$ 的情况,我们都令

其中 $\beta \in [0,1]$(通常 $\beta=0.5$),这个 $\beta$ 就是我们从非零词频词的概率上抠下来的份额。

举个例子,假设 $c(w_{j-1}=我)=48$:

$(w_{j-1}, w_j)$ $c(w_{j-1}, w_j)$ $c^*(w_{j-1}, w_j)$ $c^*(w_{j-1})/c(w_{j-1}=我)$
(我,爱) $15$ $15-0.5=14.5$ $14.5/48$
(我,吃) $13$ $13-0.5=12.5$ $12.5/48$
(我,喜欢) $10$ $10-0.5=9.5$ $9.5/48$
(我,在) $10$ $10-0.5=9.5$ $9.5/48$

由此可见,我们从非零词频的词身上抠下来 $1/24$ 的概率,我们称抠下来这部分概率为缺失质量(missing mass)。有了这部分缺失质量我们就可以将它分配给零词频的词了,通常采用的分配方法是平均分配。

以上过程我们用形式化的语言描述为:

定义 $\beta \in (0, 1)$,任意给定 $\pmb{h}$,我们将训练数据分成两部分:

对于 $w_{j} \in \mathcal{A}(\pmb{h})$,定义:

最后将 $q(\pmb{h})$ 平均分配给所有 $w_j \in \mathcal{B}(\pmb{h})$:

最终我们得到:

这种减去一个固定值 $\beta$ 的减值法称为 “绝对减值平滑”,这种方法简单易操作,但缺点也很明显:在每个非零词频词上减去相同的词频,然后平均分配给不同的零词频词,其中蕴含的平权假设过于绝对。因此,针对这个问题人们又提出一个古德-图灵减值平滑,其基本思想是未出现的词的词频和只出现过一次的词的词频相同,然后再去调整只出现过一次的词频使整个概率和为 1。关于这种方法这里就不详细介绍了,有兴趣可以看这份资料

除了以上三种平滑方法以外,还有很多其他平滑方法,比如 Kneser-Ney Smoothing、Jelinek-Mercer Smoothing 等等,更多资料看这里

4. 总结

  1. 语言模型实际上就是一个概率分布,我们可以从中得到一个句子出现的概率或者可以根据之前出现的词推断下一个可能出现的词;
  2. 构建一个统计语言模型需要马尔科夫假设;
  3. 通常使用最大似然估计对模型参数进行估计;
  4. 在遇到词频为零的情况时可以使用平滑方法来解决;
  5. 使用困惑度对构建好的语言模型进行评估,语言模型质量越好困惑度就会越低。

统计语言模型存在几个缺点:

  1. 由于存在马尔科夫假设,尤其是通常使用的马尔科夫假设不会超过四阶,也就是通常的统计语言模型不会超过 5-gram,所以很难建立词与词之间的远距离依赖关系;
  2. 无法构建词与词之间的相似性关系。

 评论