当我使用由 torch.nn.function.mse_loss 定义的损失函数时,损失将是 Nan

时间:2021-01-20 07:54:52

标签: pytorch

当我使用如下损失函数时,损失总是 Nan:

def Myloss1(source, target):
    loss = torch.nn.functional.mse_loss(source, target, reduction="none")
    return torch.sum(loss).sqrt()

...

loss = Myloss1(s, t)
loss.backward()


但是当我使用以下损失函数时,训练变得正常:

def Myloss2(source, target):
    diff = target - source
    loss = torch.norm(diff)
    return loss
...

loss = Myloss2(s, t)
loss.backward()


为什么不能用‘Myloss1’来训练?Myloss1和Myloss2不是等价的吗?

请帮帮我,非常感谢!

1 个答案:

答案 0 :(得分:0)

Myloss1Myloss2 确实应该是等价的。它们至少为我尝试过的所有张量返回相同的值。

关于Nan,让我们先试着找出它发生的时间。这里唯一可能的罪魁祸首是 sqrt,它在 0 中不可微。而且确实:

y = torch.randn(2,3)
x = y.clone()
x.requires_grad_(True)
Myloss1(x,y).backward()
print(x.grad.data)
>>> [[nan, nan, nan], [nan, nan, nan]]

另一方面:

Myloss2(x,y).backward()
print(x.grad.data)
>>> [[-0., -0., -0.],[-0., -0., -0.]]

在这两个结果中,只有第一个在数学上是“准确的”。计算平方根在 0 处的导数会产生除以 0。这就是为什么在训练神经网络或其他任何东西时,不使用 sqrt。你应该使用

good_loss = torch.nn.MSELoss(reduction='mean') # or ='sum' if you prefer

这个函数到处都是可微的,你不会再有麻烦了。

至于为什么您的 Myloss2 会产生不同的梯度,这与其实现有关。它被广泛讨论here。基本上,人们抱怨 nans,因此更改了 lib 以修改此行为,同时承认此处没有数学上正确的答案,因为此导数未定义为 0。