https://www.cnblogs.com/endlesscoding/p/10033527.html
奇异值分解(SVD)在数据降维中有较多应用。
一、特征值分解
实对称矩阵
在理角奇异值分解之前,需要先回顾一下特征值分解,如果矩阵A是一个m×m的实对称矩阵(即
A
=
A
T
A=A^{^{T}}
A=AT),那么它可以被分解成如下的形式:
其中Q为标准正交阵,即有
Q
=
Q
T
Q=Q^{T}
Q=QT,Σ为对角矩阵,且上面的矩阵的维度均为m×m。
λ
i
\lambda_{i}
λi称为特征值,
q
i
q_{i}
qi是Q(特征矩阵)中的列向量,称为特征向量。
一般矩阵
上面的特征值分解,对矩阵有着较高的要求,它需要被分解的矩阵A为实对称矩阵,但是现实中,我们所遇到的问题一般不是实对称矩阵。那么当我们碰到一般性的矩阵,即有一个m×n的矩阵A,它是否能被分解成上面的式(1-1)的形式呢?当然是可以的,这就是我们下面要讨论的内容。
2、奇异值分解(SVD)
2.1 奇异值分解定义
有一个m×n的实数矩阵A,我们想要把它分解成如下的形式
对于奇异值分解,我们可以利用上面的图形象表示,图中方块的颜色表示值的大小,颜色越浅,值越大。对于奇异值矩阵Σ,只有其主对角线有奇异值,其余均为0。
2.2奇异值求解
正常求上面的U,V,Σ不便于求,我们可以利用如下性质
可以看到式(2-2)与式(1-1)的形式非常相同,进一步分析,我们可以发现
A
A
T
AA^{^{T}}
AAT和
A
T
A
A^{^{T}}A
ATA也是对称矩阵,那么可以利用式(1-1),做特征值分解。利用式(2-2)特征值分解,得到的特征矩阵即为U;利用式(2-3)特征值分解,得到的特征矩阵即为V;对
λ
λ
T
\lambda\lambda^{^{T}}
λλT或
λ
T
λ
\lambda^{^{T}}\lambda
λTλ中的特征值开方,可以得到所有的奇异值。
3、奇异值分解应用#
3.1 纯数学例子
分别对上面做特征值分解,得到如下结果
U =
[[-0.55572489, -0.72577856, 0.40548161],
[-0.59283199, 0.00401031, -0.80531618],
[-0.58285511, 0.68791671, 0.43249337]]
V =
[[-0.18828164, -0.01844501, 0.73354812, 0.65257661, 0.06782815],
[-0.37055755, -0.76254787, 0.27392013, -0.43299171, -0.17061957],
[-0.74981208, 0.4369731 , -0.12258381, -0.05435401, -0.48119142],
[-0.46504304, -0.27450785, -0.48996859, 0.39500307, 0.58837805],
[-0.22080294, 0.38971845, 0.36301365, -0.47715843, 0.62334131]]
奇异值Σ=Diag(18.54,1.83,5.01)
3.2 在图像压缩中的应用#
准备工具#
下面的代码运行环境为python3.6+jupyter5.4
SVD(Python)#
这里暂时用numpy自带的svd函数做图像压缩。
①读取图片
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img_eg = mpimg.imread("pic1.jpg")
print(img_eg.shape)
图片大小为(687, 960, 3)
②奇异值分解
img_temp = img_eg.reshape(687, 960*3)
U,Sigma,VT = np.linalg.svd(img_temp)
我们先将图片变成687*2880,再做奇异值分解。从svd函数中得到的奇异值sigma它是从大到小排列的。
③取前部分奇异值重构图片
# 取前60个奇异值
sval_nums = 60
img_restruct1 = (U[:,0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums,:])
img_restruct1 = img_restruct1.reshape(687, 960, 3)
# 取前300个奇异值
sval_nums = 300
img_restruct2 = (U[:,0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums,:])
img_restruct2 = img_restruct2.reshape(687, 960, 3)
将图片显示出来看一下,对比下效果
fig, ax = plt.subplots(1,3,figsize = (24,32))
ax[0].imshow(img_eg)
ax[0].set(title = "src")
ax[1].imshow(img_restruct1.astype(np.uint8))
ax[1].set(title = "nums of sigma = 60")
ax[2].imshow(img_restruct2.astype(np.uint8))
ax[2].set(title = "nums of sigma = 300")
可以看到,当我们取到前面300个奇异值来重构图片时,基本上已经看不出与原图片有多大的差别。
总结
从上面的图片的压缩结果中可以看出来,奇异值可以被看作成一个矩阵的代表值,或者说,奇异值能够代表这个矩阵的信息。当奇异值越大时,它代表的信息越多。因此,我们取前面若干个最大的奇异值,就可以基本上还原出数据本身。
如下,可以作出奇异值数值变化和前部分奇异值和的曲线图,如下图所示
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].plot(Sigma)
axes[1].plot(Sigma.cumsum())
从上面的第1个图,可以看出,奇异值下降是非常快的,**因此可以只取前面几个奇异值,便可基本表达出原矩阵的信息。**从第2个图,可以看出,当取到前100个奇异值时,这100个奇异值的和已经占总和的95%左右。