Skip to content

Matrix Decompositions(矩阵分解)

Matrix 分解将复杂 matrix 分解为更简单的因子,用于求解方程组、计算逆以及压缩数据。本节涵盖 Gaussian 消元、LU、QR、Cholesky、特征分解和 SVD——这些是 PCA、推荐系统和 ML 数值稳定性背后的算法。

  • Matrix 分解(或因式分解)将一个 matrix 拆解为更易处理的简单部分。可以类比整数分解:\(12 = 3 \times 4\) 比直接处理 12 更容易推理。

  • 我们分解 matrix 的目的是:更快地求解方程组、稳定地计算逆、求 eigenvalue、压缩数据,以及理解变换的几何含义。

  • 最基本的技术是 Gaussian 消元法(行化简)。思路很简单:给定方程组 \(A\mathbf{x} = \mathbf{b}\),用三种允许的操作化简 \(A\),直到答案显而易见。

  • 这三种操作是:交换两行、将某行乘以非零 scalar、将某行的倍数加到另一行。

  • 例如,为了消去第一列主元以下的元素,从下面各行中减去第 1 行的倍数:

\[ \begin{bmatrix} 2 & 1 & 5 \\ 4 & 3 & 7 \\ 6 & 5 & 9 \end{bmatrix} \xrightarrow{R_2 - 2R_1} \begin{bmatrix} 2 & 1 & 5 \\ 0 & 1 & -3 \\ 6 & 5 & 9 \end{bmatrix} \xrightarrow{R_3 - 3R_1} \begin{bmatrix} 2 & 1 & 5 \\ 0 & 1 & -3 \\ 0 & 2 & -6 \end{bmatrix} \]
  • 目标是行阶梯形式(REF):每个主元(每行的第一个非零元素)下方为零,且每个主元位于上方主元的右边,形成阶梯状。

Gaussian 消元:行操作产生三角形式,然后从下往上求解

  • 进一步化简为简化行阶梯形式(RREF):使每个主元等于 1,且是其列中唯一的非零元素。每个 matrix 都有唯一的 RREF。

  • 化为三角形式后,通过回代求解:最后一行直接给出最后一个变量,然后向上逐行求解。

  • 这是所有其他分解的基础——分解的目标正是将 matrix 化简为三角形式,这样就可以回代并求解变量。

  • LU 分解将 Gaussian 消元法形式化,将方阵分解为 \(A = LU\)(或带行交换时 \(A = PLU\)),其中 \(L\) 是下三角 matrix,\(U\) 是上三角 matrix。

LU 分解:将一个难 matrix 拆为两个容易的三角 matrix

  • 求解 \(A\mathbf{x} = \mathbf{b}\):首先通过前代(从上往下)求解 \(L\mathbf{y} = \mathbf{b}\),然后通过回代(从下往上)求解 \(U\mathbf{x} = \mathbf{y}\)。两次简单的三角求解,而不是一次困难的一般求解。

  • 与朴素 Gaussian 消元法相比,LU 分解的优势在于复用。一旦有了 \(L\)\(U\),就可以针对许多不同的 \(\mathbf{b}\) 向量求解,而无需重做分解。

  • 如果需要对同一方程组求解 1000 个不同的右端向量(在仿真中很常见),只需分解一次并重复使用。

  • 当 matrix 是对称正定的(如协方差 matrix)时,我们可以做得更好。

  • Cholesky 分解将其分解为 \(A = LL^T\),其中 \(L\) 是下三角 matrix。例如:

\[ \begin{bmatrix} 4 & 2 \\ 2 & 5 \end{bmatrix} = \begin{bmatrix} 2 & 0 \\ 1 & 2 \end{bmatrix} \begin{bmatrix} 2 & 1 \\ 0 & 2 \end{bmatrix} \]
  • 这大约比 LU 快两倍,且保证数值稳定。可以把它看作 matrix 的"平方根"。

  • 若分解失败(某个平方根下出现负数),则 matrix 不是正定的。因此,Cholesky 也可作为正定性的验证方法。

  • 方阵 \(A\)eigenvector 是变换时只会被拉伸或压缩而不旋转的特殊方向。Eigenvalue 是对应的缩放因子:

\[A\mathbf{x} = \lambda\mathbf{x}\]

Eigenvector 保持在同一直线上(仅被缩放),普通 vector 则被旋转

  • 大多数 vector 在乘以 matrix 后会改变方向。但 eigenvector 是特殊的:输出与输入方向相同,只是被 \(\lambda\) 缩放。若 \(\lambda = 2\),eigenvector 长度加倍;若 \(\lambda = -1\),方向翻转;若 \(\lambda = 0\),被压缩为零。

  • 例如,对于:

\[ A = \begin{bmatrix} 3 & 1 \\ 0 & 2 \end{bmatrix} \]

