You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义SVD实现无法还原原矩阵,与numpy官方实现存在符号差异

SVD实现中的矩阵还原与符号问题

我自己实现了一个SVD算法,但有时候无法还原原始矩阵。以下是我的实现代码:

import numpy as np

def svd(A):
    eigvals_left, eigvecs_left = np.linalg.eig(A @ A.T)

    eigvals_right, eigvecs_right = np.linalg.eig(A.T @ A)

    sigma = np.sqrt(np.abs(eigvals_right))

    num = min(A.shape)

    sorted_indices = np.argsort(-sigma)
    sigma = sigma[sorted_indices[:num]]

    U = eigvecs_left[:, np.argsort(-eigvals_left)[:num]]
    V = eigvecs_right[:, np.argsort(-eigvals_right)[:num]]

    return U, sigma, V.T


h = np.random.rand(2, 3)

print(h)

j, k, l = svd(h)
x, y, z = np.linalg.svd(h, compute_uv=True, full_matrices=False)

print('---------------')
print(j @ np.diag(k) @ l)
print('---------------')
print(x @ np.diag(y) @ z)
print('---------------')

print(l @ z.T)

我发现一个奇怪的现象:自己实现得到的l(即V^T)和numpy自带SVD返回的z,绝对值基本一致,但始终存在符号差异。虽然已经对特征向量做了排序,l和z的转置乘积是近似对角矩阵,但对角线上的符号时而正时而负,导致偶尔还原出的矩阵和原始矩阵符号完全相反。

输出示例1

[[0.53221807 0.59786549 0.09906266]
 [0.4031512  0.38389025 0.16900433]]
---------------
[[-0.55253742 -0.55534658 -0.19184685]
 [-0.37481911 -0.44317609 -0.03963159]]
---------------
[[0.53221807 0.59786549 0.09906266]
 [0.4031512  0.38389025 0.16900433]]
---------------
[[ 1.00000000e+00  7.40766293e-17]
 [-4.02870278e-17  1.00000000e+00]]

输出示例2

[[0.23044744 0.86795113 0.64293873]
 [0.80871952 0.17031212 0.33708637]]
---------------
[[0.23044744 0.86795113 0.64293873]
 [0.80871952 0.17031212 0.33708637]]
---------------
[[0.23044744 0.86795113 0.64293873]
 [0.80871952 0.17031212 0.33708637]]
---------------
[[-1.00000000e+00 -2.56188682e-16]
 [-4.53777597e-16  1.00000000e+00]]

问题根源

特征向量的符号是不唯一的:np.linalg.eig返回的特征向量,其方向(正负)是随机的。你的实现中,U的列是基于A@A.T的特征向量排序,V的列是基于A.T@A的特征向量排序,两者的符号没有同步校准。当U的某列和对应的V的列符号相反时,U @ diag(sigma) @ V.T就会出现符号翻转,导致还原矩阵错误。

解决方法

需要根据原矩阵A来校准U和V的符号,确保对于每个奇异值sigma[i],满足U[:,i].T @ A @ V[:,i] = sigma[i](因为奇异值都是非负的)。修改后的代码如下:

import numpy as np

def svd(A):
    eigvals_left, eigvecs_left = np.linalg.eig(A @ A.T)
    eigvals_right, eigvecs_right = np.linalg.eig(A.T @ A)

    sigma = np.sqrt(np.abs(eigvals_right))
    num = min(A.shape)

    # 对奇异值排序,获取索引
    sorted_indices_sigma = np.argsort(-sigma)[:num]
    sigma = sigma[sorted_indices_sigma]

    # 对U和V的特征向量按奇异值对应的顺序排序(A@A.T和A.T@A的非零特征值相同)
    sorted_indices_left = np.argsort(-eigvals_left)[:num]
    U = eigvecs_left[:, sorted_indices_left]
    sorted_indices_right = np.argsort(-eigvals_right)[:num]
    V = eigvecs_right[:, sorted_indices_right]

    # 校准U和V的符号
    for i in range(num):
        val = U[:, i].T @ A @ V[:, i]
        if val < 0:
            U[:, i] = -U[:, i]
            V[:, i] = -V[:, i]

    return U, sigma, V.T


h = np.random.rand(2, 3)

print(h)

j, k, l = svd(h)
x, y, z = np.linalg.svd(h, compute_uv=True, full_matrices=False)

print('---------------')
print(j @ np.diag(k) @ l)
print('---------------')
print(x @ np.diag(y) @ z)
print('---------------')

print(l @ z.T)

这样修改后,U和V的符号会同步,还原出的矩阵就能和原始矩阵一致,符号差异问题也会解决。

内容的提问来源于stack exchange,提问作者chengyongru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:34:52