1. 特征值分解(EVD)
如果 A A A 是一个 m × m m\times m m×m 的 实对称矩阵( A = A T A=A^T A=AT) ,如果存在 m m m 维列向量 q q q 和实数 λ \lambda λ 满足 A q = λ q Aq=\lambda q Aq=λq,则称 q q q 为 A A A 的 特征向量、 λ \lambda λ 为 A A A 的 特征值。
如果我们求出
A
A
A 的
m
m
m 个特征值与特征向量,并将所有特征向量标准化,则可以得到标准正交矩阵
Q
=
[
q
1
,
q
2
,
⋯
,
q
m
]
,
Q
∈
R
m
×
m
Q=[q_1, q_2, \cdots, q_m], Q\in \mathbb{R}^{m\times m}
Q=[q1,q2,⋯,qm],Q∈Rm×m,与对角矩阵
Σ
=
d
i
a
g
[
λ
1
,
λ
2
,
⋯
,
λ
m
]
\Sigma = diag[\lambda_1, \lambda_2, \cdots, \lambda_m]
Σ=diag[λ1,λ2,⋯,λm],他们满足如下形式:
A
=
Q
Σ
Q
T
=
Q
[
λ
1
⋯
⋯
⋯
⋯
λ
2
⋯
⋯
⋯
⋯
⋱
⋯
⋯
⋯
⋯
λ
m
]
Q
T
A = Q\Sigma Q^T= Q\left[ \begin{matrix} \lambda_1 & \cdots & \cdots & \cdots\\ \cdots & \lambda_2 & \cdots & \cdots\\ \cdots & \cdots & \ddots & \cdots\\ \cdots & \cdots & \cdots & \lambda_m\\ \end{matrix} \right]Q^T
A=QΣQT=Q⎣⎢⎢⎡λ1⋯⋯⋯⋯λ2⋯⋯⋯⋯⋱⋯⋯⋯⋯λm⎦⎥⎥⎤QT
numpy 中对一个矩阵进行特征值分解使用 np.linalg.eig
函数,从下例可以看出,该函数求解得到的特征值大小是随机排列的,如果需要前
n
n
n 个大的特征值与特征向量,需要进一步进行处理:
#创建一个对称矩阵
sym_matrix = np.array([[1,2,3,4],
[2,5,6,7],
[3,6,8,9],
[4,7,9,10]])
#对矩阵求解特征值(se)与特征向量(q)
se, q = np.linalg.eig(sym_matrix)
print('se:')
print(se)
print('q:')
print(q)
"""
se:
[24.06253512 -0.80548492 0.5580362 0.18491359]
q:
[[-0.22593827 -0.72531367 -0.56047033 -0.32976505]
[-0.44322186 -0.31846973 0.77633831 -0.3153256 ]
[-0.57278788 -0.14246073 -0.05844028 0.805111 ]
[-0.6514755 0.59346614 -0.28241204 -0.37897369]]
"""
在特征值分解中,对要分解的矩阵有着较高的要求,它需要被分解的矩阵 A A A 为实对称矩阵,但是现实中我们遇到的矩阵并不都是实对称的,对于一个 m × n m\times n m×n 的矩阵,如果我们想分解为如上的形式,我们需要使用 SVD分解。
2. 奇异值分解(SVD)
对于一个
m
×
n
m\times n
m×n 的实数矩阵
A
A
A,我们定义其奇异值分解(SVD)的形式为:
A
=
U
Σ
V
T
A=U\Sigma V^T
A=UΣVT
其中
U
∈
R
m
×
m
U\in \mathbb{R}^{m\times m}
U∈Rm×m 和
V
∈
R
n
×
n
V\in \mathbb{R}^{n\times n}
V∈Rn×n 为单位正交矩阵,即有
U
U
T
=
I
UU^T=I
UUT=I 和
V
V
T
=
I
VV^T=I
VVT=I,
U
U
U 为左奇异矩阵,
V
V
V 为右奇异矩阵,
Σ
∈
R
m
×
n
\Sigma \in \mathbb{R}^{m\times n}
Σ∈Rm×n 仅在主对角线上有值,我们称它们为奇异值。分解示意图为:
奇异值分解的求解通过下式:
A
A
T
=
U
Σ
V
T
V
Σ
T
U
T
=
U
Σ
Σ
T
U
T
AA^T =U\Sigma V^TV\Sigma^TU^T =U\Sigma \Sigma^TU^T
AAT=UΣVTVΣTUT=UΣΣTUT
A T A = V Σ T U T U Σ V T = V Σ T Σ V T A^TA=V\Sigma^TU^TU\Sigma V^T=V\Sigma^T\Sigma V^T ATA=VΣTUTUΣVT=VΣTΣVT
因为 A A T AA^T AAT 和 A A T AA^T AAT 是对称矩阵,则通过对上面两个等式求它们的特征值和特征向量,就可以求出 U , V , Σ U,V,\Sigma U,V,Σ 。
我们可以使用 numpy 中的 np.linalg.svd
函数对矩阵求解 SVD :
#创建一个5×3的矩阵
T = np.array([[16,25,35],
[45,23,6],
[7,12,9],
[10,18,12],
[15,14,15]])
#进行SVD分解
u,ss,vh = np.linalg.svd(T, full_matrices=False)
print('u:')
print(u)
print('ss:')
print(ss)
print('vh')
print(vh)
#输出 u * ss * vh
print('u*ss*vh:')
print(u.dot(np.diag(ss)).dot(vh))
"""
u:
[[-0.57781685 -0.62450998 0.36719022]
[-0.63112018 0.74837707 0.09682015]
[-0.21931497 -0.12213601 -0.43502282]
[-0.31447256 -0.15727956 -0.7906354 ]
[-0.34759596 -0.10131625 0.20358785]]
ss:
[72.50580717 29.74834802 6.47639687]
vh
[[-0.65566262 -0.58091643 -0.48233041]
[ 0.66347606 -0.13833268 -0.73529829]
[ 0.3604248 -0.80212229 0.47612372]]
u*ss*vh:
[[16. 25. 35.]
[45. 23. 6.]
[ 7. 12. 9.]
[10. 18. 12.]
[15. 14. 15.]]
"""
3. QR 分解
一个矩阵 A ∈ R m × n ( m ≤ n ) A\in \mathbb{R}^{m\times n}(m \leq n) A∈Rm×n(m≤n) 可以被分解为 A = Q R A=QR A=QR 的形式,其中:
- Q ∈ R m × m Q\in \mathbb{R}^{m\times m} Q∈Rm×m 是一个正交矩阵,即 Q T Q = Q Q T = I Q^TQ=QQ^T=I QTQ=QQT=I
- R = [ R ^ 0 ] ∈ R m × n R=\left[\begin{matrix} \hat{R} \\ 0 \end{matrix}\right] \in \mathbb{R}^{m\times n} R=[R^0]∈Rm×n, R ^ ∈ R m × m \hat{R} \in \mathbb{R}^{m\times m} R^∈Rm×m是上三角矩阵
numpy 中,进行 QR 分解使用 np.linalg.qr
函数:
A = np.array([[16,25,35],
[45,23,6],
[7,12,9],
[10,18,12],
[15,14,15]])
q,r = np.linalg.qr(A)
print('q:')
print(q)
print('r:')
print(r)
print('q*r')
print(q.dot(r))
"""
q:
[[-0.31051867 0.63947628 0.59444254]
[-0.87333376 -0.44331977 -0.09225387]
[-0.13585192 0.33011618 -0.35331103]
[-0.19407417 0.51220924 -0.66985902]
[-0.29111125 0.15232425 0.25414072]]
r:
[[-51.52669211 -37.04875904 -24.02638224]
[ 0. 21.10425203 31.12417123]
[ 0. 0. 12.84596908]]
q*r
[[16. 25. 35.]
[45. 23. 6.]
[ 7. 12. 9.]
[10. 18. 12.]
[15. 14. 15.]]
"""
4. Lanczos 方法
Lanczos 方法是求解大规模稀疏矩阵特征值问题最常用的方法之一,其主要思想是将对称矩阵三对角化,通过对三对角矩阵进行特征分解,从而得到原对称矩阵的特征值与特征向量。
由于三对角矩阵是稀疏矩阵,因此对其进行特征值求解的时间复杂度相对是很低的。
4.1 Lanczos 方法思路
设矩阵 A ∈ R n × n A \in \mathbb{R}^{n\times n} A∈Rn×n,如果 A = A T A=A^T A=AT,那么一定存在 正交矩阵 Q n Q_n Qn ,使得 T = Q n T A Q n \boldsymbol{T=Q_n^TAQ_n} T=QnTAQn,其中 T T T 是对称三对角矩阵。
可以将以上分解中的
Q
Q
Q 写为:
Q
=
[
q
1
,
q
2
,
⋯
,
q
n
]
Q=[q_1, q_2,\cdots,q_n]
Q=[q1,q2,⋯,qn]
其中 q i q_i qi 为 Q Q Q 的列向量。
将
T
T
T 记为:
T
=
[
α
1
β
1
0
0
0
0
⋯
0
0
β
1
α
2
β
2
0
0
0
⋯
0
0
0
β
2
α
3
β
3
0
0
⋯
0
0
0
0
β
3
α
4
β
4
0
⋯
0
0
0
0
0
β
4
α
5
β
5
⋯
0
0
⋮
⋮
⋮
⋮
⋮
⋮
⋱
⋮
⋮
0
0
0
0
0
0
⋯
α
n
−
1
β
n
−
1
0
0
0
0
0
0
⋯
β
n
−
1
α
n
]
T= \left[ \begin{matrix} \alpha_1 & \beta_1 & 0 & 0 & 0& 0& \cdots & 0 & 0\\ \beta_1 & \alpha_2 & \beta_2 & 0 & 0& 0& \cdots & 0 & 0\\ 0 & \beta_2 &\alpha_3 &\beta_3 & 0& 0 & \cdots & 0 & 0\\ 0 & 0 & \beta_3 &\alpha_4 &\beta_4 & 0& \cdots & 0 & 0\\ 0 & 0 & 0 & \beta_4 &\alpha_5 &\beta_5 & \cdots & 0 & 0\\ \vdots & \vdots &\vdots &\vdots &\vdots &\vdots &\ddots &\vdots &\vdots\\ 0 & 0 & 0 & 0 & 0 & 0 & \cdots & \alpha_{n-1} & \beta_{n-1}\\ 0 & 0 & 0 & 0 & 0 & 0 & \cdots & \beta_{n-1} & \alpha_n \end{matrix} \right]
T=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡α1β1000⋮00β1α2β200⋮000β2α3β30⋮0000β3α4β4⋮00000β4α5⋮000000β5⋮00⋯⋯⋯⋯⋯⋱⋯⋯00000⋮αn−1βn−100000⋮βn−1αn⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
从上面我们可以知道,对于一个矩阵 A A A ,我们如果想要求其三对角矩阵,要求的是 T T T 中的 α 、 β \alpha、\beta α、β 参数,以及 Q Q Q 矩阵,因此通过如下的变形,我们求其中的变量。
由于
Q
Q
Q 是正交的,我们可以将求解三对角矩阵的式子写为:
A
Q
=
Q
T
AQ=QT
AQ=QT
对左边的式子,我们取
Q
Q
Q 的第一列与
A
A
A 作乘积,得到左边结果的第一列为
A
q
1
Aq_1
Aq1。同时我们取右式
T
T
T 的第一列与
Q
Q
Q 作乘积,得到
α
1
q
1
+
β
1
q
2
\alpha_1q_1 + \beta_1q_2
α1q1+β1q2 ,即
A
q
1
=
α
1
q
1
+
β
1
q
2
Aq_1=\alpha_1q_1 + \beta_1q_2
Aq1=α1q1+β1q2
同理,有:
A
q
2
=
β
1
q
1
+
α
2
q
2
+
β
2
q
3
Aq_2 = \beta_1q_1 + \alpha_2q_2 + \beta_2q_3
Aq2=β1q1+α2q2+β2q3
A q 3 = β 2 q 2 + α 3 q 3 + β 3 q 4 Aq_3 = \beta_2q_2 + \alpha_3q_3 + \beta_3q_4 Aq3=β2q2+α3q3+β3q4
由此可以得出:
A
q
i
=
β
i
−
1
q
i
−
1
+
α
i
q
i
+
β
i
q
i
+
1
,
i
=
1
,
2
,
3
,
⋯
,
n
Aq_i = \beta_{i-1}q_{i-1} + \alpha_iq_i + \beta_iq_{i+1},i=1,2,3,\cdots,n
Aqi=βi−1qi−1+αiqi+βiqi+1,i=1,2,3,⋯,n
由于 β 0 , β n , q 0 , q n + 1 \beta_0, \beta_n, q_0, q_{n+1} β0,βn,q0,qn+1 都是不存在的,所以我们令 β 0 q 0 = β n q n + 1 = 0 \beta_0q_0 = \beta_nq_{n+1} = 0 β0q0=βnqn+1=0。
我们将上式两边同时左乘以
q
i
T
q_i^T
qiT,由于
Q
Q
Q 是正交的,所以其列向量互相正交,且
q
i
T
q
i
=
1
q_i^Tq_i=1
qiTqi=1,我们可以得到:
q
i
T
A
q
i
=
α
i
q_i^T A q_i = \alpha_i
qiTAqi=αi
通过上式可以看出,如果我们知道了 q i q_i qi ,就能求出 α i \alpha_i αi,这里就是求解 α \boldsymbol{\alpha} α 的方法。
我们再对等式两边移项可以得到:
β
i
q
i
+
1
=
A
q
i
−
β
i
−
1
q
i
−
1
−
α
i
q
i
\beta_i q_{i+1} = Aq_i - \beta_{i-1}q_{i-1} - \alpha_iq_i
βiqi+1=Aqi−βi−1qi−1−αiqi
上式左侧是一个向量,由于
Q
Q
Q 是正交矩阵,所以
q
i
+
1
q_{i+1}
qi+1 是一个归一化的单位向量,我们令
r
i
=
β
i
q
i
+
1
=
A
q
i
−
β
i
−
1
q
i
−
1
−
α
i
q
i
r_i=\beta_i q_{i+1} = Aq_i - \beta_{i-1}q_{i-1} - \alpha_iq_i
ri=βiqi+1=Aqi−βi−1qi−1−αiqi
有 β i = ∥ β i q i + 1 ∥ 2 \beta_i = \|\beta_i q_{i+1}\|_2 βi=∥βiqi+1∥2 ,即
β i = ∥ r i ∥ 2 = ∥ β i q i + 1 ∥ 2 = ∥ A q i − β i − 1 q i − 1 − α i q i ∥ 2 \beta_i = \|r_i\|_2=\|\beta_i q_{i+1}\|_2 = \|Aq_i - \beta_{i-1}q_{i-1} - \alpha_iq_i\|_2 βi=∥ri∥2=∥βiqi+1∥2=∥Aqi−βi−1qi−1−αiqi∥2
从上式可以看出,如果我们知道了 α i 、 q i 、 β i − 1 、 q i − 1 \alpha_i、q_i、\beta_{i-1}、q_{i-1} αi、qi、βi−1、qi−1 就能求出 β i \beta_i βi,又因为 q q q 都是单位向量,所以我们还能通过 ∥ β i q i + 1 ∥ 2 \|\beta_i q_{i+1}\|_2 ∥βiqi+1∥2求出 q i + 1 q_{i+1} qi+1,这样就能循环求解了。
我们从初始条件看,如果我们知道了 q 1 q_1 q1 的值,就能根据上面求 α \boldsymbol{\alpha} α 的方法求出 α 1 \alpha_1 α1,因为我们假设 β 0 q 0 = 0 \beta_0q_0=0 β0q0=0,所以我们就能通过 β 0 q 0 、 α 1 、 q 1 \beta_0q_0、\alpha_1、q_1 β0q0、α1、q1 求出 β 1 \beta_1 β1,再通过令 q i + 1 = r i ∥ r i ∥ 2 q_{i+1}=\frac{r_i}{\|r_i\|_2} qi+1=∥ri∥2ri,这时 q i + 1 q_{i+1} qi+1 就是一个单位向量,我们就得到了下一轮循环的 q q q。因此所有的重点就在 q 1 q_1 q1 ,在实际求解中,我们可以随机初始化一个单位向量作为 q 1 q_1 q1,由此我们可以得到计算 A A A 的三对角矩阵的流程:
- 随机初始化一个单位向量 q 1 q_1 q1
- 计算 α 1 = q 1 T A q 1 \alpha_1=q_1^TAq_1 α1=q1TAq1
- r 1 = A q 1 − α 1 q 1 − β 0 q 0 r_1 = Aq_1-\alpha_1q_1-\beta_0q_0 r1=Aq1−α1q1−β0q0
- β 1 = ∥ r 1 ∥ 2 \beta_1 = \|r_1\|_2 β1=∥r1∥2
- q 2 = r 1 β 1 q_2=\frac{r_1}{\beta_1} q2=β1r1
- 重复以上步骤,依次求出 { q 1 , q 2 , ⋯ , q n } \{q_1,q_2,\cdots,q_n\} {q1,q2,⋯,qn}、 α 1 , β 1 , α 2 , β 2 , ⋯ , α n \alpha_1, \beta_1, \alpha_2, \beta_2, \cdots, \alpha_n α1,β1,α2,β2,⋯,αn
此即 Lanczos 迭代,其中 q i q_i qi 为 Lanczos 向量,若其中某一步 β i = 0 \beta_i=0 βi=0 ,则此时得到的 T j T_j Tj 的特征值都将是 A A A 的特征值。
到这里,我们就得到了
T
=
Q
T
A
Q
T=Q^TAQ
T=QTAQ,如果此时对
T
T
T 进行特征分解得到
T
=
W
S
W
T
T=WSW^T
T=WSWT,所以有
A
=
(
Q
W
)
S
(
W
T
Q
T
)
A=(QW)S(W^TQ^T)
A=(QW)S(WTQT)
我们得到了通过将矩阵 A A A 变换为三对角矩阵,从而求解特征值与特征向量的方法。将矩阵转换为三对角矩阵的 python 实现为:
#生成一个对称矩阵
sym_matrix = np.array([[1,2,3,4],
[2,5,6,7],
[3,6,8,9],
[4,7,9,10]])
Q = []
A = []
B = []
sum_dim = sym_matrix.shape[0]
#随机初始化q1
q1 = np.random.random((sum_dim,1))
q1 = q1/np.linalg.norm(q1)
Q.append(q1)
#计算a0
a0 = (q1.transpose()[0]).dot(sym_matrix).dot(q1)[0]
A.append(a0)
#计算r0
r = sym_matrix.dot(q1) - a0*q1
#计算b0
b0 = np.linalg.norm(r)
B.append(b0)
#循环迭代,得到A、B和Q
for i in range(1,sum_dim):
qi = r/B[i-1]
Q.append(qi)
ai = qi.transpose()[0].dot(sym_matrix).dot(qi)[0]
A.append(ai)
r = sym_matrix.dot(qi) - ai*qi - B[i-1]*Q[i-1]
if i < sum_dim - 1:
b_next = np.linalg.norm(r)
B.append(b_next)
#组成三对角矩阵
L = np.diag(A) + np.diag(B,1) + np.diag(B,-1)
print(L)
"""
[[10.99996429 6.7727395 0. 0. 0. ]
[ 6.7727395 11.4584147 5.03926679 0. 0. ]
[ 0. 5.03926679 -6.36314172 4.98605503 0. ]
[ 0. 0. 4.98605503 1.00512957 1.8902924 ]
[ 0. 0. 0. 1.8902924 0.89963316]]
"""
4.2 三对角矩阵的特征值分解
三对角化后的矩阵 T T T 有三种常用的方法进行特征分解:
- QR 法,即对 T T T 进行 QR 分解,令 T = Q R T=QR T=QR,再令 T ′ = R Q T'=RQ T′=RQ,如此反复,直至收敛。其中产生的 Q Q Q 矩阵的连乘得到的结果就是 T T T 的特征矩阵。
- 分治法。这个方法将 T T T 划均匀分成两块三对角阵和一个秩为 1 的 2 × 2 2\times 2 2×2 的块状方阵的和。分别对两个三对角阵分解,然后合并。合并时对特征向量作秩1的修正。
- MRRR 方法。这个方法理论上是最快的方式,复杂度为 O ( l 2 ) O(l^2) O(l2),主要采用逆迭代来加快收敛。
4.3 利用 QR 算法求解特征值
给定一个实对称矩阵 X X X,假设其通过特征值分解得到 X = P S P T X=PSP^T X=PSPT,其中 P P P 为正交阵, S S S 是对角阵,利用 QR 算法求解其特征值分解的算法为:
X 1 = X X_1=X X1=X
P = E P=E P=E
f o r k = 1 , 2 , ⋯ for \ k=1,2,\cdots for k=1,2,⋯
X k = Q k × R k \quad X_k=Q_k\times R_k Xk=Qk×Rk
X k + 1 = R k × Q k \quad X_{k+1}=R_k\times Q_k Xk+1=Rk×Qk
P = P × Q k \quad P=P\times Q_k P=P×Qk
这个算法很简单,每次迭代只需要做一次 QR 分解和一个矩阵乘法即可,用 python 实现为:
#创建对称矩阵
sym_matrix = np.array([[1,2,3,4],
[2,5,6,7],
[3,6,8,9],
[4,7,9,10]])
dim = sym_matrix.shape
#初始化P为单位矩阵
P = np.eye(dim[0])
X_n = sym_matrix
i = 1
while i<20: #迭代次数可以根据需要改变
i += 1
(Q, R) = np.linalg.qr(X_n) #QR分解
X_n = R.dot(Q)
P = P.dot(Q)
print("QR分解求解特征矩阵")
print(P)
se, q = np.linalg.eig(sym_matrix)
print("特征值函数求解特征矩阵:")
print(q)
"""
QR分解求解特征矩阵
[[-0.22593827 -0.72442125 -0.56162332 -0.32976505]
[-0.44322186 -0.31970419 0.77583077 -0.3153256 ]
[-0.57278788 -0.1423676 -0.05866681 0.805111 ]
[-0.6514755 0.5939146 -0.28146771 -0.37897369]]
特征值函数求解特征矩阵:
[[-0.22593827 -0.72531367 -0.56047033 -0.32976505]
[-0.44322186 -0.31846973 0.77633831 -0.3153256 ]
[-0.57278788 -0.14246073 -0.05844028 0.805111 ]
[-0.6514755 0.59346614 -0.28241204 -0.37897369]]
"""