Rust in Everything
基于 tenferro-linalg 0.8.0

奇异向量和特征向量的符号和 JAX 不同

主线位置:TRG 主线「一步 SVD 截断加收缩」:拿 JAX 的分解结果当参考值,逐步核对 tenferro。
版本:JAX 0.12.0.1(CPU,x64)、tenferro-rs 0.8.0(CPU,native,单线程)
flowchart TB
  ref["JAX 的 U 当参考"] --> cmp["逐元素比较"]
  cmp --> fail["差 1.25"]
  fail --> why["每列只定到符号"]
  why --> inv["比 U S Vt 或投影"]
  why --> canon["两边规范符号"]
  inv --> ok["差 1e-15"]
  canon --> ok

实验代码:tenferro 一侧是 bin p24_signs(release,CpuEngine::serial(),把分解结果写成文件),JAX 一侧是 p24_signs.py(读进来比较)。M2 Max、macOS 27、rustc 1.99.0。

现象

6×4 矩阵 A[i, j] = sin(1 + 0.7i² + 2.3j + 0.5ij²) 做薄 SVD,B + Bᵀ(B 是同一公式的 5×5)做 eigh。tenferro 一侧就是:

let (u, s, vt) = a.svd()?;
let (w, v) = sym.eigh()?;

拿 JAX 的结果当参考值逐元素比较:

u, s, vh = jnp.linalg.svd(a, full_matrices=False)
np.allclose(u, tu)
svd  s: max|jax - tenferro| = 1.776e-15
svd  U: max|jax - tenferro| = 1.253e+00  np.allclose: False
svd  U column signs (jax . tenferro): [1, 1, -1, 1]
eigh w: max|jax - tenferro| = 1.776e-15
eigh V: max|jax - tenferro| = 1.580e+00  column signs: [-1, -1, -1, -1, -1]

奇异值、特征值对得上(1.8e-15);U 的第 3 列、V 的全部 5 列方向相反。

出处

奇异向量和特征向量只确定到一个符号(复数时是一个相位):u → −u、v → −v 同时翻,U S Vᵀ 不变;特征向量单独翻也还是特征向量。选哪个符号由后端算法的细节决定,JAX 的 CPU 后端用 LAPACK,tenferro 用 faer,各有各的选法。tenferro 默认 SvdGauge::Raw,文档写的是「Leave the backend's raw singular vector signs or phases unchanged」。QR 两边的 diag(R) 符号恰好相同。这对梯度的影响(损失要和符号无关)和 SvdGauge 的来历见 分解的梯度。

修法

和 JAX 比较时,比和符号无关的量:

np.abs(u * s @ vh - tu * ts @ tvt).max()   # 重构 U S Vt
np.abs(u @ u.T - tu @ tu.T).max()          # 投影 U Ut
svd  U diag(s) Vt: max|jax - tenferro| = 1.554e-15
svd  U U^T:        max|jax - tenferro| = 4.857e-16

一定要比向量本身时(比如存下来的参考值),两边按同一个规则规范符号。tenferro 有选项 CanonicalPivot:每列绝对值最大的元素变成正数,SVD 把同一个符号乘到 Vᵀ 的对应行上;JAX 一侧手工做同样的事:

let (uc, _, vtc) = a.svd_with_options(SvdOptions::default().gauge(SvdGauge::CanonicalPivot))?;
let (_, vc) = sym.eigh_with_options(EighOptions::default().gauge(EighGauge::CanonicalPivot))?;
piv = np.sign(x[np.argmax(np.abs(x), axis=0), range(x.shape[1])])   # 每列主元的符号
u * piv, vh * piv[:, None]
svd  CanonicalPivot U vs JAX U * pivot sign: 4.441e-16  Vt: 6.661e-16
eigh CanonicalPivot V vs JAX V * pivot sign: 3.469e-16

规范之后差 1e-16 量级。主元规则在两个元素绝对值几乎相等时自身不稳定,那种矩阵上只能比和符号无关的量。

怎么确认没中招

  • 测试里凡是对 U、V、特征向量逐元素断言的,先问一句:这个量翻了号还对不对?翻号还对,就改成比投影或重构。
  • TRG 里一个奇异向量反号,下一步的张量 T 在对应的那个下标值上整片反号;最后的迹 T[x, y, x, y] 里每个下标出现两次,符号成对抵消,lnZ 和梯度不受影响。逐步打印中间张量去和 JAX 对照时,要先规范符号或只比范数。

小结

  • LAPACK 和 faer 选的奇异向量、特征向量符号不同;tenferro 默认不规范。本例 SVD 一列、eigh 五列反号。
  • 比较时用 U S Vᵀ、U Uᵀ 这类和符号无关的量,差 1e-15 量级。
  • 要比向量本身,tenferro 用 CanonicalPivot,JAX 一侧手工把每列主元变成正数。

💬 关联讨论

发起讨论
加载中...