首页 文章

PyTorch中的自定义丢失功能

提问于
浏览
8

我有三个简单的问题 .

  • 如果我的自定义丢失功能不可区分,会发生什么?会发生错误还是做其他事情?

  • 如果我在自定义函数中声明了一个表示模型最终丢失的损失变量,那么我应该为该变量设置 requires_grad = True 吗?或者它不重要,那么为什么呢?

  • 我见过人们有时会写一个单独的图层来计算 forward 函数中的损失 . 编写函数或层是哪种方法更可取?为什么?

我需要对这些问题做出明确而好的解释才能解决我的困惑 . 请帮忙 .

1 回答

  • 8

    让我去吧 .

    • 这取决于你所说的“不可微分” . 这里有意义的第一个定义是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
    

    有意义的第二个定义是"mathematically non-differentiable" . 显然,在数学上不可微分的操作应该不具有实现的方法或者合理的子梯度 . 考虑例如 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() 方法 .

    • 没关系 . requires_grad 的使用是为了避免对子图的渐变进行不必要的计算 . 如果对需要渐变的操作有单个输入,则其输出也需要渐变 . 相反,只有当所有输入都不需要梯度时,输出也不需要它 . 从不在子图中执行向后计算,其中所有变量都不需要渐变 .

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

    • 所有自定义PyTorch损失函数都是 _Loss 的子类,它是 nn.Module 的子类 . See here.如果您想坚持这个约定,那么在定义自定义损失函数时应该继承 _Loss . 除了一致性之外,如果您没有将目标变量标记为 volatilerequires_grad = False ,则一个优点是您的子类将引发 AssertionError . 另一个优点是你可以在 nn.Sequential() 中嵌套你的损失函数,因为它是 nn.Module 我会因为这些原因推荐这种方法 .

相关问题