参数一多,梯度怎么算?
本课一问:第 3 课的每一步都要一个梯度:每个参数一个数,「它往上挪一点点,loss 变多少」。迷你模型 60 个参数,真语料小网络 24,896 个,Qwen3.8 27,781,427,952 个。这么多个数,怎么算得过来?
这一课先不碰:自动微分框架(PyTorch 的 autograd)的工程实现;梯度太大、太小时怎么办(C5 裁剪、C8 稳定性);显存里为什么要存激活(C9)。
从已有出发
对话样本说「反向传播告诉模型内部的参数:哪些数字应该往哪个方向改一点」。第 3 课用了它的结果:「小猫 喜欢 吃 → 鱼」这个例子,「小猫」两个数的梯度是 [−0.1912, 0.3736]。这一课把这两个数亲手算出来,并且说明:为什么算两个和算两百多亿个,代价差不多。
- 手写规则 / 把例子背成一张表
- 带参数的模型:用例子调数字
- 交叉熵:正确答案概率的负对数
- 梯度下降
- 反向传播(反向模式自动微分)
- 非线性多层网络
- 验证集、早停与正则化
- 深度学习为什么是经验性的
- 词向量:按编号查表
先自己设计:一个一个参数去试
梯度的定义就是「挪一点点,看 loss 变多少」。那就照定义算:把第 j 个参数加一个很小的 h,重新前向一遍,(L(θ + h) − L(θ)) ÷ h 就是第 j 个分量;更准一点,用 θ + h 和 θ − h 两边各算一次(中心差分)。每个参数都这样来一遍:
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
数字来源:浏览器实时训练(纯 JS,Float64)。模型、语料、种子写在上方;同一组对照里所有 run 共用初始化和取批顺序,只改被比较的那一项。换种子重跑能看到噪声多大——差异小于种子间波动时不下结论。
差分的毛病是「每个参数单独跑一遍」。可这些参数都连在同一张计算图上:loss 对「小猫」那两个数的梯度,和对「喜欢」「吃」的梯度,前面一大段路是共用的。反向传播从 loss 出发往回走一遍,把这段共用的路只算一次——下面在迷你模型上一格一格地走。
上面的计时是在你的浏览器里现测的,每次运行略有不同;表格里的误差和「这批没出现的字梯度恰好为 0」每次都一样。差分并不是没用:它是检查反向传播有没有写错的标准办法(相对误差 10⁻⁸ 量级就说明两者一致),只是不能拿来训练。
改进:计算图 + 链式法则 + 反向走
把「小猫 喜欢 吃 → 鱼」的前向计算画成一张图:三个词的两个数 → 相加得 h → 打分 z = U·h + c → softmax 得 p → L = −ln p鱼。每个节点上格是值,下格是梯度:
数字来源:浏览器实时计算(纯 JS,Float64)。例子、初始化与种子写在组件说明里;按词切只为演示,真实切分见 C1。已用 Python 独立实现逐位核对默认设置下的全部读数。
试三件事:① 先连点「前向 ▶」五次,看值从左往右填满,L = 2.9444;再连点「反向 ◀」五次,看梯度从右往左填:L 的梯度是 1 → p 那一格是 −1 ÷ 0.0526 = −19.00 → z 那一格化简成 p − y(「鱼」是 0.0526 − 1 = −0.9474)→ h → 三个词,每一步的说明里都写着「本地导数 × 上游梯度」;② 看「12 条路径之和」那张表:∂L/∂h 的第 1 个数 = 12 个词各贡献一项,加起来 −0.1912,正是第 3 课那个数;③ 在「有限差分扰动」里选「小猫的第 1 个数」:h = 10⁻⁶ 时实测 ΔL ÷ h 和反向传播对到小数点后 6 位;再选「小狗的第 1 个数」:它不在这句话里,加 h 之后 L 一位都不变。
规则只有一条,在每条边上重复:
输出层 U 的梯度是一个外积:(p − y) 这一列乘 h 这一行。放到锚点模型的真实形状里,Qwen3.8 最后那张输出表(lm_head)的梯度,每个位置也是这样一个外积:
| 维度 | 含义 | 真实规模 | 小规模 |
|---|---|---|---|
| V | 词表大小(Qwen3.8 vocab_size;迷你模型 12) | 248,320 | 12 |
| d | 每个 token 的维数(Qwen3.8 hidden_size;迷你模型 2) | 5120 | 2 |
| n | 一个位置(一个例子) | 1 | 1 |
为什么反向这么省:从 L 出发往回走,每个节点只经过一次,做的乘法和前向同一个量级(实测前向 + 反向约是前向的 3 倍);一趟走完,所有参数的梯度都在。反过来从某个参数出发往前推(差分就是这种思路的笨办法),每个参数都要走一趟。loss 只有一个数、参数有几百亿个,所以从 loss 这一头往回走划算 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条反向模式自动微分:输出是 1 个标量时,求全部输入的梯度只需要常数倍于一次前向的计算量(与参数个数无关)。 出处Baydin 等 2015 自动微分综述 ↗需联网 。
常见误解一:「反向传播是一种学习算法 / 优化器。」
它只负责算梯度。拿梯度怎么改参数,是第 3 课的梯度下降,或 C5 的动量、Adam、Muon;同一个反向传播可以配任何一种优化器。
常见误解二:「反向传播算出的是近似梯度。」
它是链式法则的精确结果,误差只来自浮点舍入。近似的是有限差分:上面 h = 0.1 时误差到百分之几,h = 10⁻⁹ 时舍入误差又回来了。
常见误解三:「反向比前向贵很多倍。」
大约 2 倍:浏览器里实测前向 + 反向约是前向的 3 倍。所以「训练一步 ≈ 3 次前向的计算量」是估算训练成本的常用经验(C6、C9 会用到);真正贵的是要把前向的中间结果存下来给反向用(C9 讲激活显存)。
🔍 谁发明了反向传播?
反向模式自动微分最早见于 Linnainmaa 1970 年的硕士论文(当时与神经网络无关);Werbos 1974 年的博士论文初步讨论了用它训练神经网络,1981 年的会议论文(1982 年出版)是第一次明确的应用;LeCun、Parker 在 1985 年前后各自重新发现;Rumelhart、Hinton、Williams 1986 年在 Nature 上展示了用它训练的隐藏层能学到有用的内部表示,反向传播从此流行 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条历史出处按 Schmidhuber 2014 综述 §5.5、Goodfellow 等《Deep Learning》§6.6 与 Baydin 等 2015 核对。 出处Rumelhart 等 1986 · Nature 323:533 ↗需联网 。
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 参数几万到几百亿个,逐个扰动每步要 2n 次前向;要一种与参数个数无关的算法 |
| ② 数学上为什么可能有效? | 链式法则给出精确梯度 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条复合函数求导法则;对任意可微的计算图成立(ReLU 的拐点处取单侧导数,是工程约定)。 出处待补 ;反向模式的代价是前向的常数倍 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条「廉价梯度原理」:标量输出时,反向模式求全部梯度的代价是前向的小常数倍。 出处Baydin 等 2015 ↗需联网 ;差分的误差两头都有(h 大有截断误差,h 小有舍入误差) 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条泰勒展开给出截断误差 O(h²)(中心差分);舍入误差约为机器精度 ÷ h。 出处待补 |
| ③ 实验上呢? | 浏览器里:反向传播与中心差分(h = 10⁻⁵)相对误差 10⁻⁸ 量级;前向 + 反向约 3 倍前向 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条计时随设备变;作者机器(node / V8)上前向 0.24 ms、前向 + 反向 0.72 ms。 出处待补 |
一句话
反向传播 = 在计算图上从 loss 往回走,每一步把「本地导数 × 上游梯度」传下去;一次前向加一次反向,拿到所有参数的精确梯度。
先停在这里:记住「梯度从 loss 往回传,每条边乘本地导数,分叉处相加」。
再深一层:看 Karpathy 的 micrograd;回到组件里,把「12 条路径」每一项手算一遍。
下一个缺口
梯度会算了,可这个模型只会「各部分加起来再打分」——要看组合才能判断的规律,它学得会吗? → 第 5 课:直线拟合不了怎么办?
回扣:本课计算图里「相加」节点把梯度原样复制给三个词,第 7 课会看到,这正是「只有句子里用到的词才被更新」的原因。
你可能会问
- 「PyTorch 里的 loss.backward() 就是这个吗?」 是。前向时框架把每一步运算记成计算图的一个节点,backward() 从 loss 出发反向走一遍,把梯度存进每个参数的 .grad。区别只在规模:节点是整个张量的运算,不是一个个数。
- 「ReLU 在 0 那一点不可导,怎么办?」 框架约定在 0 处取 0(或 1)作导数。输入恰好等于 0 的概率极小,实际上不影响训练;上面 h = 0.1 的差分误差大,部分原因就是扰动跨过了这种拐点。
- 「梯度一层层乘下去,会不会越乘越小或越大?」 会,这是深网络的老问题:C0 第 2 课在 RNN 上算过 0.9¹⁰⁰,C4 讲残差怎么绕开它,C8 讲放大以后怎么稳住。