N-gram模型通过前一个词预测下一个词。为了使这些预测有用,需要为它们赋予具体概率。这些概率不是凭空设定的,而是直接从大量文本数据中学习得来的。这个过程将实际观察到的语言模式转化为一个数学模型,用以评估给定短语的可能性。从文本数据到概率传统语言模型的根本是语料库,简单来说就是大量文本的集合。这些文本可以来自任何地方:书籍、新闻文章、网站或转录的语音。核心设想是,语料库中的语言模式能代表我们希望ASR系统理解的语言。计算概率的方法基于一个直接的思路:我们通过统计事件在我们数据中出现的频率来估算其概率。这被称为最大似然估计 (MLE)。对于语言模型来说,这意味着统计词语和词语序列。计算二元语法概率:一个简单例子让我们看看这如何运作于一个二元语法模型,其中一个词的概率只依赖于它前面的单个词。这个公式非常直观:$$ P(w_n | w_{n-1}) = \frac{\text{Count}(w_{n-1}, w_n)}{\text{Count}(w_{n-1})} $$用通俗的话来说,在另一个词 ($w_{n-1}$) 之后出现一个词 ($w_n$) 的概率,就是我们看到这个特定词对同时出现的次数,除以我们看到第一个词的总次数。假设我们的整个语料库只有这三个简单句子:"the cat sat on the mat""the dog sat on the rug""the cat chased the dog"让我们计算“cat”之后出现“sat”的概率,即 $P(\text{sat} | \text{cat})$。统计词对出现次数: 词序“cat sat”出现1次。统计前一个词的出现次数: 词“cat”总共出现2次(“the cat sat”和“the cat chased”)。计算概率: $$ P(\text{sat} | \text{cat}) = \frac{\text{Count}(\text{cat, sat})}{\text{Count}(\text{cat})} = \frac{1}{2} = 0.5 $$所以,根据我们的小语料库,词“sat”跟在词“cat”后面的概率是50%。现在让我们计算 $P(\text{on} | \text{sat})$:统计词对出现次数: 词序“sat on”出现2次。统计前一个词的出现次数: 词“sat”出现2次。计算概率: $$ P(\text{on} | \text{sat}) = \frac{\text{Count}(\text{sat, on})}{\text{Count}(\text{sat})} = \frac{2}{2} = 1.0 $$在我们有限的语料库中,词“on”总是跟在词“sat”后面,所以概率是1.0。下图说明了从文本语料库中得出这些概率的过程。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fillcolor="#e9ecef", fontname="sans-serif"]; edge [fontname="sans-serif"]; subgraph cluster_corpus { label = "1. 文本语料库"; bgcolor="#f8f9fa"; style="rounded"; corpus [label="\"the cat sat on the mat\"\n\"the dog sat on the rug\"\n\"the cat chased the dog\"", shape=note, fillcolor="#fff9db"]; } subgraph cluster_counting { label = "2. 统计出现次数"; bgcolor="#f8f9fa"; style="rounded"; count_pair [label="统计(\"cat sat\") = 1\n统计(\"sat on\") = 2", fillcolor="#d0bfff"]; count_word [label="统计(\"cat\") = 2\n统计(\"sat\") = 2", fillcolor="#a5d8ff"]; } subgraph cluster_prob { label = "3. 计算概率"; bgcolor="#f8f9fa"; style="rounded"; prob_calc [label=< <table border="0" cellborder="1" cellspacing="0" cellpadding="4"> <tr><td bgcolor="#b2f2bb"><b>计算</b></td><td bgcolor="#b2f2bb"><b>结果</b></td></tr> <tr><td>P(sat | cat) = 1 / 2</td><td>0.5</td></tr> <tr><td>P(on | sat) = 2 / 2</td><td>1.0</td></tr> </table> >]; } corpus -> {count_pair, count_word} [style=dashed]; {count_pair, count_word} -> prob_calc [style=dashed]; }从文本语料库到二元语法概率表。计算整个句子的概率一旦我们有了单个N-gram的概率,我们就可以计算整个词语序列的概率了。我们通过将序列中每个部分的概率相乘来做到这一点。这是概率链式法则的应用。对于二元语法模型,句子 $W = (w_1, w_2, ..., w_k)$ 的概率计算如下:$$ P(W) = P(w_1) \times P(w_2 | w_1) \times P(w_3 | w_2) \times \dots \times P(w_k | w_{k-1}) $$为了处理没有前一个词的第一个词,通常的做法是在语料库中每个句子的开头添加一个特殊的句子开始标记(通常写作<s>)。计算方法随即变为:$$ P(W) = P(w_1 | \text{<s>}) \times P(w_2 | w_1) \times \dots \times P(w_k | w_{k-1}) $$以前面的例子为例,让我们找出句子“the cat sat”的概率。假设 <s> 出现3次(每个句子出现一次):$P(\text{the} | \text{<s>}) = 3/3 = 1.0$$P(\text{cat} | \text{the}) = 2/3 \approx 0.67$$P(\text{sat} | \text{cat}) = 1/2 = 0.5$总概率是 $P(\text{"the cat sat"}) = 1.0 \times 0.67 \times 0.5 = 0.335$。最终概率越高,表明句子越可能出现。零概率问题与平滑这种简单的计数方法存在一个重要问题。如果我们遇到一个从未在训练语料库中出现的词语序列,会发生什么?例如,使用我们的语料库, $P(\text{chased} | \text{dog})$ 是多少?词对“dog chased”从未出现,所以它的计数是0。这使得概率变为 $0/2 = 0$。当我们将这个零乘入句子的概率链中时,整个句子的概率就变成了零。这意味着句子不可能发生,这过于极端了。一个未见的事件可能性低,但并非不可能。这被称为零频率问题。解决方法是平滑。最简单的形式是拉普拉斯平滑,或加一平滑。我们假装每种可能的二元语法都多出现了一次。为了保持概率的有效性,我们也调整分母。加一平滑的公式是:$$ P(w_n | w_{n-1}) = \frac{\text{Count}(w_{n-1}, w_n) + 1}{\text{Count}(w_{n-1}) + V} $$这里,$V$ 是词汇表的大小,也就是我们语料库中独特词汇的数量。在我们的例子中,词汇表是 {the, cat, sat, on, mat, dog, rug, chased, mouse}。因此,$V=9$。让我们用加一平滑重新计算 $P(\text{chased} | \text{dog})$:词对计数 + 1: 统计(“dog chased”)是0。所以,$0 + 1 = 1$。词计数 + V: 统计(“dog”)是2。词汇表大小 $V$ 是9。所以,$2 + 9 = 11$。计算平滑后的概率: $$ P(\text{chased} | \text{dog}) = \frac{1}{11} \approx 0.091 $$现在,未见的词对有一个虽小但非零的概率。这使得我们的模型对新词组合更具适应性,这对于处理自然语言的多变性非常重要。尽管存在更高级的平滑技术(如Kneser-Ney),加一平滑阐明了解决零计数问题的基本方法。