所以我正在尝试学习 SVD 如何在 PCA(主成分分析)中使用它,但问题是它接缝我得到错误的结果,我尝试使用
np.linalg.svd
,这是我的代码:
A = np.array([[2, 2],
[1, 1]])
u, s, v = np.linalg.svd(A, full_matrices=False)
print(u)
print(s)
print(v)
这是我得到的结果:
[[-0.89442719 -0.4472136 ]
[-0.4472136 0.89442719]]
[3.16227766e+00 1.10062118e-17]
[[-0.70710678 -0.70710678]
[ 0.70710678 -0.70710678]]
我尝试在 WolframAlpha 上进行 SVD 分解,我得到了这些结果:
值的大小似乎正确但符号不正确,甚至我在 MIT OpenCourseWare 上观看了一位教授的视频在 youtube 上,他给出了这些结果:
大小相同但符号不同的答案,那么可能出了什么问题?
返回矩阵的约定不同
v
:
numpy.linalg.svd
(强调我的)的文档:
linalg.svd(a, full_matrices=True, compute_uv=True, hermitian=False)
奇异值分解。
当a是一个二维数组,并且full_matrices=False,那么它被分解为
,其中u @ np.diag(s) @ vh = (u * s) @ vh
和u
的Hermitian转置是具有正交列的二维数组,s是a的一维数组奇异值。当vh
是高维时,SVD 以堆叠模式应用,如下所述。a
Returns:
array Unitary array(s).首先u{ (…, M, M), (…, M, K) }
维度与输入a.ndim - 2
的大小相同。这 最后两个维度的大小取决于 full_matrices 的值。 仅在 compute_uv 为 True 时返回。a
array Vector(s) with the singular values, within each vector 按降序排列。第一个s(…, K)
维度有 与输入的大小相同a.ndim - 2
.a
vh
数组单一数组。第一个{ (…, N, N), (…, K, N) }
- 2 个维度的大小与输入 a 的大小相同。的大小 最后两个维度取决于 full_matrices 的值。仅有的 当 compute_uv 为真时返回。a.ndim
总结一下:给定
x
、x = u @ np.diag(s) @ vh
的 SVD 分解,numpy.linalg.svd(x)
返回的矩阵是 u
、s
和 vh
,其中 vh
是 v
的厄密共轭。其他库和软件将返回v
,导致明显的不一致。
遗憾的是,不同的图书馆有不同的约定,这也让我第一次不得不处理这个问题时感到很沮丧。
u
和 v
此外,问题的数学意味着矩阵
u
和v
不是唯一确定的.
为了检查 SVD 是否正确,您需要检查矩阵
u
和 v
确实是 酉 和 x = u @ np.diag(s) @ vh
。如果两个条件都成立,则 SVD 是正确的,否则不是。
numpy
库这里是一些简单的代码,用于检查 SVD 库在 numpy 中的实现是否确实正确(当然是,认为这是一个教学练习):
import numpy as np
A = np.array([[2, 2],
[1, 1]])
u, s, vh = np.linalg.svd(A, full_matrices=False)
smat = np.diag(s)
def is_unary(A):
return np.allclose(A @ A.T, np.eye(A.shape[0]))
print(f"Is u unary? {is_unary(u)}")
print(f"Is vh unary? {is_unary(vh)}")
print(f"original matrix A")
print(A)
print("Reconstructed matrix A")
print(u @ smat @ vh)
def check_svd(A, u, s, vh):
smat = np.diag(s)
c1 = is_unary(u)
c2 = is_unary(vh)
c3 = np.allclose(A, u @ smat @ vh)
success = c1 and c2 and c3
if not success:
raise Exception("numpy SVD failed")
for i in range(100):
A = np.random.rand(100, 100)
u, s, vh = np.linalg.svd(A, full_matrices=False)
check_svd(A, u, s, vh)
print("All tests passed")