PyTorch 后向函数中发生 RuntimeError

Posted

技术标签:

【中文标题】PyTorch 后向函数中发生 RuntimeError【英文标题】:RuntimeError occurs in PyTorch backward function 【发布时间】:2020-06-27 05:12:04 【问题描述】:

我正在尝试计算 PyTorch 中变量的 grad。但是,有一个 RuntimeError 告诉我 output 和 grad 的形状必须相同。但是,在我的情况下,输出和 grad 的形状不能相同。这是我要重现的代码:

import numpy as np
import torch
from torch.autograd import Variable as V

ne = 3
m, n = 79, 164
G = np.random.rand(m, n).astype(np.float64)
w = np.random.rand(n, n).astype(np.float64)
z = -np.random.rand(n).astype(np.float64)

G = V(torch.from_numpy(G))
w = V(torch.from_numpy(w))
z = V(torch.from_numpy(z), requires_grad=True)
e, v = torch.symeig(torch.diag(2 * z - torch.sum(w, dim=1)) + w, eigenvectors=True, upper=False)
ssev = torch.sum(torch.pow(e[-ne:] * v[:, -ne:], 2), dim=1)
out = torch.sum(torch.matmul(G, ssev.reshape((n, 1))))
out.backward(z)
print(z.grad)

错误信息是:RuntimeError: Mismatch in shape: grad_output[0] has a shape of torch.Size([164]) and output[0] has a shape of torch.Size([])

TensorFlow允许类似的计算,我可以成功得到我想要的梯度:

import numpy as np
import tensorflow as tf

m, n = 79, 164
G = np.random.rand(m, n).astype(np.float64)
w = np.random.rand(n, n).astype(np.float64)
z = -np.random.rand(n).astype(np.float64)

def tf_function(z, G, w, ne=3):
    e, v = tf.linalg.eigh(tf.linalg.diag(2 * z - tf.reduce_sum(w, 1)) + w)
    ssev = tf.reduce_sum(tf.square(e[-ne:] * v[:, -ne:]), 1)
    return tf.reduce_sum(tf.matmul(G, tf.expand_dims(ssev, 1)))

z, G, w = [tf.convert_to_tensor(_, dtype=tf.float64) for _ in (z, G, w)]
z = tf.Variable(z)
with tf.GradientTape() as g:
    g.watch(z)
    out = tf_function(z, G, w)
print(g.gradient(out, z).numpy())

我的 tensorflow 版本是 2.0,我的 PyTorch 版本是 1.14.0。我正在使用 Python3.6.9。在我看来,当输出和变量具有不同形状时计算梯度是非常合理的,我认为我没有犯任何错误。有人可以帮我解决这个问题吗?我真的很感激!

【问题讨论】:

【参考方案1】:

首先你不需要使用numpy然后转换为变量(顺便说一下不推荐使用),你可以只使用G = torch.rand(m, n)等。其次,当你写out.backward(z)时,你正在传递@ 987654325@作为out的gradient,即out.backward(gradient=z),可能是由于“out.backward(z)计算z的梯度,即dout/dz”的误解。相反,对于某些函数f(例如损失函数),这个参数是gradient = d[f(out)]/dout,它是用于计算vector-Jacobian productdout/dz * df/dout的张量。因此,您得到错误的原因是因为您的out(及其梯度df/dout)是一个标量(零维张量),而z 是一个大小为n 的张量,导致不匹配形状。

要解决这个问题,正如您自己已经弄清楚的那样,只需将out.backward(z) 替换为out.backward(),这相当于out.backward(gradient=torch.tensor(1.)),因为在您的情况下out 是一个标量,而f(out) = out,所以d[f(out)]/dout = d(out)/d(out) = tensor(1.)。如果您的out 是非标量张量,那么out.backward() 将不起作用,而您必须使用out.backward(torch.ones(out.shape))(再次假设f(out) = out)。无论如何,如果您需要将gradient 传递给out.backward(),请确保它与out 具有相同的形状。

【讨论】:

我明白了。谢谢!

以上是关于PyTorch 后向函数中发生 RuntimeError的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch 中自定义后向函数的损失 - 简单 MSE 示例中的爆炸损失

在pytorch中,后向方法的第一个参数(梯度)是啥?

pytorch 后向误差,由就地操作修改的梯度计算变量之一

当前向包含多个自动分级节点时,PyTorch 关于使用非完整后向挂钩的警告

Pytorch框架学习---hook函数和CAM类激活图

如何在 PyTorch 中使用 autograd.gradcheck?