vector \([1, 0]^T\) 是 eigenvalue 为 \(\lambda = 3\) 的 eigenvector,因为 \(A[1, 0]^T = [3, 0]^T = 3[1, 0]^T\)

  • 为了求 eigenvalue,求解特征多项式 \(\det(A - \lambda I) = 0\)。其根就是 eigenvalue。然后将每个 \(\lambda\) 代回 \((A - \lambda I)\mathbf{x} = \mathbf{0}\) 以求对应的 eigenvector。

  • 关键性质:

    • \(A\) 的迹等于其 eigenvalue 之和。
    • \(A\) 的行列式等于其 eigenvalue 之积。
    • 对称 matrix 有相互垂直的 eigenvector 和实数 eigenvalue。
    • 正定 matrix 的 eigenvalue 全为正。
    • 协方差 matrix(我们在统计学中会遇到)始终是半正定的。
  • 对大 matrix 通过特征多项式计算 eigenvalue 是不切实际的。实际中使用迭代方法:

    • 幂迭代:反复将 matrix 与某 vector 相乘并归一化。收敛到主 eigenvector(最大 eigenvalue)。简单,但只能找到一对特征对。

    • QR 算法:主力方法。反复用 QR 分解进行分解和重组,直到 matrix 收敛到三角形式,从而在对角线上揭示所有 eigenvalue。

    • 逆迭代:找到最接近给定目标值的 eigenvector。在大致知道想要哪个 eigenvalue 时很有用。

    • 对于大型稀疏 matrix,ArnoldiLanczos 迭代利用稀疏性提高效率。

  • 若方阵有一组完整的线性无关 eigenvector,它就可以被对角化\(A = PDP^{-1}\),其中 \(D\) 是 eigenvalue 构成的对角 matrix,\(P\) 的各列是 eigenvector。

  • 为什么有用?对角 matrix 极易处理。需要计算 \(A^{100}\)?与其将 \(A\) 与自身相乘 100 次,不如计算 \(PD^{100}P^{-1}\)——对角 matrix 的幂次只需对每个元素独立求幂。这将昂贵的运算变为廉价的运算。

  • 特征基(eigenbasis)是完全由 eigenvector 构成的 basis。在这个 basis 中,matrix 变为对角形式,变换只是沿各 eigenvector 方向的独立缩放。这就像找到了变换的天然坐标系。

  • QR 分解将任意 matrix \(A\) 分解为 \(A = QR\),其中 \(Q\) 是正交 matrix(各列正交归一),\(R\) 是上三角 matrix。可以把它理解为将"方向"信息(\(Q\))与"缩放和混合"信息(\(R\))分离。

  • Gram-Schmidt 过程逐列构建 \(Q\)。取 \(A\) 的第一列并归一化;取第二列,减去其在第一列上的投影(使其垂直),再归一化;对每一列重复这一过程。结果是一组正交归一的 vector。

  • QR 分解是求 eigenvalue 的 QR 算法的引擎。它也直接用于求解最小二乘问题:当 \(A\mathbf{x} = \mathbf{b}\) 没有精确解(方程多于未知量)时,QR 找到最佳近似解。

  • SVD(奇异值分解)是最一般、可以说也是最重要的分解。每个 matrix(任意形状、任意 rank)都有 SVD:\(A = U\Sigma V^T\)

    • \(V^T\)\(n \times n\),正交):旋转输入
    • \(\Sigma\)\(m \times n\),对角):沿正交轴缩放(singular value,非负,降序排列)
    • \(U\)\(m \times m\),正交):旋转输出

SVD:任意变换 = 旋转,然后缩放,然后再旋转

  • 从几何上看,SVD 表明每个线性变换,无论多复杂,都不过是一次旋转,接着沿各轴的拉伸,再接着另一次旋转。圆变成了椭圆。

  • Singular value(\(\sigma_1 \geq \sigma_2 \geq \ldots\))揭示了每个方向的"重要性"。Singular value 大的方向最为重要。\(A\) 的 rank 等于非零 singular value 的数量。

  • 低 rank 近似:通过只保留 \(k\) 个最大的 singular value 并将其余置零,可以得到 \(A\) 的最佳 rank-\(k\) 近似。图像压缩就是这样工作的:一幅 \(1000 \times 1000\) 的图像可能只需 \(k = 50\) 个 singular value 就能几乎完全保真,压缩比达 20 倍。

  • SVD 也提供伪逆:\(A^+ = V\Sigma^+U^T\),其中 \(\Sigma^+\) 对非零 singular value 取倒数。

  • 特征分解只适用于方阵,而 SVD 适用于任意 matrix——这是它的核心优势。

  • PCA(主成分分析)利用特征分解(或 SVD)进行降维。

  • 想象每个样本有 100 个 feature 的数据集(100 维 vector 堆叠成 matrix)。这些 feature 中许多是相关且冗余的。

  • PCA 找到数据实际变化的方向,让你只保留重要的部分。

