添加到("cuda:0")后 PyTorch 渐变不起作用

PyTorch Gradient not Working after adding to("cuda:0")

我要给出一组上下文无关的代码。该代码在添加“to(device)”之前有效。

def get_input_layer(word_idx) :
x = torch.zeros(vocabulary_size).float().to(device)
x[word_idx] = 1.0
return x

embedding_dims = 5
device = torch.device("cuda:0")
W1 = Variable(torch.randn(embedding_dims, vocabulary_size).float(), requires_grad=True).to(device)
W2 = Variable(torch.randn(vocabulary_size, embedding_dims).float(), requires_grad=True).to(device)
num_epochs = 100
learning_rate = 0.001

x = Variable(get_input_layer(data)).float().to(device)
y_true = Variable(torch.from_numpy(np.array([target])).long()).to(device)
z1 = torch.matmul(W1, x).to(device)
z2 = torch.matmul(W2, z1).to(device)

log_softmax = F.log_softmax(z2, dim=0).to(device)

loss = F.nll_loss(log_softmax.view(1,-1), y_true).to(device)
loss_val += loss.data
loss.backward().to(device)

## Optimize values. This is done by hand rather than using the optimizer function
W1.data -= learning_rate * W1.grad.data
W2.data -= learning_rate * W2.grad.data

我明白了

Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'data'

具体哪条触发就行

W1.data -= learning_rate * W1.grad.data

正在检查,这已得到确认,因为 W1.grad 出于某种原因是 None。

并且在清除渐变后循环。如果我删除所有 .to(device),这就很好用了。我在我的 GPU 上尝试 运行 做错了什么?

感谢您的宝贵时间。

发生这种情况是因为 .to returns 一个新的 non-leaf 张量。传输到所需设备后,您应该设置 requires_grad。此外,Variable 接口已被弃用很长时间,因为在 pytorch 1.0 之前。它什么都不做(除了在这种情况下作为一种过于复杂的方式来设置 requires_grad)。

考虑

W1 = Variable(torch.randn(embedding_dims, vocabulary_size).float(), requires_grad=True).to(device)

这里的问题是有两个不同的张量。将其分解,我们可以将您正在做的事情重写为

W1a = Variable(torch.randn(embedding_dims, vocabulary_size).float(), requires_grad=True)
W1 = W1a.to(device)

观察到 W1a 需要梯度,但是 W1 是从 W1a 派生的,所以它不被认为是叶张量,因此 .grad 的属性 W1a 会更新,但 W1 不会。在您的代码中,您不再直接引用 W1a,因此您将无法访问渐变。

相反你可以做

W1 = torch.randn(embedding_dims, vocabulary_size).float().to(device)
W1.required_grad_(True)

在传输到不同的设备后W1正确设置为叶张量。


请注意,对于您的具体情况,我们也可以只使用 devicedtyperequires_grad 参数 torch.randn 并简单地执行

W1 = torch.randn(embedding_dims, vocabulary_size, dtype=torch.float, device=device, requires_grad=True)

大多数初始化新张量的 pytorch 函数都支持这三个参数,这有助于避免您遇到的问题。


回复OP在评论中的附加问题:

Is there a good spot I would've come across this in the documentation?

据我所知,文档并未专门解决此问题。它是 python 中的变量如何工作与 pytorch 中的 autograd 机制如何工作之间的一种组合。

假设您对 python 中的变量有很好的理解,那么您可以通过首先阅读 Tensor.is_leaf,特别是

自己得出这个答案的结论

they will be leaf Tensors if they were created by the user. This means that they are not the result of an operation and so grad_fn is None.

此外 Tensor.to 的文档指出

If the self Tensor already has the correct torch.dtype and torch.device, then self is returned. Otherwise, the returned tensor is a copy of self with the desired torch.dtype and torch.device.

既然Tensor.toreturns一个copy,而一个copy就是一个操作,那么从文档中应该可以明确,原代码中的W1tensor不是叶子张量.