Embedding 的数字从哪来?
本课一问:Embedding 表里的数字,是从哪来的?
这一课先不碰:反向传播的完整推导(地基章)、优化器的细节(C5)。只看清三件事:数字一开始是什么、被什么推着改、每一步改多少。
上一课留下的那张表
第 5 课:Embedding 是一张 V × d 的表,ID 是几就取第几行。贯穿例句「小明把苹果给了小王」查表得到 5 行、每行 5,120 个数,共 25,600 个;整张表是 248,320 × 5,120,一共 1,271,398,400 个数。它们必须「有意义」——用法相近的 token,向量也该相近——否则查表和 one-hot 没有区别。
朴素的想法:人给每一维定义含义
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
对话样本(L274–L312)讲维度时用过这个思路:给「猫」「狗」「汽车」各写 4 个人能看懂的分数——动物、有生命、交通工具、抽象概念。作为入口它很直观:相近的东西,分数也相近。
但它是教学构造,不是任何模型学到的样子。把它当成做法,就要为 1,271,398,400 个格子逐个定义含义、逐个填数。
不定义任何一维的含义,让训练来决定:
- 随机初始化:每个数从一个均值 0 的小正态分布里抽(我们的项目模型:标准差 0.02,见
minillm/model.py)。这时表里没有任何语义。 - 和整个模型一起训练:模型读一段文字,在每个位置预测下一个 token;预测越离谱,loss 越大。
- 反向传播:算出 loss 对每个参数的梯度——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」。
下面是我们自己的项目模型在训练过程中,20 个 token 的 embedding 快照(投影到 2D)。这里换了例子:项目模型是在三国演义上训练的,贯穿例句里的「苹」「给」它根本没学过(第 3 课压轴),所以选的是语料里的高频词——人名、数字、方位、动词、标点各 4 个。拖动时间轴,或者点「播放训练」:
为什么用法相近的 token 会被推到一起?模型要在每个位置预测下一个 token。如果两个 token 经常出现在相似的上下文里(前后跟着差不多的词),那么「把它们表示得相似」能让同一套后续计算对两者都预测得好,loss 更低——梯度就会把它们往相似的方向推。没有人告诉模型「猫和狗应该接近」,接近是降低 loss 的副产品。
我们项目模型的实测:20 个 token 在训练前按规则分成 5 组(人名、数字、方位、动词、标点),在完整的 256 维空间里算两两余弦——同组的平均值从训练前的 0.000 变成训练后的 0.297,不同组之间只从 0.003 变到 0.047。热力图在第 5 课的改进方案里。C0 第 1 课留下的「小明和小王像不像」,答案就在这里:常在相似上下文里出现的人名,向量会被推到一起。项目语料里没有小明、小王,换成书里的四个人名(玄德、孔明、曹操、關公):两两余弦的平均值从训练前的 -0.008 升到训练后的 0.384。
还有一个细节决定了「哪些行会被改」:输入端查表只用到了这批文字里出现过的那些行,没出现的 token,它那一行在这一步拿不到输入端的梯度。组件下方给出了我们项目里一个 batch 覆盖多少行的实测。
常见误解一:「每一维对应一个人能说清的特征」。 对话样本自己在 L322 就纠正过:真实模型的某一维通常不能解释成「动物程度」。语义分布在很多维的组合里。更根本的是,单独某一维的数值本来就不唯一:把向量空间整体换一组正交坐标轴(给 embedding 表乘一个旋转矩阵),再把所有读写这些向量的权重相应改写,模型的输出可以完全不变——同一个模型,「第 1 维」可以是任何方向。
常见误解二:「Qwen 里的 token embedding,就是 RAG、向量数据库用的那种 embedding」。 不是同一个东西(对话样本 L494–L536)。token embedding 是一张表,一个 token 一行,和上下文无关,作为语言模型的一部分用「预测下一个 token」端到端训练;BGE、E5、Qwen3-Embedding 这类 embedding 模型是一整个网络,把一整段话压成一根向量,通常用对比学习训练——相似的两段话拉近、不相似的推远。两者有关(embedding 模型常从语言模型初始化),但输出、训练目标都不同。
🔍 真实事故:认识却说不出名字
MiniMax 在 2026-05-25 的官方排查记录(原文)里描述:他们的模型说不出「马嘉祺」,「嘉祺」是单个 token(ID 190467),后训练数据里含它的样本不到 5 条。排查结果:SFT 前后输入 embedding 几乎没变,但输出端 lm_head 的对应行明显偏移——全词表 lm_head 与预训练版本的余弦相似度均值 0.9837、最小只有 0.3290,4.9% 的 token 低于 0.95。原因与本课那段「哪些行会被改」同源:输入端只有出现的 token 拿到梯度,输出端的 softmax 却在每一步都给所有「不是答案」的 token 送来「把打分压低」的梯度,很少当答案的 token 就一路被压走。修复办法是混入覆盖全词表的合成数据,最小余弦回到 0.9711。前提是输入输出不共享一张表(MiniMax、Qwen3.8 都不共享)。视频讲解:五道口纳什《为什么 LLM 无法输出马嘉祺》(B 站,24:47)。
一次更新,拆开看
在上面的组件里看「一次真实更新」:整行的变化量只是这一行长度的一小部分——一步只挪一点点,几百步累积起来,点的聚拢才看得出来。对话样本里「猫 [0.20, 0.70] → [0.21, 0.68]」讲的是同一件事,但那组数是示意;这里是真的。
试三件事: ① 把时间轴拖回第 0 步:同一组的 token 散在各处——随机初始化里没有语义;② 播放训练,看哪一组最先聚到一起,同时看右边 val loss 在那段下降了多少;③ 读「哪些行会被改」:一个 batch 只覆盖词表的一部分,想想只在语料里出现过几次的罕见 token,它那一行一共能被推几次。
「训出来的」有多可靠
- 它解决什么问题:不需要任何人定义语义,就得到一张「用法相近 → 向量相近」的表。
- 数学上为什么可能有效:embedding 是模型参数,loss 对它可导,梯度下降会更新它 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条查表是对 E 的线性操作,梯度只流向被查到的行 出处待补 ;查表的梯度只落在本批出现过的行上 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条未被查到的行不参与前向计算;共享输出表时另有来自 softmax 的梯度 出处待补 ;单独某一维的含义不唯一 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条对整个残差流做正交旋转,并相应改写所有读写它的权重,输出不变;前提是先把 RMSNorm 的逐维缩放并入相邻的线性层 出处待补 。
- 实验上为什么最后选了它:把词向量和语言模型一起端到端训练,最早由 Bengio 等 2003 系统提出 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条与 n-gram 模型比困惑度 出处Bengio 等 2003 ↗需联网 ;「用法相近 → 向量相近」在我们项目模型上的实测见上面的时间轴与第 5 课的热力图 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条小模型、单一语料、几百步;大模型上的结构更丰富,但不保证每一维可解释 出处项目实测 · 本章项目推进 ↗ ;稀疏更新导致罕见 token 漂移的真实案例见上面的 Dig 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条MiniMax 自己模型上的排查数据 出处MiniMax 官方博客 2026-05-25 ↗需联网 。
一句话、下一个缺口、你的提问
一句话: Embedding 表一开始是随机数;训练时它和整个模型一起被「预测下一个 token」的 loss 推着走,用法相近的 token 慢慢被推到相近的位置——没有人给任何一维定义含义。
下一个缺口
每个 token 一行、每行 d 个数。d 该取多少——越大越好吗?Qwen3.8 的 5,120 是算出来的吗?第 7 课:Embedding 维度越大越好吗?
你的提问
C1 还没有真实提问记录。你可能会问:
- 「模型训完之后,embedding 还会变吗?」→ 后训练(C12)还会继续更新它;上面的 Dig 就是后训练把输出端的行推偏的真实案例。