跳到内容

第 10 卷 / 第 10 章 / 第 2 课

10.2 矩阵、线性变换与方程组:从规则到可解性 ​

上一课把用户、电影和系统状态写成向量。这一课处理另一个问题:怎样用同一套规则批量变换这些向量,以及怎样从观测结果反推出未知参数。

本课目标 ​

  • 用矩阵的形状检查运算是否合法;
  • 从线性变换和函数复合理解矩阵乘法;
  • 用秩判断线性系统是否包含重复或缺失信息;
  • 知道何时求逆可行,何时应直接求解方程。

1. 矩阵是一组列,也是一项变换 ​

一个 m×n 矩阵有 m 行、n 列:

A=[a11⋯a1n⋮⋱⋮am1⋯amn].

它可以从两个角度理解:

  • n 个位于 Rm 中的列向量;
  • 把 Rn 的输入映射到 Rm 的线性变换。

对输入 x,乘积 Ax 是矩阵各列的线性组合,组合系数就是 xi。所以方程 Ax=b 在问:能否用 A 的列向量组合出 b?

2. 矩阵乘法为什么不能随意换序 ​

若 A 是 m×n,B 是 n×p,则 AB 是 m×p:

(AB)ij=∑k=1nAikBkj.

中间维度必须相同,因为 B 的输出要成为 A 的输入。对列向量约定,先做 B、再做 A,复合变换写成:

x↦Bx↦A(Bx)=(AB)x.

一般情况下 AB≠BA,甚至其中一个乘积可能没有定义。图形学里“先旋转再平移”和“先平移再旋转”产生不同结果,根源就在变换复合的顺序。

在原二维空间中,用旋转和非均匀缩放就能直接演示不交换。本课代码使用 Python 3 与 NumPy 2.x:

python
import numpy as np

R = np.array([[0, -1], [1, 0]])
S = np.diag([2, 1])
v = np.array([1, 1])
print(R @ S @ v)  # [-1  2]
print(S @ R @ v)  # [-2  1]

3. 线性变换与坐标系 ​

变换 T 若满足

T(x+y)=T(x)+T(y),T(cx)=cT(x),

就是线性变换。在选定输入、输出空间的基以后,有限维线性变换可以用矩阵表示。

二维旋转矩阵为:

R(θ)=[cos⁡θ−sin⁡θsin⁡θcos⁡θ].
python
import numpy as np

theta = np.pi / 2
rotation = np.array([
    [np.cos(theta), -np.sin(theta)],
    [np.sin(theta),  np.cos(theta)],
])

point = np.array([1.0, 0.0])
print(np.round(rotation @ point, 12))  # [0. 1.]

浮点三角函数的结果通常只是接近零,因此示例使用舍入显示;不能把输出中的极小误差误判为旋转公式错误。

非零平移不满足 T(0)=0,所以它不是普通二维线性变换。图形学常引入齐次坐标,把二维仿射变换写成三维矩阵乘法,这是扩展表示后的统一,而不是平移突然变成了原空间中的线性变换。

4. 秩描述有效方向的数量 ​

矩阵的秩是线性无关列的最大数量,也等于线性无关行的最大数量。它告诉你这项变换保留了多少独立方向。

  • 满列秩意味着列向量彼此独立;
  • 秩小于列数意味着存在冗余参数;
  • 方阵秩不足意味着不可逆;
  • 数据矩阵秩低,可能表示特征高度相关或数据落在低维子空间中。

行列式只为方阵定义。|det(A)| 描述体积缩放倍数,符号还反映定向是否翻转。det(A)=0 等价于方阵奇异,但行列式不是判断大型线性系统数值状况的首选工具;实际计算更关心分解方法、秩和条件数。

精确秩与数值秩要分开。两列非常接近仍可能精确线性无关;浮点库用奇异值阈值判断哪些方向可数值区分,阈值还应考虑测量误差。高度相关并不直接证明精确低秩。参见 NumPy 的数值秩说明。

5. 线性方程不只有“唯一解” ​

对 Ax=b,可能出现:

  1. 唯一解:方阵满秩是常见情形;
  2. 无穷多解:存在自由变量;
  3. 无解:b 不在 A 的列空间中。

设 A 有 n 列。若 rank(A)<rank([A∣b]),则无解;两者相等且为 n 时唯一解;相等但小于 n 时有无穷多解。后一种情形是“先有解,再有自由变量”,单看列冗余不能保证相容。下面两组整数值很小,数值秩足以区分:

python
import numpy as np

A = np.array([[1., 2.], [2., 4.]])
for b in [np.array([3., 6.]), np.array([3., 7.])]:
    print(np.linalg.matrix_rank(A),
          np.linalg.matrix_rank(np.column_stack([A, b])))
# 1 1: infinitely many solutions, x + 2y = 3
# 1 2: no exact solution
python
import numpy as np

A = np.array([[3.0, 1.0], [1.0, 2.0]])
b = np.array([9.0, 8.0])
x = np.linalg.solve(A, b)

print(x)                         # [2. 3.]
print(np.allclose(A @ x, b))       # True

不要为了求解 Ax=b 先显式计算 A−1 再乘 b。solve 通常更高效,也避免额外放大舍入误差。矩阵接近奇异时,即使理论上可逆,结果也可能对输入误差极其敏感;第 12 章会用条件数解释这种现象。

这里的 NumPy solve 要求方阵满秩,奇异或非方阵输入会报 LinAlgError。allclose 只按给定容差核对残差,不能证明解对输入扰动稳定。

6. 形状检查是一种设计工具 ​

假设一批数据 X 有 N 个样本、每个样本 d 个特征。若按“样本为行”存储,则 X 的形状是 N×d。权重向量 w 是 d×1,预测 Xw 才会得到 N×1。

写公式时标注形状,可以在运行代码前发现大量错误:

(N×d)(d×1)=(N×1).

但形状正确不等于语义正确。把“用户×特征”误当成“物品×特征”,矩阵仍可能相乘,却会得到毫无业务意义的结果。

NumPy 的一维数组形状 (d,) 不等于二维列数组 (d,1):前者得到 (N,) 预测,后者得到 (N,1)。若把 (N,) 预测与 (N,1) 标签相减,广播可能生成 (N,N) 矩阵,误差函数也跟着变了。计算前应把两者统一成预期形状。

常见误区 ​

  • 矩阵就是二维数组:数组是存储形式,矩阵还携带变换和空间语义。
  • 乘法顺序只是语法差异:顺序对应函数复合顺序,会改变结果。
  • 不可逆就无解:非方阵系统可能唯一可解;奇异方阵若相容,则有无穷多个解。
  • 求逆是解方程的标准步骤:数值软件应优先直接求解或使用合适分解。

练习 ​

  1. 标出 (3×4)(4×2) 的结果形状,并解释为什么反向乘法未必成立。
  2. 分别计算先缩放再旋转、先旋转再缩放的结果,找一个二者不同的向量。
  3. 构造一个秩为 1 的 2×2 矩阵,说明它把平面压到哪里。
  4. 对一个无解和一个无穷多解的二元方程组写出增广矩阵。

小结 ​

矩阵把输入空间映射到输出空间,矩阵乘法描述变换复合,秩描述独立信息的数量。线性方程的本质不是“套逆矩阵”,而是判断目标是否位于列空间,并用稳定的方法找到解。

下一课面对更真实的数据:观测含噪、方程往往没有精确解。最小二乘、特征向量和 SVD 会把“无法精确满足”改写成“找到最有解释力的近似”。

Built with VitePress | Software Systems Atlas