新闻详情 资讯动态

全面了解最新资讯与建站知识,洞察行业趋势。

行业资讯

链式法则与马尔可夫假设:N-gram语言模型的原理与工程实践

发布时间:2026/10/14 5:06:37
链式法则与马尔可夫假设:N-gram语言模型的原理与工程实践 1. 从联合概率到逐词分解链式法则是怎么“拆”出一句话概率的刚开始学 NLP 的同学十有八九会对这个问题感到困惑为什么在语言模型里几乎每本教材都要先讲一遍链式法则Chain Rule再讲一遍马尔可夫假设Markov Assumption这两个东西看起来一个像数学课内容一个是简单得近乎“偷懒”的近似似乎和“自然语言处理”没有什么直接关系。但实际上它们才是理解语言模型的“第一性原理”。我这么说并不是在渲染什么玄学你可以自己走一遍这条路从“要算出某个句子出现的概率”这个朴素需求开始一步一步推导最后你会发现 N-gram 语言模型、统计机器翻译里的噪声信道模型、HMM 中的状态转移甚至 RNN 和 Transformer 认为“下一个词只和已有上下文有关”这件事全都能在这两个概念里找到影子。1.1 联合概率到底难在哪我们先从一个具体问题进入。假设我们有这样一个中文句子“我昨天在公园里看到一只白色的猫”如果想把整个句子的概率 ( P(\text{我昨天在公园里看到一只白色的猫}) ) 算出来一个最朴素的想法是直接统计这个完整句子在语料中出现了多少次然后除以语料总句子数。这就是最大似然估计的思路听起来没有任何问题。但问题出在实际数据上。自然语言的句子种类几乎是无穷的。“我昨天在公园里看到一只白色的猫”这句话很可能在任何一个有限语料库里只出现 0 次。那么概率就是 0。可是你我都知道这句话是完全合法、完全自然的汉语概率不应该是 0。这就是语言建模和普通分类问题最大的区别分类要处理的是有限类别语言建模要处理的却是一个组合爆炸的序列空间。用我刚学 NLP 时的导师的话说“你要估计的不是一个事件发生的概率而是几亿个没见过的事件各自发生的概率。”这个叫“数据稀疏”它是一切语言模型问题的起点。链式法则的妙处在于它不直接估计整句概率而是把一个联合概率 ( P(w_1, w_2, \ldots, w_n) ) 拆成一串条件概率的乘积。拆完之后每个条件概率的估计难度和原先相比并没有原则性的变化——它依然是某个特定历史的概率。所以链式法则本身并没有解决数据稀疏问题它只是提供了一个“可后续操作”的分解框架。真正让它变得可用的人是马尔可夫。1.2 条件概率的链式展开是怎么推出来的我见过不少同学看到链式法则公式就跳过理由是“这不就是 ( P(A|B)P(A,B)/P(B) ) 变来变去吗”。确实推导很简单但我想把它写得比教科书更啰嗦一点因为搞清楚它在语言上的含义比公式本身重要得多。根据条件概率的定义两个词同时出现的联合概率可以写成[ P(w_1, w_2) P(w_1) \cdot P(w_2 | w_1) ]意思是先出现 ( w_1 ) 的概率乘以在已经出现 ( w_1 ) 之后出现 ( w_2 ) 的概率。扩展到三个词[ P(w_1, w_2, w_3) P(w_1) \cdot P(w_2 | w_1) \cdot P(w_3 | w_1, w_2) ]为什么要引入 ( P(w_3 | w_1, w_2) ) 而不是继续累乘独立的 ( P(w_3) )原因也很直白在自然语言里“白色”后面出现“猫”的概率和“飞碟”后面出现“猫”的概率显然是不同的。后一个词的选择依赖于前一个词条件概率就是用来承接这种依赖关系的。把上面的模式推广到任意长度 n 个词得到链式法则的完整形式[ P(w_1, w_2, \ldots, w_n) \prod_{i1}^{n} P(w_i | w_1, w_2, \ldots, w_{i-1}) ]注意一个技术细节对于首个词 ( w_1 )它没有前序词我们就定义 ( P(w_1 | \text{BOS}) )这里的 BOSBeginning-of-Sequence是一个虚拟的起始标记。同样在句尾我们还会加一个 EOSEnd-of-Sequence标记把句子结束的概率也纳入建模。这个小细节非常容易被忽略但实际做语言模型训练时BOS 和 EOS 的处理正确与否会直接影响模型输出质量后面我会专门提到。1.3 用一个真实句子把链式展开走一遍我用一个短例子演算一下。句子是“猫 坐在 垫子 上”标注 BOS 和 EOS 之后整个序列是BOS, 猫, 坐在, 垫子, 上, EOS。那么[ P(\text{猫坐在垫子上}) P(\text{猫} | \text{BOS}) \times P(\text{坐在} | \text{BOS, 猫}) \times P(\text{垫子} | \text{BOS, 猫, 坐在}) \times P(\text{上} | \text{BOS, 猫, 坐在, 垫子}) \times P(\text{EOS} | \text{BOS, 猫, 坐在, 垫子, 上}) ]这串式子看起来毫无毛病但你仔细一看看到了什么最后一个条件概率 ( P(\text{EOS} | \text{BOS, 猫, 坐在, 垫子, 上}) ) 依赖前面整整五个词。假如你的训练语料里没出现过完全相同的“猫 坐在 垫子 上”这个前缀这一项就可能为 0整个句子的概率也跟着变成 0。这就是为什么说“链式法则给了我们一个精确的分解却没给我们一个可行的估计方案”。它把一个复杂问题拆成了 n 个小问题但每个小问题的复杂度并没有实质下降——第 i 个条件概率依赖的上下文长度是 i-1依然是指数级的组合。走到这一步任何一个正常的工程师都会想到同一个方向能不能别看那么长的历史能不能只看最近的几个词这个想法就是马尔可夫假设。2. 马尔可夫假设在做什么把“无限历史”压缩到“k个词”如果你在搜索引擎里查“马尔可夫假设”会看到一句很经典的话当前状态只与最近的状态有关与更早的历史无关。在语言模型里这句话被翻译成第 i 个词出现的概率只依赖前 k 个词而不是依赖前面所有的词。听起来挺“粗暴”的对吧我第一次接触时也觉得这近似是不是太随意了。但后来做实际项目多了我逐渐意识到马尔可夫假设的本质不是“语言真的只有 k 阶记忆”而是“我们在算力、数据都有限的前提下主动选择用一个可控的近似去替换一个不可计算的精确解”。这对工程应用来说是一个非常理性的取舍。2.1 从链式法则到 N-gram 的完整推导把马尔可夫假设套进链式法则数学上就是一步替换。原本的条件概率是 ( P(w_i | w_1, w_2, \ldots, w_{i-1}) )现在我们只保留最后 k 个词[ P(w_i | w_1, w_2, \ldots, w_{i-1}) \approx P(w_i | w_{i-k}, \ldots, w_{i-1}) ]当 k0 时模型退化为不看任何历史纯粹用每个词在语料中出现的频率来生成句子这叫 Unigram。k1 时只依赖前一个词叫 Bigram。k2 时依赖前两个词叫 Trigram。一般统称 N-gram。为什么要关注 k 的具体取值因为这背后有一个“质量 vs 可行性”的权衡。k 越大模型越接近真实的自然语言。毕竟一个句子前面的主语确实会影响到几十个词之后的动词选择。但 k 越大需要统计的历史组合种类也随之暴涨。语料库的大小有限那些出现次数极少甚至从未出现的组合会拖垮整个估计过程。这在统计学里叫“维度灾难”是所有离散符号建模都要面对的问题。2.2 一个亲手算过的 Bigram 概率例子为了让这个场景更具体我给出一个我经常在项目分享里用的微型语料BOS 我 喜欢 猫 EOS BOS 我 喜欢 狗 EOS BOS 他 喜欢 猫 EOS BOS 猫 喜欢 我 EOS想算 Bigram 模型里的 ( P(\text{喜欢}|\text{我}) )做法是统计“我”后面直接跟着“喜欢”的次数再除以“我”出现的总次数[ P(\text{喜欢}|\text{我}) \frac{\text{count(我 喜欢)}}{\text{count(我)}} \frac{2}{3} ]这里“我”出现在第一句和第二句里共 3 次其中 2 次后面跟的是“喜欢”。这种估计方式叫最大似然估计MLE它只依赖统计频次不引入任何先验知识。N-gram 模型在实际做生成任务时还会配合一个采样策略。比如用 Bigram 模型生成句子时每次生成完一个词就把这个词当作下一个条件再去抽样后面的词。这种逐词生成的过程本质上就是在链式法则的框架下不断用马尔可夫假设去截断历史的循环迭代。2.3 一阶、二阶、更高阶实际效果差别有多大我先后在几个文本生成、检索类项目里反复调过 N-gram 的阶数我的直觉经验如下Unigramk0生成的句子基本是随机词的组合只有词面正确完全不连贯。没有实际用途但它适合做朴素贝叶斯文本分类里的词概率基准。Bigramk1生成的句子能够维持短距离衔接比如“我 喜欢 猫”这种结构能出来但如果句子一长很容易出现“跑题”——因为每个词只跟前一个词有关句中的主谓关系很快就断裂了。Trigramk2在短文本生成和输入法联想这类场景里已经能看出明显改善很多中文分词和拼音输入法的候选词排序底层核心模型就是基于大规模语料统计的 Trigram 甚至更高阶模型。4-gram 及以上k≥3在受限领域比如客服问答的模板补全有提升但在开放领域数据稀疏会变得很棘手。训练语料稍微不够大绝大多数 4-gram 组合都是零次出现估计出来的概率极不可靠。这个经验提醒我一件重要的事N-gram 的阶数选择不只是模型设计问题更是一个数据规模问题。要支撑高阶 N-gram需要海量语料。如果语料不够选高阶模型就是给自己挖坑。3. N-gram 安全落地的三个关键点估计、平滑、困惑度评估链式法则和马尔可夫假设讲清楚了接下来就是工程问题。理论说得好听落到代码里你会发现三个绕不开的坑最大似然估计产生大量 0 概率、平滑方法到底怎么选、以及用什么指标来衡量模型好坏。这三个问题我都在实际项目里踩过逐个展开说。3.1 最大似然估计里那个“万恶的零概率”先复述一下最大似然估计的公式。对于 Bigram 参数 ( P(w_i | w_{i-1}) )[ P(w_i | w_{i-1}) \frac{C(w_{i-1}, w_i)}{C(w_{i-1})} ]C 表示在语料中的共现次数。最大似然估计的原理很简单但它有个突出的副作用只要某个“上下文-词”组合在语料中没出现过概率就是 0。而真实语言中有大量的合法搭配只是恰好在你的有限语料里没有出现。我在一个模拟客服语料上做过统计只有约几百条短文本直接训练 Trigram 模型结果超过 70% 的 Trigram 条件概率是 0。这意味着把模型放到真实输入上绝大多数句子的总概率都会直接乘成 0完全没法用。第一次看到这个数字时我意识到“平滑”不是一个可有可无的优化项而是一个让模型能跑起来的前提条件。3.2 加1平滑与回退法的适用场景最简单的平滑方法是加1平滑也叫拉普拉斯平滑把所有计数都加 1[ P_{\text{add-1}}(w_i | w_{i-1}) \frac{C(w_{i-1}, w_i) 1}{C(w_{i-1}) V} ]其中 V 是词表大小。这样做能保证没有零概率但也有明显副作用它给所有没见过的组合都分了一份“概率配额”导致很多本来很可靠的统计频率被稀释。在词表比较大时加1平滑对高频词的估计会偏差很大这不是我的个人偏见很多教材里的实验都印证了这一点。在实际项目里我反而更常用的是回退法Backoff和插值法Interpolation。回退法的思想是如果 Trigram 概率为零就回退到 Bigram如果 Bigram 还为零就回退到 Unigram。插值法则不用这样硬切换而是把各阶模型按权重线性组合[ P(w_i | w_{i-2}, w_{i-1}) \lambda_3 \cdot P_{\text{tri}}(w_i | w_{i-2}, w_{i-1}) \lambda_2 \cdot P_{\text{bi}}(w_i | w_{i-1}) \lambda_1 \cdot P_{\text{uni}}(w_i) ]权重 ( \lambda ) 一般通过在验证集上做 EM 算法或网格搜索来调。插值法虽然实现起来比加1平滑复杂但它对真实数据的适应能力明显更强。如果你只是做一个小 Demo加1平滑够用了如果要上生产环境我建议认真考虑回退法或插值法网上也能找到成熟的开源 N-gram 工具库直接调用现成实现比自己从零写更稳。3.3 困惑度Perplexity到底在衡量什么训练好一个语言模型之后如何评价它的好坏分类问题用准确率生成问题却不能用“对不对”来一刀切。语言模型领域最常用的指标是困惑度PerplexityPPL。困惑度的公式可以写成[ \text{PPL}(P) P(w_1, w_2, \ldots, w_n)^{-\frac{1}{n}} ]这里 n 是句子长度。看这个式子你会发现问题如果模型给某个测试句分配的概率高PPL 就低分配的概率低PPL 就高。所以困惑度越低越好。它本质上是在衡量“模型对真实语言序列的平均惊讶程度”。举个例子。假设模型认为每个词出现的概率都是均匀的那么词表大小 V100 时PPL 就是 100。如果你训练出的模型 PPL 是 50那就等价于“模型在面对真实文本时平均只有 50 个候选词处于概率较高状态”。这个概念后来对理解大模型也有用——生成式模型的输出质量通常与其评估集上的困惑度有一定相关性但并不是唯一标准。用困惑度做评估时有个细节容易出错句子长度归一化。如果不除以 n长句子的联合概率天然就小PPL 会虚高导致长句和短句之间完全不可比较。所以做评测脚本时记得按 token 数做归一化。我个人的习惯是每次改动平滑策略或训练数据后先固定一份验证集把 PPL 跑出来再结合几个实际生成样例做人工判断。PPL 下降但生成质量变差的情况时有发生它与人对“语言是否自然”的感知并非完全一致。不要只信数字也不要只看一两个生成样例两者结合才是稳妥的方式。3.4 训练脚本里容易被忽略的 BOS 和 EOS 处理接着第 1 节里埋下的那个细节我展开聊一下。在训练 N-gram 模型时每个句子开头要加 BOS结尾要加 EOS。加 BOS 的目的是让第一个真实词也有条件概率 ( P(w_1|\text{BOS}) )加 EOS 的目的是让句子的结束也纳入概率计算避免模型永远倾向于生成短句。这个处理有个容易踩的坑统计 ( C(\text{BOS}) ) 时BOS 的出现次数应该等于句子总数。如果你忘了把 BOS 放进计数表那么句首词的估计就会出问题。EOS 同理如果没有 EOS模型在生成时就不会主动停下你只能靠外部截断这会让生成结果结尾很突兀。我在一次给文本补全模块做评估时就因为没有正确加入 EOS导致模型在验证集上 PPL 偏高排查了很久才发现是统计口径错了。虽然这是个很小的实现细节但它能直接影响模型行为和指标值得在动手前想清楚。4. 链式法则和马尔可夫假设为什么至今仍是现代 NLP 的底色很多人学完 N-gram 之后会产生一个疑问现在都是 Transformer、大语言模型的时代了谁还会用 Trigram 或 Bigram 那一套这个想法我可以理解但我想说链式法则和马尔可夫假设作为思维方式并没有被淘汰它们只是以更复杂的形态存活在当代模型里。4.1 链式法则在神经网络语言模型里的变体神经网络语言模型同样需要建模 ( P(w_1, w_2, \ldots, w_n) )而它们采用的基本框架仍然是逐词分解。无论是早期的神经网络语言模型还是后来的 LSTM、GPT 系列它们做的都是同一件事给定前面已有的词预测下一个词的概率分布。也就是说输出层始终是一个条件概率分布[ P(w_i | w_1, w_2, \ldots, w_{i-1}) ]这正是链式法则。神经网络的进步本质上是让这个条件概率分布的表达能力更强它不再依赖离散的 n 元组计数而是把历史上下文压缩成一个稠密向量hidden state从而能在高维空间里泛化那些共现次数极少的组合。记得我当时从 N-gram 转向训练神经网络语言模型时的最大感触是之前必须靠平滑算法才能勉强避免零概率换成神经网络之后零概率问题从算法层面被消解了——模型输出的 softmax 分布天然给每个词一个非零概率。这并不意味着链式法则不再重要恰恰相反它依然是神经网络训练目标交叉熵损失的数学基础。4.2 马尔可夫假设的现代化身定长窗口与自注意力马尔可夫假设在经典语言模型里表现为“只看最近的 k 个词”。到了神经网络时代这个“k”以多种形式出现了最早的前缀窗口模型直接把前 n 个词拼成一个输入向量n 就是现代版的“k”。RNN 类模型理论上可以记住任意长度的历史但实践中梯度消失问题会让模型有效记忆长度受限。所以“无限历史”更多是一种理论属性。后来的 Transformer 模型用自注意力机制建模所有位置之间的依赖。严格来说它在一定程度上突破了马尔可夫假设因为一个 token 可以同时看到上下文中的任意其他 token。但你也注意Transformer 在实际部署中也经常使用“窗口限制”比如某些模型的注意力只在一个固定窗口内计算。那不就是一种“滑动窗口版的马尔可夫假设”吗区别只是注意力里的“历史”由隐向量完整编码而不是丢进离散计数表。从这个角度想经典假设和现代架构之间的距离并没有你以为的那么远。4.3 一个经典假设在噪声信道模型中的应用实例马尔可夫假设的另一个经典应用是噪声信道模型尤其在早期的统计机器翻译和拼写纠错里。简单说拼写纠错问题可以建模为给定一个观察到的错误词序列 ( y )我们要找最有可能的原始正确词序列 ( x )。用贝叶斯公式展开[ P(x | y) \propto P(y | x) \cdot P(x) ]这里的 ( P(x) ) 就是语言模型。而 ( P(y|x) ) 是“错误生成模型”。在那个年代( P(x) ) 正是用 N-gram配合马尔可夫假设来估算的。链式法则在这里扮演的角色是把 ( P(x) ) 分解成逐词条件概率然后用 Bigram 或 Trigram 近似。做这类任务时你会有一种很深刻的感受模型本身很简单复杂的是数据和概率分配。比如在纠错任务里语言模型的分支概率会直接影响最终纠错结果本来正确拼写是“今天天气很好”如果语言模型给“今天”和“天气”之间的 Bigram 概率很低模型可能就把“天气”当成错误词改成“天汽”产生反效果。这个例子后来成为我给学生讲课时的经典反面教材——它说明在传统统计模型里语言模型不是配角而是质量天花板。4.4 对“逐步生成”这一根本范式的再思考最后我想说一个更抽象但非常重要的观察。不管是 N-gram、RNN 还是 Transformer它们生成的自然语言过程都采用了“从左到右逐词生成”的范式。这个范式的数学基础正是链式法则[ P(\text{整个句子}) \prod_{i} P(w_i | \text{已有前缀}) ]为什么大家都选这个范式而不是直接用一个打分函数对完整句子打分因为逐词生成让每一步的搜索都有章可循——你可以用贪心解码、束搜索Beam Search等策略在指数级候选空间里找近似最优。如果抛弃链式法则直接对整句联合概率建模搜索空间会大到完全无法处理。从这个角度看链式法则不仅是数学形式更是连接“统计建模”和“实际解码算法”之间的那座桥。理解这一点比你背下公式本身重要得多。我在做文本生成类的项目时遇到生成结果不稳定的问题往往不是回到模型结构上找原因而是先回到解码策略和概率分布的交互方式上思考。而这一切思考的起点依然是我在最开始学语言模型时接触到的这两个概念。我自己做 LangChain、N-gram 相关项目的经验是真正的理解不在于你是否能写出公式而在于当模型效果不好时你能不能快速定位到问题出在“历史长度选择不当”还是“平滑策略不合适”还是“评估方式有偏”。这些东西都是链式法则和马尔可夫假设的工程延伸。对刚接触 NLP 的读者我也给一句建议不要急着跳过统计语言模型的部分去追新模型。如果你能把 N-gram 训练、平滑、困惑度评估完整跑一遍你得到的不只是几个概念而是一整套关于“概率建模-数据稀疏-模型评估”的思维方式。这套思维方式在后来的深度学习项目中会反复救你。

想做一个「会获客」的企业网站?

留下需求,1 小时内获取专属建站方案与透明报价。

免费咨询方案
↑