PyTorch渐变与手动计算的渐变不同

时间:2018-11-13 05:48:38

标签: python gradient pytorch derivative

我试图在不使用Pytorch的autograd的情况下计算1 / x的梯度。我使用公式grad(1 / x,x)= -1 / x ** 2。当我将这个公式的结果与Pytorch的autograd给出的梯度进行比较时,它们是不同的。

这是我的代码:

a = torch.tensor(np.random.randn(), dtype=dtype, requires_grad=True)
loss = 1/a
loss.backward()
print(a.grad - (-1/(a**2)))

输出为:

tensor(5.9605e-08, grad_fn=<ThAddBackward>)

谁能向我解释问题出在哪里?

1 个答案:

答案 0 :(得分:2)

所以我想您期望结果为零。当您仔细观察时,您会发现它非常接近。在二进制系统(计算机)上指定数字时,通常会出现舍入错误。

让我们看一下您的示例,其中添加了其他 print-statement

a = torch.tensor(np.random.randn(), requires_grad=True)
loss = 1/a
loss.backward()
print(a.grad, (-1/(a**2)))
print(a.grad - (-1/(a**2)))

由于输入是随机的,所以输出当然也是随机的(所以您不会得到这个非常相同的数字,但是只需重复一下,您将得到类似的示例),有时您也会结果为零,请注意情况:

tensor(-0.9074) tensor(-0.9074, grad_fn=<MulBackward>)
tensor(5.9605e-08, grad_fn=<ThSubBackward>)

即使两个数字都显示为相同的数字,但它们在最后一个小数位之一之间也有所不同。这就是为什么两者相减时会得到如此小的差异。

此问题作为计算机的普遍问题,某些分数仅具有大量或无限数量的小数位,但您的内存却没有。因此,它们在某些时候被切断了。

因此,您在这里遇到的实际情况是缺乏精确性。精度取决于您使用的数字数据类型(即torch.float32torch.float64)。

您还可以在此处查看更多信息:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format


但这并不是特定于PyTorch的,这是一个Python示例:

print(29/100*100)

结果:

28.999999999999996

编辑:

正如@HOANG GIANG指出的那样,将方程式更改为-(1 / a)*(1 / a)效果很好,结果为零。 可能是这种情况,因为在这种情况下,完成计算梯度的计算与-(1 / a)*(1 / a)非常相似(或相同)。因此,它具有相同的舍入误差,因此差值为零。

因此,与上述示例相比,这是另一个更合适的示例。即使-(1 / x)*(1 / x)在数学上等效于 -1 / x ^ 2 ,但在计算机上进行计算时并不总是相同的,具体取决于 x 的值:

import numpy as np
print('e1 == e2','x value', '\t'*2, 'round-off error', sep='\t')
print('='*70)
for i in range(10):
    x = np.random.randn()
    e1 = -(1/x)*(1/x)
    e2 = (-1/(x**2))
    print(e1 == e2, x, e1-e2, sep='\t\t')

输出:

e1 == e2    x value                 round-off error
======================================================================
True        0.2934154339948173      0.0
True        -1.2881863891014191     0.0
True        1.0463038021843876      0.0
True        -0.3388766143622498     0.0
True        -0.6915415747192347     0.0
False       1.3299049850551317      1.1102230246251565e-16
True        -1.2392046539563553     0.0
False       -0.42534236747121645    8.881784197001252e-16
True        1.407198823994324       0.0
False       -0.21798652132356966    3.552713678800501e-15


即使四舍五入误差似乎要少一些(我尝试了不同的随机值,很少有十分之二以上有四舍五入误差),但在仅计算 1 / x 时仍然存在很小的差异:

import numpy as np
print('e1 == e2','x value', '\t'*2, 'round-off error', sep='\t')
print('='*70)
for i in range(10):
    x = np.random.randn()
    # calculate 1/x
    result = 1/x
    # apply inverse function
    reconstructed_x = 1/result
    # mathematically this should be the same as x
    print(x == reconstructed_x, x, x-reconstructed_x, sep='\t\t')

输出:

e1 == e2    x value             round-off error
======================================================================
False       0.9382823115235075      1.1102230246251565e-16
True        -0.5081217386356917     0.0
True        -0.04229436058156134    0.0
True        1.1121100294357302      0.0
False       0.4974618312372863      -5.551115123125783e-17
True        -0.20409933212316553    0.0
True        -0.6501652554924282     0.0
True        -3.048057937738731      0.0
True        1.6236075700470816      0.0
True        0.4936926651641918      0.0