[pytorch]torch.svd() 计算错误的结果(与 numpy.linalg.svd() 相比)

2024-03-20 910 views
8
可能吗?漏洞

也许我使用的 API 是错误的(但是 numpy 和 torch 函数的参数是相同的),但是链接的矩阵会产生完全不同的伪逆矩阵。

import numpy as np, torch

array = np.load('invertme.npy')
pinv_np = np.linalg.pinv(array)
pinv_th = torch.pinverse(torch.tensor(array))

assert np.allclose(pinv_np, pinv_th)

回答

8

我将其范围缩小到 SVD 的应用。numpy 和 pytorch 的结果有很大不同。U、S、V中只有S相同,其他不同。各自的文档指出,gesdd这两种情况都使用了 LAPACK,因此如果这是由于数字问题引起的,我会感到惊讶。

0

这是 U 的 numpy 和 pytorch 结果之间的差异图。

图1

3

如果这是真的,这听起来很糟糕

4

如果有人能尽快看一下这个,我将不胜感激,因为它现在严重阻碍了我。不确定我能提供什么帮助,但我欢迎建议。

2

我可以在 CPU(使用 LAPACK)和 GPU(使用 MAGMA)上重现这一点。

6

我将其标记为 1.0.1 拦截器。如果您需要进度帮助,请告诉我们@vishwakftw

7

当然可以,谢谢。

3

只是为了明确@themightyoarfish,如果矩阵是病态的,并且因为 SVD 是一种迭代方法——如果 pytorch 和 numpy 从不同的点开始,它们可能会得到不同的解决方案。所以我正在等待维什瓦克的调查结束。但我也想确保人们不会事先感到惊慌。

8

我可以确认矩阵的病态非常严重。

条件数 = 最大奇异值 / 最小奇异值 ~ 10^8 。

仍在寻找不一致之处。

4

我懂了。这很有趣;问题发生在谷歌的 svcca上下文中,其中输入矩阵由一组数据点上的一层神经元的激活组成,即每一行都是一个神经元的激活。我想知道这样一个矩阵病态的事实是否告诉了我什么。

7

是否有任何(可能效率较低)方法来获得病态矩阵的伪逆?

7

我认为这个问题可能仅限于病态矩阵。以下脚本证明了这种行为:

import torch

device = 'cpu'  # modify to test on a different device
N = 954  # size

# Step 1: create an conditioned singular value vector
s = torch.arange(N + 1, 1, -1, device=device).float().log()  # the condition number here is ~ log(N)
s_mat = torch.diag(s)

# Step 2: generate orthogonal matrices using QR on a random matrix
q, _ = torch.randn(N, N, device=device).qr()
assert (q @ q.t() - torch.eye(N, device=device)).abs().max() < 1e-04

# Step 3: generate the well-conditioned matrix
a = q @ s_mat @ q.t()
u, sigma, v = a.svd()
assert (a - u @ torch.diag(sigma) @ v.t()).abs().max() < 1e-04

我已经在 CPU(使用 LAPACK)和 GPU(使用 MAGMA)上运行了多次(~ 100)次,并且在所有试验中断言都没有失败。

4

非常感谢您快速调查这个 Vishwak。我将关闭它,因为它是病态矩阵上的预期行为。

@themightyoarfish 病态矩阵的伪逆,这在数学上是否可能作为保证?我不是线性代数专家,但我认为矩阵首先必须有一些约束。再说一次,我不是专家:)我想@SsnL 会知道他在这里是否有很多经验。

9

是的,我不确定我现在在问什么,因为现实更复杂(svcca 计算激活协方差矩阵的伪逆)。但如果协方差矩阵是病态的,也许它能提供某种信息?

1

是的,U 和 V 对于奇异输入矩阵来说并不是唯一的(考虑 e 值 = 0 时的 e 向量)。所以这是预料之中的。

2

torch.svd 与 numpy、scipy 和 tensorflow svd 不同

3

因此,如果使用 torch.svd 进行训练,则无法将模型移植到其他框架

0

@iperov 仅适用于与奇异 e 值相对应的 UV 矩阵部分。你永远不应该依赖那些。

0

抱歉,但是您链接的 NVIDIA WCT 脚本显然会过滤掉零个奇异值。

9

我正在尝试将 torch WCT 移植到纯 numpy,以便传输https://github.com/clovaai/WCT2存储库的解码器层的样式,但结果非常不同,因为网络接受了错误的 svd 实现训练。

4

来自 WCT2 论文: firefox_2019-03-28_00-43-37 WCT 在训练中使用了错误的 svd。

6

同一个矩阵可以有多个 SVD 分解。我什至不知道你在抱怨什么。

4

我已经告诉了足够的信息。如果您不知道什么 - 就离开这个帖子。

4

照片 WCT 使用v值来重建特征。但如果这些值不同,结果也会不同,因为v矩阵乘法中使用的值。

