分词器tokenizer
笔记12
分词器 (tokenizer)作用
将原始文本转化为模型理解的数字序列
LLM负责“理解和生成”,而tokenizer负责“把语言变成模型能理解、可复用的格式”
分词器会影响模型能力:token划分得太“碎片化”或太“笼统”,都会影响表达效率;
所以:
- 分词算法不是唯一的:不同的分词算法(如BPE、WordPiece)会出现不同的划分结果;
- 它是独立优化的模块:通常先在大规模文本上训练词表(vocab),再固定下来供模型使用。
训练分词器
1 准备语料
运用命名实体识别(Named Entity Recognition,NER)技术实现数据脱敏;去除私密信息;
个人信息属于噪声;干扰分词算法的统计效率
2 预分词阶段
基于空格和标点的切分、按Unicode类别划分,或直接采用字节级切分
直接对字符流做分词;会因为跨空格或者标点合并难造成难以还原、语义混乱的token
3 统计并迭代更新
字词候选统计并迭代更新
训练分词器与训练神经网络不通;往往采用贪心合并和统计频次的方式
4 最终输出产物
vocab.json: token与id
merges.txt:记录字词合并规则或者概率模型
vocab.json是最终成品清单;已知的直接匹配
merges.txt是生成成品的工艺流程;未知的按规则拆成已知的;两者配合做到零未知词(OOV)
假设词表vocab.json里有这些 token:
"l","o","w","e","r","er","lo","low","lower","un","believe","able"现在来了一个完全没见过的词"unlowerable"(词表里没有这个词)。
Vocab 查不到→ 这时你必须把它拆成词表里有的子词。可问题是:该拆成什么样?
可以拆成:["un","low","er","able"]也可以拆成:["un","lo","w","er","able"]还可以拆成:["un","l","o","w","e","r","able"]好几种拆法都合法,但效果不一样。到底用哪种?
答案就是:看 merges.txt。
阶段①:训练分词器
原始文本 → ① 预分词 → ② BPE 合并 → 得到 vocab + merges
阶段②:使用分词器(编码新文本,推理/生成时)
新文本 → ① 预分词 → ② BPE 查询 → Token IDs
↑ 这个"预分词"和"合并"在两端各出现一次!
| 训练阶段(离线,一次) | 使用阶段(在线,每次) | |
|---|---|---|
| 输入 | 海量训练语料 | 单个用户句子 |
| 做的事 | 统计频次、迭代合并、构建词表 | 预分词 → 查表编码 |
| 有统计吗 | ✅ 有(频次统计) | ❌ 无 |
| 有迭代吗 | ✅ 有(反复合并) | ❌ 无 |
| 产出 | 固定 vocab.json + merges.txt | token ID 序列 |
常用分词器
1 字节分词器
直接维护大小256的词表;与UTF-8编码一致;压缩率为1
2 字符分词器
一个字母或者汉字为一个字符
| 字符类型 | UTF-8 字节数 | Token 数 | 压缩比 |
|---|---|---|---|
| ASCII(英文、数字) | 1 字节 | 1 token | 1 |
| 中文、日文、韩文 | 3 字节 | 1 token | 3 |
| 拉丁扩展、希腊文等 | 2 字节 | 1 token | 2 |
| Emoji 🌍 | 4 字节 | 1 token | 4 |
3 词级分词器
基于空格或者中文将文本切分为词,一个词对应一个ID
补充:正则表达式
用于描述字符串长什么样子的规则语言;正则来源于regular;表示可由一类规则描述;用于提取和判断
deepseek采用设计专门的预分词阶段;用于切块规则
4 BPE分词器
统计相邻字符对出现的频率;将频繁出现的字符对合并为Token
思考
四种分词算法对比与 LLM 为何选 BPE
机制对比
| BPE | WordPiece | Unigram | SentencePiece | |
|---|---|---|---|---|
| 合并/挑选准则 | 统计相邻字符对频率 | 用语言模型概率挑最可能的合并 | 删词:从大到小,删掉让似然损失最小的子词 | 不直接是一种算法,是框架 |
| 方向 | 自底向上(小→大拼) | 自底向上 | 自顶向下(大→小删) | 可装 BPE / Unigram |
| 评分依据 | 纯频次 | 概率/似然 | 似然 | 可配置 |
| 代表性使用 | GPT、LLaMA、DeepSeek | BERT | T5、Gemma | LLMamba、T5 等 |
| 是否依赖空格 | 依赖(需预分词) | 依赖 | 不依赖 | 不依赖(原始字节流) |
关键差异:
- BPE:每次找"出现最频繁"的相邻对合并,纯看次数。
- WordPiece:不是看出现最多,而是看合并后整体分词似然提升最大的对。
选让 score=P(xy)/(P(x)·P(y))最大的一对合并 - Unigram:反着来,先假设一个大词表,逐个删掉对总似然损失最小的子词,直到词表达标。
- SentencePiece:不是独立算法,而是处理框架。默认不依赖空格——把空格当作普通字符
▁处理,天然适配日语等无空格语言,也免去预分词步骤。
为什么现在的 LLM 普遍选 BPE
| 原因 | 说明 |
|---|---|
| 简单高效 | 只有"数频次"一个操作,训练快、实现简单、容易并行 |
| 无 OOV | 子词拆到字符/字节级兜底,任何词都能编码 |
| 字节级扩展 | 最小单元可用 UTF-8 字节 → 能编码任何语言任何字符,绝对 OOV-free |
| GPU 生态成熟 | GPT 全系都用它,工具链、复现资料最全 |
| 压缩好 | 常见词整词保留、生僻词拆零件,压缩率高,省 token |
一句话:BPE 以"够用 + 简单 + 生态成熟 + 天然无 OOV"取胜。WordPiece/Unigram 概率建模更优雅,但 LLM 追求鲁棒性与性价比,实用主义压倒了理论精致。
如何衡量"好的分词器":压缩效率 vs 语义一致性
| 维度 | 含义 | 好的表现 |
|---|---|---|
| 压缩率 | 平均每个 token 承载多少信息 | 每 token 有效信息多、token 数少 |
| 语义一致性 | token 是否对应有意义的语言单元 | “不开心"不要拆成"不”+"开心"割裂语义 |
| 鲁棒性 | 对新词、噪声、不同语言的容忍 | 新词能拆、不崩、产出稳定 |
| 可还原性 | 能否从 ID 无损还原原文 | 空格标点状态不丢失 |
| 效率 | 编码/解码速度、词表大小 | 词表不过大、延迟可控 |
核心 trade-off:压缩率 ↔ 语义一致性
压缩率和语义一致性天然冲突——很难同时要"最少的 token"和"最合理语义"。
- 压得越狠→ 词更粗更整 → token 少,但可能把不该绑的一起绑,语义边界错乱
- 拆得越细→ 语义更纯净 → 但 token 变多,序列变长,成本上升
中文例子:-整词:["不开心"]→3字1token,省,但"开心"难以复用到其他场景-子词:["不","开心"]→ 语义清晰,且"开心"可复用-单字:["不","开","心"]→ 最灵活但 token 最多现代取平衡的做法:高频词整词收进词表(保语义 + 省 token),低频生僻内容拆成子词(保证覆盖)。平衡点由词表大小和训练语料分布决定。
类比:像造乐高——大积木(整词)拼得快但形状受限;小积木(子词)灵活但拼得慢。好分词器是找到常用部分用大积木、其余用小积木的黄金配比。
分词器如何影响实际 LLM 的表现
分词器虽是"预处理",但对模型能力上限影响极大。
① 上下文窗口的"实际长度"被压缩率决定
模型窗口=2048token(固定) 压缩率高的分词器 →2048token 能塞进更多信息 → 模型"看到"更多上下文 压缩率低 → 同样文本吃满窗口 → 长文容易"截断丢失"直接影响长文档理解、多轮对话、代码生成的好坏。
② 生成成本的直接杠杆
API 按 token 收费/推理按 token 计算 好的分词器 token 少 → 便宜、生成快 英文1词≈1-2token,中文每字≈1-2token直接影响经济成本和速度,是工程上最被重视的原因。
③ 学习能力与语义:token 边界 = 模型的"注意力边界"
模型基于 token 学习,token 之间是离散的,跨 token 组合主要靠注意力:
- 语义一致的 token→ 更容易学到词的用法/语法/搭配 → 生成更流畅准确
- 语义割裂的 token→ 关联被切断,模式更难学,生成质量下降
好的切分:["在新","的","世界","里"]→ 模型抓住"在...里"的句式 坏的切分:["在","新","的","世","界","里"]→ 每字孤立,句式规律难学④ 特殊 token 的设计影响指令遵循
<BOS>/<EOS>、<PAD>、角色分隔符等设计直接影响对话模板、指令跟随能力。
⑤ 数字与代码:切片方式决定推理薄弱点
数字"20242025"拆成["2024","2025"]→ 模型可能"看出"是相邻年份 拆成["20","24","20","25"]→ 算术和数字关系更难学知名现象:LLM 在大数运算、拼写、中英文混排上出问题,根因往往是分词器把内容切碎了。
★ 三个终极小结
- 为什么选 BPE:简单高效 + 生态成熟 + 字节级无 OOV,实用主义胜出。
- 好的分词器= 在"压缩率"和"语义一致性"间找平衡,通常用"高频整词 + 低频子词"兼顾。
- 对 LLM 的影响:决定窗口能装多少信息、生成成本、模型能否学到语义规律,进而影响长文能力、质量、成本,甚至数字和代码推理。
参考链接
分词器 ↩︎
课程介绍与分词器
cs336第一节 ↩︎
