14

我有三个简单的问题。

  1. 如果我的自定义损失函数不可微,会发生什么?pytorch 会通过错误还是做其他事情?
  2. 如果我在我的自定义函数中声明了一个损失变量,它将代表模型的最终损失,我应该输入requires_grad = True那个变量吗?或者没关系?如果没关系,那为什么呢?
  3. 我见过人们有时会编写一个单独的层并计算函数中的损失forward。哪种方法更可取,编写函数还是层?为什么?

我需要对这些问题有一个清晰而好的解释来解决我的困惑。请帮忙。

4

1 回答 1

17

让我试一试。

  1. 这取决于您所说的“不可微分”是什么意思。第一个有意义的定义是 PyTorch 不知道如何计算梯度。如果您尝试计算梯度,这将引发错误。两种可能的情况是:

    a) 您正在使用尚未实现渐变的自定义 PyTorch 操作,例如torch.svd(). 在这种情况下,您将获得TypeError

    import torch
    from torch.autograd import Function
    from torch.autograd import Variable
    
    A = Variable(torch.randn(10,10), requires_grad=True)
    u, s, v = torch.svd(A) # raises TypeError
    

    b)您已经实现了自己的操作,但没有定义backward(). 在这种情况下,您将获得NotImplementedError

    class my_function(Function): # forgot to define backward()
    
        def forward(self, x):
            return 2 * x
    
    A = Variable(torch.randn(10,10))
    B = my_function()(A)
    C = torch.sum(B)
    C.backward() # will raise NotImplementedError
    

    第二个有意义的定义是“数学上不可微分”。显然,数学上不可微分的运算不应该有一个backward()实现的方法或一个合理的子梯度。例如,考虑torch.abs()backward()方法在 0 处返回次梯度 0:

    A = Variable(torch.Tensor([-1,0,1]),requires_grad=True)
    B = torch.abs(A)
    B.backward(torch.Tensor([1,1,1]))
    A.grad.data
    

    对于这些情况,你应该直接参考 PyTorch 的文档,直接挖掘backward()各自操作的方法。

  2. 没关系。的用途requires_grad是避免不必要的子图梯度计算。如果一个操作的单个输入需要梯度,那么它的输出也需要梯度。相反,只有当所有输入都不需要梯度时,输出也不需要它。在子图中永远不会执行反向计算,其中所有变量都不需要梯度。

    由于很可能有一些Variables(例如 的子类的参数nn.Module()),因此您的loss变量也将自动需要渐变。但是,您应该注意到确切的requires_grad工作原理(再次参见上文),无论如何您只能更改requires_grad图表的叶变量。

  3. 所有自定义 PyTorch 损失函数_Loss都是nn.Module. 看这里。如果您想遵守此约定,则应_Loss在定义自定义损失函数时进行子类化。除了一致性之外,一个优点是AssertionError如果您没有将目标变量标记为volatileor ,您的子类将引发requires_grad = False。另一个优点是您可以将损失函数嵌套在 中nn.Sequential(),因为nn.Module出于这些原因,我会推荐这种方法。

于 2017-06-17T20:34:50.900 回答