我想使用下面的代码计算一批特征向量的协方差矩阵(这里用 torch.randn 代替):
import torch
a = torch.randn(64, 512)
cov_mat = (a.T @ a) / (a.size(0) - 1)
生成的 cov_mat 是对称的半正定的。所以我通过添加
cov_mat = (cov_mat + cov_mat.T) / 2
来更改上面的子句
现在协方差矩阵是对称的,但仍然不是半正定的。我试过 torch.mm 但还是没用。跟精度有关系吗?
这里是具体版本:
蟒蛇3.7
PyTorch 1.8.1
我也遇到过类似的问题,结论确实是精度问题。当我执行你的代码时(没有你的修改),我观察到 cov_mat 几乎是对称的,误差在 1e-6 左右。正如here所解释的那样,这实际上是预期的:
浮点精度限制了您可以准确依赖的位数。 torch.float32 的精度在 6 到 7 位数字之间,与指数无关,即值的精度问题是预期的< 1e-6. Similarly torch.float64 will have precision issues for values < 1e-15.
如果你真的需要更高的精度,你可以使用 torch.float64 代替:
# with the default float32:
a = torch.randn(64, 512)
c = (a.T @ a) / (a.size(0) - 1)
((c - c.T) < 1e-7).all() >>> tensor(False)
((c - c.T) < 1e-6).all() >>> tensor(True)
# with float64:
a = torch.randn(64, 512, dtype=torch.float64)
c = (a.T @ a) / (a.size(0) - 1)
((c - c.T) == 0).all() >>> tensor(True)
虽然您使用
cov_mat = (cov_mat + cov_mat.T) / 2
的解决方法也使用 float32 实现了类似的精度(在矩阵完全对称的意义上),但它会使矩阵“更少”psd。
不幸的是,无论你做什么,你总是会遇到精度错误(float32 为 1e-6,float64 为 1e-15)。因此,我们可以看到协方差矩阵的特征值可能略低于零。我尝试手动将特征值设置为0,但仍然有错误。
TL;DR 我最后的建议是:
c += 1e-12 * torch.eye(a.size(1))
(如果你不需要它我不推荐它因为它修改了矩阵)希望对你有帮助!