奇异值分解SVD
0 什么是奇异值分解
奇异值分解(Singular-Value Decomposition,简称SVD)是一种矩阵分解算法,用于将矩阵简化为其组成部分,以使某些后续矩阵计算更简单。它和特征分解不同,SVD并不要求要分解的矩阵为方阵。那为什么将矩阵进行分解呢?先看以下几个例子:
例子1.以下是一个所有元素值都相等的矩阵
A
=
(
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
)
A=\begin{pmatrix} 1 &1 &1 &1 &1 &1 \\ 1 &1 &1 &1 &1 & 1\\ 1 &1 &1 &1 &1 &1 \\ 1 &1 &1 &1 &1 &1 \\ 1 &1 &1 &1 &1 &1 \\ 1 &1 &1 &1 &1 &1 \end{pmatrix}
A=
111111111111111111111111111111111111
如果要保存这个矩阵,通常可以压缩这个矩阵,压缩的方法有很多,此时如果用向量积的方式实现,如下所示:
A
=
(
1
1
1
1
1
1
)
(
1
1
1
1
1
1
)
A=\begin{pmatrix} 1\\ 1\\ 1\\ 1\\ 1\\ 1 \end{pmatrix}\begin{pmatrix} 1 &1 &1 &1 &1 &1 \end{pmatrix}
A=
111111
(111111)
此时只需保存12个元素,而不是36个元素
例子2.意大利国旗
意大利国旗可以写成以下矩阵形式:
A
=
(
g
g
w
w
r
r
g
g
w
w
r
r
g
g
w
w
r
r
g
g
w
w
r
r
g
g
w
w
r
r
g
g
w
w
r
r
)
A=\begin{pmatrix} g &g &w &w &r &r \\ g &g &w &w &r &r \\ g &g &w &w &r &r \\ g &g &w &w &r &r \\ g &g &w &w &r &r \\ g &g &w &w &r &r \end{pmatrix}
A=
ggggggggggggwwwwwwwwwwwwrrrrrrrrrrrr
同样的,也能用一种非常好的方式来压缩它,如下:
A
=
(
1
1
1
1
1
1
)
(
g
g
w
w
r
r
)
A=\begin{pmatrix} 1\\ 1\\ 1\\ 1\\ 1\\ 1 \end{pmatrix}\begin{pmatrix} g & g & w &w &r &r \end{pmatrix}
A=
111111
(ggwwrr)
例子3:现有如下矩阵A
A
=
(
1
0
1
1
)
A=\begin{pmatrix} 1 &0 \\ 1 &1 \end{pmatrix}
A=(1101)
分解这样的矩阵可以考虑用以上的方法,显然,不能只用一对行列向量完成分解。可以考虑将矩阵分解为两项的和(差),如下:
A
=
(
1
1
)
(
1
1
)
−
(
1
0
)
(
0
1
)
A=\begin{pmatrix} 1\\ 1 \end{pmatrix}\begin{pmatrix} 1 &1 \end{pmatrix}-\begin{pmatrix} 1\\ 0 \end{pmatrix}\begin{pmatrix} 0 &1 \end{pmatrix}
A=(11)(11)−(10)(01)
从以上的例子可以看出,SVD中数据压缩上起着很重要的作用。比如一个典型的机器学习问题可能有着几百个或者更多的变量,而许多机器学习算法如果出现超过几十个就会崩溃,这使得奇异值分解在ML中对于变量减少是必不可少的。除此以外,
那么奇异值分解实际上在做什么?
SVD将矩阵分解为一组向量v(可以看成多个列向量)、一组向量u(可以看成多个行向量)和一个对角矩阵(可以看成多个标量),即将矩阵分解为多项式。
如果有一个秩r=2的矩阵,我们可以将矩阵分解为
u
1
v
1
T
+
u
2
v
2
T
u_{1}v_{1}^{T}+u_{2}v_{2}^{T}
u1v1T+u2v2T
如果有一个秩r=1的矩阵,结果如下
u
1
v
1
T
u_{1}v_{1}^{T}
u1v1T
稍微复杂一点的分解是添加标量sigma存储在对角矩阵中,对于秩r=2的矩阵,分解结果如下
σ
1
u
1
v
1
T
+
σ
2
u
2
v
2
T
\sigma _{1}u_{1}v_{1}^{T}+\sigma _{2}u_{2}v_{2}^{T}
σ1u1v1T+σ2u2v2T
显然,sigma值可以认为是加权系数。
1 特征值分解和几何意义
再理解SVD之前,先了解矩阵的特征值分解。
对于某一个矩阵A可以看作是通过乘法作用于向量x以产生新向量Ax的变换,如果A是m x p矩阵,B是p x n矩阵,则矩阵乘积C=AB(即m x n矩阵)定义为:
C
i
j
=
∑
k
=
1
p
a
i
k
b
k
j
C_{ij} = \sum_{k=1}^{p}a_{ik}b_{kj}
Cij=k=1∑paikbkj
例如,二维空间的旋转矩阵定义如下,即将某一向量围绕原点旋转一定的角度。
A
=
[
cos
θ
−
sin
θ
sin
θ
cos
θ
]
A=\begin{bmatrix} \cos\theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix}
A=[cosθsinθ−sinθcosθ]
二维空间中的拉伸矩阵定义如下,即将某一向量沿x轴拉伸一个常数因子k。
B
=
[
k
0
0
1
]
B=\begin{bmatrix} k &0 \\ 0&1 \end{bmatrix}
B=[k001]
类似的,在y轴有一个拉伸,矩阵定义如下。
C
=
[
1
0
0
k
]
C=\begin{bmatrix} 1 &0 \\ 0&k \end{bmatrix}
C=[100k]
如果有一个向量:
x
=
[
1
0
]
x=\begin{bmatrix} 1\\ 0 \end{bmatrix}
x=[10]
那么y=Ax是在向量x上旋转一定角度得到的向量,而Bx是在x方向上进行拉伸得到的向量。如下图所示:
如果是其他类型的变换矩阵呢?假设
A
=
[
3
2
0
2
]
A=\begin{bmatrix} 3 & 2 \\ 0 &2 \end{bmatrix}
A=[3022]
如果我们将该矩阵作用于一堆向量上而不是仅一个向量,假设我们有一个圆,其中包含距离原点一个单位的所有向量,则该向量一般形式为:
x
=
[
x
i
y
i
]
x=\begin{bmatrix} x_{i} \\ y_{i} \end{bmatrix}
x=[xiyi]
将矩阵A作用于向量x上,则最初的向量x形成的是一个圆,矩阵A作用后将其变成了一个椭圆,如下图所示。
假设圆中的样本向量 x1 和 x2 分别变换为 t1 和 t2,则有:
t
1
=
A
x
1
t
2
=
A
x
2
t_{1} = A x_{1} \\ t_{2} = A x_{2}
t1=Ax1t2=Ax2
向量是一个既有大小又有方向的量。上式例子中,矩阵A对x向量的影响是旋转和拉伸的组合,其中对x1的影响是既改变了大小又改变了方向,对x2只有幅度上的变化,因此改变矢量大小而不改变其方向的唯一方法是将其乘以标量。对于上式x2这样的向量,乘以A的效果等同于与标量相乘,如下所示。
t
2
=
A
x
2
=
λ
x
2
t_{2} = A x_{2} = \lambda x_{2}
t2=Ax2=λx2
以上式子不适用于x中的所有向量。实际上,对于每个矩阵A,只有部分向量具有这个性质,这个特殊向量称为A的特征向量,对应的标量lambda称为该特征向量的特征值。因此,n x n的矩阵A的特征向量被定义为非零向量u,使得:
A
u
=
λ
u
Au = \lambda u
Au=λu
任何与特征向量u具有相同方向的向量也具有对应特征值的特征向量。
举个例子,有一个矩阵:
B
=
[
−
1
1
0
−
2
]
B=\begin{bmatrix} -1 &1 \\ 0&-2 \end{bmatrix}
B=[−101−2]
其特征值分别为-1和-2,对应的特征向量为:
u
1
=
[
1
0
]
u_{1} = \begin{bmatrix} 1 \\ 0 \end{bmatrix}
u1=[10]
u
1
=
[
−
1
1
]
u_{1} = \begin{bmatrix} -1 \\ 1 \end{bmatrix}
u1=[−11]
这意味着矩阵B作用于所有可能向量时,它不会改变这两个向量的方向,而只会拉伸它们,所以对于特征向量,矩阵乘法变成了一个简单的标量乘法。
已知矩阵C并计算其特征向量:
C
=
[
3
2
0
2
]
C=\begin{bmatrix} 3 & 2 \\ 0 &2 \end{bmatrix}
C=[3022]
得到两个特征向量分别为:
u
1
=
[
1
0
]
u_{1} = \begin{bmatrix} 1 \\ 0 \end{bmatrix}
u1=[10]
u
2
=
[
−
0.8944
0.4472
]
u_{2} = \begin{bmatrix} -0.8944\\ 0.4472 \end{bmatrix}
u2=[−0.89440.4472]
对应的特征值为:
λ
1
=
3
λ
2
=
2
\lambda _{1} =3\\ \lambda _{2} =2
λ1=3λ2=2
对于矩阵D并计算其特征向量:
D
=
[
3
1
1
2
]
D=\begin{bmatrix} 3 & 1 \\ 1 &2 \end{bmatrix}
D=[3112]
其特征向量分别为:
u
1
=
[
0.8507
0.5257
]
u_{1} = \begin{bmatrix} 0.8507\\ 0.5257 \end{bmatrix}
u1=[0.85070.5257]
u
2
=
[
−
0.5257
0.8507
]
u_{2} = \begin{bmatrix} -0.5257\\ 0.8507 \end{bmatrix}
u2=[−0.52570.8507]
对应的特征值为:
λ
1
=
3.618
λ
2
=
1.382
\lambda _{1} =3.618\\ \lambda _{2} =1.382
λ1=3.618λ2=1.382
观察可发现,特征向量是沿着椭圆的长轴和短轴,那为什么矩阵C的特征向量没有这个性质呢?那是因为矩阵D是一个对称矩阵,对称矩阵是等于其转置矩阵,该矩阵的主对角线上的元素是任意的,但对于其他元素,第i行第j列上的每一个元素等于第j行第i列的元素,以下是一个对称矩阵的例子:
[
5
0
4
3
0
7
9
2
4
9
6
1
3
2
1
3
]
\begin{bmatrix} 5&0 &4 &3 \\ 0 & 7 & 9 & 2\\ 4 & 9 & 6 & 1\\ 3 & 2 & 1&3 \end{bmatrix}
5043079249613213
对称矩阵始终是方阵,它是用过沿其特征向量进行拉伸或缩放来变换向量,因此则有:
D
u
1
=
λ
u
1
D
u
2
=
λ
u
2
Du_{1}=\lambda u_{1}\\ Du_{2}=\lambda u_{2}
Du1=λu1Du2=λu2
对称矩阵的性质:
以上实例可以看出:对称矩阵D的特征向量相互垂直并形成正交向量。
对称矩阵的一个重要性质是n x n对称矩阵有n个线性独立且正交的特征向量,并且它有n个与这些特征向量对应的实特征值,这些特征值不一定彼此不相等。对称矩阵另一个重要特性是它们是正交可对角化的。
特征分解:
对称矩阵是可以正交对角化的,意味着如果我们有一个对称矩阵A,可以将其分解为:
A
=
P
D
P
T
A=PDP^{T}
A=PDPT
其中,D是由A的n个特征值组成的n x n对角矩阵,P也是n x n矩阵,P的列是A的n个线性独立特征向量,分别对应于D中的那些特征值,如果u1, u2, u3 …, un是A的特征向量, λ1, λ2, …, λn分别是它们对应的特征值,那么A可以写成:
A
=
[
u
1
u
2
…
u
n
]
[
λ
1
0
…
0
0
λ
2
…
0
⋮
⋮
⋮
⋮
0
0
…
λ
n
]
[
u
1
T
u
2
T
⋮
u
n
T
]
A = \begin{bmatrix} u_{1} & u_{2} & \dots &u_{n} \end{bmatrix}\begin{bmatrix} \lambda _{1} & 0 & \dots& 0\\ 0 & \lambda _{2} & \dots &0 \\ \vdots & \vdots& \vdots &\vdots \\ 0 & 0 & \dots &\lambda _{n} \end{bmatrix}\begin{bmatrix} u_{1}^{T} \\ u_{2}^{T} \\ \vdots \\ u_{n}^{T} \end{bmatrix}
A=[u1u2…un]
λ10⋮00λ2⋮0……⋮…00⋮λn
u1Tu2T⋮unT
以上, A的这种因式分解成为特征分解。
特征分解的几何解释:
为了更好的理解特征分解方程,首先需要对其进行化简,如果假设每一个特征向量ui是一个n x 1的列向量。
u
i
=
[
u
i
1
u
i
2
⋮
u
i
n
]
u_{i}= \begin{bmatrix} u_{i1} \\ u_{i2} \\ \vdots \\ u_{in} \end{bmatrix}
ui=
ui1ui2⋮uin
转置后:
u
i
T
=
[
u
i
1
u
i
2
…
u
i
n
]
u_{i}^{T} = \begin{bmatrix} u_{i1} & u_{i2} & \dots &u_{in} \end{bmatrix}
uiT=[ui1ui2…uin]
相乘后,得到n x n矩阵:
u
i
u
i
T
=
[
u
i
1
u
i
2
⋮
u
i
n
]
[
u
i
1
u
i
2
…
u
i
n
]
=
[
u
i
1
u
i
1
u
i
1
u
i
2
…
u
i
1
u
i
n
u
i
2
u
i
1
u
i
2
u
i
2
…
u
i
2
u
i
n
⋮
⋮
⋮
⋮
u
i
n
u
i
1
u
i
n
u
i
2
…
u
i
n
u
i
n
]
u_{i}u_{i}^{T} = \begin{bmatrix} u_{i1} \\ u_{i2} \\ \vdots \\ u_{in} \end{bmatrix} \begin{bmatrix} u_{i1} & u_{i2} & \dots &u_{in} \end{bmatrix}=\\ \begin{bmatrix} u_{i1}u_{i1} & u_{i1}u_{i2} & \dots & u_{i1}u_{in}\\ u_{i2}u_{i1} & u_{i2}u_{i2} & \dots & u_{i2}u_{in}\\ \vdots & \vdots & \vdots &\vdots \\ u_{in}u_{i1} & u_{in}u_{i2}& \dots &u_{in}u_{in} \end{bmatrix}
uiuiT=
ui1ui2⋮uin
[ui1ui2…uin]=
ui1ui1ui2ui1⋮uinui1ui1ui2ui2ui2⋮uinui2……⋮…ui1uinui2uin⋮uinuin
首先,我们计算 DP^T 以简化特征分解方程:
[
λ
1
0
…
0
0
λ
2
…
0
⋮
⋮
⋮
⋮
0
0
…
λ
n
]
[
u
1
T
u
2
T
⋮
u
n
T
]
=
[
λ
1
u
1
T
λ
2
u
2
T
⋮
λ
n
u
n
T
]
\begin{bmatrix} \lambda _{1} &0 &\dots &0 \\ 0 &\lambda _{2} &\dots & 0\\ \vdots & \vdots & \vdots & \vdots\\ 0 & 0 &\dots &\lambda _{n} \end{bmatrix}\begin{bmatrix} u_{1}^{T} \\ u_{2}^{T} \\ \vdots \\ u_{n}^{T} \end{bmatrix}=\begin{bmatrix} \lambda _{1}u_{1}^{T} \\ \lambda _{2}u_{2}^{T} \\ \vdots \\ \lambda _{n}u_{n}^{T} \end{bmatrix}
λ10⋮00λ2⋮0……⋮…00⋮λn
u1Tu2T⋮unT
=
λ1u1Tλ2u2T⋮λnunT
现在特征分解方程变为:
A
=
[
u
1
u
2
…
u
n
]
[
λ
1
u
1
T
λ
2
u
2
T
⋮
λ
n
u
n
T
]
=
λ
1
u
1
u
1
T
+
λ
1
u
1
u
1
T
+
⋯
+
λ
n
u
n
u
n
T
A=\begin{bmatrix} u_{1} & u_{2} & \dots &u_{n} \end{bmatrix}\begin{bmatrix} \lambda _{1}u_{1}^{T} \\ \lambda _{2}u_{2}^{T} \\ \vdots \\ \lambda _{n}u_{n}^{T} \end{bmatrix}\\ =\lambda _{1}u_{1}u_{1}^{T} + \lambda _{1}u_{1}u_{1}^{T} + \dots +\lambda _{n}u_{n}u_{n}^{T}
A=[u1u2…un]
λ1u1Tλ2u2T⋮λnunT
=λ1u1u1T+λ1u1u1T+⋯+λnununT
因此nxn矩阵A可以分解成n个形状相同(n x n)的矩阵,每个矩阵都有对应的特征值λi。每个矩阵称为投影矩阵:
u
i
u
i
T
u_{i}u_{i}^{T}
uiuiT
假设我们有一个向量 x 和一个单位向量 v,v 和 x 的内积等于 vx=v^T x 给出了 x 在 v 上的标量投影(这是 x 到 v 的向量投影的长度 v),如果我们再次将它乘以 v,它会给出一个向量,称为 x 到 v 上的正交投影。如下图所示:
因此将 ui ui^T 乘以 x,我们得到 x 到 ui 的正交投影。
现在我们计算一下之前提到的矩阵D的投影矩阵:
D
=
[
3
1
1
2
]
D=\begin{bmatrix} 3 & 1 \\ 1 &2 \end{bmatrix}
D=[3112]
得到特征分解方程中的第一项:
D
1
=
λ
1
u
1
u
1
T
=
3.618
[
0.8507
0.5257
]
[
0.8507
0.5257
]
=
[
2.618
1.618
1.618
1
]
D_{1}= \lambda _{1}u_{1}u_{1}^{T} =3.618\begin{bmatrix} 0.8507 \\ 0.5257 \end{bmatrix}\begin{bmatrix} 0.8507 &0.5257 \end{bmatrix}\\ =\begin{bmatrix} 2.618 &1.618 \\ 1.618 &1 \end{bmatrix}
D1=λ1u1u1T=3.618[0.85070.5257][0.85070.5257]=[2.6181.6181.6181]
可见,它是一个对称矩阵。事实上,特征分解方程中的所有投影矩阵都是对称的,是因为每一项的第m行第n列的元素与第n行第m列的元素具有相同的值。
因此,特征分解方程是具有 n 个特征向量的对称 n×n 矩阵。特征向量与原始矩阵 D相同,即 u1, u2, … un, ui 对应的特征值为 λi(与 D 相同),但其他所有特征值均为零。对称矩阵转换向量是将沿其特征向量拉伸或收缩向量,拉伸或收缩的量与对应的特征值成正比,所以这个矩阵将沿着 ui 拉伸一个向量,但由于其他特征值为零,它会在这些方向上将其缩小为零。
假设我们将对称矩D作用于任意向量x,现在特征分解方程变为:
D
x
=
λ
1
u
1
u
1
T
x
+
λ
1
u
1
u
1
T
x
+
⋯
+
λ
n
u
n
u
n
T
x
Dx=\lambda _{1}u_{1}u_{1}^{T}x + \lambda _{1}u_{1}u_{1}^{T} x+ \dots +\lambda _{n}u_{n}u_{n}^{T}x
Dx=λ1u1u1Tx+λ1u1u1Tx+⋯+λnununTx
因此在特征分解方程的每一项中,它是x到ui到正交投影,然后再乘以λi,由于λi是标量,它乘以一个向量只会改变向量到大小,而不改变向量到方向。如下图所示。
所以,特征分解在数学上解释了我们之前在图中看到的对称矩阵的一个重要性质,对称矩阵通过沿其特征向量拉伸或者收缩向量来变换向量,并且沿每个特征向量的拉伸或收缩量与对应的特征值呈正比。此外,特征分解可以将一个n x n对称矩阵分解为n个具有相同形状(n x n)的矩阵乘以一个特征值。投影矩阵仅将 x 投影到每个 ui 上,但特征值缩放矢量投影的长度 (ui ui^ T x)。特征值越大,得到的向量 (λi ui ui^ T x) 的长度越大,赋予其对应矩阵 (ui ui^ T) 的权重越大。因此,我们可以通过对具有最高特征值的项求和来近似我们的原始对称矩阵 D。例如,如果我们假设特征值 λi 已按降序排序:
D
=
λ
1
u
1
u
1
T
+
λ
1
u
1
u
1
T
+
⋯
+
λ
n
u
n
u
n
T
λ
1
≥
λ
2
≥
⋯
≥
λ
k
≥
⋯
≥
λ
n
D=\lambda _{1}u_{1}u_{1}^{T} + \lambda _{1}u_{1}u_{1}^{T} + \dots +\lambda _{n}u_{n}u_{n}^{T}\\ \lambda _{1}\ge \lambda _{2}\ge \dots \ge \lambda _{k}\ge \dots \ge \lambda _{n}
D=λ1u1u1T+λ1u1u1T+⋯+λnununTλ1≥λ2≥⋯≥λk≥⋯≥λn
那么我们取特征分解方程中的前k项来对原始矩阵有一个很好的近似:
D
≈
D
k
=
λ
1
u
1
u
1
T
+
λ
1
u
1
u
1
T
+
⋯
+
λ
k
u
k
u
k
T
D \approx D_{k} =\lambda _{1}u_{1}u_{1}^{T} + \lambda _{1}u_{1}u_{1}^{T} + \dots +\lambda _{k}u_{k}u_{k}^{T}
D≈Dk=λ1u1u1T+λ1u1u1T+⋯+λkukukT
因此在原始矩阵 D 中,我们遗漏的其他 (n-k) 个非常小且接近于零的特征值,则近似矩阵与原始矩阵非常相似,并且我们有一个很好的近似。如下图所示,将矩阵C作用于向量x(左图)上得到Cx(中图),而特征分解方程的第一项(右图)就已经很接近Cx(中图)了。
那么为什么特征分解方程是有效的?为什么它需要一个对称矩阵呢?
源于对称矩阵的重要性质。假设 x 是一个 n×1 列向量。如果 A 是一个 n×n 对称矩阵,那么它有 n 个线性独立且正交的特征向量,可以作为一个新的基,并且基于基的定义,任何向量 x 都可以唯一地写为 D 的特征向量的线性组合。如果是非对称矩阵,则这里的特征向量是线性独立的,而非正交,且它们没有显示变换后该矩阵的正确拉伸方向。
**因此,特征分解方法是非常有用的,但仅适用于对称矩阵,对称矩阵一定是一个方阵,如果有一个不是方阵的矩阵或者一个方阵但是是非对称矩阵,那么就不能使用特征分解方法来用其他矩阵逼近它,SVD分解能克服这个问题。
2 SVD分解
2.1 奇异值
在讨论SVD之前,我们应该找到一种方法来计算非对称矩阵的拉伸方向。
假设A是一个 m x n 矩阵,它不是一个对称矩阵。那么可以证明矩阵
A
T
A
A^{T} A
ATA
是一个n x n对称矩阵。由线性代数相关知识可知:
(
A
T
A
)
T
=
A
T
(
A
T
)
T
=
A
T
A
\left (A^{T}A \right ) ^{T} =A^{T}\left ( A^{T} \right ) ^{T} =A^{T}A
(ATA)T=AT(AT)T=ATA
所以A^T A 等于它的转置,是一个对称矩阵。我们想计算非对称矩阵的拉伸方向。但是我们如何在数学上定义拉伸方向?
假设现在列向量有 3 个元素。最初,我们有一个球体,其中包含距离原点一个单位的所有向量,如下图所示。如果我们将这些向量称为 x,则 ||x||=1。现在,如果我们将它们乘以一个 3×3 对称矩阵,Ax 就变成了一个 3-d 椭圆。第一拉伸方向可以定义为该椭圆中长度最大的向量的方向(下图中的 Av1)。实际上Av1是||Ax||的最大值在所有单位向量 x 上。这个向量是向量 v1 通过 A 的变换。
拉伸的第二个方向是沿矢量 Av2, Av2 是 ||Ax|| 的最大值在 x 中垂直于 v1 的所有向量上,所以在 x 的所有向量中,我们最大化 ||Ax||在这个约束下,x 垂直于 v1,最后v3 是垂直于 v1 和 v2 的向量,并且在这些约束下给出了 Ax 的最大长度,Av3 的方向决定了拉伸的第三方向。所以一般在n维空间中,第i个拉伸方向是向量Avi的最大长度方向,并且垂直于之前的(i-1)个拉伸方向。
因此,对于一个m x n的矩阵A,A^ T A是一个对称矩阵,它有 n 个实特征值和 n 个线性独立和正交特征向量,这些特征向量可以构成它可以变换的 n 个元素向量的基础(在 R^n 空间中)。
2.2 Singular Value Decomposition (SVD)
令 A 为 m×n 矩阵,秩 A = r,所以 A 的非零奇异值的个数是 r。 由于它们是正数并按降序标记,我们可以将它们写为
σ
1
≥
σ
2
≥
⋯
≥
σ
n
\sigma _{1} \ge \sigma _{2} \ge \dots \ge \sigma _{n}
σ1≥σ2≥⋯≥σn
其中
σ
r
+
1
=
σ
r
+
2
=
⋯
=
σ
n
=
0
\sigma _{r+1} = \sigma _{r+2} = \dots = \sigma _{n} =0
σr+1=σr+2=⋯=σn=0
我们知道每个奇异值 σi 是 λi 的平方根(A^T A的特征值),对应一个同阶的特征向量 vi。 现在我们可以将 A 的奇异值分解写为:
A
=
U
Σ
V
T
A=U\Sigma V^{T}
A=UΣVT
其中 V 是一个 n×n 矩阵,其列是 vi。则有:
V
=
[
v
1
v
2
…
v
n
]
V=\begin{bmatrix} v_{1} & v_{2} &\dots &v_{n} \end{bmatrix}
V=[v1v2…vn]
我们称一组正交且归一化向量为正交集合,所以集合 {vi} 是一个正交集合,V是正交矩阵。
Σ 是一个 m×n 对角矩阵,其形式为:
所以我们首先制作一个 r × r 对角矩阵,其中对角元素为 σ1, σ2, …, σr,然后我们用零填充它,使其成为一个 m × n 矩阵。
我们还知道集合 {Av1, Av2, …, Avr} 是 Col A 的正交基,并且 σi = ||Avi||。 所以我们可以通过将 Avi 向量除以它们的长度来归一化它们:
u
i
=
A
v
i
∥
A
v
i
∥
=
A
v
i
σ
i
(
1
<
i
<
r
)
u_{i}=\frac{Av_{i}}{\parallel A v_{i}\parallel }=\frac{A v_{i}}{\sigma _{i} } (1< i< r)
ui=∥Avi∥Avi=σiAvi(1<i<r)
现在我们有一个集合 {u1, u2, …, ur},它是 r 维的 Ax 的标准正交基。我们知道A是一个m×n的矩阵,A的秩最多可以是m(当A的所有列都是线性独立的时候)。 由于我们需要 U 的 m×m 矩阵,我们将 (mr) 个向量添加到 ui 集合中,使其成为 m 维空间 R^m 的归一化基。
所以现在我们有一个正交基{u1, u2, … ,um}。 这些向量将是 U 的列,它是一个正交 m×m 矩阵。
U
=
[
u
1
u
2
…
u
m
]
U=\begin{bmatrix} u_{1} & u_{2}& \dots &u_{m} \end{bmatrix}
U=[u1u2…um]
综上,矩阵A最终分解为
A
=
[
u
1
u
2
…
u
m
]
[
σ
1
0
…
0
0
…
0
0
σ
2
…
0
0
…
0
⋮
⋮
⋱
⋮
⋮
⋱
⋮
0
0
…
σ
r
0
…
0
0
0
…
0
0
…
0
⋮
⋮
⋱
⋮
⋮
⋱
⋮
0
0
…
0
0
…
0
]
[
v
1
T
v
2
T
⋮
v
n
T
]
A=\begin{bmatrix} u_{1} & u_{2}& \dots &u_{m} \end{bmatrix}\begin{bmatrix} \sigma _{1} &0 &\dots &0 &0 &\dots &0 \\ 0 &\sigma _{2} &\dots &0 &0 &\dots &0 \\ \vdots &\vdots &\ddots &\vdots &\vdots &\ddots &\vdots \\ 0 &0 &\dots &\sigma _{r} &0 &\dots &0 \\ 0 &0 &\dots &0 &0 &\dots &0 \\ \vdots &\vdots &\ddots &\vdots &\vdots &\ddots &\vdots \\ 0 &0 &\dots &0 &0 &\dots &0 \end{bmatrix}\begin{bmatrix} v_{1}^{T} \\ v_{2}^{T} \\ \vdots \\ v_{n}^{T} \end{bmatrix}
A=[u1u2…um]
σ10⋮00⋮00σ2⋮00⋮0……⋱……⋱…00⋮σr0⋮000⋮00⋮0……⋱……⋱…00⋮00⋮0
v1Tv2T⋮vnT
简化为:
A
=
[
u
1
u
2
…
u
m
]
[
σ
1
v
1
T
σ
2
v
2
T
⋮
σ
r
v
r
T
0
⋮
0
]
=
σ
1
u
1
v
1
T
+
σ
2
u
2
v
2
T
+
⋯
+
σ
r
u
r
v
r
T
A=\begin{bmatrix} u_{1} & u_{2}& \dots &u_{m} \end{bmatrix}\begin{bmatrix} \sigma _{1}v_{1}^{T} \\ \sigma _{2}v_{2}^{T} \\ \vdots \\ \sigma _{r}v_{r}^{T} \\ 0 \\ \vdots \\ 0 \end{bmatrix}\\ =\sigma _{1}u_{1}v_{1}^{T} +\sigma _{2}u_{2}v_{2}^{T}+\dots +\sigma _{r}u_{r}v_{r}^{T}
A=[u1u2…um]
σ1v1Tσ2v2T⋮σrvrT0⋮0
=σ1u1v1T+σ2u2v2T+⋯+σrurvrT
所以SVD方程将矩阵A分解成r个形状相同(m x n)的矩阵。
所以上式中的每一项 ai = σivi ^ Tx 是 Ax 在 ui 上的标量投影,如果乘以 ui,结果是一个向量,它是 Ax 在 ui 上的正交投影,奇异值 σi 沿 ui 缩放该向量的长度。所以在特征分解方程中,每个 ui ui^T 是一个投影矩阵,它将给出 x 到 ui 的正交投影。
2.3 summary
从以上可以看出,任何矩阵都可以分解为向量u和向量v,现在需要思考的是矩阵是如何分解的,SVD的思想如下:
A
=
U
Σ
V
T
A=U\Sigma V^{T}
A=UΣVT
- U是一个为mxm的矩阵;
- V是一个为nxn的矩阵,和矩阵U一样也为酉矩阵;
- sigma是一个为mxn的矩阵,除了主对角线上的元素以外全为0,主对角线上的每一个元素都称为奇异值。
通过以下公式求出SVD分解后的三个矩阵:
首先,将A的转置和A做矩阵乘法运算,得到一个nxn的方阵A^{T}A,此时可以进行特征分解,得到特征值和特征向量满足下式:
(
A
T
A
)
v
i
=
λ
i
v
i
\left ( A^{T}A \right )v_{i}=\lambda _{i}v_{i}
(ATA)vi=λivi
因此得到了该nxn方阵的n个特征值和对应的n个特征向量v,如果将所有的特征向量张成一个nxn的矩阵V,就是SVD公式里面的V矩阵,一般称V中的每个特征向量叫做A的右奇异向量。
其次,将A和A的转置做矩阵乘法,得到一个mxm的方阵AA^{T},同样,对它进行特征分解,得到特征只和特征向量满足下式:
(
A
A
T
)
v
i
=
λ
i
v
i
\left ( AA^{T} \right )v_{i}=\lambda _{i}v_{i}
(AAT)vi=λivi
因此得到了该mxm方阵的m个特征值和对应的m个特征向量u,同样,将所有的特征向量张成一个mxm的矩阵U,就是SVD公式里的U矩阵,一般称U中的每个特征向量叫做A的左奇异值。
对于奇异值矩阵,因为该矩阵除了对角线上是奇异值其他位置都是0,因此只需要求出奇异值即可。
通过以下公式可以求出每一个奇异值,进而求出奇异值矩阵。
A
=
U
Σ
V
T
⇒
A
V
=
U
Σ
V
T
V
⇒
A
V
=
U
Σ
⇒
A
v
i
=
σ
i
u
i
⇒
σ
i
=
A
v
i
/
u
i
A=U\Sigma V^{T}\Rightarrow AV=U\Sigma V^{T}V\Rightarrow AV=U\Sigma \Rightarrow Av_{i}=\sigma _{i}u_{i}\Rightarrow \sigma _{i}=Av_{i}/u_{i}
A=UΣVT⇒AV=UΣVTV⇒AV=UΣ⇒Avi=σiui⇒σi=Avi/ui
用图像进行解释说明,矩阵A可以认为将图像进行矢量化并存储为列,矩阵U可以认为与A中的列对应的相似列。与特征向量类似,我们将这些“向量面”成为特征面,用特征面来表示人脸,这是图像处理和计算机视觉中用于人脸分析的基本方法。
矩阵U和V为实对称矩阵,即将U和UT相乘、V和VT相乘,得到的是单位矩阵。
因此,
3 SVD代码实现
3.1 基于opencv实现SVD
#include <iostream>
#include "eigen3/Eigen/Core"
#include "eigen3/Eigen/SVD"
#include <opencv2/opencv.hpp>
#include <opencv2/core/eigen.hpp>
cv::Mat load(const std::string & p_filePath)
{
cv::Mat mat;
cv::FileStorage fs;
if (!fs.open(p_filePath, cv::FileStorage::READ))
{
std::cout << "Can't open matrix file" << std::endl;
return cv::Mat();
}
fs["matrix"] >> mat;
fs.release();
return mat;
}
int main (void)
{
cv::Mat src = load("svd_src.xml"); // input (rows: 6 ; cols: 515 808 ; type: CV_64FC1)
cv::Mat u; // output
// Eigen implementation
Eigen::MatrixXd m;
cv::cv2eigen(src, m);
std::cout << "[start] Eigen SVD decomposition" << std::endl;
Eigen::JacobiSVD<Eigen::MatrixXd> svd(m, Eigen::ComputeThinU);
std::cout << "[end] Eigen SVD decomposition" << std::endl;
cv::eigen2cv(svd.matrixU(), u);
// OpenCV implementation
cv::Mat w, vt;
std::cout << "[start] OpenCV SVD decomposition" << std::endl;
cv::SVD::compute(src, w, u, vt);
std::cout << "[end] OpenCV SVD decomposition" << std::endl;
return 0;
}
3.2 基于eigen实现SVD
#include <Eigen/Dense>
#include <iostream>
int main()
{
Eigen::MatrixXf A = Eigen::MatrixXf::Random(3, 2);
std::cout << "A: " << A << std::endl;
Eigen::JacobiSVD<Eigen::MatrixXf> svd(m, Eigen::ComputeFullV | Eigen::ComputeFullU);
std::cout << "singular_values: " << svd.singularValues() << std::endl;
std::cout << "U: " << svd.matrixU() << std::endl;
std::cout << "V: " << svd.matrixV() << std::endl;
return 0;
}
4 应用
输入一张人脸图像
faces = X[0,:].reshape((64,64))
通过奇异值进行分解
# we decompose the image of a face
U, S, VT = np.linalg.svd(faces)
S = np.diag(S)
faces = faces.astype('float')
#U, VT = truncate_u_v(S, U, VT)
draw_svd(faces, U, S, VT, 'gray')
输出为:
可以求出前30个特征值
diag_elem = np.diag(S)
plt.stem(diag_elem[1:])
当然,这里可以取前n个特征值进行重建,剩下的特征值舍弃,显然,取得的特征值越多,图像的轮廓越清晰。因为越靠前的特征值和特征向量,权重越大,越靠后,权重越小。
ps:如果矩阵A为mxn,用奇异值分解,如果A是方阵,用特征值分解即可。
5 最后,简单说下SVD和PCA之间的关系
PCA(Principal Component Analysis)和SVD(Singular Value Decomposition)是常用的用于数据降维和特征提取的线性代数算法。
他们的相似点是,都利用了矩阵分解的思想,旨在将一个高维矩阵用一组低维度的线性关系来描述。
这里简单的说明一下矩阵分解的方法,矩阵分解有很多种方法:
- 对于方阵,可以进行特征分解;
- 对于非方阵,不能进行特征分解,但可以做SVD分解。
那么PCA和SVD的区别在于分解的方式不同:
- PCA求解的关键在于求解协方差矩阵 C = ( 1 / m ) X X T C = (1/m) X X^T C=(1/m)XXT 的特征值分解,然后对其排序,选取前k大的特征值对应的特征向量作为基矢,从而得到投影矩阵。
- SVD分解是直接对数据矩阵进行分解,即分解为三个矩阵的乘积, A = U Σ V T A=U\Sigma V^T A=UΣVT,其中U和V是正交矩阵, Σ \Sigma Σ是除对角线外其他元素都为0的矩阵。其中 Σ \Sigma Σ的对角线元素就是奇异值,是一个降序排列的数列。将其前k个奇异值及其对应的左右奇异向量作为基矢,就可以得到投影矩阵。
因此,两者的不同点在于PCA是基于协方差矩阵分解的,而SVD则是直接对数据矩阵分解。同时,PCA得到的特征向量是标准正交的,而SVD得到的奇异向量是不一定正交的。
最后,PCA和SVD在实际应用中有各自的优缺点,需要根据实际情况选择使用。PCA更适合于解释性强的数据分析问题,而SVD则是更通用的矩阵分解方法,适用于许多场景,如推荐系统、数据压缩等。