· liyu · ai · 9 min read
大模型分词器(Tokenizer)核心原理与 BPE 算法图解
一文讲清大模型分词器的本质:从“为什么需要子词分词”,到结合具体数据的 BPE 训练推导,再到现代大模型标配 BBPE 的核心机制。
大模型虽然表现出惊人的理解力,但其底层本质只是数学张量计算器——它既不认识汉字,也不认识英文单词,只能对一系列数字(Token IDs)进行矩阵运算。
**分词器(Tokenizer)**就是人类语言与大模型数字世界之间的“同传翻译官”。
输入: "你好世界" ──[Tokenizer 编码]──► Token IDs: [57648, 54221] ──► 神经网络计算
输出: Token IDs: [1024, 3056] ──[Tokenizer 解码]──► "很高兴为你解答"本文用最直白的方式,带你快速弄懂分词器的核心作用、训练机制与经典 BPE(Byte Pair Encoding) 算法。
一、 分词器是做什么的?
1. 为什么不用传统切词方式?
在处理文本时,我们通常有三种切分粒度:
┌──────────────┬───────────────────────────────┬──────────────────────────────────────────┐
│ 切分粒度 │ 怎么切 │ 致命缺点 │
├──────────────┼───────────────────────────────┼──────────────────────────────────────────┤
│ 词级 (Word) │ ["unhappiness"] │ 1. 词表动辄上百万,参数爆炸 │
│ │ │ 2. 遇到新词直接变成 <unk>(未登录词) │
├──────────────┼───────────────────────────────┼──────────────────────────────────────────┤
│ 字符级 (Char)│ ['u', 'n', 'h', 'a', 'p' ...] │ 1. 序列长度翻几倍,Attention O(N²) 算不动 │
│ │ │ 2. 单个字符几乎没有独立语义 │
├──────────────┼───────────────────────────────┼──────────────────────────────────────────┤
│ 子词 (Subword)│ ["un", "happi", "ness"] │ ✅ 词表可控 (32k~150k)、序列长度合理、 │
│ (现代主流) │ │ 0 OOV(任何新词都能拆解表示) │
└──────────────┴───────────────────────────────┴──────────────────────────────────────────┘子词切分(Subword) 是现代大模型的标准方案:
- 高频词直接保留:如
the、apple、深度学习,只占 1 个 Token,压缩效率极高。 - 低频/生僻词拆解组合:如
unhappiness拆分为un+happi+ness,模型能借助词根理解语义,且永不报错。
二、 分词器是怎么训练出来的?(数据推导演练)
💡 核心常识:分词器的训练是纯统计过程,与神经网络权重的训练完全无关。它不需要 GPU,也不需要反向传播,纯靠 CPU 统计海量文本里的字符共现频率。
分词器训练的目标很简单:在限定的词表容量内,通过不断把“高频相邻对”合并为新词,让文本整体压缩率最高。
1. 初始语料与词频统计
假设我们的训练语料库统计后包含以下 4 个词(词尾加上 </w> 区分词边界):
l o w </w>: 出现 5 次l o w e r </w>: 出现 2 次n e w e s t </w>: 出现 6 次w i d e s t </w>: 出现 3 次
初始基础词表(11 个字符):{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd', '</w>'}
2. 逐步合并过程
算法每轮统计语料库中所有相邻两两组合(Pair)出现的总频次,贪心地挑出最高频的一个合并为新 Token。
| 轮次 | 统计最高频相邻对 | 频次统计来源 | 执行合并 | 词表新增 | 语料当前状态 |
|---|---|---|---|---|---|
| 第 1 轮 | ('e', 's') | 6 (newest) + 3 (widest) = 9 | 'e' + 's' ➔ 'es' | es | l o w </w> (5)l o w e r </w> (2)n e w es t </w> (6)w i d es t </w> (3) |
| 第 2 轮 | ('es', 't') | 6 (newest) + 3 (widest) = 9 | 'es' + 't' ➔ 'est' | est | l o w </w> (5)l o w e r </w> (2)n e w est </w> (6)w i d est </w> (3) |
| 第 3 轮 | ('est', '</w>') | 6 (newest) + 3 (widest) = 9 | 'est' + '</w>' ➔ 'est</w>' | est</w> | l o w </w> (5)l o w e r </w> (2)n e w est</w> (6)w i d est</w> (3) |
| 第 4 轮 | ('l', 'o') | 5 (low) + 2 (lower) = 7 | 'l' + 'o' ➔ 'lo' | lo | lo w </w> (5)lo w e r </w> (2)n e w est</w> (6)w i d est</w> (3) |
| 第 5 轮 | ('lo', 'w') | 5 (low) + 2 (lower) = 7 | 'lo' + 'w' ➔ 'low' | low | low </w> (5)low e r </w> (2)n e w est</w> (6)w i d est</w> (3) |
| 第 6 轮 | ('n', 'e') | 6 (newest) = 6 | 'n' + 'e' ➔ 'ne' | ne | low </w> (5)low e r </w> (2)ne w est</w> (6)w i d est</w> (3) |
| 第 7 轮 | ('ne', 'w') | 6 (newest) = 6 | 'ne' + 'w' ➔ 'new' | new | low </w> (5)low e r </w> (2)new est</w> (6)w i d est</w> (3) |
| 第 8 轮 | ('new', 'est</w>') | 6 (newest) = 6 | 'new' + 'est</w>' ➔ 'newest</w>' | newest</w> | low </w> (5)low e r </w> (2)newest</w> (6)w i d est</w> (3) |
| 第 9 轮 | ('low', '</w>') | 5 (low) = 5 | 'low' + '</w>' ➔ 'low</w>' | low</w> | low</w> (5)low e r </w> (2)newest</w> (6)w i d est</w> (3) |
📌 注:在遇到频次相同的情况时(如第 1 轮
('e', 's')与('s', 't')均为 9 次,第 6 轮('n', 'e')与('e', 'w')均为 6 次),算法依据在语料中首次出现的顺序或字典序打破并列(Tie-breaking)。
3. 训练产物
当达到指定的词表大小或合并轮数后训练结束,输出两大成果:
- 词表(Vocabulary):基础字符 + 新合并出的子词(每个子词赋予一个唯一数字 ID)。
- 有序合并规则表(Merge Rules):按合并先后排序的规则表(决定后续分词时的优先级)。
三、 BPE 算法如何对新文本分词?(Inference 过程)
训练完成后,当给分词器输入一个从未在训练集中作为独立完整词出现过的词 "lowest" 时,分词器是如何切分的?
输入单词: "lowest"
1. 初始切分为基础字符序列(加上词尾边界):
['l', 'o', 'w', 'e', 's', 't', '</w>']
2. 按训练好的合并规则顺序依次匹配执行:
- 匹配规则 1: ('e', 's') ➔ ['l', 'o', 'w', 'es', 't', '</w>']
- 匹配规则 2: ('es', 't') ➔ ['l', 'o', 'w', 'est', '</w>']
- 匹配规则 3: ('est', '</w>') ➔ ['l', 'o', 'w', 'est</w>']
- 匹配规则 4: ('l', 'o') ➔ ['lo', 'w', 'est</w>']
- 匹配规则 5: ('lo', 'w') ➔ ['low', 'est</w>']
- 后续规则 6~9 (ne, new, newest, low</w>) 均无法在当前序列匹配,合并结束。
3. 最终切分结果:
["low", "est</w>"] ──► 查词表映射为 Token IDs: [15, 13]可以看到,即便 "lowest" 从未在训练集里完整出现过,它也能被优雅地拆分为 low + est,不会出现任何信息丢失。
四、 现代大模型的进阶:Byte-level BPE (BBPE)
传统的 BPE 依然以字符为单位,但在处理多语言(中文、Emoji、罕见符号)时,Unicode 字符过多,生僻符号依然可能造成 OOV。
因此,GPT-4、LLaMA 3、DeepSeek、Qwen 均升级为了 Byte-level BPE(BBPE,字节级 BPE):
中文 "大模型"
└── UTF-8 字节编码 ──► [0xE5, 0xA4, 0xA7, 0xE6, 0xA8, 0xA1, 0xE5, 0x9E, 0x8B] (9 个字节)
└── BBPE 合并 ─────► [Token ("大"), Token ("模型")] (2 个 Token)- 基础词表极小:以 256 个 UTF-8 字节(
0x00 ~ 0xFF)作为最小原子,初始词表只需 256 个单元。 - 真正的 100% 零 OOV:任何文字、代码、Emoji 都是合法的字节序列,彻底消灭
<unk>。 - 预分词正则(Pre-tokenization):
- 为避免把标点、空格、数字和英文随意黏连(例如把
hello!合并为一个 Token),现代分词器在合并前会先用正则表达式将单词、数字、标点隔开,禁止跨界合并。
- 为避免把标点、空格、数字和英文随意黏连(例如把
五、 3 句话总结
- 为什么用分词器:大模型只能算矩阵,分词器用子词机制在“词表大小”与“计算效率”之间实现了最佳平衡。
- BPE 训练的本质:从单个字符/字节出发,按频率不断“两两合并”,直到攒够词表容量。
- 现代 BBPE 的威力:基于 256 个字节起步,搭配预分词正则约束,实现真正无死角的多语言与代码切分。