先说结论
Tokenizer 是将原始文本转换为模型可处理整数序列的桥梁,其设计直接决定模型的词汇覆盖度、多语言能力和推理效率。
为什么我会单独记这一篇
原始文本是连续的字符流,神经网络只能接受离散的数值输入。Tokenizer 解决的核心问题是:如何将文本切分为有限词表中的子词单元(subword),使得任意输入文本都能被表示,同时控制词表大小在合理范围内。一个好的 tokenizer 需要同时平衡以下需求:
- 覆盖度:任何语言的任何文本都能被编码,不出现 UNK(unknown token)
- 紧凑性:编码后的 token 数量尽可能少,减少序列长度和推理开销
- 语义性:切分结果与语言直觉一致,"un-""break-""able" 比 "unb""rea""kable" 好
- 效率:编码和解码速度快,支持大规模数据预处理
- 多语言公平性:不应对某一语言系统性分配过多 token
如果 tokenizer 设计失误,下游影响极其深远:非英语文本编码膨胀 5-10 倍、训练效率下降、推理成本翻倍,且无法通过后训练弥补。
先把核心脉络捋清楚
Byte Pair Encoding (BPE)
- 由 Sennrich et al. (2016) 引入 NMT 领域,源自数据压缩算法
- 训练过程:
- 将语料以字符级(或字节级)初始化为初始词表
- 统计所有相邻 token 对的共现频率
- 合并频率最高的 token 对,加入词表
- 重复步骤 2-3 直到词表达到目标大小
- 编码过程:对输入文本贪心地从左到右应用学到的合并规则
- 确定性:BPE 的分词结果是确定性的(给定合并规则序列)
- 代表模型:GPT-2/3/4、RoBERTa、Claude、Qwen(部分版本)
Byte-level BPE (BBPE)
- GPT-2 引入的改进:以 256 个字节作为基础词表,而非 Unicode 字符
- 好处:天然覆盖所有语言、不会产生 UNK
- 缺点:字节级基础词表导致初始序列较长,需要更多合并步才能收敛到合理词表
Unigram Language Model
- 由 Kudo (2018) 提出,是 SentencePiece 框架的两种核心算法之一
- 训练过程(与 BPE 方向相反):
- 从一个巨大的候选词表开始(如所有子串或高频子串)
- 用期望最大化(EM)算法估计每个子串的概率
- 计算移除每个子串后对整体损失的影响
- 移除对损失影响最小的子串(贪心剪枝)
- 重复直到词表缩减到目标大小
- 编码过程:给定输入,用 Viterbi 算法找到概率最大的分词路径
- 概率性:同一个字符串可能有多种合法分词,Unigram 自然支持这种多义性
- 代表模型:T5、ALBERT、mT5、Gemma
SentencePiece
- Google 开发的语言无关 tokenizer 工具包
- 核心设计理念:将输入视为原始字节流,不依赖任何语言特定的预分词(不需要空格切分)
- 同时支持 BPE 和 Unigram 两种算法
- 空格也用特殊字符(▁, U+2581)编码,实现了真正的语言无关性
- 代表模型:LLaMA(SentencePiece BPE)、T5(SentencePiece Unigram)
WordPiece
- Google 为 BERT 开发的算法,类似 BPE 但使用互信息(PMI)而非频率来选择合并对
- 合并的是使得
P(AB) / (P(A) * P(B))最大的对 - BERT、DistilBERT 使用此算法
Chat Template
- HuggingFace 提出的标准化机制,用 Jinja2 模板定义对话消息到模型输入格式的映射
- 存储在
tokenizer_config.json的chat_template字段中 - 通过
tokenizer.apply_chat_template(messages)调用 - 不同模型的格式差异:
| 模型 | 特殊 Token | 格式示例 |
|---|---|---|
| LLaMA 2 | [INST], <<SYS>> |
[INST] <<SYS>>\n{system}\n<</SYS>>\n{user} [/INST] |
| LLaMA 3 | `< | begin_of_text |
| ChatML (Qwen) | `< | im_start |
| Mistral | [INST], [/INST] |
类似 LLaMA 2 |
原理拆开看
BPE 的信息论直觉
BPE 本质上在做 贪心熵减:每次合并最高频的 token 对,相当于用一个新符号替代两个频繁共现的符号,从而减少描述语料所需的总比特数。这与经典的数据压缩(如 gzip 使用的 LZ77)在精神上一致。
Unigram 的概率直觉
Unigram 假设每个子串的出现服从 unigram 语言模型,即 P(text) = ∏ P(subword_i)。训练目标是最大化语料的边际似然。剪枝的标准是:移除哪个 token 对似然的损害最小。这保证了保留在词表中的 token 都是"信息量最大"的。
为何子词而非字符或整词?
- 整词:词表爆炸(尤其 agglutinative 语言如土耳其语),OOV 问题严重
- 字符:序列过长,模型难以学习长距离依赖
- 子词:高频词保留完整,低频词拆成有意义的片段,是两者的最优折中
放到工程里怎么落
- 语料采样:从训练数据中采样代表性子集(通常 5M-50M 文档)
- 归一化:NFKC Unicode 归一化、小写化(取决于设计决策)
- 预分词(BPE 系列):用正则(如 GPT-2 的
's|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+)预切分 - 训练:运行 BPE/Unigram 算法至目标词表大小
- 人工审核:检查高频 token 是否合理,多语言覆盖是否均衡
- 集成:将 tokenizer 与模型绑定,确保训练和推理使用完全相同的分词逻辑
工具链:tiktoken(OpenAI)、tokenizers(HuggingFace)、sentencepiece(Google)、sentencepiece_pb2(protobuf 接口)
与相邻概念的区别
| 概念 | 区别 |
|---|---|
| 预训练目标 | Tokenizer 在预训练之前,决定输入表示;预训练目标决定输出目标 |
| Word Segmentation(中文分词) | 中文分词是语言学驱动的整词切分;子词 tokenization 是统计驱动的,不追求语言学正确性 |
| Embedding Layer | Tokenizer 输出 token ID,Embedding 层将 ID 映射为向量;两者串联但职责不同 |
设计时真正要权衡什么
| 取舍 | 分析 |
|---|---|
| 词表大小 | 太小 → 序列过长、推理慢;太大 → Embedding 矩阵内存开销大、稀疏 token 学不好 |
| 字节级 vs 字符级基础 | 字节级保证覆盖所有语言但初始编码长;字符级对 ASCII 高效但 Unicode 处理复杂 |
| BPE vs Unigram | BPE 简单快速但分词固定;Unigram 概率化但训练慢 |
| 预分词规则 | GPT-2 的正则预分词决定了数字、标点、空格的处理方式,影响深远且无法后期更改 |
| 多语言词表配比 | 英文过多 → 其他语言编码膨胀;需精心设计采样比例 |
容易踩的坑
- 多语言编码膨胀:GPT-4 的 cl100k 对中文编码效率比英文低 3-5 倍,导致推理成本剧增
- Chat Template 不匹配:用 LLaMA 2 模板格式化 LLaMA 3 模型的输入,导致模型输出混乱
- 词表残留问题:复用他人的 tokenizer 但其词表包含不需要的语言 token,浪费词表空间
- Tokenizer 训练数据偏差:只在英文数据上训练 tokenizer,导致日韩文等 CJK 语言全被切成单字节
- 特殊 token 冲突:自定义特殊 token 与已有 token ID 冲突,导致训练不稳定
- 忽略
eos_token/bos_token:不同模型对首尾 token 要求不同,遗漏会导致生成异常
工程落地时我会怎么做
- 永远用
apply_chat_template(),不要手工拼格式——模板细节极易出错 - 训练新 tokenizer 时,采样数据需与训练数据分布一致(包括多语言比例)
- 评估 tokenizer 质量:用 Fertility(每词平均 token 数)和 Parity(多语言公平性)指标
- 扩展词表时(如添加中文词到英文 tokenizer),用 embed resize 技术,新 token 的 embedding 用已有 token 的均值初始化
- 锁死 tokenizer 后不要再改——改 tokenizer 等于让所有已训练的 checkpoint 作废
如果要对外讲,可以怎么概括
Tokenizer 是 LLM 的"眼睛",它决定了模型如何"看到"世界。主流方法有 BPE(贪心合并高频对)和 Unigram(概率剪枝大词表),分别被 GPT 系列和 T5 系列采用。关键 trade-off 是词表大小 vs 编码效率。做错的后果很严重:多语言支持差会导致推理成本翻倍,且无法通过后训练修复。工程上,chat template 是连接 tokenizer 与对话格式的桥梁,必须与模型严格匹配。
最后记几条
- BPE 是自底向上(合并),Unigram 是自顶向下(剪枝)
- SentencePiece 是语言无关的,不需要预分词
- 词表大小通常在 32K-256K 之间,太大太小都有问题
- Chat Template 必须与模型匹配,用
apply_chat_template()而非手拼 - Tokenizer 一旦确定就不可更改——它绑定在整个模型生命周期中
面试高频题
QUESTION BPE 和 WordPiece 的核心区别是什么?
特性 BPE WordPiece 合并标准 频率最高 互信息(PMI)最大 选择依据 最大 最大 直觉 最常一起出现的对 最不应该被拆分的对 代表模型 GPT-2/3/4, LLaMA, Qwen BERT, DistilBERT BPE 简单贪心合并高频对;WordPiece 更关注统计显著性,优先合并在语料中"关联度最高"的对。
QUESTION BPE 和 Unigram 的训练方向有什么不同? BPE 是自底向上:从字符级词表开始,逐步合并最高频对,词表从小到大。 Unigram 是自顶向下:从巨大候选词表开始,用 EM 算法估计概率,逐步剪枝对损失影响最小的子串,词表从大到小。
特性 BPE Unigram 方向 合并(自底向上) 剪枝(自顶向下) 编码确定性 确定(贪心合并规则) 概率性(Viterbi 最优路径) 多义性支持 不支持 自然支持 训练速度 快 慢(需 EM 迭代) 代表模型 GPT, LLaMA, Qwen T5, mT5, Gemma
QUESTION SentencePiece 解决了什么问题? 传统 tokenizer 依赖空格分词(如 BPE 在英文空格处预切分),这对中文、日文等不使用空格的语言不友好。SentencePiece 将输入视为原始字节流,空格也用特殊字符
▁(U+2581) 编码,实现了语言无关的分词。同时支持 BPE 和 Unigram 两种算法。
QUESTION 字节级 BPE (BBPE) 相比字符级 BPE 有什么优势? GPT-2 引入 BBPE,以 256 个字节为基础词表。优势:(1) 天然覆盖所有语言的字符,不会产生 UNK;(2) 无需 Unicode 归一化处理;(3) 词表完全固定。代价是初始编码序列较长,需要更多合并步才能达到合理词表。
QUESTION Tokenizer 对多语言支持有什么影响? 如果 tokenizer 训练数据以英文为主,非英语语言(尤其 CJK)会被切成更多、更短的 token,导致:
- 编码膨胀:同一段中文编码后的 token 数可能是英文的 3-5 倍
- 推理成本增加:更多 token = 更长序列 = 更多计算
- 信息密度降低:单个 token 携带的信息更少,影响模型对文本的理解
这也是为什么 Qwen、Yi 等国产模型会专门优化中文 token 覆盖。
QUESTION 为什么说 Tokenizer 一旦确定就不能改? Tokenizer 决定了模型的"词汇表"——每个 token ID 对应的子串。更改 tokenizer 意味着所有已训练的 embedding 和 output 权重都失效了。例如,如果将 "apple" 从 token #1234 改为两个 token "app" #5678 + "le" #9012,那么 token #1234 对应的 embedding 向量就不再有意义。因此 tokenizer 绑定在整个模型生命周期中,无法通过后训练修复。
主流模型 Tokenizer 配置
| 模型 | 算法 | 词表大小 | 工具 | 特点 |
|---|---|---|---|---|
| GPT-4 | BBPE | ~100K | tiktoken | 字节级,多语言覆盖好 |
| LLaMA 3 | BPE | 128K | SentencePiece | 大词表,多语言优化 |
| Qwen 2 | BBPE | 151K | tiktoken | 中文覆盖优秀 |
| BERT | WordPiece | 30K | WordPiece | 英文为主 |
| T5 | Unigram | 32K | SentencePiece | 语言无关 |
| Gemma | Unigram | 256K | SentencePiece | 超大词表 |
延伸阅读
-
Transformer — Tokenizer 输出如何被模型消费
-
注意力机制 — 注意力计算在 token 表示上操作
-
位置编码 — 每个 token 对应一个位置编码
参考资料
- Sennrich et al., "Neural Machine Translation of Rare Words with Subword Units" (ACL 2016)
- Kudo, "Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates" (ACL 2018)
- Kudo & Richardson, "SentencePiece: A simple and language independent approach to neural network text generation" (ACL 2018)
- HuggingFace Tokenizer Documentation: https://huggingface.co/docs/tokenizers/
- OpenAI tiktoken: https://github.com/openai/tiktoken
- Ahia et al., "Do All Languages Cost the Same? Tokenization in the Era of Commercial Language Models" (2023)