从0训练LLM · 现代大模型体系 岔路 阅读 + 实验 ≈ 25 分钟 页内实验 ×1
C1 文字怎么变成数字 岔路课

为什么中文更「贵」?

中文税
字节级 BPE 永远不缺字,但一个没学过中文合并的词表,会把一个汉字拆成最多 3 个字节 token。中文的「贵」来自词表,不来自中文本身。

本课一问:为什么中文更「贵」?

这是第 3 课旁边的岔路。这一课先不碰:词表该多大(第 4 课)。只回答两件事——BPE 的最小单元为什么是字节,以及这个选择怎样让没学过中文的词表把中文切得很碎。

② 从已有出发

BPE 从「最小单元」出发

从已有出发 · 累计数据流 点已学环节看形状与参数
解码层

第 3 课的第一步:「把语料拆成最小单元,作为初始词表」。最小单元选什么?最自然的答案是字符。先记下贯穿例句的账:「小明把苹果给了小王」9 个字、27 个 UTF-8 字节,GPT-2 切成 21 个 token,Qwen3.8 切成 5 个——同样是字节级 BPE,差了 4.2 倍。这一课回答差在哪。

③ 先自己设计

朴素的想法:以字符为最小单元

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案以字符(Unicode 码点)为初始单元

Sennrich 2015 的 BPE 就是这么做的:初始词表 = 语料里出现过的所有字符。对英文这没问题,字母就几十个;对中文和几百种文字的混合语料,麻烦来了:

  • Unicode 收录的字符在 2026-09 发布的 18.0 版里已有 172,808 个;全部放进初始词表,词表还没开始合并就已经很大;
  • 只收语料里出现过的字,词表小了,但一遇到训练时没见过的字就只能记成 UNK。我们的项目语料实测:训练集里有 4,073 个不同字符,验证集里仍有 83 种字训练集从没出现过,占验证集字次 0.42%。
④ 改进到现代做法

透视:同一串字节,两个词表怎么切

本课在演进图里的位置:第二代,字节级 BPE。

文字 → 数字的演进:每一步都被上一步的某个真数字逼出来 · 高亮 = 本课
  1. 起点 每个字符直接用 Unicode 码点当编号 最容易想到的做法
    解决了不用训练,任何文字都能编号。
    卡在码点从 0 排到 1,114,111,embedding 表得开 1,114,112 行,是 Qwen3.8 实际 248,320 行的 4.5 倍;而且一个字一个 ID,序列一点不缩短。
  2. 朴素 ① 按字切(字符级词表) 只收语料里出现过的字
    解决了字符集封闭,词表小。
    卡在序列最长:209 字的段落按字切要做 43,681 次注意力打分,是 Qwen3.8 子词切分(106 个 token)的 3.9 倍。
  3. 朴素 ② 按词切 + 固定词表 早期神经机器翻译的做法(Sennrich 2015 的对照组)
    解决了一个词一块,序列短。
    卡在词是开放集合:我们的语料按空白和标点切出 70,310 种块,94.8% 只出现过一次;验证集里连训练集没见过的字都有 83 种——含它们的词只能记成 UNK。
  4. 第一代 子词 BPE(以字符为初始单元) Gage 1994 压缩算法 → Sennrich 2015 用于切词
    解决了按频次反复合并相邻对:常见的整块、罕见的拆碎,词表大小由合并次数控制。
    卡在初始单元是字符:要覆盖全部 Unicode,基础词表就超过 13 万(GPT-2 论文 §2.2);只收见过的字,我们的验证集照样缺 83 种字。
  5. 第二代 字节级 BPE(256 个字节兜底) GPT-2 2019 本课
    解决了初始词表就是 256 个字节,任何文字都能编码,永远不出 UNK。
    卡在词表没学过的语言会被切碎:同义的中文段落,GPT-2 切 461 个 token,Qwen3.8 切 106 个,差 4.3 倍;同义英文段落两者一样多。
  6. 第三代 为多语言训练的大词表 2024–2026
    解决了中文常用字串整块进词表:「我喜欢机器学习」只要 2 个 token(GPT-2 要 17 个)。各家词表:DeepSeek-V3 129,280 · Qwen3 151,936 · GLM-5.3 154,880 · Kimi K3 163,840 · Qwen3.8 248,320。
    卡在两头各一张 V × d 的表:2 × 248,320 × 5,120 = 2,542,796,800 个参数,占 Qwen3.8 的 9.2%;同样的词表搬到我们 d = 256 的小模型上,embedding 占 95.3%。
  7. 表示 ① ID → Embedding 查表 神经语言模型的标准做法
    解决了ID 只是行号,取出一行 5,120 维向量;one-hot 乘矩阵与查表等价,但查表只拷现成的数。
    卡在表里的 1,271,398,400 个数(输入端一张表)从哪来?人填不完,也不知道每一维该是什么意思。
  8. 表示 ② 随机初始化、和整个模型一起训练的 Embedding Bengio 等 2003 → 今天的 LLM
    解决了用「预测下一个 token」的 loss 端到端训练,用法相近的 token 被推到相近的位置。
    卡在只在出现时才被推:我们的项目里一个 batch 8,192 个位置,平均只出现 1,453 个不同 token;罕见 token 学得少——MiniMax 的「嘉祺」在后训练里被推偏,输出层那一行与原来的余弦只剩 0.329。
  9. 表示 ③ 向量该多宽(hidden size d) 各家自选:GPT-2 768 · Qwen3-8B 4,096 · Qwen3.8-2.4T 8,192 · Qwen3.8-27B 5,120
    解决了维度越高,每个 token 能装的信息越多。
    卡在参数按 d² 涨:我们的模型 d 从 128 到 256,总参数 1,066,112 → 3,688,704,val loss 4.776 → 4.641(d = 256 这一档是参照 run ref_baseline,与项目基线同配置同种子;基线记录的 4.626 与它之差是重跑波动,见 C14);没有公式能给出「最优的 d」。
