为什么不一字一个 Token,也不一词一个 Token?
本课一问:为什么不一字一个 Token,也不一词一个 Token?
这一课先不碰:子词表具体是怎么学出来的(第 3 课)、词表该定多大(第 4 课)。只回答「切多细」这一个取舍。
上一课留下的两个数
上一课:分词器把文字切成词表里的块,再把每块换成行号。贯穿例句「小明把苹果给了小王」有 9 个字:按字切是 9 块,Qwen3.8 切成 5 块(小明 / 把 / 苹果 / 给了 / 小王),GPT-2 切成 21 块。块切多大,同时决定了两个数——序列长度 T(一句话变成几个 token)和词表大小 V(一共要准备多少种块)。
T 不只是个计数:训练要处理多少 token、按 token 计费的 API 要花多少钱、上下文窗口能装下多少内容,全都按 token 数算。注意力这一步还要让每个 token 给每个 token 打分,打分次数按 T² 增长(C3 细讲)。
两个最自然的方案:按字切,按词切
按字切,字符集是封闭的,词表很小;按词切,一句话只有几块,序列很短。选一段文字,看这两个方案各在哪个数上失败。(组件默认换成一段 209 字的段落:贯穿例句只有 9 个字,T² 的差距要在长一点的文字上才看得出来;第三个按钮就是一句短句,可以对照。)
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
| 切法 | token 数 T上面这段文字 | 注意力打分 T²上面这段文字 | 词表要多大项目语料实测 | 遇到没见过的项目 val 实测 |
|---|
子词(subword)切分的思路:让常见的字串整块进词表,让罕见的字串拆成更小的、词表里已有的碎片。常见的「机器学习」「今天」是一块,序列短;没见过的新词拆成几块也能表示,不会变成 UNK。
表格里新出现的一行就是 Qwen3.8 的子词切分:这段中文 106 个 token,比按字切少一半;词表固定 248,077 项;任何输入都能落进词表——最坏的情况退回到单个字节,而 256 个字节永远在词表里(岔路课「为什么中文更贵」细讲)。
三种切法并排:真切分
本课在演进图里的位置——两个朴素方案,一个太碎、一个太粗:
- 解决了不用训练,任何文字都能编号。卡在码点从 0 排到 1,114,111,embedding 表得开 1,114,112 行,是 Qwen3.8 实际 248,320 行的 4.5 倍;而且一个字一个 ID,序列一点不缩短。
- 解决了字符集封闭,词表小。卡在序列最长:209 字的段落按字切要做 43,681 次注意力打分,是 Qwen3.8 子词切分(106 个 token)的 3.9 倍。
- 解决了一个词一块,序列短。卡在词是开放集合:我们的语料按空白和标点切出 70,310 种块,94.8% 只出现过一次;验证集里连训练集没见过的字都有 83 种——含它们的词只能记成 UNK。
- 解决了按频次反复合并相邻对:常见的整块、罕见的拆碎,词表大小由合并次数控制。卡在初始单元是字符:要覆盖全部 Unicode,基础词表就超过 13 万(GPT-2 论文 §2.2);只收见过的字,我们的验证集照样缺 83 种字。
- 解决了初始词表就是 256 个字节,任何文字都能编码,永远不出 UNK。卡在词表没学过的语言会被切碎:同义的中文段落,GPT-2 切 461 个 token,Qwen3.8 切 106 个,差 4.3 倍;同义英文段落两者一样多。
- 解决了中文常用字串整块进词表:「我喜欢机器学习」只要 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%。
- 解决了ID 只是行号,取出一行 5,120 维向量;one-hot 乘矩阵与查表等价,但查表只拷现成的数。卡在表里的 1,271,398,400 个数(输入端一张表)从哪来?人填不完,也不知道每一维该是什么意思。
- 解决了用「预测下一个 token」的 loss 端到端训练,用法相近的 token 被推到相近的位置。卡在只在出现时才被推:我们的项目里一个 batch 8,192 个位置,平均只出现 1,453 个不同 token;罕见 token 学得少——MiniMax 的「嘉祺」在后训练里被推偏,输出层那一行与原来的余弦只剩 0.329。
- 解决了维度越高,每个 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」。
同两个例子,三种切法(字符数、字母数由定义给出;子词一行是 Qwen3.8 与 GPT-2 真分词器的结果):
| 切法 | 「今天天气真好」 | unbelievable | 好处 | 致命伤 |
|---|---|---|---|---|
| 按字符 | 今 / 天 / 天 / 气 / 真 / 好:6 块 | 12 个字母:12 块 | 字符集封闭,词表小 | 序列最长,注意力打分数按 T² 涨 |
| 按词 | 取决于分词工具(上面的组件用浏览器内置分词器实时切) | 1 块 | 序列短 | 词是开放集合:新词、人名、错拼永远收不完,词表爆炸,没见过的只能记成 UNK |
| 子词(Qwen3.8) | 今天 / 天气 / 真好:3 块 | un / belie / vable:3 块 | 常见的整块留、罕见的拆碎;词表大小可控;字节兜底,永不 UNK | 切法要从数据里学(第 3 课) |
| 子词(GPT-2) | 10 块(大多是半个字的字节) | un / bel / iev / able:4 块 | 同上 | 词表没学过中文合并,中文被拆得很碎 |
同一个「子词」思路,两个词表切出来差了三倍多:「今天天气真好」Qwen3.8 用 3 个 token,GPT-2 用 10 个(旧版课程写的「Qwen 4 个」是上一代 Qwen2.5 的数字)。子词切分只是方法,切得好不好取决于词表是在什么数据上学出来的。
常见误解:「中文模型就是一字一个 token」。 Qwen3.8 把 7 个字的「我喜欢机器学习」切成 2 个 token;GPT-2 把它切成 17 个——比字数还多。两者都不是一字一 token:token 数取决于词表学到了哪些合并,和「一个字」没有固定对应。
压缩率:一个 token 平均装几个字符
衡量「切得多粗」最常用的数叫压缩率。点公式里的每一项:
再算一次注意力的账:同一段中文,按字切 T = 209,打分 209² = 43,681 次;Qwen3.8 切 T = 106,打分 106² = 11,236 次;GPT-2 切 T = 461,打分 461² = 212,521 次——对这段中文,GPT-2 的子词切分比按字切还贵。
试三件事: ① 在上面的组件里切到「同义英文段落」,看按字符切的 T² 比 BPE 大多少倍——英文按字母切比中文按字切更吃亏;② 切到「我喜欢机器学习」,词级那一列的块数和 Qwen3.8 的 2 块谁多?③ 回头看表里词级那一行:项目环境没有中文分词器,词级的完整 UNK 率没有实测;表里给的是能实测的下界——含「训练集没见过的字」的词一定是 UNK。
子词为什么赢
- 它解决什么问题:在「序列短」和「词表有限、不出 UNK」之间取中间。
- 数学上为什么可能有效:注意力打分次数是 T × T(因果掩码下约一半) 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条每个位置对每个位置各算一个分数,计数即得 出处待补 ;字节兜底保证任何文本都能表示 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条初始词表含全部 256 个字节值,任何 UTF-8 字节串都能被切成词表内的 token 出处GPT-2 论文 §2.2 ↗ 。
- 实验上为什么最后选了它:Sennrich 等在机器翻译上对比了按词切(罕见词回退到词典)与子词切分,子词切分对罕见词、没见过的词翻译得更好 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条WMT 15 英→德、英→俄翻译,对照组是按词切 + 罕见词回退词典;结论只在该论文的设置下成立 出处Sennrich 2015 §4–§5 ↗ 。之后几乎所有主流大模型都用子词切分——这是工程上的普遍选择,不是证明出来的最优。
一句话、下一个缺口、你的提问
一句话: 切得越细,序列越长;切得越粗,词表越大还会遇到没见过的词——子词取中间:常见的整块留,罕见的拆成碎片。
下一个缺口
「常见的整块留」——谁来决定哪些字串算常见、该进词表?第 3 课:BPE 怎么学会「该怎么切」?
你的提问
C1 还没有真实提问记录。你可能会问:
- 「既然 strawberry 被切成几块,模型是不是就看不见里面的字母?」→ 岔路课:模型为什么数不清 strawberry 里的 r