[paddle] 矩阵的分解
特征值
设
A
A
A 是一个
n
×
n
n \times n
n×n 的方阵,
λ
\lambda
λ 是一个标量,
v
\mathbf{v}
v 是一个非零向量。如果满足以下方程:
A
v
=
λ
v
A\mathbf{v} = \lambda\mathbf{v}
Av=λv
则称
λ
\lambda
λ 为矩阵
A
A
A 的一个 特征值(Eigenvalue),而
v
\mathbf{v}
v 称为对应于 特征值
λ
\lambda
λ 的特征向量(Eigenvector)。 行列式
∣
A
−
λ
I
∣
=
0
|A-\lambda I|=0
∣A−λI∣=0
paddle.linalg.eigvals(x, name=None) 一般方阵
paddle.linalg.eigvalsh(x, name=None) 实对称或厄尔米特矩阵
import paddle
paddle.seed(2023)
x = paddle.rand(shape=[3, 3], dtype='float64')
print(x)
print(paddle.linalg.eigvals(x))
特征分解
如果矩阵
A
A
A 有
n
n
n 个线性独立的特征向量
v
1
,
v
2
,
…
,
v
n
\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_n
v1,v2,…,vn,分别对应于特征值
λ
1
,
λ
2
,
…
,
λ
n
\lambda_1, \lambda_2, \ldots, \lambda_n
λ1,λ2,…,λn,那么矩阵
A
A
A 可以被分解为:
A
=
V
Λ
V
−
1
A = V \Lambda V^{-1}
A=VΛV−1
其中:
- V V V 是一个 n × n n \times n n×n 的矩阵,其列向量是 A A A 的特征向量,即 V = [ v 1 , v 2 , … , v n ] V = [\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_n] V=[v1,v2,…,vn]。
- Λ \Lambda Λ 是一个 n × n n \times n n×n 的对角矩阵,其对角线上的元素是 A A A 的特征值,即 Λ = diag ( λ 1 , λ 2 , … , λ n ) \Lambda = \text{diag}(\lambda_1, \lambda_2, \ldots, \lambda_n) Λ=diag(λ1,λ2,…,λn)。
- V − 1 V^{-1} V−1 是矩阵 V V V 的逆矩阵。
特征分解的性质
- 特征值的唯一性:对于一个给定的矩阵,其特征值是唯一的(考虑重根的情况)。
- 特征向量的线性独立性:不同特征值对应的特征向量是线性独立的。
- 对角化:现实中存在大量方阵不能与对角矩阵相似,(但与其若尔当标准型总相似), 这不妨碍软件求解的特征值和特征向量的正确性。
paddle.linalg.eig(x)
- x为方阵)
paddle.linalg.eigh(x)
- x为实对称矩阵或这复数共轭对称矩阵(Hermit 矩阵)
import paddle
x = paddle.to_tensor([[1.6707249, 7.2249975, 6.5045543],
[9.956216, 8.749598, 6.066444 ],
[4.4251957, 1.7983172, 0.370647 ]])
w, v = paddle.linalg.eig(x)
print(v)
print(w)
# paddle.multi_dot 没有定义复数域矩阵乘法,此处给出作者定义的矩阵乘法进行结果检验
def mat_dot(List):
A=List[0]
B=List[1]
sizeA=A.shape
sizeB=B.shape
if len(sizeA)==1 and len(sizeB)==1:
if sizeA[0]==sizeB[0]:
C= addle.sum(A*B)
else:
print("矩阵乘法维数不匹配")
elif len(sizeA)==1 and len(sizeB)==2:
if sizeA[0]==sizeB[1]:
C=paddle.zeros([sizeB[1]])
for j in range(sizeB[1]):
C[j]= addle.sum(A*B[:,j])
else:
print("矩阵乘法维数不匹配")
elif len(sizeA)==2 and len(sizeB)==1:
C=paddle.zeros([sizeA[0]])
if sizeA[1]==sizeB[0]:
for i in range(sizeA[0]):
C[i]=paddle.sum(A[i,:]*B)
elif len(sizeA)==2 and len(sizeB)==2:
C=paddle.zeros([sizeA[0],sizeB[1]])
if sizeA[1]==sizeB[0]:
for i in range(sizeA[0]):
for j in range(sizeB[1]):
C[i]=paddle.sum(A[i,:]*B[:,j])
return(C)
# 特征向量与特征值的检验比较 Aν v.s. λν
for i in range(3):
print(mat_dot([x,v[:,i]]))
print(w[i]*v[:,i])
LU 分解
X = L U X=LU X=LU, 其中 L L L 为下三角矩阵, U U U 为上三角矩阵,常用与求解线性方程组的解。
paddle.linalg.lu(x, pivot=True, get_infos=False, name=None)
- x (Tensor) - 需要进行 LU 分解的输入矩阵 x,x 是维度大于 2 维的矩阵。
- pivot (bool,可选) - LU 分解时是否进行旋转。若为 True 则执行旋转操作,若为 False 则不执行旋转操作,该选项只在 gpu 下起作用,cpu 下暂不支持为 False,会报错。默认 True。
- get_infos (bool,可选) - 是否返回分解状态信息,若为 True,则返回分解状态 Tensor,否则不返回。默认 False。
- name (str,可选) - 具体用法请参见 Name,一般无需设置,默认值为 None。
import paddle
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]).astype('float64')
lu,p,info = paddle.linalg.lu(x, get_infos=True)
print(lu)
print(p)
print(info)
P,L,U = paddle.linalg.lu_unpack(lu,p)
print(P)
print(L)
print(U)
# one can verify : X = P @ L @ U ;
Householder 乘积
Householder变换的乘积
Householder变换可以通过一个向量来定义。设
w
\mathbf{w}
w 是一个单位向量,即
w
⊤
w
=
1
\mathbf{w}^\top \mathbf{w} = 1
w⊤w=1,则Householder变换矩阵
H
\mathbf{H}
H 可以表示为:
H
=
I
−
2
w
w
⊤
\mathbf{H} = \mathbf{I} - 2\mathbf{w}\mathbf{w}^\top
H=I−2ww⊤其中
I
\mathbf{I}
I 是单位矩阵。
Householder变换的乘积
当多个Householder变换矩阵相乘时,其结果仍然是一个Householder变换矩阵。具体来说,如果 H 1 , H 2 , … , H k \mathbf{H}_1, \mathbf{H}_2, \ldots, \mathbf{H}_k H1,H2,…,Hk 都是Householder变换矩阵,那么它们的乘积 H = H 1 H 2 … H k \mathbf{H} = \mathbf{H}_1\mathbf{H}_2 \ldots \mathbf{H}_k H=H1H2…Hk 也是一个Householder变换矩阵。常用于QR分解。
乘积的性质
- 正交性:每个Householder变换矩阵都是正交矩阵,即 H ⊤ = H − 1 \mathbf{H}^\top = \mathbf{H}^{-1} H⊤=H−1。因此,它们的乘积也是正交矩阵。
- 行列式:Householder变换矩阵的行列式为 − 1 -1 −1 或 1 1 1。因此,多个Householder变换矩阵相乘后,其行列式为 ( − 1 ) k (-1)^k (−1)k 或 1 1 1,其中 k k k 是变换的个数。
- 反射性:Householder变换矩阵的乘积仍然具有反射性,即它可以将一个向量反射到另一个向量上。
paddle.linalg.householder_product(x, tau, name=None)
import paddle
x = paddle.to_tensor([[-1.1280, 0.9012, -0.0190],
[ 0.3699, 2.2133, -1.4792],
[ 0.0308, 0.3361, -3.1761],
[-0.0726, 0.8245, -0.3812]])
tau = paddle.to_tensor([1.7497, 1.1156, 1.7462])
Q = paddle.linalg.householder_product(x, tau)
print(Q)
QR 分解
计算一个或一批矩阵的正交三角分解,也称 QR 分解(暂不支持反向)。
记 X = Q R X=QR X=QR, Q Q Q是正交矩阵 Q Q ⊤ = I QQ^\top =I QQ⊤=I, R R R 是上三角矩阵。
paddle.linalg.qr(x, mode=‘reduced’, name=None)
- Tensor Q,正交三角分解的 Q 正交矩阵,需注意如果 mode = “reduced”,则不返回 Q 矩阵,只返回 R 矩阵。
- Tensor R,正交三角分解的 R 上三角矩阵。
import paddle
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]).astype('float64')
q, r = paddle.linalg.qr(x)
print (q)
print (r)
# one can verify : X = Q * R ;
Cholesky 分解
Cholesky分解是用于将一个正定矩阵分解为一个下三角矩阵和其转置的乘积的方法。设
A
\mathbf{A}
A 是一个
n
×
n
n \times n
n×n 的正定矩阵,则存在一个下三角矩阵
L
\mathbf{L}
L,使得:
A
=
L
L
⊤
\mathbf{A} = \mathbf{L}\mathbf{L}^\top
A=LL⊤其中
L
⊤
\mathbf{L}^\top
L⊤表示
L
\mathbf{L}
L 的转置矩阵。
Cholesky分解的性质
- 唯一性:对于给定的正定矩阵 A \mathbf{A} A,其Cholesky分解是唯一的。
- 正定性:只有正定矩阵才能进行Cholesky分解。
- 下三角性:分解得到的矩阵 L \mathbf{L} L 是一个下三角矩阵,即其上三角部分的元素均为零。
Cholesky分解的算法
Cholesky分解的算法通常通过迭代计算下三角矩阵
L
\mathbf{L}
L 的各个元素。对于
i
≤
j
i \leq j
i≤j,有:
l
i
j
=
1
l
i
i
(
a
i
j
−
∑
k
=
1
i
−
1
l
i
k
l
j
k
)
l_{ij} = \frac{1}{l_{ii}} \left( a_{ij} - \sum_{k=1}^{i-1} l_{ik} l_{jk} \right)
lij=lii1(aij−k=1∑i−1likljk)其中
l
i
j
l_{ij}
lij是矩阵
L
\mathbf{L}
L的元素,
a
i
j
a_{ij}
aij是矩阵
A
\mathbf{A}
A 的元素。
paddle.linalg.cholesky(x, upper=False, name=None)
- x (Tensor)- 输入变量为多维 Tensor,它的维度应该为 [, M, N],其中为零或更大的批次尺寸,并且最里面的两个维度上的矩阵都应为对称的正定矩阵,支持数据类型为 float32、float64。
- upper (bool)- 指示是否返回上三角矩阵或下三角矩阵。默认值:False。
import paddle
paddle.seed(2023)
a = paddle.rand([3, 3], dtype="float32")
a_t = paddle.transpose(a, [1, 0])
x = paddle.matmul(a, a_t) + 1e-03
out = paddle.linalg.cholesky(x, upper=False)
print(out)
SVD 分解
SVD分解的定义
SVD(Singular Value Decomposition,奇异值分解)是线性代数中的一种矩阵分解方法,它将一个矩阵分解为三个矩阵的乘积:一个正交矩阵、一个对角矩阵和一个正交矩阵的转置。具体来说,对于任意一个
m
×
n
m \times n
m×n的矩阵
A
A
A,都可以表示为:
A
=
U
Σ
V
T
A = UΣV^T
A=UΣVT
其中:
- U U U是一个 m × m m \times m m×m的正交矩阵,其列向量称为左奇异向量。
- Σ Σ Σ是一个 m × n m \times n m×n的对角矩阵,对角线上的元素称为奇异值,按照从大到小的顺序排列。
- V V V是一个 n × n n \times n n×n的正交矩阵,其列向量称为右奇异向量。
SVD分解的性质
- 奇异值是非负的,并且按照从大到小的顺序排列。
- 左奇异向量和右奇异向量是正交的。
- 原矩阵 A A A的秩等于其非零奇异值的个数。
- SVD分解是唯一的,只要奇异值不重复。
SVD分解的几何意义
SVD分解可以看作是将矩阵 A A A的作用分解为三个步骤:
- 通过矩阵 V V V将原始空间旋转到一个新的坐标系。
- 通过对角矩阵 Σ Σ Σ进行各个方向的缩放。
- 通过矩阵
U
U
U将缩放后的空间旋转到最终的坐标系。
这种分解方式揭示了矩阵 A A A在几何上的本质作用,即旋转、缩放和再旋转。
paddle.linalg.svd(x, full_matrices=False, name=None)
- x (Tensor) - 输入的欲进行奇异值分解的一个或一批方阵,类型为 Tensor。 x 的形状应为 [*, M, N],其中 * 为零或更大的批次维度,数据类型支持 float32, float64。
- full_matrices (bool) - 是否计算完整的 U 和 V 矩阵,类型为 bool 默认为 False。这个参数会影响 U 和 V 生成的 Shape。
返回
- Tensor U,奇异值分解的 U 矩阵。如果 full_matrics 设置为 False,则 Shape 为 [ ∗ , M , K ] [*, M, K] [∗,M,K],如果 full_metrices 设置为 True,那么 Shape 为 [ ∗ , M , M ] [*, M, M] [∗,M,M]。其中 K 为 M 和 N 的最小值。
- Tensor S,奇异值向量,Shape 为 [ ∗ , K ] [*, K] [∗,K] 。
- Tensor VH,奇异值分解的 VH 矩阵。如果 full_matrics 设置为 False,则 Shape 为 [ ∗ , K , N ] [*, K, N] [∗,K,N],如果 full_metrices 设置为 True,那么 Shape 为 [ ∗ , N , N ] [*, N, N] [∗,N,N]。其中 K 为 M 和 N 的最小值。
import paddle
x = paddle.to_tensor([[1.0, 2.0], [1.0, 3.0], [4.0, 6.0]]).astype('float64')
x = x.reshape([3, 2])
u, s, vh = paddle.linalg.svd(x)
print (u)
print (s)
print (vh)
低秩矩阵的SVD分解
paddle.linalg.svd_lowrank(x, q=None, niter=2, M=None, name=None)
- x (Tensor) - 输入的需要进行奇异值分解的一个或一批方阵,类型为 Tensor。 x 的形状应为 [*, M, N],其中 * 为零或更大的批次维度,数据类型支持 float32, float64。
- q (int,可选) - 对输入 X
的秩稍微高估的预估值,默认值为 None,代表预估值取 6。- niter (int) - 需要进行的子空间迭代次数。默认值为 2。
- M (Tensor) - 输入矩阵在 axis=-2 维上的均值,形状应为 [*, 1, N],默认为 None。
import paddle
paddle.seed(2024)
x = paddle.randn((5, 5), dtype='float64')
U, S, V = paddle.linalg.svd_lowrank(x)
print(U)
print(S)
print(V)