离散的词怎么进网络?
本课一问:前六课里,每个词都「有两个数」:「小猫」开局是 [0.20, 0.70]。可网络的输入本来是「小猫」这个符号,不是数。符号怎么变成数进入网络?梯度又怎么回到这两个数身上?
这一课先不碰:一句话怎么切成 token、编号从哪来(C1 第 1–4 课);编号为什么不能当数、one-hot 为什么让所有词一样远(C1 第 5 课);这些行最后学成什么样(C1 第 6 课);维度取多大(C1 第 7 课)。
从已有出发
对话样本里最具体的一组数就在这里:「猫的 Embedding:[0.20, 0.70]」,反向传播说它「第一维 +0.01、第二维 −0.02」,于是变成 [0.21, 0.68]。第 3 课在迷你模型上真算过这一步。这一课回答两件还没说的事:这一行数是怎么被「查」出来的;为什么这一步只有它和句子里另外两个词在动。
- 手写规则 / 把例子背成一张表
- 带参数的模型:用例子调数字
- 交叉熵:正确答案概率的负对数
- 梯度下降
- 反向传播(反向模式自动微分)
- 非线性多层网络
- 验证集、早停与正则化
- 深度学习为什么是经验性的
- 词向量:按编号查表
先自己设计:one-hot 进第一层
网络的第一层是矩阵乘法,输入得是一串数。给每个词一根长度等于词表大小的向量,自己那一位是 1、其余是 0(one-hot),再乘一个矩阵 E:这就是一个普通的线性层,第 3、4 课的一切都照常适用。
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
one-hot 乘矩阵,结果的第 j 个数 = Σi one-hoti × Ei,j。248,320 项里只有一项的 one-hoti 是 1,其余全是 0:一次查询做了 248,320 × 5,120 次乘加,几乎全在乘 0。在迷你模型里亲眼看一次(下面组件的第 ① 部分):12 个词,每个数 12 次乘法,只有 1 次是有用的。
既然结果恰好是 E 的第 i 行,那就直接取第 i 行:0 次乘法,只是复制 5,120 个数。这就是 Embedding 查表。它和 one-hot 乘矩阵是同一个函数,所以前向一样、梯度也一样——只是省掉了乘 0。
数字来源:浏览器实时计算(纯 JS,Float64)。例子、初始化与种子写在组件说明里;按词切只为演示,真实切分见 C1。已用 Python 独立实现逐位核对默认设置下的全部读数。
试三件事:① 在第 ① 部分换几个词:乘出来的结果每次都等于表里那一行,24 次乘法里 22 次在乘 0;② 看第 ② 部分(η = 0.05):「小猫」「喜欢」「吃」三行变了,小猫从 [0.20, 0.70] 变成 [0.21, 0.68]——对话样本那组数;其余 9 行的梯度、Δ 恰好是 [0, 0];③ 点第 ③ 部分的按钮:如果不用表、直接拿编号当数,换 6 种同样随意的编号,训练 3,000 步后 loss 从 0.19 到 0.59 不等——模型学到什么,竟然取决于一个本来没有意义的选择。
梯度怎么回到那一行
第 4 课的计算图里,h = e小猫 + e喜欢 + e吃,相加节点把梯度原样复制给三个词。再往回一步,e小猫 是从表 E 里取出来的第「小猫」行,所以这份梯度就加回表的那一行:
用张量的形状看,就是一根 one-hot 列乘一行梯度,结果是一整张表大小的矩阵,但只有一行不是 0:
| 维度 | 含义 | 真实规模 | 小规模 |
|---|---|---|---|
| V | 词表大小(Qwen3.8 vocab_size;迷你模型 12) | 248,320 | 12 |
| d | 每个 token 的维数(Qwen3.8 hidden_size;迷你模型 2) | 5120 | 2 |
| n | 一个位置 | 1 | 1 |
真语料上看一批数据:
数字来源:浏览器实时训练(纯 JS,Float64)。模型、语料、种子写在上方;同一组对照里所有 run 共用初始化和取批顺序,只改被比较的那一项。换种子重跑能看到噪声多大——差异小于种子间波动时不下结论。
试三件事:① 左图:第一批 32 条 × 4 个字 = 128 个输入位置,只涉及 58 个不同的字,256 行里 198 行的梯度恰好是 0(灰色);② 右图:200 步里,最常见的「,」每一步都被更新,字表里最罕见的 50 个字,中位数只轮到 23 步;③ 想一想:词表越大、字越罕见,那一行被训练到的机会就越少——这是 C1 讲「词表该多大」时的一个代价。
常见误解一:「Embedding 是单独训练好、再装进模型的。」
不是。它就是网络的第一层,和其他参数一起被同一个 loss、同一次反向传播、同一个更新规则调整——本章第 1–4 课的梯度一直在流进这些行。C1 第 6 课会拿我们项目的真实训练过程,看这些行怎么从随机数变成有结构的向量。(另有一类专门训练的「embedding 模型」用于检索,那是另一回事,C1 第 6 课一起讲。)
常见误解二:「one-hot 就是 embedding。」
one-hot 是输入的编码方式(一根只有一个 1 的向量,不可学习);embedding 是它乘出来的那一行(可学习的数)。实际实现里 one-hot 从不出现,只有「按编号取行」。
常见误解三:「没出现在这一批里的词,也在跟着学。」
纯梯度下降下,它那一行的梯度恰好是 0,一个数都不动(上面组件的第 ② 部分)。换成带动量或权重衰减的优化器,这些行也会动——那是优化器的「惯性」和「衰减」,不是从这批数据里学到的东西(C5)。
🔍 「词向量」这个想法从哪来?
用一个可学习的向量表示每个词、和预测下一个词的概率函数一起训练,出自 Bengio 等的神经概率语言模型(NIPS 2000,JMLR 2003 完整版)。摘要原话大意:模型同时学到(1)每个词的分布式表示,和(2)用这些表示表达的词序列概率函数 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条「这样学出来的向量让相似的词彼此靠近、从而帮助泛化」是论文的动机与实验结论,不是定理。 出处Bengio et al. 2003 · JMLR 3 ↗需联网 。本章的迷你模型就是它最小的版本:上下文词向量相加,线性打分,softmax。
交给 LLM 段
到这里,这一章的训练循环是完整的:例子 → 词变向量 → 模型 → 预测 → loss → 梯度 → 更新,再加一个验证集。LLM 段从下面这条流水线接手,本课讲的就是「Embedding 查表」这一格:
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 符号进网络:给每个词一行可学习的数,既不引入假的大小关系,也不做几乎全是乘 0 的矩阵乘法 |
| ② 数学上为什么可能有效? | one-hot 乘矩阵恰好等于取一行;梯度只落在用到的行 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条矩阵乘法的定义;反向时 one-hot 的外积只有一行非零。 出处待补 ;查表时编号只决定第几行,把行重新排列、初始值跟着词走,训练结果完全相同 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条置换矩阵 P 满足 PᵀP = I,重新编号只是对表的行做一次置换。 出处待补 |
| ③ 实验上呢? | 这些行会学成「用法相近的词靠近」:本章的迷你模型里「小猫」「小狗」被推向两边;真实模型上的证据在 C1 第 6 课 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条迷你模型种子 3:训练后小猫 [3.24, 2.02]、小狗 [−3.67, 0.70]。「相近」只是训练的副产品,不是被要求的目标。 出处待补 |
一句话
词进网络 = 用编号去一张可学习的表里取一行向量;它就是 one-hot 乘矩阵、只是省掉了乘 0,反向传播只改这句话用到的那几行。
先停在这里:地基章到此结束。带着「例子 → 向量 → 模型 → loss → 梯度 → 更新 → 验证」这一整圈进入 LLM 段。
再深一层:回章首页做一遍「项目推进」里的三个决策。
下一个缺口
词进了网络,「下一个词」的概率怎么算、为什么终点是 Transformer? → C0 序章:为什么是 Transformer + 预测下一个词
回扣和去向:
- 本课省掉的「编号从哪来」:C1 第 1–4 课(真 tokenizer)。
- 编号为什么不能当数、one-hot 为什么让所有词一样远:C1 第 5 课,用 Qwen3.8 的真编号讲。
- 这些行最后学成什么样:C1 第 6 课,用我们项目的真实训练快照。
你可能会问
- 「输出层也是一张每词一行的表,和 Embedding 是一回事吗?」 形状一样(词表 × 维数),作用相反:Embedding 把编号变成向量,输出层把向量变回每个词的分数。有的模型让两者共用同一张表(tie_word_embeddings);Qwen3.8 没有共用,两张表各 1,271,398,400 个参数;我们的项目模型共用。
- 「那句子里的顺序呢?e 相加之后『小猫 喜欢 吃』和『吃 喜欢 小猫』一样了。」 对,这是本章迷你模型故意留下的缺口。C2 整章回答它。