本系列的上一篇文章介绍了 NLP 中处理分词的两种方法,其中基于统计语言模型的方法以巨大的优势胜出。
在上一篇文章的末尾,我们讲了优秀的算法模型在形式上应该是简洁优雅的。具体来说:
- 一个正确的数学模型在形式上应该是简洁优雅的。
- 一个正确的数学模型,在刚开始的时候可能还不如一个经过精心调教的错误模型准确。但是错误模型不论如何调教,因为方向错了,所以终究会有处理不了实际问题的时候。
- 正确的数学模型可能因为受到干扰而不准确。此时应该找出干扰、噪音,并解决它们,而不是简单凑合去修修补补。
这篇文章我们继续讲分词和统计语言模型。不过,这次的内容会比上次要深入、深奥,并且会涉及到一些数学推导,以及一些工程上的 Dark Side。不过,其中有些非常 Tricky 的技巧对于大多数读者来说没有必要阅读,而剩下的部分中简单的数学推导也不致枯燥。
统计语言模型的具体描述
上一篇文章简单介绍了统计语言模型的思想,这里将给出统计语言模型的具体描述和简化。不过,在此之前,我们先看一个更简单直观的例子。
一个简单的例子
下面这个例子我们已经见过,在文法上,这是一个比较复杂的句子。不过,虽然这个句子在文法上比较复杂,但是对于人类来说,理解它并不费劲。
- 由于理解自然语言,需要关于外在世界的广泛知识以及运用操作这些知识的能力,自然语言认知,同时也被视为一个人工智能完备的问题。
现在我们对句子略作修改,变成下面这样,理解起来就有些费劲了,但是勉强还能理解。
- 自然语言由于理解,需要知识广泛关于外在世界的以及运用这些知识操作的能力,认知自然语言,也被视为同时人工只能完备的一个问题。
如果对句子继续变形,变成下面这样,基本上就没人搞得懂这是在说什么了。
- 然自语言由理于解,需知要关泛识需广关泛于外在界世以及的运用及的些这识知操运用些的作能力,认语知自然语知言,也视被为同备时的人被工个问只视能完的备一个题问。
从文法上说,第一个句子合乎文法,因而容易理解;第二个句子虽然不合文法,但是勉强有迹可循;第三个句子则完全莫名其妙,自然就没法读懂。
上一篇文章告诉我们,这样基于文法的解释是合情合理的,但是在实际应用上却迈不开步子、走不出实验室。要解释这三个句子的情况,终究还是要依靠统计的方法。
我们把这三个句子分别记作 $S_1$, $S_2$, $S_3$,用 $P(S_k)$ 表示三个句子在人类交流中出现的概率(可能性)。于是事情就变得简单起来:$P(S_k)$ 越大,说明句子出现在人类交流中的概率越大;换言之,这个句子越符合人类交流的习惯(特别是文法规则),因此也就容易读懂。就实际情况来说,
\begin{align}
P(S_1)\approx 10^{-20}\notag\\
P(S_2)\approx 10^{-25}\notag\\
P(S_3)\approx 10^{-70}\notag\\
\end{align}
亦即,第一个句子出现的概率是第二个句子的一万倍,是第三个句子的 $10^{50}$ 倍。我已经不想去数 $10^{50}$ 这个数字写作中文后会有多少个「亿」字了。
统计语言的描述
上一小节我们看到根据句子的概率判断句子合理性效果是十分显著的。为了能将这个方法用于实际,我们必须解决概率的计算问题。
假定 $S$ 是一个有意义的句子,它由一串排序确定的词组成,即
$$S = w_1,,w_2,,\ldots,,w_n$$
这里每个 $w_k$ 表示一个词语,$n$ 是句子包含词语的数量,即句子的长度。
显而易见,把 $S$ 放在从古至今所有的语境里去检查概率是不可行的,因此我们需要做一个模型来估算。我们把 $P(S)$ 展开,得到
\begin{equation}
P(S) = P(w_1,,w_2,,\ldots,,w_n)
\label{eq:sentence-exp}
\end{equation}
考虑到 $w_1,,w_2,,\ldots,,w_n$ 是有特定顺序的。因此,记 $m = n-1$,则 \eqref{eq:sentence-exp} 可以展开成 \eqref{eq:contional-prob-sent} 中的条件概率形式。
\begin{equation}
\begin{aligned}
P(S) = {}& P(w_1,,w_2,,\ldots,,w_n) \\
= {}& P(w_1)\cdot P(w_2\mid w_1)\cdot P(w_3\mid w_1,,w_2)\cdot\cdots\cdot P(w_n\mid w_1,,w_2,,\ldots,, w_m)
\end{aligned}
\label{eq:contional-prob-sent}
\end{equation}
其中 $P(w_k\mid w_1,,w_2,,\ldots,, w_i),\quad (i = k - 1)$ 表示 $w_k$ 接在序列 $w_1,,w_2,,\ldots,, w_i$ 之后的条件概率。不难看出,$w_k$ 的概率,取决于它本身在整个语言中出现的概率,以及它前面 $i=k-1$ 个词顺序出现的概率。
学过概率论的读者应该知道,计算三元的条件概率(即计算类似 $P(w_3\mid w_1,,w_2)$)已经很困难了,更别说计算整个句子最后一个词的条件概率。因此,我们需要对模型进行简化。
数学专业的读者可能会发现,这样的条件概率,实际上是一个随机过程的概率。在随机过程中,为了简化分析,对此类过程有一个马尔科夫假设(Markov Hypothesis),即假设 $ w_k $ 的概率只和 $w_i,\quad (i = k - 1)$ 有关。即假设
$$ P(w_k\mid w_1,,w_2,,\ldots,, w_i) = P(w_k\mid w_i),\quad (i = k - 1). $$
这样一来,我们就有
\begin{equation}
\begin{aligned}
P(S) = {}& P(w_1,,w_2,,\ldots,,w_n) \\
= {}& P(w_1)\cdot P(w_2\mid w_1)\cdot P(w_3\mid w_1,,w_2)\cdot\cdots\cdot P(w_n\mid w_1,,w_2,,\ldots,, w_m) \\
= {}& P(w_1)\cdot P(w_2\mid w_1)\cdot P(w_3\mid w_2)\cdot\cdots\cdot P(w_n\mid w_m)
\end{aligned}
\label{eq:bigram-model}
\end{equation}
\eqref{eq:bigram-model} 即是二元统计语言模型(Bigram Model)。在形式上,二元模型是非常简洁优雅的,这正符合了我们之前对模型形式的预期。
条件概率的估算
模型已经构建完毕,接下来需要考虑如何解模,而核心问题就是如何估算条件概率 $P(w_k\mid w_i),\quad(i = k - 1)$。根据条件概率的定义
$$ P(w_k\mid w_i) = \frac{P(w_i,,w_k)}{P(w_i)},\quad(i = k - 1), $$
我们只需要在语料库(Corpus)中统计 $w_i,,w_k$ 和 $w_i$ 出现的次数,再除以语料库的体积,就能算出 $w_i,,w_k$ 和 $w_i$ 的频率。如果语料库足够大,我们就有大数定律作支撑,得到
\begin{equation}
P(w_k\mid w_i) = \frac{\langle w_i,,w_k\rangle/\langle\text{Corpus}\rangle}{\langle w_i\rangle/\langle\text{Corpus}\rangle} = \frac{\langle w_i,,w_k\rangle}{\langle w_i\rangle}
\label{eq:con-prob-solu}
\end{equation}
\eqref{eq:con-prob-solu} 给出了计算二元条件概率的一般方法,其中 $\langle w_i\rangle$ 表示 $w_i$ 在语料库中出现的次数。
至此,二元统计语言模型已经构建完毕,基本原理也已介绍完毕。数学在此展现了非凡的简洁和优雅,同时带给了我们极高的效率。当然,这个模型还很粗糙,要想让二元模型走出实验室,走向实际应用,还有很多细节需要讨论。接下来的讨论会有一些枯燥乏味,不过对数学细节不感兴趣的读者可以略过数学推导,把注意力放在模型的原理之上。
统计语言模型用于分词的细节讨论
二元模型的扩展
马尔科夫假设将当前词语的概率限定只与之前一个词有关。这样的假设能在很大程度上降低计算的复杂性,但是却和实际生活的情况相差甚远。比如
- 小明的铅笔
在这里「铅笔」之前的词是结构助词「的」。考虑到结构助词「的」的后面几乎可以出现任何名词,如果我们约定「铅笔」出现的概率只和「的」有关,那显然是不合理的。
为了解决这个问题,比较显然的办法是对马尔科夫假设做扩展。即,从假设当前词只与之前的一个词有关,扩展到与之前的 $p$ 个词有关。也就是将模型从二元模型扩展到 $p$-元模型。很显然,$p$ 的值越大,模型越接近真实情况;同时,计算量和计算的复杂度以及耗费的算力都会大大提升。因此,在实际使用中,需要在真实性和计算量之间取一个平衡点,互相取舍。
经过前人的大量实验,当 $p = 3$ 时,模型的效果比较好,算力要求也在容忍范围之内。当 $p$ 的取值从 $3$ 增加到 $4$ 的时候,效果的增长不太明显,算力的要求却大大增加。因此,在实际使用过程中,大都是三元模型;对准确性要求高,且不差钱的时候,一般会选择使用四元模型。
统计语言模型的局限性
统计语言模型的主要思想是通过统计人类的语言习惯,代替文法分析,判断某个句子的「合理性」(这个合理性通过概率的大小来量化)。
统计语言模型的局限性一个主要的方面来自计算机算力和容量的限制。这一点上一小节已经讨论过了。
另一方面,也有一些人为创造的歧义句子。这类句子,让真实的人类来阅读,也是可以有多种理解的;因此,统计语言模型也就不可能完美处理这类句子。比如
- 有小便宜 得大解脱
这是李淡愚先生妙笔生花,将污浊之地「净化」成「道场」的俗联。根据句读不同(还有多音字),可以理解出不同的意思。
- 有 | 小便(pian)宜,得(de) | 大解脱。
- 有小便(bian) | 宜,得(dei)大解 | 脱。
两种断句都有道理,都可以理解出通顺的意思。好在这类句子在生活中并不常见,对统计语言模型影响甚小。
统计语言模型的训练问题
根据之前的分析,我们知道,在统计语言模型正式工作之前,需要用一个足够大的语料库进行训练。训练的目的就是得到各个 $\langle w_k\rangle$ 和 $\langle w_i,,w_k\rangle$ 的值备用。
但是,在实际运用过程中,还需要解决一个棘手的问题。我们考虑以下问题:
如果 $w_i, w_k$ 是一个极其罕见的词组,它在实际生活中可能遇到,但是并未包含在语料库中。根据 \eqref{eq:con-prob-solu},我们会认为 $P(w_k\mid w_i) = 0$。再根据 \eqref{eq:bigram-model},$P(w_k\mid w_i) = 0$ 会导致整个 $P(S) = 0$。在这种情况下,语料库不够大导致了模型失真,而语料库的不足是一个无法解决的问题。所以我们必须接受模型在某些情况下可能失真,并找出办法消除(或者至少是减弱)这种失真给模型带来的影响。
在实际动手之前,我们先来分析一下实际情况,搞清楚我们到底要修复什么。
问题的根源在于语料库不够大,不足以反映真实语境的具体情况。而罕见词语未在语料库中出现,导致整句话的概率为零,这样的失真现象,只不过是语料库不够大的一个表现而已。那么,很自然地,我们需要思考一下,语料库不够大,只是会导致这一种失真的情况吗?如果我们只对未出现在语料库中的罕见词语进行适当的处理,而忽略了其他可能的失真情况,那么模型依旧不够好。
所谓「未出现」,其实是「出现次数为 0」的另一种表达方式。我们现在对出现次数为 0 的那些词语产生了怀疑,怀疑语料库是否能够真实反映这部分词语的情况;那么我们很自然地会怀疑那些出现次数为 1 甚至为 2 的词语:由于语料库不够大,这些词语在语料库中的出现次数,是否足以反映它们在真实语境中出现的频率呢?
于是我们发现,我们对出现次数较少的那些词语产生了一定的怀疑。特别地,出现次数越少,我们怀疑它的程度就越高。这样一来,我们的修复方案必须满足一些要求:
- 对出现次数为 0 的那些词语,我们应该赋予它们一个不为零但非常小的概率;
- 对于出现次数较少的那些词语,我们应该对它们的统计结果做适当的折算,出现的次数越少,可疑程度越大,因此折算程度也应该越大。
现在我们假设,语料库中共有 $N$ 个词语,在语料库中有 $N_r$ 个词语出现了 $r$ 次。特别地,有 $N_0$ 个词语出现了 $0$ 次。显然我们有:
$$ N = \sum_{r = 0}^{\infty}r\cdot N_r. $$
此外,一般来说,我们有:
$$ \text{revise}(r) = N_{r + 1}/N_r \lt 1. $$
即出现 $k$ 次的词语的数量,一般来说会比出现 $k + 1$ 次词语的数量要多。并且 $k$ 越小,$\text{revise}(r)$ 越大。于是我们可以这样定义
\begin{equation}
d_r = (r + 1)\cdot \text{revise}(r).
\label{eq:d-r}
\end{equation}
如果我们将 $d_r$ 作为折算后的频次,那么显然有:
$$ N = \sum_{r = 0}^{\infty}d_r\cdot N_r, $$
即,这样的折算依然满足全概率为 1。注意到,$d_0$ 是一个大于零的值;而且随着 $r$ 的增大,$\text{revise}(r)$ 会减小,也就是说折算的比例会减小。这些特性正符合我们对折算规则的预期要求。
尽管这样的折算方案符合我们的预期要求,但它有点过于粗暴了。实际上,通常我们会认为,在语料库中出现频次大于某一个阈值 $T$ 的词不需要进行折算。而 \eqref{eq:d-r} 则囫囵地修改了所有词的频次。对于 $P(w_k)$ 来说,这样粗暴的折算问题不太大,但是对于条件概率 $P(w_k\mid w_i),\quad (i = k - 1)$ 来说,不考虑阈值 $T$ 地这样囫囵地折算,误差就比较大了。
基于这样的分析,我们对于二元条件概率的折算修正如下:
\begin{equation}
P(w_k\mid w_i) = \begin{cases}
\frac{\langle w_i,,w_k\rangle}{\langle w_i\rangle} & \text{if $\langle w_i,,w_k\rangle \gt T$,}\\
d_r\cdot\frac{\langle w_i,,w_k\rangle}{\langle w_i\rangle} & \text{if $0 \lt \langle w_i,,w_k\rangle \leq T$,}\\
Q(w_i)\cdot\frac{\langle w_k\rangle}{\langle \text{Corpus}\rangle} & \text{otherwise.}
\end{cases}
\label{eq:bi-d-r}
\end{equation}
这里,$r = \langle w_i,,w_k\rangle$ 而 $d_r$ 是据 \eqref{eq:d-r} 计算出的值。$Q(w_i)$ 是一个修正函数,其定义为:
$$A = \sum_{w_k\in \text{Corpus}} P(w_k\mid w_i),$$
$$B = \sum_{w_k\notin \text{Corpus}} P(w_k),$$
$$Q(w_i) = \frac{1 - A}{B}.$$
至此,二元统计语言模型的修正就结束了。
语料库的选取
上一小节我们修正了当出现频次很低时,统计结果不准确,导致的模型失真的问题。这一小节我们讨论可能导致模型不准确的另一个方向的问题。
仔细观察 \eqref{eq:con-prob-solu} 你会发现,模型计算语句 $S$ 的概率,是依据语料库中各个词汇出现的频次的。也就是说,最终的计算结果 $P(S)$ 是和语料库紧密相关的。
众所周知,中国有很多所谓的「网络流行语」。在网络上发帖交流的网友,他们的说话习惯和正式的新闻稿的语言习惯是有很大差别的。如果我们以新闻稿件为语料库去训练模型,然后用于网络语言的分词,那么效果显然不会太好。而如果用网络语言作为语料库,虽然其中可能包括一些杂七杂八的奇怪单词,但是由于语料库与实际使用的类型一致,效果反而会更好。
分词一致性与颗粒度
我们回到最初的分析方法,先来看两个词:
- 清华大学
- 山东大学
这是两个大学的名字。在这里,对山东大学的分词不会有什么分歧,它就是一个词,不可分割。但是对清华大学的分词就存在分歧了。有的人认为清华大学是密不可分的,也有人认为清华和大学应该分开:清华作为修饰部分修饰大学二字。
这实际上是人们对于词语颗粒度大小认知不同导致的分词不一致。对清华大学的两种分词方法都不能说错,关键是要看语言的使用场合。
在平时写文章的时候,没有必要把清华和大学分开,它们就是一个词。但是在做搜索引擎的时候,就有必要把分词的颗粒度调整一下,变得小一些,认为清华和大学是两个词。毕竟,如果用户搜索「清华」而无法获得和清华大学有关的结果,这样的搜索引擎显然是不合格的。
这样的分析对我们是有启发的。我们没有必要纠结哪一种分词颗粒度更好,实际情况告诉我们,在某些时候大颗粒度更好,某些时候小颗粒度更好。
那么,是否有必要为不同的颗粒度搭建不同的模型呢?答案是否定的。实际上,不管是大颗粒度的分词,还是小颗粒度的分词,模型方法都是一样的,差别只在于对词语的认知问题。如果站在模型的角度上去考虑:模型是不了解词汇的含义的,具体应该用何种颗粒度去构建分词,只取决于语料库中对词语的划分。
因此,解决方案呼之欲出:
我们不需要设计两套模型,只需要一套模型就可以完成工作。我们需要做的,是对「清华大学」这类复合词做一个统计,做成两个词表 $L_1$ 和 $L_2$。其中 $L_1$ 包含小颗粒度的分词结果,比如「清华」和「大学」;$L_2$ 包含颗粒度较大的分词结果,比如「清华大学」。在实际使用的过程中,根据需求,分别将 $L_1$ 或者 $L_2$ 与语料库合并,交由模型去训练就好了。
小结
这篇文章接着第一篇文章,讨论了统计语言模型,特别是二元模型,在分词方面的应用。这篇文章给出了二元模型的具体描述,以及模型的具体解法。
之后,文章讨论了二元模型不可避免的一些困难,同时对一些可以修复的问题做了讨论。
总的来说,运用统计语言模型解决分词是一个成熟的方案。实际运用时效果的好坏,主要取决于以下几个方面:
- 工程实现的精度;
- 语料库的选取;
- 复合词表的完整度。
下一篇文章将简单介绍统计模型和信息理论在自然语言处理其他领域的应用情况。