使用Python求解特征值、特征向量及奇异值分解(SVD)
SVD也是对矩阵进行分解,但是和特征分解不同,SVD并不要求要分解的矩阵为方阵。假设我们的矩阵A是一个m×n的矩阵,那么我们定义矩阵A的SVD为:A=UΣVT
其中U是一个m×m的矩阵,Σ是一个m×n的矩阵,除了主对角线上的元素以外全为0,主对角线上的每个元素都称为奇异值,V是一个n×n的矩阵。U和V都是酉矩阵,即满足UTU=I,VTV=I。
以下是一个SVD求解过程:
以下是我使用Python实现的SVD求解过程
# -*- coding: utf-8 -*-
"""
Created on Wed Oct 17 13:43:48 2018
@author: hanzi5
"""
import numpy as np
#import scipy as sc
# A=UΣVT
#A=np.mat([[0,1],[1,1],[1,0]])
#A=np.mat([[4,0],[3,5]])
#A=np.mat([[1,0,0,0,2],[0,0,3,0,0],[0,0,0,0,0],[0,4,0,0,0]])
A=np.mat([[4,4],[-3,3]])
#A=np.mat([[4,3],[8,6]])
ATA=A.T*A
AAT=A*(A.T)
#求解矩阵的特征值、特征向量
lambda1,vector1 = np.linalg.eig(ATA)
lambda2,vector2 = np.linalg.eig(AAT)
# 自己计算U、Σ、VT
U=vector2
#sigma=np.mat([[np.sqrt(3),0],[0,1],[0,0]])
#sigma=np.mat([[6.3245,0],[0,3.1622]])
#sigma=np.mat([[4,0,0,0,0],[0,3,0,0,0],[0,0,np.sqrt(5),0,0],[0,0,0,0,0]])
sigma=np.mat([[np.sqrt(32),0],[0,np.sqrt(18)]])
#sigma=np.mat([[np.sqrt(125),0],[0,0]])
VT=vector1.T # 注意需要对V矩阵进行转置
# 使用np自带包计算U、Σ、VT
U2,S,VT2=np.linalg.svd(A)
# 酉矩阵验证,两矩阵乘积为单位阵I
U.T*U
VT.T*VT
# 还原矩阵A
U*sigma*VT # 自己计算的结果
U2*sigma*VT2 # np计算结果
以上源代码中A矩阵我列出了多个,sigma矩阵是我根据A特征值开根号得到的奇异值,没有找到好的写法,所有手动把这个矩阵写出来了。
如果你运行了以上代码,可能会发现有多个结果U*sigma*VT与原矩阵A不相等,我最终在参考资料2“23、奇异值分解svd”一问中找到了答案,建议大家仔细看看这篇文章。以下引用原文一段作者答疑:
当SVD结果的矩阵出现以下这两种情况时:
1、需要整个矩阵添加相反的正负号才等于原矩阵;
2、SVD求出的矩阵中某一列(多列也一样)需要添加相反的正负号才等于原矩阵;
以上两种情况都认为:你求解SVD成功,求的这个SVD和原矩阵一致。
解SVD常遇见的疑问
1、需要添加正负符号问题
在例题2中出现了最后的矩阵需要再绝对值才等于原矩阵,或者出现某一列需要添加正负正反的符号才等于原矩阵,
为什么会这样?
答:原因是你所用的计算软件对“它”动的毛手毛脚。
我非常肯定这一点。在查阅网上大量相似问题又最后无解,自己在matlab中发现:如果你只是一步一步求解
AA^{T} 或者 A^{T}A ,最后将结果分别依照公式拼凑上去,那么问题就在你求解 AA^{T} 和 A^{T}A 的特征向量时,
计算软件有时会给某一列添加和你预想结果完全每一个都相反的符号。
所以在最后得出的计算结果中,将会出现某一列或者整个矩阵出现需要添加正负号才等于原矩阵的原因。
但是,这并不干扰你求解SVD的正确性。它给你添加的相反符号也没有错。
因为它们本质是“特征向量”,本质是线性或非线性方程的基础解析,只要它是相应的线性方程组的基础解析就没问题。
同一特征值的特征向量的非零线性组合,仍然是这个特征值的特征向量。比如,若 x 是方阵A某个特征值对应的特征向量,
则kx 也是A对应于该特征值的特征向量。(k取正数或者负数,不取0)在之前的章节里,
很清楚得说明白了k是基础解析的k倍,基础解析的k倍,无论k几倍都是某个特征值的特征向量,
它们都存在在一条轴上,一条由同一个方向上各个长短不同的特征向量组成的“旋转轴”。
所以对某一列向量,或者所有的列向量添加相反的符号(乘于-1),得到的SVD分解结果都是正确的。
但若非整个列向量取相反符号,而是仅仅存在某一个分量有符号问题,那就是你求解中出现了问题。
但是!这种情况却有一个例外,请参考下一点 2
2、“求错”的地方刚好撞见“转置”
比如,在麻省理工大学Gilbert Strang教授讲的《线性代数》的奇异值分解那一课,他出现错误的问题在于,
“求错”的地方刚好撞见“转置”,V矩阵正确的符号应该是:
正 负
正 正
他求成了:
正 正
正 负
本来这样也是没错的,因为在1中,我说特征向量kx和x一样,在上面两个矩阵中,下面的列2只不过乘于-1就
等于上面的列2,本来是正确的,但是问题就在于,V矩阵最后在SVD公式里需要转置,而转置的位置刚好就是将E12变到E21,
将E21变到E12,所以原本在属于那一列中没有错误的计算,转置后就错误了。解决的方法并不是手解,
我想没人蠢到用手解解一个"万阶矩阵"吧?有的话请告诉我。。。解决的方法在下一段将出现。
(下一段为:计算软件求解SVD的要注意的问题)
3、其它常见出错的问题
有可能是V没有进行转置:反正我是看到Gilbert Strang教授那一课中,其实不仅没有发现“错误点”在转置位置,而且,
他还忘了对V进行转置。(所幸当时那个矩阵转不转置两者都一样,所以他忘了也没什么。解错,这不毫不奇怪,
但可笑是,国内的相关文章上都依照他错误的方式去解,不仅没有解答真正的本质在于转置位置,
就连V需要转置这么基础的也没有一个人记得。我看过很多文章,大家都生搬硬抄直接将黑板的写上去,
完全不怀疑他有没有可能写错了。真是读死书。也怪不得国外有Wolframalpha、Mathpad、Mathematics等等软件,
国内却只能在每个软件的评论区喊着“有没有中文版?”)
有可能是U的符号出现错误
在多次实验当中,我发现如果最后的数字出现了错误,那么很可能是因为左乘矩阵出现了问题,
在放到这个SVD的例子当中,SVD的左乘矩阵就是U。
也就是可以这么总结:
如果只是列向量的正负出现问题,要么你是在V上出心大意忘了添加某个符号,要么就是你刚好碰上"错误点"和"转置位置";
如果你发现整个SVD结果的矩阵数字都出现问题了,数字都完全不相同了,而且可能正负号都不按规律来,那么,首先检查左乘矩阵U矩阵的正负符号。
计算软件求解SVD的要注意的问题
首先,用哪一款软件计算SVD都不是重点,重点是:不要一个一个去求,即是不要去求 AA^{T} 和 AA^{T} ,
然后再一一去求各自的特征向量组成V、U。千万不要这样去求!!!
因为有可能出现上面出现常见问题1中描述的,当正确而又不恰当的特征向量的分量出现在V的E12转置位置时,
有问题出现。所以,我们在用计算软件求解SVD时,请直接使用求解SVD的命令。而不要一一求解,再自己去组合计算。
最后提醒大家千万不要用Python包一个一个去求ATA以及AAT的特征值、特征向量,你会发现你求得的UΣVT与原矩阵不相等!!!
本文只是帮大家试下错,Python一定要用np.linalg.svd(A)求解SVD,千万不要单独求解,切记!!
参考资料:
1、特征值分解、奇异值分解、PCA概念整理
2、23、奇异值分解svd
3、奇异值分解(SVD)原理与在降维中的应用