为什么我自己在 python 中简单实现的 svd 算法不起作用?
Why does my own simple Implemantion of the svd algorithm in python not work?
我在 Python 中实现了一个非常简单的奇异值分解(不用担心我知道 numpy 版本),它更有趣。它基于 A^TA 和 AA^T 的特征向量的计算(参见示例 here)。这在理论上很容易实现 Python:
def calc_svd(A: np.array):
ATA = A.T@A
AAT = A@A.T
#compute the eigenvectors and sort them in ascending size of the eigenvalues
eigenvalues_ATA, eigenvectors_ATA = np.linalg.eig(ATA)
ATA_sorted = np.flip(np.argsort(eigenvalues_ATA))
eigenvectors_ATA = eigenvectors_ATA[:, ATA_sorted]
eigenvalues_AAT, eigenvectors_AAT = np.linalg.eig(AAT)
AAT_sorted = np.flip(np.argsort(eigenvalues_AAT))
eigenvalues_AAT = eigenvalues_AAT[AAT_sorted]
eigenvectors_AAT = eigenvectors_AAT[:, AAT_sorted]
sing_values = np.sqrt(eigenvalues_AAT)
return U, sing_values, V.T
据我所知,这应该是 return 正确的矩阵。但是这有一个问题:
test = np.array([[1.0, 1.0, 0.0], [1.0, 3.0, 1.0], [2.0, -1.0, 1.0]])
u_np, svd_np, v_np = np.linalg.svd(test2)
u_own, svd_own, v_own = calc_svd(test2)
print(u_np@np.diag(svd_np)@v_np)
print(u_own@np.diag(svd_own)@v_own)
产生以下输出:
[[ 1.00000000e+00 1.00000000e+00 5.76517542e-16]
[ 1.00000000e+00 3.00000000e+00 1.00000000e+00]
[ 2.00000000e+00 -1.00000000e+00 1.00000000e+00]]
[[-0.53412934 -0.91106401 -0.94056803]
[-1.17456455 -3.03332485 -0.64756347]
[-2.08209125 0.98432856 -0.83426215]]
问题似乎是我的实现中U和V的一些特征向量是numpy之一的负值:
print(u_np)
print(u_own)
#results in
[[-0.35881632 0.13270783 -0.92392612]
[-0.9317945 -0.10910581 0.34620071]
[-0.05486216 0.98513174 0.16280537]]
[[ 0.35881632 0.13270783 -0.92392612]
[ 0.9317945 -0.10910581 0.34620071]
[ 0.05486216 0.98513174 0.16280537]]
#and
print(v_np)
print(v_own)
#in
[[-0.39543728 -0.87521453 -0.27861961]
[ 0.80500544 -0.47631006 0.35368767]
[-0.44226191 -0.08442901 0.89290321]]
[[-0.39543728 -0.87521453 -0.27861961]
[-0.80500544 0.47631006 -0.35368767]
[-0.44226191 -0.08442901 0.89290321]]
我的问题是我不明白为什么这不起作用。从数学的角度来看,采用特征向量 v 还是 -v 应该没有区别,因为它们都是具有相同特征值的特征向量。那么也许有人可以告诉我推理中的错误。
非常感谢您:)
该方法本身是正确的,但是您遇到了问题,因为 U 和 V 的特征向量在乘以 -1 时不是唯一确定的。
身份很重要
A * v_i = σ_i * u_i
适用于列 v_i、u_i 和奇异值 u_i。
我在 Python 中实现了一个非常简单的奇异值分解(不用担心我知道 numpy 版本),它更有趣。它基于 A^TA 和 AA^T 的特征向量的计算(参见示例 here)。这在理论上很容易实现 Python:
def calc_svd(A: np.array):
ATA = A.T@A
AAT = A@A.T
#compute the eigenvectors and sort them in ascending size of the eigenvalues
eigenvalues_ATA, eigenvectors_ATA = np.linalg.eig(ATA)
ATA_sorted = np.flip(np.argsort(eigenvalues_ATA))
eigenvectors_ATA = eigenvectors_ATA[:, ATA_sorted]
eigenvalues_AAT, eigenvectors_AAT = np.linalg.eig(AAT)
AAT_sorted = np.flip(np.argsort(eigenvalues_AAT))
eigenvalues_AAT = eigenvalues_AAT[AAT_sorted]
eigenvectors_AAT = eigenvectors_AAT[:, AAT_sorted]
sing_values = np.sqrt(eigenvalues_AAT)
return U, sing_values, V.T
据我所知,这应该是 return 正确的矩阵。但是这有一个问题:
test = np.array([[1.0, 1.0, 0.0], [1.0, 3.0, 1.0], [2.0, -1.0, 1.0]])
u_np, svd_np, v_np = np.linalg.svd(test2)
u_own, svd_own, v_own = calc_svd(test2)
print(u_np@np.diag(svd_np)@v_np)
print(u_own@np.diag(svd_own)@v_own)
产生以下输出:
[[ 1.00000000e+00 1.00000000e+00 5.76517542e-16]
[ 1.00000000e+00 3.00000000e+00 1.00000000e+00]
[ 2.00000000e+00 -1.00000000e+00 1.00000000e+00]]
[[-0.53412934 -0.91106401 -0.94056803]
[-1.17456455 -3.03332485 -0.64756347]
[-2.08209125 0.98432856 -0.83426215]]
问题似乎是我的实现中U和V的一些特征向量是numpy之一的负值:
print(u_np)
print(u_own)
#results in
[[-0.35881632 0.13270783 -0.92392612]
[-0.9317945 -0.10910581 0.34620071]
[-0.05486216 0.98513174 0.16280537]]
[[ 0.35881632 0.13270783 -0.92392612]
[ 0.9317945 -0.10910581 0.34620071]
[ 0.05486216 0.98513174 0.16280537]]
#and
print(v_np)
print(v_own)
#in
[[-0.39543728 -0.87521453 -0.27861961]
[ 0.80500544 -0.47631006 0.35368767]
[-0.44226191 -0.08442901 0.89290321]]
[[-0.39543728 -0.87521453 -0.27861961]
[-0.80500544 0.47631006 -0.35368767]
[-0.44226191 -0.08442901 0.89290321]]
我的问题是我不明白为什么这不起作用。从数学的角度来看,采用特征向量 v 还是 -v 应该没有区别,因为它们都是具有相同特征值的特征向量。那么也许有人可以告诉我推理中的错误。
非常感谢您:)
该方法本身是正确的,但是您遇到了问题,因为 U 和 V 的特征向量在乘以 -1 时不是唯一确定的。
身份很重要
A * v_i = σ_i * u_i
适用于列 v_i、u_i 和奇异值 u_i。