用numpy的eigh和svd计算的特征向量不匹配

mat*_*tus 16 python numpy svd eigenvector

考虑奇异值分解M = USV*.然后,M*M的特征值分解给出M*M = V(S*S)V*= VS*U*USV*.我希望通过显示eigh函数返回的特征向量与函数返回的特征向量相同来验证numpy的这种相等svd性:

import numpy as np
np.random.seed(42)
# create mean centered data
A=np.random.randn(50,20)
M= A-np.array(A.mean(0),ndmin=2)

# svd
U1,S1,V1=np.linalg.svd(M) 
S1=np.square(S1)
V1=V1.T  

# eig
S2,V2=np.linalg.eigh(np.dot(M.T,M))
indx=np.argsort(S2)[::-1]
S2=S2[indx]
V2=V2[:,indx]

# both Vs are in orthonormal form
assert np.all(np.isclose(np.linalg.norm(V1,axis=1), np.ones(V1.shape[0])))
assert np.all(np.isclose(np.linalg.norm(V1,axis=0), np.ones(V1.shape[1])))
assert np.all(np.isclose(np.linalg.norm(V2,axis=1), np.ones(V2.shape[0])))
assert np.all(np.isclose(np.linalg.norm(V2,axis=0), np.ones(V2.shape[1])))

assert np.all(np.isclose(S1,S2))
assert np.all(np.isclose(V1,V2))
Run Code Online (Sandbox Code Playgroud)

最后一个断言失败了.为什么?

gg3*_*349 14

只需使用小数字来调试您的问题.

A=np.random.randn(3,2)大小开始,而不是更大的矩阵(50,20)

在我的随机案例中,我发现了这一点

v1 = array([[-0.33872745,  0.94088454],
   [-0.94088454, -0.33872745]])
Run Code Online (Sandbox Code Playgroud)

并为v2:

v2 = array([[ 0.33872745, -0.94088454],
   [ 0.94088454,  0.33872745]])
Run Code Online (Sandbox Code Playgroud)

它们只对符号有所不同,显然,即使标准化为具有单位模块,矢量也可以因符号而不同.

现在,如果你尝试这个技巧

assert np.all(np.isclose(V1,-1*V2))
Run Code Online (Sandbox Code Playgroud)

对于你原来的大矩阵,它失败了...再次,这没关系.发生的事情是有些载体已经成倍增加-1,有些则没有.

检查向量之间相等性的正确方法是:

assert allclose(abs((V1*V2).sum(0)),1.)
Run Code Online (Sandbox Code Playgroud)

事实上,为了了解这是如何工作的,你可以打印这个数量:

(V1*V2).sum(0)
Run Code Online (Sandbox Code Playgroud)

确实是+1或者-1取决于向量:

array([ 1., -1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
    1., -1.,  1.,  1.,  1., -1., -1.])
Run Code Online (Sandbox Code Playgroud)

编辑:在大多数情况下会发生这种情况,特别是从随机矩阵开始.但是请注意,如果一个或多个特征值的维度空间大于大于1,如@Sven Marnach在下面的评论中指出的那样,该测试可能会失败:

除了矢量乘以-1之外,可能还有其他差异.如果任何特征值具有多维本征空间,则可能得到该本征空间的任意标准正交基,并且这些基本可以通过任意单位矩阵相互旋转.