跳转到内容

矩阵求逆与伪逆

矩阵求逆是线性代数中的基本运算之一。对于一个 n×n 的方阵 A,如果存在矩阵 B 使得 AB = BA = I(单位矩阵),则称 B 为 A 的逆矩阵,记作 A⁻¹。在深度学习中,矩阵求逆和伪逆虽然不像矩阵乘法那样频繁使用,但在某些场景下非常重要,比如线性模型的闭式解、正规方程求解等。NumPy 提供了 np.linalg.inv 用于计算方阵的逆,以及 np.linalg.pinv 用于计算伪逆,后者可以处理非方阵或奇异矩阵。

矩阵求逆的基本概念

对于一个可逆矩阵 A,其逆矩阵 A⁻¹ 满足 AA⁻¹ = A⁻¹A = I。这意味着通过逆矩阵,我们可以"解"线性方程组。例如,方程 Ax = b 的解可以写成 x = A⁻¹b(当 A 是方阵且可逆时)。

需要特别注意的是,不是所有矩阵都是可逆的。一个矩阵可逆的充分必要条件是它的行列式不为零(det(A) ≠ 0),或者等价地,它的秩等于其维度(满秩)。如果矩阵是奇异的(singular,行列式为零)或接近奇异(病态条件数很大),直接求逆可能会导致数值不稳定。

np.linalg.inv:计算方阵的逆

python
import numpy as np

A = np.array([[1, 2], [3, 4]])
A_inv = np.linalg.inv(A)

print(f"矩阵 A:\n{A}")
print(f"逆矩阵 A⁻¹:\n{A_inv}")
print(f"AA⁻¹ (应为单位矩阵):\n{A @ A_inv}")

输出显示 AA⁻¹ 和 A⁻¹A 都接近单位矩阵(由于浮点数精度,会有极小的误差)。

np.linalg.pinv:计算伪逆

对于非方阵或奇异矩阵,我们无法直接求逆,但可以使用伪逆(Moore-Penrose Pseudoinverse)。伪逆是逆矩阵的推广,对于任意矩阵 A,其伪逆 A⁺ 满足以下四个 Penrose 条件:

  1. AA⁺A = A
  2. A⁺AA⁺ = A⁺
  3. (AA⁺)ᵀ = AA⁺
  4. (A⁺A)ᵀ = A⁺A

NumPy 使用 SVD 分解来计算伪逆,这是数值最稳定的方法:

python
# 非方阵的伪逆
A = np.array([[1, 2, 3], [4, 5, 6]])
A_pinv = np.linalg.pinv(A)

print(f"原矩阵 A (2×3):\n{A}")
print(f"伪逆 A⁺ (3×2):\n{A_pinv}")
print(f"A @ A⁺ @ A (应等于 A):\n{A @ A_pinv @ A}")

闭式解与正规方程

在线性回归等简单线性模型中,我们可以得到闭式解(closed-form solution)。给定数据矩阵 X 和目标向量 y,线性回归的解是:

θ = (XᵀX)⁻¹Xᵀy

其中 (XᵀX)⁻¹Xᵀ 就是 X 的伪逆。当 X 的列数少于行数且列线性独立时,(XᵀX) 是可逆的,此时伪逆等于 (XᵀX)⁻¹Xᵀ。

python
def linear_regression_closed_form(X, y):
    """使用闭式解计算线性回归参数

    参数:
        X: 特征矩阵 (n_samples, n_features)
        y: 目标向量 (n_samples,)
    返回:
        theta: 参数向量 (n_features,)
    """
    # 计算伪逆
    X_pinv = np.linalg.pinv(X)
    theta = X_pinv @ y
    return theta

# 示例数据
np.random.seed(42)
X = np.random.randn(100, 3)
true_theta = np.array([1.5, -2.0, 0.5])
y = X @ true_theta + np.random.randn(100) * 0.1

# 拟合模型
theta_hat = linear_regression_closed_form(X, y)
print(f"真实参数: {true_theta}")
print(f"估计参数: {theta_hat}")
print(f"估计误差: {np.linalg.norm(theta_hat - true_theta):.4f}")

数值稳定性问题

