词表该多大?
本课一问:词表该多大?
这一课先不碰:embedding 表具体怎么查、里面的数字从哪来(第 5、6 课)。这里只需要一个事实:词表里的每一项,在模型的输入端和输出端各对应一行 d 个参数(d 是模型宽度,Qwen3.8 是 5,120)。
词表大小是 BPE 唯一要你定的数
第 3 课:BPE 合并到多大为止,是你来定的超参数。贯穿例句「小明把苹果给了小王」在 Qwen3.8 里只要 5 个 token,靠的是一张 248,320 行的大词表;GPT-2 的 50,257 行词表要 21 个。它牵动流水线的两头:输入端灰色的「Embedding 查表」是一张 V 行的表,输出端的「lm_head」要对 V 个候选各打一个分——两张都是 V × d 的矩阵。
朴素的想法:词表越大越好
词表越大,常见字串越能整块进词表,序列越短——看起来只有好处。拖动词表大小,看它在两个模型里各吃掉多少参数:
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
词表大小没有通用的「最好」,只有「对这个模型、这份语料合适」。办法是扫描:同一份语料,训练几档词表,量每一档的压缩率和它让 embedding 占多少参数,再各训一个模型比 loss。下面是我们项目五档词表的实测:
交易的两边各是什么
本课在演进图里的位置:第三代,大词表。
- 解决了不用训练,任何文字都能编号。卡在码点从 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」。
词表变大,得到的:常见字串整块进词表,同样的文字 token 更少——训练吃得下更多文字,推理更快,上下文窗口装得更多。付出的:
- 参数:输入 embedding 与输出 lm_head 各是 V × d;不共享时合计 2 × V × d。
- 每个 token 的输出计算:lm_head 要对 V 个候选各算一个打分,V 越大越慢。
- 训练信号被摊薄:词表越大,每个 token 在语料里出现得越少,它那一行参数得到的训练越少——罕见 token 的向量学得差(第 6 课回扣)。
现实里的词表一直在变大。各模型官方 config.json 里的词表(embedding 行数):
| 模型 | 词表 V | 宽度 d | 输入输出共享? |
|---|---|---|---|
| GPT-2(2019) | 50,257 | 768 | 共享 |
| DeepSeek-V2 | 102,400 | 5,120 | 不共享 |
| DeepSeek-V3 | 129,280 | 7,168 | 不共享 |
| Qwen3-8B / Qwen3-32B | 151,936 | 4,096 / 5,120 | 不共享 |
| GLM-5.3 | 154,880 | 6,144 | 不共享 |
| Kimi K3 | 163,840 | 7,168 | 不共享 |
| Qwen3.8-27B(锚点) | 248,320 | 5,120 | 不共享 |
大词表配大模型时,两张表占的比例并不大:Qwen3.8-27B 是 9.2%,Qwen3-32B 是 4.7%,Qwen3-8B 是 15.2%(按各自 config 与实测总参数现算)。同一代 Qwen3 里 0.6B 到 4B 的小模型则让输入输出共享一张表(tie_word_embeddings = true),正是因为小模型里两张表太占比例。
常见误解一:「词表大小拍脑袋定个数就行」。 旧版课程说「小模型配 15 万词表,embedding 占掉 70% 参数」,这个数没有实算过。用我们的模型结构现算:d = 256、共享一张表时,151,936 项的词表让 embedding 占 92.5%;248,320 项占 95.3%。词表要按模型规模和语料实测来定。
常见误解二:「vocab_size 就是分词器里的 token 数」。 Qwen3.8 的分词器有 248,077 项(248,044 个 BPE token + 33 个特殊 token),config 里的 vocab_size 却是 248,320 = 970 × 256——embedding 表按 256 的倍数多留了 243 行,多出来的行永远不会被查到。这是为了让矩阵形状对齐硬件,方便高效计算;nanoGPT 把 GPT-2 的 50,257 补到 50,304(64 的倍数)也是同一个做法。
🔍 大模型该配多大词表
Tao 等 2024《Scaling Laws with Vocabulary: Larger Models Deserve Larger Vocabularies》(arXiv:2407.13623,NeurIPS 2024)在 33M 到 3B 参数的模型上扫描词表大小,结论是最优词表随算力预算增大;按他们的拟合,Llama2-70B 的词表应至少是 21.6 万,而它实际只用了 3.2 万。这是实验拟合的结论,只在他们的模型族和数据上验证过。
两个要会算的量
第一个:词表占多少参数。 点公式里每一项:
第二个:词表切得好不好。 压缩率(第 2 课)看每个 token 装几个字符;fertility 反过来看每个「词」平均被切成几个 token:
我们的里程碑分词器(词表 2,048)在验证集上:压缩率 1.022 字符/token,fertility 1.195 token/汉字;验证集里出现的 2,319 个不同汉字中,只有 1,015 个整字就是一个 token;1,791 次合并里有 1,059 次刚好拼成一个字、299 次只拼出半个字——小词表的合并名额,大多花在把单字拼完整上。
五档各训了一个模型(其余同基线)。val bpb 在 4,096 档最低(2.164),8,192 档反而回升到 2.184。读这个「最低点」要小心,实验脚本自己写明了两个局限:每步看的 token 数相同,而大词表每个 token 装的字节更多,400 步里 8,192 档把语料过了 8.52 遍、512 档只过了 3.99 遍;而且这是 370 万参数、50 万 token 的小规模,结论不能外推。这正是第 4 课的主题:没有通用的最优词表,只有在自己的规模上测出来的折中。
试三件事: ① 在上面的滑杆上依次点 GPT-2、Qwen3、Qwen3.8,看我们小模型里 embedding 的占比怎么变;② 展开改进方案,在五档之间切换,找压缩率涨得最慢的那一段;③ 看「val bpb」一栏:词表不同的模型不能直接比 val loss(每个 token 装的文字多少不同),要比每字节比特数。
词表大小怎么定才算有依据
- 它解决什么问题:在「序列更短」与「参数更多、每个 token 训练更少」之间,给出一个能验收的选择。
- 数学上为什么可能有效:两张表的参数是 2 × V × d,占比随 V 线性上升 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条形状计数;Qwen3.8 与 safetensors 实测一致 出处Qwen3.8 config ↗需联网 ;vocab_size 补到 256 的倍数是为了计算对齐 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条硬件上按整块处理矩阵更快;具体收益随硬件而变 出处nanoGPT model.py 注释 ↗需联网 。
- 实验上为什么最后选了它:压缩率随词表增大、但增幅递减,要看我们五档扫描的实测 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条三国演义语料、字节级 BPE、d = 256 的模型;换语料或模型规模结论会变 出处项目实测 · 本章项目推进 ↗ ;大模型该配更大的词表是拟合出来的规律 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条33M–3B 参数模型的扫描与外推 出处Tao 等 2024 ↗需联网 。
一句话、下一个缺口、你的提问
一句话: 词表每多一项,模型两头就各多 d 个参数;词表太小,同一段话就要更多 token——词表大小是在参数和序列长度之间做交易,要用实测来定。
下一个缺口
到这里,一句话已经变成了一串 ID,词表也定了。可 ID 只是编号——能直接拿来做数学运算吗?第 5 课:Token ID 为什么不能直接拿来算?
你的提问
你可能会问:「Qwen3.8 的 248,320 是怎么定出来的?」 官方没有公开选择过程。能核对的只有:分词器实际 248,077 项,补到 256 的倍数得到 248,320;这张词表从 Qwen3.5 开始使用(R2 调研按发布说明整理)。本课程不替官方编理由。