本想使用python实现的一下SVD的各个步骤,然后就用一些库来实现特征向量和特征值的求解分解,然后最后得到SVD的结果,但是在上手的过程中,发生了一些很诡异的事情,在这里记录一下
首先是以下的实现
def SVD(A):
AtA = np.dot(A.T, A)
AAt = np.dot(A, A.T)
AtAe, AtAv = np.linalg.eig(AtA)
AAte, AAtv = np.linalg.eig(AAt)
print(AtAe)
print(AtAv)
dia=np.eye(A.shape[0],A.shape[1])*np.sqrt(AtAe)
return AAtv,dia,AtAv.T
SVD的思路很简单,给一个需要进行SVD的矩阵A(以下以https://zhuanlan.zhihu.com/p/29846048中的矩阵A为例)
这是我们的A:
[[0 1]
[1 1]
[1 0]]
1、计算 、以及其特征值(AtAe)和特征向量(AtAv),结果如下:
AtA:[[2 1]
[1 2]]AtAe:[3. 1.]
AtAv:[[ 0.70710678 -0.70710678]
[ 0.70710678 0.70710678]]
可以看到和源例子中手解的结果相同
2、计算、以及其特征值(AAte)和特征向量(AAtv),结果如下:
AtA:[[1 1 0]
[1 2 1]
[0 1 1]]
AAte:[ 3.00000000e+00 1.00000000e+00 -3.36770206e-17]
AAtv:[[-4.08248290e-01 7.07106781e-01 5.77350269e-01]
[-8.16496581e-01 2.61214948e-16 -5.77350269e-01]
[-4.08248290e-01 -7.07106781e-01 5.77350269e-01]]
可以看到和源例子中手解的结果当特征值为3的时候,特征向量相差一个负号,但是不影响特征值特征向量的性质
3、最后使用,程序中为dia,
[[1.73205081 0. ]
[0. 1. ]
[0. 0. ]]
4、返回AAtv、dia、AtAv.T作为U,S,V完成SVD分解
AAtv:[[-4.08248290e-01 7.07106781e-01 5.77350269e-01]
[-8.16496581e-01 2.61214948e-16 -5.77350269e-01]
[-4.08248290e-01 -7.07106781e-01 5.77350269e-01]]
dia:[[1.73205081 0. ]
[0. 1. ]
[0. 0. ]]
AtAv.T:[[ 0.70710678 0.70710678]
[-0.70710678 0.70710678]]
但是当演算A=U*S*V的时候,得到的结果却是
U*S*V:[[-1.00000000e+00 3.32444579e-16]
[-1.00000000e+00 -1.00000000e+00]
[-1.21168839e-16 -1.00000000e+00]]
并不等于源A
因此就想要直接调用np.linalg.svd看一下它给出的结果是什么,结果如下
U:[[-4.08248290e-01 7.07106781e-01 5.77350269e-01]
[-8.16496581e-01 2.64811510e-17 -5.77350269e-01]
[-4.08248290e-01 -7.07106781e-01 5.77350269e-01]]
S:[1.73205081 1. ]
V:[[-0.70710678 -0.70710678]
[-0.70710678 0.70710678]]
发现它所求出的U、S和我求出的相同,但是V却和我的大有不同,查遍所有资料,后来在https://zhuanlan.zhihu.com/p/43578482中找到解释,由于V是求解出来的特征向量需要在进行transpose,然而计算机给出的特征向量可能会有一个符号的差距,而那个符号的差距正好可能会被tranpose,就导致了最终的结果不正确