PyTorch 梯度不同于手动计算的梯度
PyTorch gradient differs from manually calculated gradient
我正在尝试在不使用 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>)
谁能给我解释一下这是什么问题?
所以我猜你希望结果为零。当你仔细观察时,你会发现它非常接近。在二进制系统(计算机)上划分数字时,您经常会遇到舍入错误。
让我们看看您的示例,其中添加了额外的 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.float32
或 torch.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
我正在尝试在不使用 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>)
谁能给我解释一下这是什么问题?
所以我猜你希望结果为零。当你仔细观察时,你会发现它非常接近。在二进制系统(计算机)上划分数字时,您经常会遇到舍入错误。
让我们看看您的示例,其中添加了额外的 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.float32
或 torch.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