自动微分
这一部分把求导主线拆开讲。candle 的自动微分是反向模式(reverse mode),只做一阶导数。代码很集中:candle-core 的 backprop.rs(809 行,几乎全是一个 backward 函数里的 match)、op.rs 里的 Op 枚举和 BackpropOp、variable.rs(152 行的 Var),再加上 candle-nn 的 optim.rs(201 行)和 var_map.rs。没有单独的图对象,没有全局登记表,也没有「打开 autodiff」的设备或后端:每个张量自己带着「我是怎么算出来的」。
- 计算图怎么记下来:
Tensor_里的op: BackpropOp是一个Option<Op>,Op的 36 个变体各自存着输入张量;只有某个输入是Var或者自己记了Op时才记;Var是is_variable为真的张量加一个原地写入的set;detach换一个新 id、丢掉Op;图活得和最后一个结果张量一样久。 - backward 与 GradStore:
sorted_nodes深度优先、后序收集通向Var的节点,already_seen让共享的节点只走一次;backward倒着套每个Op的规则,梯度累加进以TensorId为键的GradStore;用有限差分验证;对同一个 loss 可以 backward 两次。 - 优化器与训练循环:
Optimizertrait、没有动量的SGD、AdamW;VarMap按名字存Var,模型拿到的权重和Var是同一个张量,GradStore直接用它的 id 查;一个两层 MLP 的手写训练循环,在 CPU 和 Metal 上各训 200 步。
flowchart TB var["Var 权重"] --> fwd["前向 每个结果记 Op"] fwd --> loss["loss 张量"] loss --> sort["sorted_nodes"] sort --> rev["倒序套求导规则"] rev --> gs["GradStore"] gs --> opt["SGD 或 AdamW"] opt -->|"Var set"| var
前向的每个算子仍然走算子主线;求导规则本身也是用张量算子写的,所以反向在 Metal 上一样跑在 Metal 上。和 burn 的做法(装饰器后端 + 全局图登记)的整体对照放在设计取舍。
💬 关联讨论
发起讨论加载中...