主题
矩阵求逆与伪逆
矩阵求逆是线性代数中的基本运算之一。对于一个 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 条件:
- AA⁺A = A
- A⁺AA⁺ = A⁺
- (AA⁺)ᵀ = AA⁺
- (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) | 推荐,比求逆更稳定高效 |
理解矩阵求逆和伪逆的适用场景对于编写数值稳定的代码至关重要。在深度学习实践中,伪逆虽然不直接用于训练,但在理解底层数学原理时非常重要。