最近在梳理早期 NLP 技术时,重新归纳了 n-gram 模型。虽然现在更关注 Transformer 等架构,但回头看这些 "古老" 的方法,其实能学到很多东西。
n-gram 的基本思想#
n-gram 的核心想法其实很直观:一个词出现的概率只取决于它前面的几个词(具体来说是前 n-1 个词)。这听起来像是对语言的简化,但人类说话时确实经常基于前面的词来选择后面的词。
几种常用的 n-gram 模型:
- Unigram:每个词独立出现,不考虑上下文
- Bigram:每个词只看前面 1 个词
- Trigram:每个词看前面 2 个词
刚开始接触时会觉得这种假设太粗糙了,但实际上这种简化在很多场景下是有效的。
马尔可夫假设#
n-gram 背后的理论基础是马尔可夫假设。数学表达为:
这个假设的关键是 "无记忆性"—— 下一个状态只依赖当前状态,不依赖历史路径。
举例计算 "I love deep learning" 的概率:
Bigram 计算:
Trigram 计算:
复杂的句子概率被分解成了简单的条件概率乘积,这种分解方式既优雅又实用。
数据稀疏性问题#
n-gram 有个很现实的问题:数据稀疏性。如果某个词组合在训练数据中没出现过,它的概率就是 0,这会让整个句子概率变成 0。
为了解决这个问题,前人想出了几种平滑策略:
加一平滑#
给所有词的计数都加 1,这样就不会有 0 概率了。
加 K 平滑#
是加一平滑的推广版本,通过调整 K 值来控制平滑程度,效果上比加一平滑要好一些。
- 加一平滑是加 K 平滑的特例,使用加 K 平滑,K 的取值具体关注于训练数据,需要在验证集上进行调优。
- 当训练数据数量较小时,可能会出现一些极端的分布,这时 K 值一般取较大,以平衡每个词的出现概率。
- 当训练数据量较大时,使用较小的 K 值以保持原始的统计分布。
Kneser-Ney 平滑#
更复杂但效果更好的方法,考虑了词在不同上下文中的分布。
这些平滑方法体现了早期 NLP 研究者的实用精神 —— 遇到问题就想办法解决,不纠结于理论的完美性。
n-gram 的特点#
简单有效:虽然假设简单,但在很多任务上效果不错。现在的输入法、拼写检查还在用类似的技术。
计算友好:训练和推理都很快,这在计算资源有限的年代很重要。
局限明显:无法处理长距离依赖是个根本问题。比如 "昨天下雨... 今天很累",这种跨越多个词的语义关系 n-gram 就捕捉不到。
从 n-gram 到现代模型#
现在回头看,n-gram 其实为后来的模型奠定了重要基础:
- 概率建模思路:把语言看作概率分布,这个思想一直延续到现在
- 序列预测框架:基于历史预测未来的基本框架
- 统计学习方法:从数据中学习规律的方法论
即使是最新的大语言模型,本质上还是在做类似的事情 —— 基于前面的 token 预测下一个 token。只是现在的模型能够考虑更长的上下文,捕捉更复杂的依赖关系。
技术演进的思考#
理解技术发展的历史脉络很重要。现在的很多 "创新" 其实都能在早期技术中找到影子。n-gram 虽然简单,但它体现的核心思想 —— 通过统计规律来理解和预测语言 —— 至今仍然有效。
就像学数学一样,掌握基础概念比追求最新最复杂的技术更重要。n-gram 不是最强的模型,但它提供了一个很好的思考框架,帮助理解语言建模的本质。
技术的进步是渐进的,每一代技术都在前一代的基础上改进。n-gram 教会我们如何用概率的眼光看待语言,这个视角在今天仍然很有价值。而且简单的方法往往能够揭示深层的规律,理解问题的本质比追求复杂的解决方案更加重要。
动手实现#
参考 Github 仓库