Pytorch Autograd 在使用 .clamp 而不是 torch.relu 时给出不同的梯度
Pytorch Autograd gives different gradients when using .clamp instead of torch.relu
我仍在努力了解 PyTorch autograd 系统。我正在努力的一件事是理解为什么 .clamp(min=0)
和 nn.functional.relu()
似乎有不同的向后传球。
它特别令人困惑,因为 .clamp
在 PyTorch 教程中等同于 relu
使用,例如 https://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-nn.
我在分析具有一个隐藏层和 relu 激活(输出层中的线性)的简单全连接网络的梯度时发现了这一点。
根据我的理解,以下代码的输出应该只是零。我希望有人能告诉我我缺少什么。
import torch
dtype = torch.float
x = torch.tensor([[3,2,1],
[1,0,2],
[4,1,2],
[0,0,1]], dtype=dtype)
y = torch.ones(4,4)
w1_a = torch.tensor([[1,2],
[0,1],
[4,0]], dtype=dtype, requires_grad=True)
w1_b = w1_a.clone().detach()
w1_b.requires_grad = True
w2_a = torch.tensor([[-1, 1],
[-2, 3]], dtype=dtype, requires_grad=True)
w2_b = w2_a.clone().detach()
w2_b.requires_grad = True
y_hat_a = torch.nn.functional.relu(x.mm(w1_a)).mm(w2_a)
y_a = torch.ones_like(y_hat_a)
y_hat_b = x.mm(w1_b).clamp(min=0).mm(w2_b)
y_b = torch.ones_like(y_hat_b)
loss_a = (y_hat_a - y_a).pow(2).sum()
loss_b = (y_hat_b - y_b).pow(2).sum()
loss_a.backward()
loss_b.backward()
print(w1_a.grad - w1_b.grad)
print(w2_a.grad - w2_b.grad)
# OUT:
# tensor([[ 0., 0.],
# [ 0., 0.],
# [ 0., -38.]])
# tensor([[0., 0.],
# [0., 0.]])
#
原因是clamp
和relu
在0
处产生了不同的梯度。使用标量张量 x = 0
检查两个版本:(x.clamp(min=0) - 1.0).pow(2).backward()
与 (relu(x) - 1.0).pow(2).backward()
。结果 x.grad
对于 relu
版本是 0
,但对于 clamp
版本是 -2
。这意味着 relu
选择 x == 0 --> grad = 0
而 clamp
选择 x == 0 --> grad = 1
.
我仍在努力了解 PyTorch autograd 系统。我正在努力的一件事是理解为什么 .clamp(min=0)
和 nn.functional.relu()
似乎有不同的向后传球。
它特别令人困惑,因为 .clamp
在 PyTorch 教程中等同于 relu
使用,例如 https://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-nn.
我在分析具有一个隐藏层和 relu 激活(输出层中的线性)的简单全连接网络的梯度时发现了这一点。
根据我的理解,以下代码的输出应该只是零。我希望有人能告诉我我缺少什么。
import torch
dtype = torch.float
x = torch.tensor([[3,2,1],
[1,0,2],
[4,1,2],
[0,0,1]], dtype=dtype)
y = torch.ones(4,4)
w1_a = torch.tensor([[1,2],
[0,1],
[4,0]], dtype=dtype, requires_grad=True)
w1_b = w1_a.clone().detach()
w1_b.requires_grad = True
w2_a = torch.tensor([[-1, 1],
[-2, 3]], dtype=dtype, requires_grad=True)
w2_b = w2_a.clone().detach()
w2_b.requires_grad = True
y_hat_a = torch.nn.functional.relu(x.mm(w1_a)).mm(w2_a)
y_a = torch.ones_like(y_hat_a)
y_hat_b = x.mm(w1_b).clamp(min=0).mm(w2_b)
y_b = torch.ones_like(y_hat_b)
loss_a = (y_hat_a - y_a).pow(2).sum()
loss_b = (y_hat_b - y_b).pow(2).sum()
loss_a.backward()
loss_b.backward()
print(w1_a.grad - w1_b.grad)
print(w2_a.grad - w2_b.grad)
# OUT:
# tensor([[ 0., 0.],
# [ 0., 0.],
# [ 0., -38.]])
# tensor([[0., 0.],
# [0., 0.]])
#
原因是clamp
和relu
在0
处产生了不同的梯度。使用标量张量 x = 0
检查两个版本:(x.clamp(min=0) - 1.0).pow(2).backward()
与 (relu(x) - 1.0).pow(2).backward()
。结果 x.grad
对于 relu
版本是 0
,但对于 clamp
版本是 -2
。这意味着 relu
选择 x == 0 --> grad = 0
而 clamp
选择 x == 0 --> grad = 1
.