模块 04 · 约 18 分钟 · 过渡章节

从 Bigram 到 MLP:神经语言模型的源头

Transformer 不是从天而降的。它是几十年来语言建模思路一步步演化的结果。 这一节我们走一遍这条路:从最朴素的 Bigram(数频率)→ Bengio 2003 的神经语言模型(MLP + embedding),看每一步加了什么、解决了什么问题。 理解了这条进化路径,Transformer 的设计动机会清晰很多。

① 起点:Bigram —— 不需要神经网络

最简单的语言模型只做一件事:数前一个字后面跟什么字的频率。这叫 Bigram(二元模型)。 给定语料,扫一遍,得到一个 V×VV \times V 的"转移表":每行是 P(下一个字 | 当前字)。生成时按概率采样即可。

听起来很 trivial?但它确实是能用的最小语言模型。下面这个玩具版用 30 字小语料训练,你可以看每个字的"下一个字概率分布",并采样生成:

训练语料(30 字小语料):
今天天气真好今天我去散步今天我喝咖啡天气好的时候适合散步散步让我开心
起始字:
P(下一个字 | 当前 ="") —— 直接从语料里数出来:
100.0%

这就是最简单的语言模型 —— 没有神经网络,没有训练,只数频率。 但你能感受到:"今 → 天"高频,"天 → 气"高频,生成的句子看起来"像那么回事",但缺乏长程依赖。

观察:「今」后面 100% 是「天」,「天」后面有多个选项(气、的)。整个"训练"过程就是上面那张概率表,根本没有 梯度、没有 loss

② Bigram 的公式

P(tntn1)=count(tn1,tn)tcount(tn1,t)P(t_n \mid t_{n-1}) = \frac{\mathrm{count}(t_{n-1}, t_n)}{\sum_{t'} \mathrm{count}(t_{n-1}, t')}
↓ 对应的 Python 实现(可以直接改、直接运行)

③ 代码:自己训练一个 Bigram

不到 10 行 Python 就能"训完"。点「▶ 运行」看每个字符后面的概率分布:

python
直接编辑这段代码即可。输入 np. 看自动提示,⌘/Ctrl + Enter运行。

再看从中采样生成:

python
直接编辑这段代码即可。输入 np. 看自动提示,⌘/Ctrl + Enter运行。

④ Bigram 的三个致命问题

但 Bigram 在真实场景下惨不忍睹。原因有三:

  1. 只看前 1 个 token: P(下一个 | 上一个) 完全忽略更早的上文。「我昨天去公园然后吃了 ___」里的「___」对 Bigram 来说只看「了」 —— 根本不知道前面在说"去公园",可能采样出"了机器"。
  2. 没有泛化能力: 训练时见过「猫吃鱼」,没见过「狗吃鱼」?那 P(吃 | 狗) = 0,因为数没数到。「猫」和「狗」在 Bigram 表里是完全独立的两行, 没有任何机制让模型知道它们意思相近。
  3. 稀疏 + 爆炸: 升级到 Trigram(看前 2 个 token)需要 V3V^3 的表;4-gram 需要 V4V^4。 V=50000 时 4-gram 表大小是 6 × 10¹⁸,根本存不下,而且绝大多数 cell 还是 0。

⑤ Bengio 2003 的突破:用神经网络替代查表

Yoshua Bengio 在 2003 年的论文《A Neural Probabilistic Language Model》提出了革命性的想法:用神经网络 + embedding 替代 N-gram 表。这就是后来一切的源头 —— word2vec、Transformer、GPT 都是这条路上的延伸。

核心结构非常直观:

  1. 把每个 token 映射到一个稠密向量 eRd\mathbf{e} \in \mathbb{R}^d(这就是词表大小 V × d 的 embedding 矩阵)
  2. 把上下文窗口里的 ctx 个 token 的 embedding 拼接起来:xRctx×d\mathbf{x} \in \mathbb{R}^{\text{ctx} \times d}
  3. 过一个 MLP(多层感知机):h=tanh(W1x+b1)\mathbf{h} = \tanh(W_1 \mathbf{x} + b_1)
  4. 输出层投影到词表大小:logits=W2h+b2\text{logits} = W_2 \mathbf{h} + b_2,再 softmax 得到下一个 token 概率

⑥ 代码:8 个矩阵,一个完整的 Bengio 模型

python
直接编辑这段代码即可。输入 np. 看自动提示,⌘/Ctrl + Enter运行。

这里我们没训练它(参数是随机初始化的),但结构就是这样。 训练就是让 W1, W2, b1, b2 + E 这五组参数往"让真值 token 概率最大"的方向走。 Karpathy 的 makemore 项目把这个过程完整实现了一遍,强烈推荐看:github.com/karpathy/makemore

⑦ MLP 比 Bigram 强在哪

维度BigramMLP (Bengio 2003)
能看多长上文1 tokenctx 个(论文里 3)
token 之间能否共享语义不能(每对独立)能(通过共享 embedding)
参数量V·d + ctx·d·h + h·V
需要梯度下降训练不需要(数频率)需要(SGD + 反向传播)
泛化未见过的搭配P = 0(OOV 灾难)由相似 embedding 推断

⑧ 从 MLP 到 Transformer 的进化路径

Bengio MLP 也有自己的问题:

  • 上下文窗口固定(论文里 ctx=3)。要看更长就要把权重矩阵的输入维度加大,参数量线性涨。
  • 位置信息硬编码。"上文第 1 个 token"和"上文第 2 个 token"用的是 W1 的不同部分 — 信息没共享。
  • 不能并行训练(其实可以,但 RNN 出现后大家都去搞 RNN 了)。

这些问题后来催生了几代模型:

  1. RNN/LSTM(2010s)—— 解决变长上下文,但训练串行、长程依赖差
  2. Attention(2014, 用于翻译)—— 让 RNN 能"看回头"
  3. Transformer(2017)—— 完全用 attention 替代 RNN,纯并行、长程依赖好、scaling 友好
  4. GPT / BERT / LLaMA(2018+)—— 在 Transformer 基础上 scale 到大规模数据 + 大规模参数

所以你正在学的 Transformer 不是"从头发明",而是"用 attention 替换 MLP 第一层"的演化。 理解 Bengio MLP,再看 Transformer Block 里的 attention,会发现它就是"自适应的、可变长的、共享权重的"第一层。

⑨ 小测验

Q1.Bigram 模型是怎么"训练"的?
Q2.从 Bigram 升级到 MLP,主要解决了什么问题?

⑩ 延伸阅读