Appearance
第3章 · 从导数到自动微分
本地测试证据与学习者 clean-room/故障口试保持分离。 先修:会读 Python 函数、类和 NumPy 数组。
本章目标
- 能从空白接口写出标量
Value、基础算子、激活函数和拓扑反向传播。 - 能解释局部导数、链式法则、叶子节点、梯度累加和反向顺序。
- 用有限差分和一个小型 MLP 证明梯度正确、参数确实更新、loss 在受控数据上下降。
公式与 shape
对计算图中的 u = f(x)、L = g(u),反向传播使用
若一个节点同时被两条边使用,梯度是所有路径贡献之和,而不是最后一条路径的覆盖:
| 对象 | shape | 约束 |
|---|---|---|
标量值 Value.data | () | 一个节点携带一个数值和一个梯度 |
| MLP 输入 | (D,) 或 (B, D) | batch 维不能混入参数维 |
| MLP 输出 | (C,) 或 (B, C) | loss 必须有明确 reduction |
| 有限差分 | 与输入相同 | f(x+h)-f(x-h) 使用同一参数快照 |
数学桥接:第2章 → 第3章
第2章 §3 的 chain_rule_2layer(f2, f1, x, y) 实现了最简链式法则:
第2章的 gradient_vector(f, x, y) 返回 Value.backward() 对所有叶节点最终要算的东西。difference_quotient(f, x, h) 是本章 gradcheck 的数值基础——当解析梯度实现有 bug 时,有限差分是唯一的 arbiter。
前端类比:Vue computed 的依赖追踪 = 前向传播(数据流向下),重新计算 = 反向传播(变化向上传播)。共享节点梯度累加 = computed A 依赖 B 和 C,B 变了算一次,C 变了再算一次,两次变化叠加才是 A 的真正变化。
交互观察
交互:最小自动微分
y = tanh(x * w + b)
y = 0.000000 ∂y/∂x = 2.000000 ∂y/∂w = 0.500000 ∂y/∂b = 1.000000
拖动滑块时,浏览器里即时重算前向与反向。对应 Python: python/llm_core/autograd.py
拖动 x、w、b 后,用链式法则解释输出和三个局部梯度的变化。浏览器演示只用于建立直觉;真正证据仍来自 Python 数值对拍和故障题。
从零实践
只依据接口和公式实现
Value.__add__、乘法、幂、tanh/relu、父节点集合和局部梯度闭包。反向时先收集从输出可达的节点,再按反向拓扑序执行;重复调用前明确梯度是否清零。
在
python/llm_core/autograd.py的实现旁写一个独立的 clean-room 小模型,不直接复制参考实现。运行现有基础回归:
bashPYTHONPATH=python python -m pytest python/tests/test_autograd.py -q用
python/llm_core/gradcheck.py(若接口已在当前 checkout 中存在)做多点有限差分;否则该步骤保持 gate,不以手算样例代替。
当前仓库已有 Value、Neuron、Layer、MLP 和基础测试,但现有绿测不自动证明 clean-room、共享节点专项题和故障注入已完成。通过条件是:梯度误差低于冻结阈值、共享节点累加正确、MLP 参数改变且受控 loss 下降。
Clean-room submission contract(学习者本地提交)
clean_room/autograd.py 是 blank interface 起点,合同冻结了 Value.__init__(self, data: float) 与 Value.backward(self) 的参数签名。学习者应在私有提交中填写 submission.json 的 modules[]、test_evidence[]、failure_records[]、oral_records[] 和 human_review;实现或测试不得导入 clean_room、llm_core、python.llm_core、llm_train 或 python.llm_train。故障记录至少要有注入、预期/实际信号、修复前后观察和证据路径;口述记录至少要有 prompt、主题、回答摘要、局限、审核备注和状态。
bash
pnpm clean-room:contract
node scripts/validate_clean_room.mjs --submission /path/to/private-submission第二条命令只读提交结构和已经保存的测试证据,不执行学习者代码,也不判断 clean-room provenance、故障定位或口述质量。缺少提交、测试/故障记录未通过或口述未审核时明确保持 gate;合同校验成功也只是 human-review-required,不能宣称掌握。
论文与延伸
- Learning representations by back-propagating errors(Rumelhart、Hinton、Williams,1986)
- 选读:Karpathy 的 micrograd;只作接口和思想参考,clean-room 作业独立完成。
前端/Agent 迁移
把计算图看成响应式依赖图:节点是状态,边是派生关系,反向传播是从最终观测沿依赖边回传影响。迁移到 Agent 时,先记录 event → state transition → derived output,再决定重试或恢复;不能只记录最后一条日志。
口述与自测(不看资料,5–10 分钟)
- 从
z = (xw+b)^2画出计算图并口述x、w、b的梯度如何沿拓扑逆序累加;解释共享节点为何不能覆盖梯度。 - 选一个有限差分不一致或 loss 不下降的故障,说明你先看哪个局部导数/参数,再如何用数值对拍确认修复。
- 用第2章的
chain_rule_2layer思路手算对 的链式展开 ,说明这和 autograd 的反向路径完全一致。 - 解释
gradient_vector(f, x, y)和Value.backward()的关系——两者都在算梯度,但前者一次只输出一个点,后者要把整张图的梯度都算出来。
资源 / 成本 / 隐私
本地 Python/NumPy、CPU 即可;预计 gross cost 为 0。只用合成数值和公开论文链接,不上传履历、飞书资料或账号信息。
实验与参考
动手实验
把标量计算图、链式法则、共享节点的梯度累加这三个概念,从口头说法变成可复现的数值证据:先跑通 Value/Neuron/Layer/MLP 的基础测试确认实现不是空壳,再用 gradcheck.py 对同一份参数做有限差分对拍,确认 ∂L/∂θ 在小数点后 4 位与真值对齐。
环境准备
bash
cd <仓库根>
export PYTHONPATH="$PWD/python"命令与预期输出
bash
# 1) 基础正确性:算子、MLP、激活函数的有限差分对拍(已在「从零实践」跑过,此处补充另外两条)
python -m pytest python/tests/test_clean_room_faults.py -q
# 2) 一键总览:打印 autograd 数值与梯度
python -m labs.run_alltext
clean_room_faults: 4 cases injected, 4 expected signals reproduced
autograd 0.693147 0.9999998
# 判定条件:
# - 共享节点样例的梯度与有限差分误差 < 1e-5
# - MLP 在受控数据上 final loss < initial loss概念图
图:第3章 autograd 知识流 — 从第2章的数学基础(闭包、偏导、链式法则)到本章的 Value.backward(),再到后续第4章训练循环、第6章 attention 投影、第8章 nn.Module 的自动微分。每个箭头代表"本章概念直接消费的前提"。
故障注入清单
| 故障 | 表现 | 修复 |
|---|---|---|
| 反向时用赋值覆盖梯度而非累加 | 共享节点数值梯度偏小,多路径样例与有限差分不一致 | 把 self.grad = ... 改为 self.grad += ...,多路径误差回到阈值内 |
调用 backward 之前忘记 zero_grad | 第二次反向结果随调用次数漂移,loss 看起来在降其实是累加 | 训练循环开头先清零,并用重复调用测试覆盖 |
| 反向按节点创建顺序执行而非拓扑逆序 | 依赖较深的叶子节点梯度为 0 或符号反掉 | 先 DFS 收集从输出可达的节点,再逆序遍历 backward |
有限差分步长 h 取太小(如 1e-12) | 与解析梯度比较时全是数值噪声 | 取 h ≈ 1e-5,同时用相对误差而非绝对误差 |
Evidence
仓库当前机器证据(只读快照)
evidence/module-manifest-v1.json 中 03.evidence 指向当前文件:evidence/03-runtime-v1.json。这是当前 checkout 的脱敏机器运行记录,只覆盖该 JSON 记录的命令、指标、产物和已知失败;它不是学习者提交,也不能推出学习者已完成本章。
clean-room 合同的脱敏索引见 evidence/clean-room-submission-index-v1.json。当前索引只证明 7 个 blank interface、提交字段和自动化边界存在,status: gate 且 learner_submission.status: not-provided;它不是学习者实现、测试结果或毕业判定。
学习者提交模板(待填写,不是当前机器证据)
复制下面模板并填写自己的真实运行结果。所有 <...> 都是未填写状态;actual 和 artifacts 尤其不能被当作已运行或已通过。artifacts 必须替换为本次提交中真实存在的仓库相对路径。
yaml
schema: learn-llm.evidence.v1
module: 03-autograd
commit: <learner-commit-sha>
verified_at: <iso-date>
environment: <sanitized-python-device>
seed: 1
commands:
- PYTHONPATH=python python -m pytest python/tests/test_autograd.py -q
- PYTHONPATH=python python -m pytest <learner-clean-room-gradcheck> -q
metrics:
- name: finite_difference_relative_error
expected: <versioned-threshold>
actual: <recorded-value>
- name: controlled_mlp_loss_delta
expected: <versioned-threshold>
actual: <recorded-value>
artifacts:
- <learner-repo-relative-artifact-path>
cost:
gross_usd: 0
credit_usd: 0
licenses:
- source: <source>
version: <version>
license: <license>
attribution: <attribution>
redistribution: <redistribution>
known_failures:
- <sanitized-failure-or-none>没有 clean-room 测试、故障前失败/修复后通过和实际指标时,本章状态为 gate,不是完成。