模型为什么数不清 strawberry 里有几个 r?
本课一问:模型为什么数不清 strawberry 里有几个 r?
这是第 2 课旁边的岔路。这一课先不碰:模型在训练里怎样间接「记住」一个 token 的拼写(涉及第 6 课的训练过程);也不评比哪个模型现在能数对——那是评测问题,不是分词问题。
子词切分把单词切成了块
第 2 课:子词切分让常见的字串整块进词表。「strawberry」很常见,所以它在词表里是整块的——而模型收到的,只是这些块的编号。
这一课换了例子,原因是:贯穿例句「小明把苹果给了小王」里没有英文单词,看不到「字母藏在 token 里」这件事。其实中文里同样的事一直在发生:「苹果」在 Qwen3.8 里是一个 token(ID 99203),「苹」和「果」两个字并不单独出现在输入里。
朴素的想法:模型读到了 10 个字母
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
人读这个问题时,眼睛落在 s、t、r、a、w、b、e、r、r、y 上,数出 3 个 r。如果模型也「读字母」,这件事不该出错。
模型读到的是问句被分词器切出来的 8 个 token,其中「 strawberry」是 ID 70135。
把单词拼开写成「s t r a w b e r r y」,Qwen3.8 会把它切成 10 个 token,每个字母一块;其中「 r」(ID 427)出现了 3 次。数 r 就变成了数一个编号出现几次,字母第一次真正进入了输入。用连字符写成「s-t-r-a-w-b-e-r-r-y」也是 10 个 token;GPT-2 会切成 19 个(连字符单独成块)。
两个视角:人看到的,模型收到的
这件事在演进图里的位置:它是子词切分的直接后果——常见的字串整块进词表,块内部的字母就不再出现在输入里。
- 解决了不用训练,任何文字都能编号。卡在码点从 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」。
切到「模型收到的」视角,你会发现输入里没有任何一个位置写着「r」。模型要答对「strawberry 有几个 r」,必须事先在训练里间接学到「ID 70135 拼出来是哪些字母」——比如训练数据里恰好有人把这个词拼开写过。这条知识不在输入里,只能存在参数里,学得好不好没有保证。
还有一个容易忽略的细节:同一个词,位置不同切法就不同。单独出现的「strawberry」被 Qwen3.8 切成 str / aw / berry 三块(GPT-2 是 st / raw / berry);在句子里前面带着空格,「 strawberry」就是另一个 token,整个一块。所以「它被切成几块」这个问题,答案取决于上下文里的空格。
常见误解:「模型看得到字母,只是数错了」。 模型的输入是 token 编号序列。字母只存在于人看到的那一层;对模型来说,拼写是一条需要从训练数据里间接学到的知识,和「看见」无关。
数一遍:这句话里有几个 r,藏在哪几个 token 里
在上面的组件里选「问句里的 strawberry」,看「人看到的」视角:整句有 5 个 r——「 r」里 1 个、「 are」里 1 个、「 strawberry」里 3 个。Qwen3.8 收到 8 个编号,其中 3 个编号的内部含有 r,而编号本身不告诉模型这件事。把「数 r」写成算式,就能看清缺的是哪一块:
换成「空格隔开的字母」:10 个 token,「 r」这个编号出现 3 次——数字母的任务变成了在输入里数同一个编号,这是模型能直接看到的信息。
试三件事: ① 同一句中文问句「strawberry 里有几个 r?」,GPT-2 用了多少个 token?汉字被拆成了字节;② 对比「单独的 strawberry」和「问句里的 strawberry」,Qwen3.8 的块数差了多少;③ 在「连字符隔开的字母」里,为什么 GPT-2 的 token 数几乎是 Qwen3.8 的两倍?(看「-」是不是单独成块)
这个解释靠得住吗
- 它解决什么问题:解释一类「模型很强却做不好字符级小事」的现象:数字母、倒着拼写、改一个字母。
- 数学上为什么可能有效:模型的输入就是 token 编号序列,token 的字母组成不在输入里 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条编号经 embedding 查表变成向量;向量里有没有拼写信息,取决于训练学到了什么,输入本身不提供 出处待补 ;把单词拼开写后字母各成 token,这是对真分词器的测量 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条Qwen3.8 与 GPT-2 真分词器离线切分;换一个分词器切法可能不同 出处c1-tokenize.json ↗需联网 。
- 实验上为什么最后选了它:「拼开写再数」能让任务落到模型看得见的编号上 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条只说明任务变得可见,不保证模型一定数对;实际正确率要靠评测 出处Jian Liao 2025 ↗需联网 。本课没有做正确率评测,不下「现在的模型能不能数对」的结论。
一句话、回到主线、你的提问
一句话: 模型数的是 token,不是字母——问句里的「 strawberry」只是一个编号。
回到主线
岔路讲完了。主线的问题还在:「strawberry」为什么是整块、「今天」为什么是整块,这些块是谁决定的?第 3 课:BPE 怎么学会「该怎么切」?
你的提问
你可能会问:「数字也会被切成奇怪的块吗?算术会不会因此出错?」 会被切,而且各家切法不同:Qwen3.8 的预切分规则把数字一位一位切开,「100」是「1」「0」「0」三块;GPT-2 把「100」整个当一个 token(ID 3064)。LLaMA 论文 §2 也写明「把所有数字拆成单个数位」 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条论文只说明做法,没有给出消融对比 出处LLaMA 2023 §2 · 本地 PDF ↗ 。到实验课里切「价格与数字」看看。