PCA 找到数据中方差最大的方向

  • 第一主成分(PC1)是方差最大的方向。

  • 第二主成分(PC2)捕捉剩余方差最多的部分,且与第一个主成分垂直。

  • 如果大部分方差集中在少数几个方向上,就可以将数据投影到这些维度,丢弃其余部分,同时保持最小的信息损失。

  • 步骤:

    • 对数据进行标准化(减去均值,除以标准差),使所有 feature 平等地贡献
    • 计算协方差 matrix
    • 求其 eigenvalue 和 eigenvector
    • 选取 eigenvalue 最大的 \(k\) 个 eigenvector(即主成分)
    • 将数据投影到这些主成分上
  • 标准化至关重要:若不进行标准化,以千米为单位的 feature 会主导以厘米为单位的 feature,无论其实际重要性如何。

  • 在实际中,PCA 用于可视化(将高维数据投影到二维或三维)、降噪(丢弃主要是噪声的低方差方向),以及通过减少输入 feature 数量来加速 ML 模型。

  • Kernel PCA 将 PCA 扩展到非线性关系。它通过 kernel 函数将数据映射到高维空间,使结构变为线性,然后应用标准 PCA 并映射回来。

  • Schur 分解将方阵分解为 \(A = QTQ^\ast\),其中 \(Q\) 是酉 matrix,\(T\) 是上三角 matrix。每个方阵都有 Schur 分解,即使它不能被对角化。

  • 非负 Matrix 分解(NMF)将 matrix 分解为两个非负 matrix:\(A \approx WH\),其中 \(W\)\(H\) 的所有元素均 \(\geq 0\)。与 SVD 不同,NMF 只做加法,不做减法。这使得各部分具有可解释性:在主题建模中,\(W\) 给出每篇文档的主题权重,\(H\) 给出每个主题的词权重——全部非负,与我们对文档"含有多少各主题"的直觉一致。

  • 谱定理指出,对称(或 Hermitian)matrix 总可以用正交(或酉)matrix 对角化。它们的 eigenvalue 总是实数,eigenvector 总是正交的。这是 PCA 的理论基础。

编程练习(使用 CoLab 或 notebook)

  1. 计算对称 matrix 的 eigenvalue 和 eigenvector。验证 eigenvector 垂直,并从特征分解重建 matrix。

    import jax.numpy as jnp
    
    A = jnp.array([[4.0, 2.0],
                   [2.0, 3.0]])
    
    eigenvalues, eigenvectors = jnp.linalg.eigh(A)
    print(f"Eigenvalue: {eigenvalues}")
    print(f"Eigenvector 正交性: {jnp.dot(eigenvectors[:,0], eigenvectors[:,1]):.6f}")
    
    # 重建:A = P D P^T
    D = jnp.diag(eigenvalues)
    A_reconstructed = eigenvectors @ D @ eigenvectors.T
    print(f"重建结果匹配: {jnp.allclose(A, A_reconstructed)}")
    

  2. 实现幂迭代求最大 eigenvalue,实现逆迭代求最小 eigenvalue。与 jnp.linalg.eigh 比较。然后尝试自己实现 QR 算法。

    import jax.numpy as jnp
    
    A = jnp.array([[4.0, 2.0],
                   [2.0, 3.0]])
    
    # 幂迭代:求最大 eigenvalue
    v = jnp.array([1.0, 0.0])
    for _ in range(20):
        v = A @ v
        v = v / jnp.linalg.norm(v)
    print(f"最大 eigenvalue:  {v @ A @ v:.4f}")
    
    # 逆迭代:乘以 A^{-1} 而非 A,求最小 eigenvalue
    v = jnp.array([1.0, 0.0])
    for _ in range(20):
        v = jnp.linalg.solve(A, v)
        v = v / jnp.linalg.norm(v)
    print(f"最小 eigenvalue: {1.0 / (v @ jnp.linalg.solve(A, v)):.4f}")
    
    print(f"jnp.linalg.eigh 结果:    {jnp.linalg.eigh(A)[0]}")
    

  3. 计算 matrix 的 SVD,然后仅使用前 k 个 singular value 重建,观察随 k 变化的近似质量。

    import jax.numpy as jnp
    
    A = jnp.array([[1.0, 2.0, 3.0],
                   [4.0, 5.0, 6.0],
                   [7.0, 8.0, 9.0]])
    
    U, S, Vt = jnp.linalg.svd(A)
    
    for k in [1, 2, 3]:
        approx = U[:, :k] @ jnp.diag(S[:k]) @ Vt[:k, :]
        error = jnp.linalg.norm(A - approx)
        print(f"k={k},重建误差: {error:.4f}")