添加到("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
正确设置为叶张量。
请注意,对于您的具体情况,我们也可以只使用 device
、dtype
和 requires_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.to
returns一个copy,而一个copy就是一个操作,那么从文档中应该可以明确,原代码中的W1
tensor不是叶子张量.
我要给出一组上下文无关的代码。该代码在添加“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
正确设置为叶张量。
请注意,对于您的具体情况,我们也可以只使用 device
、dtype
和 requires_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 correcttorch.dtype
andtorch.device
, thenself
is returned. Otherwise, the returned tensor is a copy ofself
with the desiredtorch.dtype
andtorch.device
.
既然Tensor.to
returns一个copy,而一个copy就是一个操作,那么从文档中应该可以明确,原代码中的W1
tensor不是叶子张量.