数字口径:码点总数按 Unicode 标准(U+0000–U+10FFFF);词表与宽度取各模型官方 config.json(anchor-model.json、frontier-models.json);token 数取真 Qwen3.8 / GPT-2 分词器的离线切分(c1-tokenize.json);语料统计、batch 覆盖、宽度扫描取项目实测(c1-tokens.json);「13 万以上」出自 GPT-2 论文 §2.2;「嘉祺」的余弦 0.329 出自 MiniMax 官方博客 2026-05-25(官方自报)。

先看文字在 UTF-8 里是几个字节,再看 GPT-2 和 Qwen3.8 这两个真词表,把同一串字节切成了什么(B 部分默认就是贯穿例句)。

透视 · UTF-8 字节与两个真词表的切法
A · 你的文字在 UTF-8 里是几个字节
1 字节(ASCII)2 字节3 字节(常用汉字、全角标点)4 字节(emoji、扩展区汉字)
B · 同一串字节,两个真词表怎么切

在 B 部分切到「今天天气真好」:6 个字、18 个字节。GPT-2 用了 10 个 token,其中「今」被切成 2 块(E4 BB 与 8A)、「天」恰好是 1 块;Qwen3.8 只用 3 个:「今天」「天气」「真好」各是词表里的一项。

差别不在中文,而在词表学到了哪些合并。GPT-2 的训练语料以英文为主,中文字节对的频次不够高,轮不到被合并;Qwen3.8 的 24.8 万词表学了大量中文合并。同义的两段文字:中文段落 209 字,GPT-2 切 461 个 token、Qwen3.8 切 106 个;同义英文段落两者都是 149 个——英文上两者打平,中文上差 4.3 倍。

这就是成本:API 按 token 计费,上下文窗口按 token 计量。同一段中文,用 GPT-2 的词表要花约 4.3 倍的 token,钱和窗口都按这个倍数消耗。这也是 Qwen、GLM、DeepSeek 都训练自己的大词表的原因——它们官方配置里的词表(embedding 行数)分别是:Qwen3.8 248,320、GLM-5.3 154,880、DeepSeek-V3 129,280,而 GPT-2 是 50,257。

⚠

常见误解一:「显示出 � 是 bug」。 单独一个字节 token 常常只是半个汉字,不是合法的 UTF-8,单独解码就显示成 �。凑齐同一个字的几个 token 才能还原。实验课里的橙色虚线框,就是把「凑齐一个字」的几个 token 圈在一起。

常见误解二:「一个汉字固定是 3 个 token」「ASCII 字符 1 字节 1 token」。 字节数只是上限。GPT-2 切「今天天气真好」时,一个字有的是 1 个 token、有的是 2 个;英文字母同样会被合并,「apple」在两个词表里都是 1 个 token。扩展区汉字(如 𠀀)和 emoji 在 UTF-8 里是 4 个字节,也不是 3 个。

🔍 GPT-2 为什么选字节:原文怎么说

GPT-2 论文 §2.2「Input Representation」:参考实现的 BPE 在 Unicode 码点上操作,要覆盖所有 Unicode 字符串,就得把全部码点放进基础词表,「在加入任何多字符 token 之前,基础词表就超过 130,000」,而当时常用的 BPE 词表只有 32,000 到 64,000;字节级 BPE 的基础词表只需要 256。它同时规定不跨字符类别合并(第 3 课的 Dig)。(本地 PDF)

⑤ 极小真数字,算一次

一个字几个字节:UTF-8 的分段规则

一个字符占几个字节,完全由它的码点落在哪一段决定。点每一段看它的意思和例子:

UTF-8:码点范围 → 字节数
· · ·
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
这四段是 UTF-8 编码的定义(RFC 3629),可以直接推出:没有学过任何合并时,一个常用汉字就是 3 个 token,这是上限;学到的合并越多,越接近 1 个。上面 A 部分的每个字节都是浏览器现场编码的,可以自己输入验证。
👀

试三件事: ① 在 A 部分输入「é 的两种写法」:看起来一样的两个 é,一个是 2 字节的单个码点,一个是「e + 组合重音」共 3 个字节——Qwen3.8 分词前会先做 NFC 规范化,把后者统一成前者;② 在 B 部分切到「a TAB b CRLF c」:Qwen3.8 把 TAB 和后面的 b 合成了一块,CRLF 也是一块,GPT-2 则一个一个切;③ 切到「价格 NBSP 100 NBSP 元」,看 Qwen3.8 怎样把 100 一位一位切开。

⑦ 证据三问

字节级 BPE 的账

⑧ ⑨ ⑩ 带走与下一步

一句话、回到主线、你的提问

📌

一句话: 字节级 BPE 永远不缺字,但一个没学过中文合并的词表,会把一个汉字拆成最多 3 个字节 token——中文的「贵」来自词表,不来自中文本身。

回到主线

Qwen3.8 用 24.8 万的大词表换来了中文的低 token 数。那词表是不是越大越好?它的代价记在哪里?第 4 课:词表该多大?

你的提问

你可能会问:「我们自己的分词器只在繁体古白话上训练,切简体现代文会怎样?」 在第 3 课的真实日志压轴输入框里切「小明把苹果给了小王。」,虚线框就是它没学过的字;实验课里还能把同一句交给三个分词器并排比。