c_u, c_e, c_v = torch.svd(contentConv, some=False)
s_u, s_e, s_v = torch.svd(styleConv, some=False)

step1 = torch.mm(c_v[:, 0:k_c], torch.diag(c_d))
step2 = torch.mm(step1, (c_v[:, 0:k_c].t()))
whiten_cF = torch.mm(step2, cont_feat)

s_d = (s_e[0:k_s]).pow(0.5)
targetFeature = torch.mm(torch.mm(torch.mm(s_v[:, 0:k_s], torch.diag(s_d)), (s_v[:, 0:k_s].t())), whiten_cF)
0

( numpy.svd == scipy.svd == tf.svd ) != torch.svd

这太糟糕了:(

1

我对 pytorch 中的 gesvd/gesdd 绑定非常熟悉,我认为您从未告诉过有关您所看到的问题的任何有用信息,即矩阵是奇异的吗?如果是这样,它的状况有多差?np、tf、pytorch 和其他库的输出是什么?而且,最重要的是,您是否使用零空间的奇异向量?

并且您坚持认为您所指的两个存储库使用零奇异值的奇异向量。我只是指出那些没有被使用。例如,在 NVIDIA WCT 的片段中,k_ck_s是排名。因此,与您声称的相反,这些向量没有被使用。

7

你好,我在 torch 中遇到了 svd 问题,并找到了这个线程。如果你愿意的话,这里有一些“证据”。

R=[[ 0.41727819, -0.87345426,  0.25091147],
       [ 0.32246181,  0.40043949,  0.85771009],
       [-0.84964539, -0.27699435,  0.44875031]]
Rnp = np.array(R)
Rt = torch.from_numpy(Rnp)

# numpy
U,S,V=np.linalg.svd(np.array(R))
np.matmul(U,V) # gets back R as expected

# torch
U,S,V=torch.svd(Rt)
torch.matmul(U,V) # did not gets back R, and that's causing me headache...:(
2

@zawlin 你好,np返回V^Hpytorch返回V,所以你错过了 V 上的转置!


In [1]: import torch

In [2]: import numpy as np

In [3]: R=[[ 0.41727819, -0.87345426,  0.25091147],
   ...:        [ 0.32246181,  0.40043949,  0.85771009],
   ...:        [-0.84964539, -0.27699435,  0.44875031]]
   ...: Rnp = np.array(R)
   ...: Rt = torch.from_numpy(Rnp)
   ...:

In [4]:

In [4]: U,S,V=np.linalg.svd(np.array(R))

In [5]: S
Out[5]: array([1., 1., 1.])

In [6]: Rt.svd().S
Out[6]: tensor([1.0000, 1.0000, 1.0000], dtype=torch.float64)

In [7]:
   ...: # numpy
   ...: U,S,V=np.linalg.svd(np.array(R))
   ...: np.matmul(U,V) # gets back R as expected
Out[7]:
array([[ 0.41727819, -0.87345426,  0.25091147],
       [ 0.32246181,  0.40043949,  0.85771009],
       [-0.84964539, -0.27699435,  0.44875031]])

In [8]: # torch
   ...: U,S,V=torch.svd(Rt)
   ...: torch.matmul(U,V.t()) # did not gets back R, and that's causing me headache...:(
Out[8]:
tensor([[ 0.4173, -0.8735,  0.2509],
        [ 0.3225,  0.4004,  0.8577],
        [-0.8496, -0.2770,  0.4488]], dtype=torch.float64)

In [9]:
7

@SsnL,哦,非常感谢。对于噪音表示歉意。

3

@zawlin 不用担心!

4

@SsnL 很抱歉问同样的问题,您可能已经解释清楚了。我对 SVD 不太熟悉,但我确实遇到过类似的情况,我想将 numpy/scipy.linalg.svd 转换为 pytorch,希望有完全相同的分解。不幸的是,我确实需要所有 U、S、V 矩阵来执行以下代码。我并不是说这是一个错误(因为我不知道里面的技术),我只是想问,与 numpy/scipy 相比,没有解决方法可以获得完全相同的结果吗?如果是这样的话,我想我会在 CPU 上使用 numpy 计算 SVD,并将它们转换回 GPU 用于 pytorch...

这是我使用的 scipy 函数: U, S, V = scipy.linalg.svd(X, full_matrices=False, lapack_driver='gesvd')

这里讨论了在 pytorch 中包含“ gesvd ”方法的努力:https: //github.com/pytorch/pytorch/issues/25978

2

这里只是评论一下。两个链接片段中的排名计算代码似乎不正确:如果没有奇异值 >= 0.00001,则它们默认为满排名,而不是零排名。OTOH,如果保证排名 >= 1,则代码将是正确的,但根本不需要默认值,因为循环中的 if 块始终会被输入,并且排名将在那里设置。

例如https://github.com/NVIDIA/FastPhotoStyle/blob/master/photo_wct.py#L150-L154