在反向传播的训练中,经常碰到这样的错误提示:
RuntimeError: grad can be implicitly created only for scalar outputs
因为我们的输出不是标量,无法反向求导。PyTorch在求导的过程中,分为下面两种情况:
(1)标量对向量求导
(2)(向量)矩阵对(向量)矩阵求导
当面对向量对向量求导的过程中,我们可以利用backward函数的grad_variables来解决。backward函数的定义如下所示:
torch.autograd.backward(variables, grad_variables=None, retain_graph=None, create_graph=None, retain_variables=None)
如果是(向量)矩阵对(向量)矩阵求导,实际上是先求出Jacobian矩阵中每一个元素的梯度值,然后将这个Jacobian矩阵与grad_tensors参数对应的矩阵进行对应的点乘,得到最终的结果。
向量对向量求导问题的解决方案
在程序中我们的损失函数定义如下所示:
loss = nn.CrossEntropyLoss(reduction='none')
但在执行loss.backward()时出现了下面这条报错信息:
RuntimeError: grad can be implicitly created only for scalar outputs
原因分析:
在定义损失函数loss时,我们设置了参数reduction='none',这导致我们计算出的loss是一个二维的张量,行数为batchsize的大小。backward只有对标量输出时才会计算梯度,而无法对张量计算梯度。
解决办法:
将张量转变成一个标量,比如我们可以对loss求和,然后用求和得到的标量再反向传播求各个参数的梯度,这样不会对结果造成影响。
loss.backward -> loss.sum().backward 或者 loss.backward -> loss.backward(torch.ones_like(loss))
上面这两个方法表示对loss求和后再进行反向传播,其中第一个方法比较好理解,第二方法中的torch.ones_like(loss)是传入的grad_tensors参数,表示创建了一个与loss同样大小的全1张量,另loss与torch.ones_like(loss)点乘后再进行反向传播,也相当于对loss求和后再进行反向传播。