· 线性代数

10 · 数值计算与稳定性

理论上成立的公式,在浮点数计算机上可能算出错得离谱的结果。本节讲为什么代码要这么写。

理论上成立的公式,在浮点数计算机上可能算出错得离谱的结果。本节讲「为什么代码要这么写」,是从理论走向实践的必修补。


一、浮点误差

计算机用有限位数表示实数,每步运算都有微小舍入误差。单步无所谓,但成千上万步累积、或者遇到「坏」的矩阵时,误差会被放大到头重脚轻。

二、条件数:误差被放大多少倍

第 6 节定义过:κ(A)=σmax/σmin\kappa(\mathbf{A})=\sigma_{\max}/\sigma_{\min}。它是「输入相对误差」被放大到「输出相对误差」的最大倍数:

δxx    κ(A)δbb\frac{\|\delta\mathbf{x}\|}{\|\mathbf{x}\|}\;\lesssim\;\kappa(\mathbf{A})\,\frac{\|\delta\mathbf{b}\|}{\|\mathbf{b}\|}

三、为什么不要显式求逆

这是初学者最常犯的错。inv(A) @ bsolve(A, b) 又慢又不准:

原则:能用 solve / 分解就不要求逆。 理论里写 A1b\mathbf{A}^{-1}\mathbf{b},代码里写 np.linalg.solve(A, b)

四、正则化:给病态问题上保险

XX\mathbf{X}^\top\mathbf{X} 病态或不可逆(特征多重共线性),加一个 λI\lambda\mathbf{I}

β^=(XX+λI)1Xy\hat{\boldsymbol{\beta}}=(\mathbf{X}^\top\mathbf{X}+\lambda\mathbf{I})^{-1}\mathbf{X}^\top\mathbf{y}

这就是岭回归(Ridge)。几何上,加 λI\lambda\mathbf{I} 把所有特征值抬高了 λ,最小特征值不再贴近 0,条件数下降,解变得稳定。L2 正则化控制过拟合的本质,就是这个数值稳定性的改善。

五、代码:看条件数怎么毁掉结果

import numpy as np

# 构造一个病态矩阵(希尔伯特矩阵)
n = 8
H = np.array([[1.0/(i+j+1) for j in range(n)] for i in range(n)])
print("条件数 =", f"{np.linalg.cond(H):.1e}")     # 极大,高度病态

x_true = np.ones(n)
b = H @ x_true
x_solve = np.linalg.solve(H, b)
print("solve 误差 =", np.linalg.norm(x_solve - x_true))

# 加正则化(岭回归)后变稳
lam = 1e-3
x_ridge = np.linalg.solve(H.T@H + lam*np.eye(n), H.T@b)
print("岭回归误差 =", np.linalg.norm(x_ridge - x_true))

# 错误示范:显式求逆(不要学)
x_bad = np.linalg.inv(H) @ b
print("显式求逆误差 =", np.linalg.norm(x_bad - x_true))

六、工程速记(抓住这几条就够用)

  1. 解方程用 solve/分解,不要 inv
  2. 最小二乘用 lstsq 或 QR,不要手搭正规方程(条件数会被平方)。
  3. 担心病态就看 np.linalg.cond;偏大就加正则化。
  4. 对称正定用 Cholesky;要主方向用 SVD/特征分解。

七、动手想一想

  1. 为什么“手搭正规方程 XX\mathbf{X}^\top\mathbf{X}”比 QR 数值差?
  2. 深度网络的权重初始化为什么偏爱正交矩阵?

答案:(1) XX\mathbf{X}^\top\mathbf{X} 的条件数是 X\mathbf{X} 的平方,原本稍差的矩阵会变得更坏;QR 直接处理 X\mathbf{X},不平方条件数。(2) 正交矩阵条件数为 1,反复相乘不放大也不衰减信号,缓解梯度爆炸/消失。