np.linalg.svd 默认返回 full_matrices=true 的完整 svd 分解,即对 m×n 矩阵 a 返回 u(m×m)、s(min(m,n),)、vh(n×n),内存和计算开销大;实际常用 full_matrices=false 得经济型分解:u(m×r)、s(r,)、vh(r×n),其中 r=min(m,n)。

NumPy 的 np.linalg.svd 默认返回什么?别直接用 U, s, Vh = np.linalg.svd(A)
它确实能跑通,但默认 full_matrices=True,对非方阵会返回超大矩阵——比如 A 是 100×50,U 就是 100×100,浪费内存且拖慢后续计算。实际多数场景(如降维、推荐系统)只需要经济型分解。
- 加参数
full_matrices=False:得到U(100×50),s(50,),Vh(50×50),空间和计算都更紧凑 -
s是一维数组,不是对角矩阵;要构造 Σ,得用np.diag(s)或更省内存的np.zeros((m,n)); np.fill_diagonal(Σ, s) - 验证重构:
U @ np.diag(s) @ Vh应该和原矩阵A数值一致(容差内),但注意浮点误差累积,别用==直接比
QR 分解选 np.linalg.qr 还是 scipy.linalg.qr?
NumPy 版够用,但有几个硬限制必须知道:
-
np.linalg.qr只支持mode='reduced'和'complete',不支持'raw'(后者能拿到底层 Householder 向量,对迭代算法有用) - 如果矩阵列数远大于行数(比如 1000×10000),
mode='reduced'返回的Q是 1000×1000,R是 1000×10000;而mode='complete'的Q是 1000×1000,R会补零成 1000×10000 —— 两者R形状一样,但语义不同 - 性能上,NumPy 调的是 LAPACK 的
dgeqrf,和 SciPy 本质一样;除非你要overwrite_a=True原地计算省内存,否则不用切 SciPy
分解后想快速截断(比如只留前 k 个奇异值)?别手动切片再重构
手动做 U[:,:k] @ np.diag(s[:k]) @ Vh[:k,:] 看似直观,但 np.diag(s[:k]) 会生成 k×k 密集矩阵,k=500 时就是 2MB 内存+无谓乘法。更高效的方式是利用广播和向量化:
Uk = U[:, :k] Sk = s[:k] Vhk = Vh[:k, :] # 一行搞定,不显式构造 Σ A_k = Uk @ (Sk * Vhk)
-
Sk * Vhk是 (k,) × (k, n) → 自动广播成 (k, n),再和 (m, k) 相乘,全程无中间大矩阵 - 如果
k很小(np.linalg.multi_dot([Uk, np.diag(Sk), Vhk]),但通常广播方式更快 - 注意
Vh是共轭转置,所以截断必须取Vh[:k, :],不是Vh[:, :k]
复数矩阵的 SVD 用法和实数有区别吗?
函数接口完全一样,但结果含义和验证方式容易出错:
-
Vh是V.conj().T,不是简单的转置;验证时必须用U @ np.diag(s) @ Vh,不能写成U @ np.diag(s) @ V.T - 奇异值
s总是非负实数,即使输入是复数;但U和Vh是酉矩阵(U.conj().T @ U≈ I),不是正交矩阵 - 如果用
np.allclose(A, U @ np.diag(s) @ Vh)验证失败,先检查是否漏了.conj()—— 比如手算验证时误把V当实数处理
复数运算本身不慢,但调试时容易在共轭和转置上栽跟头,建议第一次用复数矩阵时,拿一个 2×2 小例子手算对照。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