直接求逆有几个重要的数值稳定性问题需要注意:

病态矩阵求逆的困难:如果矩阵的条件数很大(条件数衡量输出对输入扰动的敏感性),求逆会放大数值误差。条件数越大,矩阵越"病态"。

python
# 病态矩阵示例
A = np.array([[1, 2], [2, 4]])  # 接近奇异
print(f"条件数: {np.linalg.cond(A)}")  # 条件数很大

# 使用伪逆更稳定
A_pinv = np.linalg.pinv(A)
print(f"伪逆:\n{A_pinv}")

大矩阵求逆的计算成本:对于大型矩阵,直接求逆的时间复杂度是 O(n³)。在深度学习中,权重矩阵可能非常大,此时求逆是不切实际的。通常我们不会直接求一个大矩阵的逆,而是使用其他方法(如 Cholesky 分解、LU 分解或迭代方法)来解线性方程组 Ax = b。

python
# 计算成本对比
for n in [100, 500, 1000]:
    A = np.random.randn(n, n)
    b = np.random.randn(n)

    # 方法1:直接求逆
    start = time.time()
    x1 = np.linalg.inv(A) @ b
    t1 = time.time() - start

    # 方法2:使用 solve(更推荐)
    start = time.time()
    x2 = np.linalg.solve(A, b)
    t2 = time.time() - start

    print(f"n={n}: 求逆方法 {t1:.4f}s, solve方法 {t2:.4f}s")

优先使用 np.linalg.solve 而不是求逆:解线性方程组 Ax = b 时,应该优先使用 np.linalg.solve(A, b) 而不是计算 np.linalg.inv(A) @ b。前者更快、更数值稳定、不会因为 A 是奇异矩阵而崩溃(它会给出明确的错误信息)。

常见误区

误区一:认为所有矩阵都可以求逆

这是最常见的错误。只有方阵才可能有逆矩阵(即使是方阵也可能不可逆,如奇异矩阵)。对于非方阵,必须使用伪逆。

误区二:忽略数值稳定性

当矩阵条件数很大时,求逆会放大误差。在实际应用中,应该在使用前检查矩阵的条件数,或者直接使用伪逆。

误区三:用求逆代替求解线性方程组

如果你只是想解 Ax = b,永远不要先求 A 的逆再乘以 b。np.linalg.solve(A, b) 是专门为这个问题设计的,它更快、更稳定。

python
# 错误做法
A = np.array([[1, 2], [3, 4]])
b = np.array([5, 6])
x = np.linalg.inv(A) @ b  # 效率低,数值稳定性差

# 正确做法
x = np.linalg.solve(A, b)  # 效率高,数值稳定

在深度学习中的应用

虽然现代深度学习很少直接求逆,但在某些场景下仍然有用。例如,在线性二次调节器(LQR)控制问题中,或者在计算某些解析解时。理解求逆和伪逆有助于理解更复杂的矩阵运算,如梯度下降的正规方程解。

伪逆还常用于数据预处理和降维。例如,在计算投影矩阵或将数据映射到低维空间时,伪逆提供了最小二乘意义下的最优解。

python
# 投影矩阵:P = X(XᵀX)⁻¹Xᵀ
# 使用伪逆更稳定:P = X @ pinv(Xᵀ @ X) @ Xᵀ
# 或直接:P = X @ pinv(X)

X = np.random.randn(50, 10)
X_pinv = np.linalg.pinv(X)
P = X @ X_pinv  # 投影矩阵

# 验证投影矩阵的性质:幂等性 P² = P
print(f"P² - P 的范数: {np.linalg.norm(P @ P - P):.10f}")

API 总结

函数适用场景返回形状注意事项
np.linalg.inv(A)方阵求逆(n, n)A 必须可逆,条件数不能太大
np.linalg.pinv(A)任意矩阵(n, m)使用 SVD 计算,数值稳定
np.linalg.solve(A, b)解线性方程组(n,) 或 (n, k)推荐,比求逆更稳定高效

理解矩阵求逆和伪逆的适用场景对于编写数值稳定的代码至关重要。在深度学习实践中,伪逆虽然不直接用于训练,但在理解底层数学原理时非常重要。

基于 MIT 许可发布