分词:把文字变成数字
这一步在整条主线的哪里
我们的目标:亲手从 0 训练一个属于你自己的小 LLM。
整个项目的第一件事,不是搭神经网络,而是解决一个更基础的问题:模型的输入到底是什么?
你将在这个模块里为我们的项目做出第一批真正的工程决策——训练一个属于你自己的分词器(tokenizer)。它决定了:
- 下游(M2/M3):模型词表多大 → embedding 层多大 → 参数量和显存怎么分配;
- 同一篇文档被切成多少个 token → 训练要算多少步 → 直接决定训练成本;
- 模型「看世界的最小单位」——很多著名的模型失误(数不清 strawberry 里有几个 r)根源就在这一层。
上游: M0 我们盘点了算力,定下了要训练的模型规模。
本节: 把文字世界翻译成数字世界的「翻译官」。
下游: M2 的 Transformer 收到的第一个输入,就是这里输出的 token id 序列。
神经网络只吃数字,怎么把文字喂给它?
以及:切多细?这是一个两难。
神经网络内部只有矩阵乘法,输入必须是数字。所以必须有一个环节:把文本切成单元(token),再给每个单元一个编号(id)。问题是切多细:
| 切法 | 例:「今天天气真好」/ “unhappiness” | 好处 | 致命伤 |
|---|---|---|---|
| 按字符切 | 今|天|天|气|真|好 · u|n|h|a|p|p|i|n|e|s|s | 词表极小,永不缺字 | 序列太长。注意力计算量随长度平方增长(M2 会亲眼看到),成本爆炸 |
| 按词切 | 今天|天气|真好 · unhappiness | 序列短 | 词是开放集合:新词、错拼、生僻词永远收不完 → 词表爆炸 + 一堆「未知词」 |
| 子词(subword) | 今天|天气|真|好 · un|happi|ness | 折中:常见词整块保留,罕见词拆成碎片。词表大小可控,且理论上永不缺字。现代所有主流 LLM 都用这条路。 |
那「子词」怎么来?谁决定 un、happi、ness 是合法单元?——**不是人工规定的,是从数据里统计出来的。**这就是下一节的 BPE 算法。
BPE:把「最常一起出现的相邻碎片」不断合并
Byte Pair Encoding。GPT 系、LLaMA、Qwen、DeepSeek 的分词器都是它(或它的变体)。
BPE 的训练过程只有一条规则,重复执行:
🔍 深挖 BPE 的来历
BPE 不是为 NLP 发明的——它 1994 年就存在,原本是一个数据压缩算法。把它引入 NLP 做子词切分的是 Sennrich 等人 2015 年的论文 《Neural Machine Translation of Rare Words with Subword Units》(arXiv:1508.07909 · ACL 2016)。
1. 把语料全部拆成最小单元(字符/字节),作为初始词表;
2. 统计所有相邻单元对的出现次数;
3. 把出现最多的那一对合并成一个新单元,加进词表;
4. 回到第 2 步,直到词表达到你设定的大小(这是一个你来定的超参数)。
训练完成后得到两样东西:词表(每个单元一个 id)和合并规则表(按学习顺序排列)。之后任何新文本,按同样顺序应用合并规则,就能切出 token——哪怕这个词从没见过。
语料只有 4 个词(右上角是出现次数)。每点一次「执行合并」,就执行一步真正的 BPE 训练:先看下面的相邻对统计表,最高频的一对(紫色行)将被合并。
| 相邻对 | 出现次数 |
|---|
lowest →
从字符到字节:中文怎么办
上面演示用的是英文字符。真实的现代分词器(GPT-2 起)用的是 字节级 BPE:初始单元不是字符,而是 256 个字节。一个汉字在 UTF-8 里是 3 个字节,所以「没学过中文合并规则」的分词器,会把一个汉字拆成 2~3 个 token——同样一句话,token 数是中文友好词表的两三倍,训练和调用成本也翻倍。这就是为什么 Qwen、GLM、DeepSeek 都训练了自己的大词表。字节级的好处:任何语言、emoji、乱码都能表示,永远不会出现「未知字符」。
🔍 字节级 BPE 的出处
字节级 BPE 首次被系统提出和使用,是在 GPT-2 论文的 §2.2 “Input Representation”(本地 PDF)。它做了两个关键决定:① 用 256 个字节值而非 Unicode 码点作基础词表(Unicode 有 14 万+ 字符,直接当基础词表太大);② 规定禁止跨字符类别合并(空格除外)——否则贪心合并会产生 dog. dog! dog? 这种把标点粘在词上的劣质 token,白白浪费词表空间。
关键指标(本模块的验收数字)
用真正的分词器切你自己的话
下面加载的是真实模型的真实词表(GPT-2 英文向 vs Qwen 中文友好),不是演示数据。第一次加载需要从 HuggingFace 下载几 MB 词表文件,请稍等。
看什么: ① 同一句中文,GPT-2 要用多少 token、Qwen 要多少(这就是「词表对语言的友好度」);② 橙色虚线框=一个字符被拆成了好几个字节 token(下面详解);③ strawberry 被切成 st|raw|berry——模型看到的是这 3 块,而不是 10 个字母,所以它数不出有几个 r。
橙色虚线框:字节级 BPE 的真面目
用 GPT-2 切中文时,你会看到大量橙色虚线框,每个框里挤着 2~3 个 token id。这不是显示错误,而是字节级 BPE 的本来面目:
- 一个汉字在 UTF-8 里是 3 个字节(如「今」=
E4 BB 8A); - GPT-2 的词表几乎没学过中文合并规则,所以这 3 个字节各自成为一个 token;
- 单独拿出任何一个字节都不是合法字符,所以直接显示会变成
�——必须 3 个字节凑齐才能还原出「今」。虚线框就是把「凑齐一个字」所需的 token 圈在一起。
这就是成本: 同样写「今天天气真好」,Qwen 花 4 个 token,GPT-2 要花 10 个(实测)。token 数 × 单价 = 钱,同时你的上下文窗口也被消耗了 2.5 倍。这是「词表要不要为你的语言而训练」的最直接后果。
反过来看 Qwen: 它的 15 万词表里学过大量中文合并规则,「今天」直接就是一个 token——所以你几乎看不到虚线框。这正是我们在项目里要为自己的语料训练分词器的原因。
给我们的模型,训练它自己的分词器
记住分工:代码 AI 来写,决策你来做。这里列出的是你要拍板的事,和验收标准。
本模块的项目任务单
| 步骤 | 你的决策 / 动作 | 说明 |
|---|---|---|
| 1. 选语料 | 中英比例(建议中文为主 + 少量英文/代码) | 分词器只认识「它见过统计的」,语料配比直接决定压缩率 |
| 2. 定词表大小 | 8,192(我们的推荐起点) | 我们的模型很小(~50M 参数),词表太大 embedding 会占掉大半参数预算 |
| 3. 让 AI 写训练脚本 | 向 Claude 描述需求,指定用 HF tokenizers 库(或 Karpathy 的 minbpe 学习版) | 脚本 50 行左右;跑几分钟 |
| 4. 验收 | 算三个指标:中文压缩率、英文压缩率、每汉字 token 数 | 及格线:中文压缩率 ≥ 1.5 字符/token(即平均每 token 至少 1.5 个汉字) |
| 5. 冻结 | 把词表和合并规则存入 04-实践项目/tokenizer/,git 提交 | 从此不可再改——它将陪伴模型一生(原因见下) |
为什么「冻结后不可再改」: M2 里模型的 embedding 矩阵按「id → 向量」一一对应训练。换了分词器,id 全变,之前训练的所有权重立即作废。分词器和模型是终身绑定的。
预告下一个坑(M3 会遇到): 训练语料里如果混有大量重复网页模板,BPE 会把整段模板学成一个 token——词表被垃圾占据。到时候我们要做数据去重,那是 M1 数据部分的延续。
常见的坑
| 坑 | 症状 / 后果 | 怎么办 |
|---|---|---|
| 用英文向词表处理中文 | token 数 ×2~3,训练成本、API 费用翻倍,上下文窗口被浪费 | 中文项目必须用中文友好词表;上面实验亲测对比 |
| 特殊 token 漏配 | 模型不知道对话在哪结束,无限续写 | BOS/EOS/对话模板 token 在 M4(SFT)会正式登场,届时回看 |
| 训练后换分词器 | 全部权重作废 | 训练前定死,git 冻结 |
| 数不出 strawberry 的 r | 被误认为「模型太笨」 | 本质是 token 粒度问题:模型看不见 token 内部的字符 |
| 词表大小拍脑袋 | 小模型配 15 万词表 → embedding 占掉 70% 参数 | 词表大小要和模型规模匹配(我们 50M 模型配 8K 词表) |
这一模块你应该带走的
- 模型的世界只有 token id——文字在进模型前就消失了;
- 子词是统计出来的,不是规定的:BPE = 反复合并最高频相邻对;你亲手跑过它了;
- 字节级 BPE 保证永不缺字,但词表没学过的语言会被拆得很碎、很贵;
- 词表大小是你要拍板的超参:大→序列短省算力,小→embedding 省参数;
- 分词器与模型终身绑定,训练前冻结;
- 三个验收指标:词表大小、压缩率、fertility。
有不明白的? 随时在对话里问我(比如「为什么合并次数越多词表越大」「Qwen 的词表怎么定到 15 万的」)。我会把每个问题和解答记录到 05-问答记录/M1-分词-问答.md,它们会成为你自己的 FAQ 库。
概念索引(本模块登场): token · 词表 vocabulary · BPE · 字节级 BPE · 压缩率 · fertility · embedding(预告,M2 正式登场)· 特殊 token(预告,M4 正式登场)
想往下钻,按你愿意花的时间选
下面每条我都实际验证过可访问、且讲清了「为什么」。速通型内容、同质化转载已被排除。
🎮 想更直观 10–30 分钟
🔬 想吃透原理 1–3 小时
st|raw|berry 三块;字母 R 被「囚禁」在 token 内部,模型只能基于 token 关系猜测而非逐字符计数。📄 想读原文 按需
dog. dog! 这类劣质 token)。我替你排除了什么(也是筛选标准): 「5 分钟搞懂 BPE」类速通视频(只给结论无推导)· CSDN/掘金上互相转载的同质化模板文 · strawberry 问题的猎奇新闻稿(停在「AI 好笨」层面)· 2023 年前只讲 jieba 中文分词的旧文(已被 subword 范式取代)。
另注:李沐、李宏毅的频道整体质量很高,但没有专门讲分词/BPE 的深度单集,所以本模块没列——他们的内容会在 M2/M3 出现。