常见坑
每一章从一个 JAX(或 PyTorch)的习惯出发:先给 JAX 那边的写法和输出,再把它照搬到 tenferro,给出报错原文或静默的错误结果,然后找出处、给修法和检查办法。输出是 M2 Max、macOS 27、rustc 1.99.0 上 JAX 0.12.0.1(CPU)、PyTorch 2.15.0 和 tenferro-rs 0.8.0 的原文。机制在 tenferro 卷的常见坑里已经讲过的,这里只讲从 JAX 走过去为什么会撞上,然后链过去。
有两条是 JAX 一侧的坑(8、20):搬程序时要先有一份可信的 JAX 参考结果,这两条会让参考本身不对。最危险的是不报错的那几类:布局和批维(1、2)、下标和 scatter(4、5)、整数系数(7)、被替换的输入(10)、二阶导(12)、复数(17、18)、有限差分(29)。
数组与 dtype
- 同一段数据 reshape 出来的矩阵不一样:tenferro 的 reshape 按列主序,相当于
order="F"。 - 照搬 JAX 的批维形状,静默算成别的批:批维在最右边,
[4,2,3]被当成 3 批 4×2。 - 整数加 f32 变成了 f64:
vec![1, 2]是 I32,I32 加 F32 提升成 F64。 - x[-1] 取到的是第一个元素:负下标不回绕,直接夹到 0。
- scatter 把更新加了上去:tenferro 的 scatter 是累加的,越界的更新也夹到边上。
- real value must be finite when casting to i32:NaN、无穷和超范围的浮点转整数,到
run才报错。 - 整数乘 0.5 原样返回:
scale_real作用在整数上,系数先四舍五入。 - JAX 一侧静默降成 float32,对不上:没开 x64 的 JAX 参考差到 1e-7。
变换与 eager
- binding shape mismatch for concrete-shape placeholder:jit 换形状会重编译,tenferro 的具体形状占位符直接拒绝。
- 切向量、种子、常量都能被 run 的输入替换:传齐所有输入时按位置对应,顺序错了不报错。
- 同时要值和梯度:has no runtime input binding:
compile_many没有带输入规格的版本。 - 经过 SVD 的 grad(grad) 静默偏了约 6%:反向套反向撞上展宽的 F,用
jvp(grad)。 - no_grad 下 backward 成功,却没有梯度:PyTorch 会报错,tenferro 返回空的梯度集。
- with_eager_session 返回元组:E0283:闭包的错误类型推不出来,编译器的提示也编不过。
- 每次 backward 都一样慢:eager 每次都重建求导图,深图改用 traced 或分段。
复数
- placeholder semantic input 2 has no runtime input binding:
vjp_program的种子排在最后,没有默认值。 - 复数梯度下降沿虚轴发散:JAX 的梯度要取共轭,tenferro 的不用,照搬就多取了一次。
- 复数输出求导不报错,给的是实部的梯度:忘了取模的损失在 JAX、PyTorch 里报错,在 tenferro 里静默下降到负无穷。
- unsupported dtype conversion from C64 to F64:取实部要用
cast,convert只做无损转换。
SVD 与线性代数
- JAX 自带 SVD 的梯度从第 5 步起是 NaN:重复的奇异值;自己写规则时还会撞上 jit 的 FMA 对角线。
- slice input requires constant exact or upper-bound extents:对 SVD、QR 的输出切片后求导失败,上游 bug。
- sqrt(s) 的梯度是 NaN:faer 给出精确为零的奇异值,
derivative_eps管不到。 - lu_factor_solve: rank mismatch: expected 2, actual 1:
solve的右端必须是矩阵。 - 奇异向量和特征向量的符号和 JAX 不同:拿存下来的 JAX 向量直接比会失败,比不依赖符号的量。
求导的代价、计时与比对
- 整张图求导,几步之后就停不下来:值被重复使用时求导变换按指数增长,上游问题;分段。
- checkpoint 之后栈溢出:检查点链每步膨胀,5 步就在丢弃时压爆主线程栈,上游问题。
- 换了线程数,末位不一样:同一宽度逐位可重复,换宽度就变;比对时固定宽度。
- 第一次跑慢、满载时慢二十倍:新二进制的第一次启动、超订的多线程 engine。
- 有限差分的比热看上去合理,其实差 1.3%:差分步长跨过了截断处奇异值的交叉点,换两个步长核对。
flowchart TB
q{"碰到了什么"}
q -->|报错| err{"在哪一步"}
err -->|"编译"| c["14"]
err -->|"prepare 或 run"| r["6 9 11 16"]
err -->|"求导"| d["21 23"]
err -->|"转换"| v["19"]
q -->|结果不对| w{"哪里不对"}
w -->|"数值错位"| layout["1 2 4 5 7"]
w -->|"梯度"| g["10 12 13 17 18 22"]
w -->|"NaN"| n["20 22"]
w -->|"和 JAX 差末位"| bits["3 8 24 27"]
w -->|"比热"| fd["29"]
q -->|慢或停不下来| s["15 25 26 28"]
💬 关联讨论
发起讨论加载中...