M03:用 PyTorch autograd 重建并检查梯度
autograd 根据张量运算动态建立计算图并执行向量—雅可比积,梯度检查则用数值扰动验证其结果是否符合预期。
内容类型:预习教材(不代表已完成)
日期:2026-08-26
阶段:P1 · AI Model Engineering 90
周次:W1 · 张量、梯度、autograd
节奏:周三引导练习
状态:教材已备;学习未完成
标签:PyTorch、autograd、有限差分、CPU、MPS
一句话定义
autograd 根据张量运算动态建立计算图并执行向量—雅可比积,梯度检查则用数值扰动验证其结果是否符合预期。
学习目标
- 用 PyTorch 重建 M02 的同一表达式并查看梯度。
- 理解叶子张量、grad_fn、detach 与 no_grad 的差别。
- 会用中心有限差分检查一个参数的梯度。
- 知道 CPU/MPS 对比应控制输入、dtype 和随机性。
核心知识
当 requires_grad=True 的张量参与运算时,PyTorch 会为后续结果保存反向所需的信息。用户创建且不是其他运算结果的可训练参数通常是叶子张量,它的 grad 在 backward 后可见;中间张量默认不保留 grad,但带有 grad_fn,表示它由哪个反向算子产生。
backward 对标量输出计算梯度;若输出是向量,需要显式提供同形状的上游向量,本质上计算向量—雅可比积。计算图默认在一次 backward 后释放,再次反向需重新正向计算,或在确有需要时保留图。训练循环通常每轮重新建图,这让 Python 控制流也能参与模型定义。
detach 返回共享数据但脱离当前计算图的张量;no_grad 是作用域级开关,常用于评估和参数更新。两者都不是简单的“节省内存按钮”:错误地截断图会导致预期参数没有梯度。
机制与推导
对参数 θ,中心差分 g_num = [L(θ+ε)-L(θ-ε)]/(2ε)。autograd 给出 g_auto。可以比较绝对误差 |g_auto-g_num|,更稳妥的是相对误差 |差值|/(|g_auto|+|g_num|+δ)。双精度和适中的 ε 更适合检查;float32、MPS 或非光滑点附近可能产生更大偏差。
CPU/MPS 对比需要区分三类问题:能否执行、数值是否在容差内一致、性能是否更好。先用相同数据和参数在 CPU 建立基线,再把模型与数据一起迁移;跨设备张量不能直接运算。小任务的单次耗时容易被首次编译、同步和数据搬运扭曲,因此本日重点是机制观察,不做性能结论。
最小练习
- 用标量张量复现 M02 的正向公式,保留与手算相同的中间节点。
- 调用一次 backward,记录叶子参数的 grad,并与手算表达式逐项对应。
- 在 no_grad 作用域中分别扰动参数 ±ε,算中心差分。
- 若 MPS 可用,用完全相同的输入重复正反向,只记录是否执行与容差观察。
- 对一个中间张量调用 detach,预测哪条梯度路径会消失,再决定是否实验。
常见误区
- 把 tensor.grad 为 None 一律当作错误;中间张量默认就不保留梯度。
- 在同一图上连续 backward,却不知道图已释放或梯度在累加。
- 用 detach 取数后继续计算,误以为原路径仍可求导。
- CPU 与 MPS 出现末位差异就判断实现错误;浮点运算顺序可能不同。
- 只检查一个随机点;在饱和区、非光滑点或梯度接近零时检查可能失真。
场景连接
自定义金融损失、排序目标或风险惩罚项比标准分类损失更容易写错。先在小张量和双精度上做有限差分,可以把“目标设计有问题”与“梯度实现有 bug”分开。
自检问题
- 为什么非标量输出调用 backward 时需要上游梯度?
- detach 和 no_grad 分别作用在什么层面?
- 为什么有限差分通常使用中心形式而不是单边形式?
- CPU/MPS 的一次耗时为何不能直接代表真实吞吐?
专业课程对齐
- PyTorch Autograd 教程:对应计算图、Jacobian-vector product、梯度关闭与累积,是本日主教材。
- PyTorch Tutorials:对应 autograd 与模型训练的官方示例,重点查阅张量梯度、优化循环和梯度检查相关内容。
- Stanford CS229 Materials:对应数值优化基础,为有限差分为何只能近似梯度提供数学背景。
深入学习提示
按“autograd 精读 → 官方训练示例对照 → CS229 选读”的顺序进行。代码上同时保留解析梯度、autograd 梯度和中心差分三列,使用相对误差而非只看绝对差,并扫描多个 ε;过大时截断误差明显,过小时浮点消减会放大。观察非叶子张量默认为何没有 .grad、detach() 如何切断图、原地操作何时破坏反传需要的中间值。反例应包含 ReLU 在零点不可导、float32 下极小 ε 检查失败、损失未缩放导致梯度量级变化;结论应是梯度检查能发现实现不一致,却不能证明目标函数或数据设计正确。
学后填写区
- autograd 与手算的对应:____
- 梯度检查设置:____
- CPU/MPS 实际观察(未运行可留空):____
- 新出现的问题:____
- 实际学习日期与用时:____