为什么要有这一章?
你一定见过这种奇怪现象:
- GPT-4 这种顶级模型,有时候连简单的“Strawberry 里有几个 r”都会数错。
- API 计费是按 Token 算的,而不是按字数或单词数算的。
- 有时候中文稍微改一个字,模型的回答风格就突然变了。
这一切的根源,都在于 AI 的“嘴巴” —— Tokenization (分词)。
1. 计算机看不懂字,只看得懂数
我们在第一章说过,模型本质上就是一个巨大的数学函数。函数的输入只能是数字。 所以,当我们把 "I love AI" 喂给模型时,必须先把它变成一串数字。
问题来了:怎么切?
方案 A:按字母切 (Character Level)
把每个字母变成一个数字。a=1, b=2, c=3...
- 优点:字表很小(26个字母+符号,不到200个)。
- 缺点:序列太长了! 一篇 1000 字的文章可能会变成 5000 个字母。Transformer 的注意力机制计算复杂度是 ,序列变长一点,计算量爆炸增长。模型会变得极慢。
方案 B:按单词切 (Word Level)
把每个英语单词变成一个数字。apple=1, banana=2...
- 优点:序列短,语义清晰。
- 缺点:词表无限大! 英语有几十万个词,而且每天都在造新词(比如
iPhone,ChatGPT)。如果遇到没见过的词(Out of Vocabulary),模型就瞎了,只能标记为<UNK>(Unknown)。
2. 中庸之道:Subword Tokenization (BPE)
为了平衡“序列长度”和“词表大小”,现代大模型(GPT, Llama, Claude)都采用了一种折中方案:子词分词 (Subword Tokenization)。
最经典 的算法叫 BPE (Byte Pair Encoding)。
🧩 类比:乐高积木
想象语言是乐高积木搭成的。
- 我们不需要为每一个“城堡”、“汽车”都造一个专门的零件(太占地)。
- 我们也不想只用最小的 1x1 豆豆去搭(太累)。
- 我们保留一些常用的组件:比如 2x4 的砖、轮子、窗户。
BPE 的逻辑就是:把经常在一起出现的字符组合,合并成一个 Token。
举个例子
假设我们要处理单词 unhappiness(不快乐):
- 按字切:
u, n, h, a, p, p, i, n, e, s, s(11个 token) -> 太长。 - 按词切:
unhappiness(1个 token) -> 词表里可能没有这个冷门词。 - BPE 切法:
- 模型在训练数据里发现
un经常出现(unhappy, undo)。 - 发现
ness经常出现(kindness, darkness)。 - 于是把它们存进词表。
- 最终切分:
un+happi+ness(3个 Token)。
- 模型在训练数据里发现
这样既缩短了序列,又能处理没见过的生僻词(只要能拆成认识的零件就行)。
2.1 现代分词的“门派之争”
基于子词(Subword)的核心思想,AI 界繁衍出了几个最主流的分词流派。它们代表了不同时期顶尖模型的选择:
- 🔥 BPE 与 字节级 BPE (BBPE):从字母开始,每次挑出一对“最常挨在一起的兄弟”并合体。后来演进出 Byte-level BPE(GPT 系列专属),它不再从语言的字母表开始合并,而是直接从计算机底层的 256 个字节 (Byte) 开始。这样不管你输入的是火星文还是 Emoji,它都能切成字节吞下去,彻底消灭了
<UNK>乱码。 - 🔥 WordPiece:和 BPE 类似,但它合并的标准不是盲目追求“谁重逢最多次数”,而是基于语言模型“合并后能不能让整句话的概率最大化”。它会在切碎的词根前面打上
##标记(如run+##ning)。这是 BERT 时代的绝对御用标准。 - ⭐ Unigram 语言模型:做法和前两者完全“逆向”。它一上来先假设一个庞大(比如上百万)的词表,然后一边训练,一边评估每个词的保留价值,把那些“对预测没啥用”的词大刀阔斧地砍掉。
- ⭐ SentencePiece 框架:你可以把它理解为“不挑食的万能切词刀”。像英文有天然的空格,切词很容易;但在中文、日文里词和词是连在一起的。Google 发布的 SentencePiece 把所有输入(连同空格一起)全部看作纯原始的 Unicode 字符流,屏蔽了语言间的排版差异。它是目前许多主流开源模型(如 LLaMA / Mistral)最喜 欢用的通用分词底层框架。
3. 为什么 Strawberry 数不对?(Tokenization 的诅咒)
现在你能理解为什么 AI 数不清单词里的字母了吗?
当你问 GPT-4:“Strawberry 里有几个 r?”
在人类眼里: S-t-r-a-w-b-e-r-r-y (清晰的3个r)
在模型眼里:
[Token 135] + [Token 892]
(可能是 Straw + berry,或者是其他奇怪的组合)
模型根本“看不见”单词里面的字母! 它看到的是两个 ID。就像这种感觉: 我问你:“张伟”这个名字里有几个撇? 你第一反应是“张伟”这个整体概念,除非你在脑子里把字写出来(画图),否则很难直接数出来。
如何解决?
如果你强迫模型
