autodiff 教程
本教程是了解本仓库的最快途径:只需导入 Luna-Flow/autodiff 这一个包,你就能对函数求导、针对重新导出的 trait 编写泛型代码,并处理带检查运算的失败。每一节都链接到更深入的对应包教程。
快速上手
moon add Luna-Flow/autodiff@0.2.0
import {
"Luna-Flow/autodiff",
}
fn main {
let (value, slope) = @autodiff.value_and_diff(x => x * x.exp(), 1.0)
println("f(1) = \{value}")
println("f'(1) = \{slope}")
}
f(1) = 2.718281828459045
f'(1) = 5.43656365691809
的导数为 ,因此 。
日常任务
对闭包求导
fn main {
let slope = @autodiff.diff(x => x.sin() * x.cos(), 0.0)
println("d/dx sin x cos x at 0 = \{slope}")
}
d/dx sin x cos x at 0 = 1
使用重新导出的 trait 编写泛型代码
所有约束都来自这一个导入:
fn[T : @autodiff.Ring + @autodiff.Exponential + @autodiff.IntegralHomomorphism] softplus_like(
x : T,
) -> T {
let one : T = @autodiff.IntegralHomomorphism::from_integral(1)
one + @autodiff.Exponential::exp(x)
}
fn main {
println("value at 0: \{softplus_like(0.0)}")
println("slope at 0: \{@autodiff.diff(softplus_like, 0.0)}")
}
value at 0: 2
slope at 0: 1
使用数学常数
Constants 以对偶常数的形式提供 、 和 :
fn main {
let area_slope = @autodiff.diff(
r => {
let pi : @autodiff.Dual[Double] = @autodiff.Constants::pi()
pi * r * r
},
2.0,
)
println("d/dr pi r^2 at 2 = \{area_slope}")
}
d/dr pi r^2 at 2 = 12.566370614359172
处理带检查运算的失败
fn main {
let ctx = @autodiff.ArithmeticContext::new(53)
let x = @autodiff.Dual::variable(2.0)
match @autodiff.DivChecked::div_checked(x, x - x, ctx) {
Ok(_) => println("unexpected")
Err(e) => println("division by zero: \{e.is_division_by_zero()}")
}
}
division by zero: true
深入学习
- 手动设定种子、方向导数以及与有限差分的比较:见 dual 教程。
- 高阶导数与牛顿法:见 forward 教程。
- 梯度与雅可比矩阵:见 linalg 教程。
- 多项式导数:见 poly 教程。
- 深入了解带检查的运算:见 checked 教程。
常见陷阱
- 根包不提供梯度。 要使用
gradient和jacobian,请导入Luna-Flow/autodiff/linalg。 - 重新导出的 trait 就是原始 trait。 你为
@lg.Ring编写的实例就是@autodiff.Ring的实例;不要两者都实现。 - 在被求导的代码中,字面量需要用
Dual::constant或from_integral包装。
后续阅读
- autodiff API 列出了所有重新导出的名称。
- autodiff 设计 解释了这个门面包。
- 概览 给出了所有包的全貌。