奇异值分解 SVD
学习目标
完成本节后,你将能够: - 理解 SVD 的几何和代数意义 - 计算 SVD 分解 - 应用 SVD 进行数据压缩和降维 - 理解 SVD 与 PCA 的关系
1. SVD 的几何意义
定理:任意 \(m \times n\) 矩阵 \(A\) 可以分解为:
\[A = U \Sigma V^T\]
其中 \(U\) 和 \(V\) 是正交矩阵,\(\Sigma\) 是对角矩阵(奇异值 \(\sigma_1 \geq \sigma_2 \geq \cdots \geq \sigma_r > 0\))。
几何分解: 1. \(V^T\):在输入空间旋转 2. \(\Sigma\):沿坐标轴缩放(\(\sigma_i\) 倍) 3. \(U\):在输出空间旋转
> **直觉 (3Blue1Brown)**:任何线性变换都可以分解为三个简单步骤:先旋转,再缩放,再旋转。SVD 就是这种分解的精确数学表达。与特征值分解不同,SVD 适用于**任意**形状的矩阵。
2. SVD 与特征值分解的关系
- \(U\) 的列是 \(AA^T\) 的特征向量
- \(V\) 的列是 \(A^T A\) 的特征向量
- \(\sigma_i^2\) 是 \(A^T A\)(或 \(AA^T\))的特征值
def svd_manual(A):
"""手工计算 SVD"""
# 计算 V 和 Σ:A^T A 的特征分解
AT_A = A.T @ A
eigvals_V, V = np.linalg.eigh(AT_A)
idx = np.argsort(eigvals_V)[::-1]
V = V[:, idx]# 计算 U U = A @ V / s # 使 U 正交 U, _ = np.linalg.qr(U) return U, s, V.T
A = np.array([[3, 1], [1, 3], [0, 0]]) U_manual, s_manual, Vt_manual = svd_manual(A) U_scipy, s_scipy, Vt_scipy = np.linalg.svd(A) print("手工奇异值:", s_manual) print("scipy奇异值:", s_scipy) ```
3. SVD 与四个基本子空间
SVD 完整揭示四个基本子空间:
- \(U\) 的前 \(r\) 列张成**列空间** \(C(A)\)
- \(U\) 的后 \(m-r\) 列张成**左零空间** \(N(A^T)\)
- \(V\) 的前 \(r\) 列张成**行空间** \(C(A^T)\)
- \(V\) 的后 \(n-r\) 列张成**零空间** \(N(A)\)
这可能是理解 SVD 最重要的角度——它一次给出所有四个子空间的正交基。
4. 最佳低秩近似
Eckart-Young 定理:截断 SVD 给出矩阵的最佳秩 \(k\) 近似:
\[A_k = \sum_{i=1}^k \sigma_i u_i v_i^T\]
这是最小化 \(\|A - B\|_F\) 的秩 \(k\) 矩阵。
### 4.1 图像压缩
def svd_compress(A, k):
"""SVD 压缩"""
U, s, Vt = np.linalg.svd(A, full_matrices=False)# 模拟一个 100×100 的矩阵 np.random.seed(42) A = np.random.randn(100, 100)
# 不同压缩比 orig_size = 100 * 100 # 10000
for k in [5, 10, 20, 50]: A_k = svd_compress(A, k) error = np.linalg.norm(A - A_k) / np.linalg.norm(A) comp_size = k * (100 + 100 + 1) # U_k + s_k + V_k ratio = comp_size / orig_size print(f"k={k:2d}: 相对误差={error:.3f}, 压缩率={ratio:.1%}")
# 结论:k=20 时压缩率42%,误差已很小 ```
5. SVD 与 PCA
SVD 是主成分分析 (PCA) 的数学基础:
- 数据中心化:\(X \leftarrow X - \bar{X}\)
- 做 SVD:\(X = U\Sigma V^T\)
- 主成分方向 = \(V\) 的列
- 主成分得分 = \(U\Sigma\)
- 解释方差比 = \(\sigma_i^2 / \sum \sigma_j^2\)
6. 条件数
\[\kappa(A) = \frac{\sigma_{\max}}{\sigma_{\min}}\]
- \(\kappa \approx 1\):良态(数值稳定)
- \(\kappa\) 很大:病态(对误差敏感)
def condition_number(A):
_, s, _ = np.linalg.svd(A)# 对比良态和病态矩阵 well_conditioned = np.array([[1, 0], [0, 1]]) ill_conditioned = np.array([[1, 1], [1, 1.001]])
print("良态条件数:", condition_number(well_conditioned)) print("病态条件数:", condition_number(ill_conditioned)) ```
7. 本节习题
- 对 \(A = \begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix}\) 做 SVD,与特征值分解对比
- 证明:\(\|A\|_2 = \sigma_{\max}(A)\)
- 用 SVD 计算 \(A\) 的伪逆 \(A^{+}\)
- 用 SVD 对一张简单的"图像"(16×16 矩阵)做压缩
- 解释为什么 \(\sigma_i^2\) 是 \(A^T A\) 的特征值
总结
- SVD 分解任意矩阵为旋转 × 缩放 × 旋转 - 完整揭示四个基本子空间 - 截断 SVD 给出矩阵的最佳低秩近似 - SVD 是 PCA、数据压缩、推荐系统的基础 - 条件数衡量数值稳定性