变换
这一部分走变换主线:写函数 → 记图 → grad / vjp / jvp / hvp → 编译、准备 → 执行,函数是 f(x) = Σ sin(x)·x²。JAX 的变换作用在 Python 函数上,jit(grad(f)) 一层包一层,第一次调用时一起追踪、变换、编译。tenferro 的变换作用在图上:Rust 代码直接建出 TracedTensor 图,AdContext 把它变成导数的图,GraphCompiler、Runtime::prepare、Runtime::run 三个显式的调用代替 jit 的第一次调用。每页都是同一个任务的两份程序,数字从实测输出里贴。tenferro 内部怎么建图、变换、执行,见 tenferro 卷的 运行时 和 自动微分。
- 记图与 jit:jaxpr 和 tenferro 的图都是 4 个算子;
jit的参数是input_concrete_shape占位符;jit对新形状静默重编译,tenferro 直接报binding shape mismatch,要自己重建(85 µs),符号形状的叶子一个程序跑多个长度;闭包常量是能在run时覆盖的输入;建图时没有数,没有cond/while/scan。 - grad、vjp、jvp:变换返回导数的图;种子、余切、切向量建图时必须带数据,但在程序里是带默认值的输入,
run时给全输入就能换;value_and_grad要compile_many,x只能是带数据的叶子;多个wrt用vjp_many;运算族的规则要AdContext。 - 高阶导数:hvp 两种叠法两边都对;没有
hessian/vmap,按列做;经过 SVD 时 tenferro 的grad(grad)偏 6%,jvp(grad)对;整图 TRG 的二阶变换 1 步 0.2 s、2 步超过 45 s,要分段。
flowchart TB fn["Rust 函数"] --> tg["TracedTensor 图"] tg --> ad["AdContext 变换"] ad --> dg["导数的图"] dg --> ad tg --> gc["GraphCompiler"] dg --> gc gc --> pr["Runtime prepare"] pr --> rn["Runtime run"]
💬 关联讨论
发起讨论加载中...