奇异值分解的定义
SVD(Singular Value Decomposition)可以理解为:将一个比较复杂的矩阵用更小更简单的3个子矩阵的相乘来表示,这3个小矩阵描述了大矩阵重要的特性。
定义:矩阵的奇异值分解是指将一个秩为
r的实矩阵
Am×n?分解为三个实矩阵乘积的形式:
Am×n?=Um×m?Σm×n?Vn×nT?≈Um×k?Σk×k?Vk×nT?
其中
U是
m阶正交矩阵(
U的列向量称为左奇异向量),
V是
n阶正交矩阵(
V的列向量称为右奇异向量),
Σ是
m×n矩形对角矩阵,称为奇异值矩阵,对角线上的元素称为奇异值。
Σ=[Dr×r?0?00?]m×n?
D是一个
r×r的对角阵,
D的对角线元素是
A的前
r个奇异值
σ1?≥σ2?≥?≥σr?>0(非负,降序)。
知识点:任意一个实矩阵
A可以由其外积展开式表示
A=σ1?u1?v1T?+σ2?u2?v2T?+?+σr?ur?vrT?
其中
uk?vkT?为
m×n矩阵,是列向量
uk?和行向量
vkT?的外积,
σk?为奇异值,
uk?,vkT?,σk?通过矩阵
A的奇异值分解得到。
知识点:奇异值在矩阵中按照从大到小排列,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。我们可以用最大的
k个奇异值的矩阵和
UVT相乘来近似描述矩阵,从而实现了降维、减少数据存储、提升计算性能等效果。
奇异值分解的计算
设矩阵
A的奇异值分解为
A=UΣVT,则有
ATA=V(ΣTΣ)VTAAT=U(ΣΣT)UT?
即对称矩阵
ATA和
AAT的特征分解可以由矩阵
A的奇异值分解矩阵表示。
证明:
ATA的特征值非负。
令
A是
m×n矩阵,那么
ATA是对称矩阵且可以正交对角化,让
{v1?,…,vn?}是
Rn的单位正交基且构成
ATA的特征向量,
λ1?,…,λn?是
ATA对应的特征值,那么对
1≤i≤n,
∥Avi?∥2=(Avi?)T(Avi?)=viT?ATAvi?=viT?λi?vi?=λi?
所以
ATA的所有特征值都非负,如果必要,通过重新编号我们可以假设特征值的重新排列满足
λ1?≥λ2?≥?≥λn?≥0
A的奇异值是
ATA的特征值的平方根,记为
σ1?,σ2?,…,σn?,且它们递减顺序排列。
σj?=∥Avj?∥=λj?
?,j=1,2,?,n
可见,对
A进行奇异值分解需要求矩阵
ATA的特征值及其对应的标准正交的特征向量来构成正交矩阵
V的列,特征值
λj?的平方根得到奇异值$\sigma _ { i }
也即得到奇异值矩阵\Sigma$。
证明:假设
{v1?,v2?,…,vn?}是包含
ATA特征向量的
Rn上的标准正交基,重新整理使得对应
ATA的特征值满足
λ1?≥λ2?≥?≥λn?。若
A有
r个非零奇异值,则
{Av1?,Av2?,…,Avr?}是
ColA的一个正交基,且
rankA=r。
当
i不等于
j时,
viT?vj?=0。
(Avi?)TAvj?=viT?ATAvj?=λj?viT?vj?=0
所以,
{Av1?,Av2?,…,Avn?}是一个正交基。由于向量
Av1?,Av2?,…,Avn?的长度是
A的奇异值,且因为有
r个非零奇异值,
Avi?(r≥i≥1)为非零向量。所以
Av1?,Av2?,…,Avr?线性无关,且属于
ColA。
对任意属于
ColA的
y,如
y=Ax,我们可以写出
x=c1?v1?+?+cn?vn?,且
y=Ax=c1?Av1?+?+cr?Ar?vr?
这样,
y在
Span{Av1?,…,Avr?}中,这说明
{Av1?,Av2?,…,Avr?}是
ColA的一个正交基,因此
rankA=dim(ColA)=r。
由于
{Av1?,Av2?,…,Avr?}是
ColA的一个正交基,将每一个
Avi?单位化得到一个标准正交基
{u1?,u2?…ur?},此处
ui?=∥Avi?∥1?Avi?=σi?1?Avi?(r≥i≥1)
将
{u1?,u2?…ur?}扩充为
Rm的单位正交基
{u1?,u2?…um?}。
取
U=(u1?,u2?,…,um?),V=(v1?,v2?,…,vn?)
由构造可知,
U和
V是正交矩阵,
AV=(Av1?,…,Avr?,0,…,0)=(σ1?u1?,…,σr?ur?,0…,0)=UΣ
即:
A=UΣVT,从而得到
U。
知识点:任意给定一个实矩阵,其奇异值分解一定存在,但并不唯一。
奇异值分解的实现
1. 手动实现
import numpy as np
def rebuildMatrix(U, sigma, V):a = np.dot(U, sigma)a = np.dot(a, np.transpose(V))return a
def sortByEigenValue(Eigenvalues, EigenVectors):index = np.argsort(-1 * Eigenvalues)Eigenvalues = Eigenvalues[index]EigenVectors = EigenVectors[:, index]return Eigenvalues, EigenVectors
def SVD(matrixA, NumOfLeft=None):matrixAT_matrixA = np.dot(np.transpose(matrixA), matrixA)lambda_V, X_V = np.linalg.eig(matrixAT_matrixA)lambda_V, X_V = sortByEigenValue(lambda_V, X_V)sigmas = lambda_Vsigmas = list(map(lambda x: np.sqrt(x) if x > 0 else 0, sigmas))sigmas = np.array(sigmas)sigmasMatrix = np.diag(sigmas)if NumOfLeft is None:rankOfSigmasMatrix = len(list(filter(lambda x: x > 0, sigmas)))else:rankOfSigmasMatrix = NumOfLeftsigmasMatrix = sigmasMatrix[0:rankOfSigmasMatrix, :]X_U = np.zeros((matrixA.shape[0], rankOfSigmasMatrix))for i in range(rankOfSigmasMatrix):X_U[:, i] = np.transpose(np.dot(matrixA, X_V[:, i]) / sigmas[i])X_V = X_V[:, 0:rankOfSigmasMatrix]sigmasMatrix = sigmasMatrix[0:rankOfSigmasMatrix, 0:rankOfSigmasMatrix]return X_U, sigmasMatrix, X_VA = np.array([[4, 11, 14], [8, 7, -2]])
X_U, sigmasMatrix, X_V = SVD(A)
print(A)
print(X_U.shape)
print(sigmasMatrix.shape)
print(X_V.shape)
print(rebuildMatrix(X_U, sigmasMatrix, X_V))
2. 使用numpy.linalg.svd函数
import numpy as npA = np.array([[4, 11, 14], [8, 7, -2]])
print(A)
u, s, vh = np.linalg.svd(A, full_matrices=False)
print(u.shape)
print(s.shape)
print(vh.shape) a = np.dot(u, np.diag(s))
a = np.dot(a, vh)
print(a)
奇异值分解的应用
1. 数据压缩
奇异值分解可以有效地表示数据。例如,假设我们希望传输下面的图像,它由一个
25×15个黑色或白色像素组成的数组。
由于此图像中只有三种类型的列,如下图所示,因此可以用更紧凑的形式表示数据。
我们将图像表示为一个
25×15矩阵,矩阵的元素对应着图像的不同像素,如果像素是白色的话,就取 1,黑色的就取 0。 我们得到了一个具有375个元素的矩阵,如下图所示
如果对
M进行奇异值分解,我们会发现只有三个非零奇异值
σ1?=14.72,σ2?=5.22,σ3?=3.31(非零奇异值的数目等于矩阵的秩,在这个例子中,我们看到矩阵中有三个线性无关的列,这意味着秩将是3)。
M=σ1?u1?v1T?+σ2?u2?v2T?+σ3?u3?v3T?
vi?具有15个元素,
ui? 具有25个元素,
σi? 对应不同的奇异值。我们就可以用123个元素来表示具有375个元素的图像数据了。通过这种方式,奇异值分解可以发现矩阵中的冗余,并提供消除冗余的格式。
2. 去噪
前面的例子展示了如何利用许多奇异值为零的情况。一般来说,大的奇异值对应的部分会包含更多的信息。假设我们使用扫描仪将此图像输入计算机。但是,我们的扫描仪会在图像中引入一些缺陷(通常称为“噪声”)。
我们可以用同样的方法进行:用一个
25×15矩阵表示数据,并执行奇异值分解。我们发现以下奇异值:
σ1?=14.15,σ2?=4.67,σ3?=3.00,σ4?=0.21,…,σ15?=0.05
显然,前三个奇异值是最重要的,所以我们假设其它的都是由于图像中的噪声造成的,并进行近似。
M≈σ1?u1?v1T?+σ2?u2?v2T?+σ3?u3?v3T?
这导致下面的改进图像。
3. 数据分析
我们搜集的数据中总是存在噪声:无论采用的设备多精密,方法有多好,总是会存在一些误差的。如果你们还记得上文提到的,大的奇异值对应了矩阵中的主要信息的话,运用SVD进行数据分析,提取其中的主要部分的话,还是相当合理的。
假设我们收集了一些数据,如下所示:
我们可以将数据放入矩阵中:
[?1.03?2.23?0.741.61??0.02?0.02?0.510.88??1.31?2.39?0.992.02?0.691.62??0.12?0.35??0.72?1.67?1.112.46?]
经过奇异值分解后,得到
σ1?=6.04,σ2?=0.22。
由于第一个奇异值远比第二个要大,可以假设
σ2?的小值是由于数据中的噪声产生的,并且该奇异值理想情况下应为零。在这种情况下,矩阵的秩为1,意味着所有数据都位于
ui?定义的行上。
参考文献
- https://zhuanlan.zhihu.com/p/54693391
- http://www.ams.org/publicoutreach/feature-column/fcarc-svd