Rust in Everything
基于 burn 0.22.1

自动求导

PyTorch 的 autograd 和 burn 的 autodiff 都是反向模式(reverse mode):前向时记一张图,backward() 时倒着走一遍。算出来的梯度两边对得上(主线 MLP 的 67 个梯度最大误差 1.49e-8)。不同的是「状态放在哪」:PyTorch 把要不要梯度、梯度本身、图都挂在张量上,用全局的模式开关(torch.no_grad())控制记不记图;burn 把「记不记图」放在设备上,梯度放在 backward() 返回的值里,图只能用一次。

这一部分按训练主线的顺序走,每页一个 PyTorch 习惯:

  1. 打开求导:requires_grad=True 在 burn 里是两步:Device::flex().autodiff() 加 require_grad();PyTorch 一个 requires_grad 标志对应 burn 的三个查询;哪些错误在运行期报、哪一个仍在编译期。
  2. backward 与梯度:.grad 对 x.grad(&grads);每次 backward() 一个新容器,所以没有累加、也没有 zero_grad;梯度累积用 GradientsAccumulator;非标量输出、中间结果、二阶导数两边各怎么处理。
  3. 图只能用一次:retain_graph=True 没有对应;PyTorch 的第二次 backward 看有没有释放保存的张量,burn 一律拒绝;把 loss 先加起来是两边都对的写法。
  4. 不求导的区域:torch.no_grad() 对 without_autodiff()、Module::valid()、不带 autodiff 的设备;detach 两边语义不同;只前向不反向时 burn 的图会一直占内存。
flowchart TB
  dev["设备带 autodiff"] --> leaf["require_grad 叶子"]
  leaf --> fwd["forward 记图"]
  fwd --> bw["loss.backward"]
  bw --> grads["Gradients 容器"]
  grads --> get["x.grad 取梯度"]
  bw -.->|"图被消耗"| again["再 backward 就 panic"]
  plain["设备不带 autodiff"] --> infer["推理不记图"]

burn 内部怎么记图、怎么执行反向,见 burn 卷的自动微分。这一部分只讲从 PyTorch 过来要改的写法,机制链过去。

💬 关联讨论

发起讨论
加载中...