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

模型为什么数不清 strawberry 里有几个 r?

strawberry 里的 r
人看到的是 10 个字母,模型收到的是几个编号——在问句里,「 strawberry」整个只是一个编号。字母藏在 token 里面,输入本身不提供它们。

本课一问:模型为什么数不清 strawberry 里有几个 r?

这是第 2 课旁边的岔路。这一课先不碰:模型在训练里怎样间接「记住」一个 token 的拼写(涉及第 6 课的训练过程);也不评比哪个模型现在能数对——那是评测问题,不是分词问题。

② 从已有出发

子词切分把单词切成了块

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

第 2 课:子词切分让常见的字串整块进词表。「strawberry」很常见,所以它在词表里是整块的——而模型收到的,只是这些块的编号。

这一课换了例子,原因是:贯穿例句「小明把苹果给了小王」里没有英文单词,看不到「字母藏在 token 里」这件事。其实中文里同样的事一直在发生:「苹果」在 Qwen3.8 里是一个 token(ID 99203),「苹」和「果」两个字并不单独出现在输入里。

③ 先自己设计

朴素的想法:模型读到了 10 个字母

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案模型读到 s-t-r-a-w-b-e-r-r-y 这 10 个字母,挨个数就行

人读这个问题时,眼睛落在 s、t、r、a、w、b、e、r、r、y 上,数出 3 个 r。如果模型也「读字母」,这件事不该出错。

模型读到的是问句被分词器切出来的 8 个 token,其中「 strawberry」是 ID 70135。

④ 改进到现代做法

两个视角:人看到的,模型收到的

这件事在演进图里的位置:它是子词切分的直接后果——常见的字串整块进词表,块内部的字母就不再出现在输入里。

文字 → 数字的演进:每一步都被上一步的某个真数字逼出来 · 高亮 = 本课
  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(官方自报)。
双视角 · 人看到的字母 vs 模型收到的编号(真 Qwen3.8 / GPT-2 切分)

切到「模型收到的」视角,你会发现输入里没有任何一个位置写着「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」写成算式,就能看清缺的是哪一块:

「句子里有几个 r」按 token 拆开
=
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
把单词拼开写之后,每个含 r 的 token 恰好就是「 r」本身(ID 427),countr 变成了「这个位置是不是 427」——一个直接看得见的判断。

换成「空格隔开的字母」:10 个 token,「 r」这个编号出现 3 次——数字母的任务变成了在输入里数同一个编号,这是模型能直接看到的信息。

👀

试三件事: ① 同一句中文问句「strawberry 里有几个 r?」,GPT-2 用了多少个 token?汉字被拆成了字节;② 对比「单独的 strawberry」和「问句里的 strawberry」,Qwen3.8 的块数差了多少;③ 在「连字符隔开的字母」里,为什么 GPT-2 的 token 数几乎是 Qwen3.8 的两倍?(看「-」是不是单独成块)

⑦ 证据三问

这个解释靠得住吗

⑧ ⑨ ⑩ 带走与下一步

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

📌

一句话: 模型数的是 token,不是字母——问句里的「 strawberry」只是一个编号。

回到主线

岔路讲完了。主线的问题还在:「strawberry」为什么是整块、「今天」为什么是整块,这些块是谁决定的?第 3 课:BPE 怎么学会「该怎么切」?

你的提问

你可能会问:「数字也会被切成奇怪的块吗?算术会不会因此出错?」 会被切,而且各家切法不同:Qwen3.8 的预切分规则把数字一位一位切开,「100」是「1」「0」「0」三块;GPT-2 把「100」整个当一个 token(ID 3064)。LLaMA 论文 §2 也写明「把所有数字拆成单个数位」 。到实验课里切「价格与数字」看看。