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

一句话怎么变成数字?

一句话的旅程
模型的输入层只接受整数。分词器先把文字切成词表里现成的块,再把每块换成它在词表里的行号——文字在进模型之前就消失了。

本课一问:一句人类写的话,是怎么变成模型能处理的数字的?

这一课先不碰:切分规则是怎么学出来的(第 3 课)、编号之后怎么变成向量(第 5 课)。只看清一件事——从文字到一串整数,中间发生了什么。

② 从已有出发

模型的第一步收到的是什么

神经网络内部只有矩阵乘法、加法和几个非线性函数,它的输入必须是数字。下面是整条流水线的全貌,本课只点亮最左边三格:文字 → 切分 → Token ID。灰色的环节后面各章逐个讲,先看清它们在哪。

本章贯穿两个例句:对话样本里的「我喜欢机器学习」,以及后面各章都会沿用的「小明把苹果给了小王」——这一课就跟着它们从文字走到整数。

累计数据流 · 从文字到下一个 token 的概率 已学本课新增还没学
解码层

点任一已学环节,看这一步的输入 → 输出形状、参数量,以及它解决了什么问题(数字取自 Qwen/Qwen3.8-27B 真实 config)。

点开「Token ID」:这一步输出的形状是 [T],T 是切出来的块数;每个元素是一个整数,也就是词表的行号。本课要弄清的,就是这个 T 和这些整数从哪来。

③ 先自己设计

最直接的办法:每个字直接用它的 Unicode 编号

先别看现成答案,自己设计一个。计算机里每个字符本来就有编号——Unicode 码点,「我」是 U+6211,也就是十进制的 25105。最省事的方案:一个字符一个 ID,ID 就用码点。下面输入任意一句话,看它变成什么。

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案每个字符直接用它的 Unicode 码点当 ID
④ 改进到现代做法

真实的旅程:四步,每一步都是真数字

先看一眼全章的演进图:从最朴素的「码点当编号」出发,每一步都被上一步的某个真数字逼出来;本课站在起点,后面各课沿着它一代代往下走。

文字 → 数字的演进:每一步都被上一步的某个真数字逼出来 · 高亮 = 本课
  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(官方自报)。

下面用 Qwen3.8 的真实分词器走一遍。所有切法和编号都来自真 tokenizer 的离线计算,不是示意值;换几个例句,注意每一步的块数和编号。

动画 · 一句话的旅程(真 Qwen3.8 切分)

两个词从这里开始要用得很精确:

  • Token:词表里的一项,本质是一段固定的字节。它可以是半个汉字、一个字、几个字、一个英文词或词的一部分。
  • Token ID:这一项在词表里的行号,一个整数。

分词器把一句话变成一串 Token ID,这串整数就是模型收到的全部输入。模型从来没见过「字」——它对文字的全部了解,都要从这些编号在训练数据里怎样出现、怎样搭配里学出来。

⚠

常见误解:「Token = 一个字」,或者「Token = 一个词」。 两个都不对。Qwen3.8 把「我喜欢机器学习」切成「我喜欢」「机器学习」2 块——对话样本里画的 4 块(我 / 喜欢 / 机器 / 学习)是示意;「人工智能非常强大」切成「人工智能」「非常」「强大」,一块可以比一个词还长;英文 strawberry 单独出现时切成 str / aw / berry 3 块,放在句子里前面带着空格时,「 strawberry」整个只是 1 块。切法由词表决定,词表由数据统计出来。

同一段文字换一个分词器,切法和 ID 全都不同:同一句「我喜欢机器学习」,GPT-2 的词表要用 17 个 token。为什么差这么多,第 2 课和岔路课「为什么中文更贵」会讲清楚。

⑤ 极小真数字,算一次

编码和解码:一来一回,一个字节都不差

分词器是一对函数:encode 把文字变成 ID 序列,decode 把 ID 序列变回文字。Qwen3.8 和 GPT-2 用的都是字节级 BPE,它有一个可以证明的性质——点公式里每一项看它的意思:

字节级 BPE 的往返性质
(())x
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
本章用到的每个例句,预计算脚本都逐条核对过「token 字节拼接 = 原文字节」与「decode = 原文」;项目测试 test_project_tokenizer_roundtrip 在完整验证集上对我们自己的分词器做同样的检查。

这个性质保证了一件事:分词不丢信息。模型读到的编号序列和原文一一对应,它生成的编号序列也一定能还原成一段确定的文字。

👀

试三件事: ① 在旅程动画里切到「人工智能非常强大」,对照对话样本那句「不一定切成这 3 块」——真分词器恰好就是这么切的;② 停在第 ③ 步,看词表横条上 ID 挤在哪一段——ID 的大小有没有意义,第 5 课兑现;③ 切到「今天天气真好」,记下 Qwen3.8 与 GPT-2 的块数,第 2 课会用到。

⑦ 证据三问

「先切分再查表」凭什么成立

⑧ ⑨ ⑩ 带走与下一步

一句话、下一个缺口、你的提问

📌

一句话: 分词器先按词表把文字切成块,再把每块换成它在词表里的行号;从这一刻起,模型只看得到整数。

下一个缺口

Qwen3.8 把 7 个字切成 2 块,码点方案一字一块。那为什么不干脆按字切,或者干脆按词切?两个极端各在哪个真数字上失败——第 2 课:为什么不一字一个 Token,也不一词一个 Token?

另一个埋下的疑问:词表横条上,「我喜欢」和「机器学习」的 ID 挨得很近。ID 的大小有意义吗?这个疑问在第 5 课兑现。

你的提问

C1 的问答记录里还没有真实提问(提问请直接在对话里问,会归档到 05-问答记录/)。你可能会问:

🔍 为什么说文字进模型前就消失了

模型的第一层是 embedding 查表(第 5 课):拿 ID 去一张表里取出一行数字。这一步之后,网络里流动的只有向量,再也没有「字」这个东西。所以分词器的每个决定——词表多大、怎么切——都会跟着模型一辈子:换一个分词器,ID 全变,查表查到的就全是别的行,训练好的权重立刻作废(第 5 课回扣)。