我有三个简单的问题。
- 如果我的自定义损失函数不可微,会发生什么?pytorch 会通过错误还是做其他事情?
- 如果我在我的自定义函数中声明了一个损失变量,它将代表模型的最终损失,我应该输入
requires_grad = True
那个变量吗?或者没关系?如果没关系,那为什么呢? - 我见过人们有时会编写一个单独的层并计算函数中的损失
forward
。哪种方法更可取,编写函数还是层?为什么?
我需要对这些问题有一个清晰而好的解释来解决我的困惑。请帮忙。
我有三个简单的问题。
requires_grad = True
那个变量吗?或者没关系?如果没关系,那为什么呢?forward
。哪种方法更可取,编写函数还是层?为什么?我需要对这些问题有一个清晰而好的解释来解决我的困惑。请帮忙。
让我试一试。
这取决于您所说的“不可微分”是什么意思。第一个有意义的定义是 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()
各自操作的方法。
没关系。的用途requires_grad
是避免不必要的子图梯度计算。如果一个操作的单个输入需要梯度,那么它的输出也需要梯度。相反,只有当所有输入都不需要梯度时,输出也不需要它。在子图中永远不会执行反向计算,其中所有变量都不需要梯度。
由于很可能有一些Variables
(例如 的子类的参数nn.Module()
),因此您的loss
变量也将自动需要渐变。但是,您应该注意到确切的requires_grad
工作原理(再次参见上文),无论如何您只能更改requires_grad
图表的叶变量。
所有自定义 PyTorch 损失函数_Loss
都是nn.Module
. 看这里。如果您想遵守此约定,则应_Loss
在定义自定义损失函数时进行子类化。除了一致性之外,一个优点是AssertionError
如果您没有将目标变量标记为volatile
or ,您的子类将引发requires_grad = False
。另一个优点是您可以将损失函数嵌套在 中nn.Sequential()
,因为nn.Module
出于这些原因,我会推荐这种方法。