【问题标题】:The grad of F.normalize in pytorch is strangepytorch中F.normalize的grad很奇怪
【发布时间】:2020-07-10 11:57:12
【问题描述】:

F.normalize 在 pytorch 中的梯度几乎为零。

python3 & pytorch==1.3.0 中的代码:

import torch
import torch.nn.functional as F

x = torch.autograd.Variable(torch.Tensor([[1, 0]]), requires_grad=True)
x_norm = F.normalize(x, dim=-1)
z = x_norm.mm(x_norm.t())
print('x_norm', x_norm)
print('z', z)

x.register_hook(lambda g: print(g))
x_norm.register_hook(lambda g: print(g))
z.register_hook(lambda g: print(g))
z.backward()

输出:

x_norm tensor([[1., 0.]], grad_fn=<DivBackward0>)
z tensor([[1.]], grad_fn=<MmBackward>)
tensor([[1.]])                      # z grad
tensor([[2., 0.]])                  # x_norm grad
tensor([[0., 0.]])                  # x grad

为什么 x 的 grad 为零? 'x.grad' 给出相同的结果。

根据链式法则,我认为结果应该是[-1, 1]*[2, 0]=[-2, 0]。

实际上,当我在整个网络中使用上面的代码时,grad 工作正常。

我的代码有什么问题吗?

试过

x_norm = x / torch.sqrt((x[0, 0]**2 + x[0, 1]**2))

同样的结果。

试过CUDA,还是一样。

尝试了以下代码,将x_norm替换为y_norm

import torch
import torch.nn.functional as F

x = torch.autograd.Variable(torch.Tensor([[1, 0]]), requires_grad=True)
y = torch.autograd.Variable(torch.Tensor([[2, 0]]), requires_grad=True)
x_norm = F.normalize(x, dim=-1)
y_norm = F.normalize(y, dim=-1)
z = x_norm.mm(y_norm.t())

y.register_hook(lambda g: print(g))
x.register_hook(lambda g: print(g))
x_norm.register_hook(lambda g: print(g))
z.register_hook(lambda g: print(g))
z.backward()

输出:

tensor([[1.]])                      # z grad
tensor([[2., 0.]])                  # x_norm grad
tensor([[0., 0.]])                  # y grad
tensor([[0., 0.]])                  # x grad

但是,如果我将 z = x_norm.mm(y_norm.t()) 替换为 z = x_norm.mm(y.t()),则 y 具有非零值,而 x 则没有。

还尝试将F.normalize放在代码中间:

import torch
import torch.nn.functional as F

#x = torch.autograd.Variable(torch.randn(1, 2), requires_grad=True)
x = torch.autograd.Variable(torch.Tensor([[1,0]]), requires_grad=True).cuda()                                                                                            y = torch.autograd.Variable(torch.Tensor([[2,0]]), requires_grad=True).cuda()
x1 = x * 2
x1_norm = F.normalize(x1, dim=-1)                                                                                                                                         

print('x1', x1)
z = x1_norm.mm(y.t())
print('x1_norm', x1_norm)
print('z', z)
y.register_hook(lambda g: print('y', g))
x.register_hook(lambda g: print('x', g))
x1.register_hook(lambda g: print('x1', g))
x1_norm.register_hook(lambda g: print('x1_norm', g))
z.register_hook(lambda g: print('z', g))
z.backward()

输出

x1 tensor([[2., 0.]], device='cuda:0', grad_fn=<MulBackward0>)
x1_norm tensor([[1., 0.]], device='cuda:0', grad_fn=<DivBackward0>)
z tensor([[2.]], device='cuda:0', grad_fn=<MmBackward>)
z grad tensor([[1.]], device='cuda:0')            # z grad
x1_norm grad tensor([[2., 0.]], device='cuda:0')  # (x*2)_norm grad
x1 grad tensor([[0., 0.]], device='cuda:0')       # x*2 grad
y grad tensor([[1., 0.]], device='cuda:0')        # y grad
x grad tensor([[0., 0.]], device='cuda:0')        # x grad

【问题讨论】:

    标签: python pytorch backpropagation


    【解决方案1】:

    z的梯度只是它的导数,也就是1。

    x_norm 的梯度是mm(x_norm)(1; 0) 中计算的梯度。也就是说,如果x_norm = (x; y),它的梯度是2 * (x; y),在(1; 0)中得到(2; 0)

    接下来,由于(x / norm(x)) * (x / norm(x))^T = 1 是一个常数,x 的梯度是零。

    编辑:你有以下等式:

    取一个常数的梯度得到 0。

    【讨论】:

    • 对不起,你能进一步解释最后一句话吗?我试图用x_norm.mm(y_norm.t()) 替换x_norm.mm(x_norm.t()),但没有任何反应。但是,如果我将其替换为x_norm.mm(y.t())x_norm.mm(x.t()),则没有“规范”的那个具有非零梯度值。
    • 感谢您的耐心等待。我现在知道问题所在,你是完全正确的。但是,我尝试将其替换为x_norm.mm(y_norm.t()),而yx 完全不同,这意味着z 不是常数,但grad 也为零。我也尝试将F.normalize 放在代码中间,但 grad 也为零。实际上,从F.normalize 到输入,grad 变为零。你能帮我解释一下吗?您可以在编辑后的问题中看到我的代码。
    • z 相对于y1 的偏导数在此处计算:shorturl.at/bwAQX 你看到对于y = (y1, y2) = (2, 0),它给出了0y2 也是如此:shorturl.at/dmvB7 我已经插入 x = (1, 0) 就像在你的代码中一样
    猜你喜欢
    • 1970-01-01
    • 2018-06-01
    • 2017-10-31
    • 2020-03-20
    • 1970-01-01
    • 2021-12-04
    • 2019-01-03
    • 2012-03-27
    • 1970-01-01
    相关资源
    最近更新 更多