4. 数学预备知识(下)
好,咱们接着聊。上一节我们把向量和矩阵的基本概念捋了一遍,这一节要动真格的了。矩阵运算、特征值、奇异值分解,这三个东西在模型辨识和策略训练里,简直就是家常便饭。我个人习惯,每次拿到一个新系统,第一件事就是看看它的矩阵结构长什么样,心里大概就有数了。
4.1 矩阵运算:不只是加减乘除
矩阵运算看着简单,但坑不少。我刚开始做控制的时候,就吃过亏。
4.1.1 矩阵乘法
矩阵乘法,说白了就是「行乘列」。但要注意,不满足交换律。AB 和 BA 通常不一样,甚至可能一个能乘,另一个根本乘不了。
核心规则:A(m×n) 乘以 B(n×p),结果 C 是 m×p 的。内维度必须相等。
举个例子:
import numpy as np
A = np.array([[1, 2], [3, 4]]) # 2x2
B = np.array([[5, 6], [7, 8]]) # 2x2
C = A @ B # 或者 np.dot(A, B)
print(C)
# 输出: [[19 22]
# [43 50]]
嗯,这里要注意:在 Python 里,@ 是矩阵乘法,* 是逐元素乘法。我见过不少新手在这上面翻车。
4.1.2 矩阵的逆与伪逆
矩阵的逆,只有方阵且满秩的时候才存在。但在实际工程中,我们遇到的矩阵往往不是方阵,或者不满秩。这时候就需要伪逆了。
我的经验:在系统辨识中,我们经常用最小二乘法。这时候伪逆就是救星。比如你有一堆测量数据,想拟合一个模型,直接求逆可能炸了,但伪逆稳稳的。
# 伪逆示例
A = np.array([[1, 2], [3, 4], [5, 6]]) # 3x2 矩阵
A_pinv = np.linalg.pinv(A)
print(A_pinv)
4.1.3 矩阵的迹与范数
迹就是对角线元素之和。范数嘛,可以理解为矩阵的「大小」。常用的有 Frobenius 范数,就是所有元素平方和再开方。
| 范数类型 | 公式 | 用途 |
|---|---|---|
| Frobenius 范数 | ||A||_F = sqrt(Σ a_ij²) | 衡量误差矩阵 |
| 2-范数(谱范数) | ||A||_2 = σ_max(A) | 最大奇异值 |
| 1-范数 | ||A||_1 = max_j Σ_i |a_ij| | 列和最大 |
4.2 特征值与特征向量:系统的「灵魂」
特征值这个东西,你想想看,它描述了一个矩阵在变换时,哪些方向被拉伸了,哪些方向被压缩了。在控制理论里,特征值直接决定了系统的稳定性。
4.2.1 定义与几何意义
对于方阵 A,如果存在非零向量 v 和标量 λ,使得:
A v = λ v
那么 λ 就是特征值,v 就是对应的特征向量。几何上,特征向量方向上的向量,经过 A 变换后,只改变长度,不改变方向。
我曾经踩过的坑:在辨识一个机械臂的动力学模型时,算出来的特征值有正有负。我当时没在意,结果仿真直接炸了。后来才发现,正特征值对应的是不稳定模态。所以,看到特征值有正数,一定要警惕!
4.2.2 特征值分解
如果 A 可以对角化,那么:
A = V Λ V⁻¹
其中 V 的列是特征向量,Λ 是对角矩阵,对角线上是特征值。
import numpy as np
A = np.array([[4, 1], [2, 3]])
eigvals, eigvecs = np.linalg.eig(A)
print("特征值:", eigvals)
print("特征向量:\n", eigvecs)
4.2.3 在策略训练中的应用
在强化学习中,我们经常用特征值来分析值函数的收敛性。比如,贝尔曼算子的谱半径(最大特征值的值)如果小于 1,那迭代就收敛。否则,就发散。
一句话总结:特征值告诉你系统「稳不稳」,特征向量告诉你「往哪稳」。
4.3 奇异值分解(SVD):矩阵的「万能钥匙」
如果说特征值分解只适用于方阵,那 SVD 就是所有矩阵的福音。任何矩阵都能做 SVD,而且它比特征值分解更稳定、更强大。
4.3.1 SVD 的定义
对于任意矩阵 A (m×n),可以分解为:
A = U Σ Vᵀ
其中:
- U 是 m×m 的正交矩阵(左奇异向量)
- Σ 是 m×n 的对角矩阵(奇异值从大到小排列)
- V 是 n×n 的正交矩阵(右奇异向量)
4.3.2 SVD 的几何解释
说白了,SVD 就是把一个线性变换拆成三步:旋转(Vᵀ)、拉伸(Σ)、再旋转(U)。你想想看,任何复杂的矩阵变换,本质上就是这三个动作的组合。
我的习惯:在模型辨识中,我拿到数据矩阵后,第一件事就是做 SVD。看看奇异值的分布,就能判断数据是「有效信息多」还是「噪声多」。如果奇异值衰减很快,说明有效维度很低,可以用低秩近似。
4.3.3 SVD 在策略训练中的实战
在策略梯度方法中,我们经常需要处理高维观测空间。SVD 可以用来做降维,提取主要特征。比如:
# 假设 X 是观测数据矩阵 (样本数 x 特征数)
U, s, Vt = np.linalg.svd(X, full_matrices=False)
# 保留前 k 个奇异值,实现降维
k = 10
X_reduced = U[:, :k] @ np.diag(s[:k])
print("降维后的数据形状:", X_reduced.shape)
4.3.4 奇异值与特征值的关系
这里有个有趣的联系:矩阵 A 的奇异值的平方,等于 AᵀA 的特征值。所以,SVD 某种意义上比特征值分解更「底层」。
| 性质 | 特征值分解 | 奇异值分解 |
|---|---|---|
| 适用矩阵 | 仅方阵 | 任意矩阵 |
| 数值稳定性 | 一般 | 优秀 |
| 物理意义 | 系统模态 | 能量分布 |
| 计算复杂度 | O(n³) | O(mn²) |
4.4 本章知识体系
下面这张图,是我自己梳理的本章核心逻辑。你看一眼,就能明白这些知识点是怎么串起来的。
4.5 实战中的避坑指南
最后,分享几个我这些年积累的经验:
- 数值稳定性:计算特征值时,尽量用
np.linalg.eig而不是手算。手算容易炸。 - 矩阵条件数:条件数太大的矩阵,求逆会很不稳定。SVD 可以帮你诊断这个问题。
- 低秩近似:在策略训练中,如果状态空间很大,试试用 SVD 做低秩近似。我试过,训练速度能快 3-5 倍。
- 不要迷信特征值:特征值只适用于方阵。遇到非方阵,老老实实用 SVD。
我曾经犯过的错:有一次做系统辨识,我直接用特征值分解分析一个非方阵的数据矩阵,结果算出来的东西完全不对。后来才意识到,非方阵要用 SVD。嗯,从那以后,我每次做矩阵分析前,都会先看一眼矩阵的形状。
好了,这一节的内容就到这。矩阵运算、特征值、SVD,这三板斧用好了,后面的模型辨识和策略训练,你会觉得顺手很多。