【问题标题】:PyTorch gradient differs from manually calculated gradientPyTorch 梯度不同于手动计算的梯度
【发布时间】:2018-11-13 05:48:37
【问题描述】:

我试图在不使用 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>)

谁能解释一下问题出在哪里?

【问题讨论】:

    标签: python gradient pytorch derivative


    【解决方案1】:

    所以我猜你期望结果为零。当您仔细观察时,您会发现它非常接近。在二进制系统(计算机)上除数时,您经常会遇到舍入错误。

    让我们看看您的示例,并添加了一个额外的 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
    

    【讨论】:

    • 我发现当我改变计算顺序时(即我的公式变为-(1/a)*(1/a)),差异变为零(即== 0)
    • @HOANGGIANG 是的,这很好!即使 -(1/x)*(1/x) 在数学上等价于 -1/x^2,但在计算机上计算时并不总是如此。我在回答结束时进行了编辑。
    • @HOANGGIANG 如果您能就“谁能给我解释一下问题是什么?”这个问题给我一些反馈,那就太好了。如果您发现解释有用,我会很高兴,如果您接受答案,重视所做的努力,谢谢!
    • 对不起 :) 我刚刚对你的答案投了赞成票,却忘了接受它作为正确答案
    猜你喜欢
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多