Rust in Everything
基于 tenferro-ad 0.8.0

变换

这一部分走变换主线:写函数 → 记图 → 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 卷的 运行时 和 自动微分。

  1. 记图与 jit:jaxpr 和 tenferro 的图都是 4 个算子;jit 的参数是 input_concrete_shape 占位符;jit 对新形状静默重编译,tenferro 直接报 binding shape mismatch,要自己重建(85 µs),符号形状的叶子一个程序跑多个长度;闭包常量是能在 run 时覆盖的输入;建图时没有数,没有 cond / while / scan。
  2. grad、vjp、jvp:变换返回导数的图;种子、余切、切向量建图时必须带数据,但在程序里是带默认值的输入,run 时给全输入就能换;value_and_grad 要 compile_many,x 只能是带数据的叶子;多个 wrt 用 vjp_many;运算族的规则要 AdContext。
  3. 高阶导数: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"]

💬 关联讨论

发起讨论
加载中...