【问题标题】:PyTorch Gradient not Working after adding to("cuda:0")添加到(“cuda:0”)后PyTorch渐变不起作用
【发布时间】:2020-11-23 02:52:39
【问题描述】:

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

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 上运行它时做错了什么?

感谢您的宝贵时间。

【问题讨论】:

    标签: python neural-network pytorch gradient


    【解决方案1】:

    这是因为 .to 返回一个新的非叶张量。您应该在传输到所需设备后设置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,因此它不被视为叶张量,因此W1a.grad 属性将被更新,但W1 不会不是。在您的代码中,您不再有对W1a 的直接引用,因此您将无法访问渐变。

    你可以这样做

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

    这会将W1 正确设置为被转移到不同设备后的叶张量。


    请注意,对于您的具体情况,我们也可以只使用torch.randndevicedtyperequires_grad 参数,然后简单地做

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

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


    回复 OP 在 cmets 中的附加问题:

    我会在文档中遇到这个好地方吗?

    AFAIK 文档并未专门解决此问题。它是 Python 中变量的工作方式和 pytorch 中 autograd 机制的工作方式之间的一种组合。

    假设您对 python 中的变量有很好的理解,那么您可以通过首先阅读 Tensor.is_leaf 自己得出这个答案的结论,尤其是

    如果它们是由用户创建的,它们将是叶张量。 这意味着它们不是操作的结果,因此grad_fn 为无。

    还有Tensor.to 的文档说明

    如果self 张量已经具有正确的torch.dtypetorch.device,则返回self否则,返回的张量是self 的副本,其中包含所需的torch.dtypetorch.device

    既然Tensor.to返回的是一个副本,而一个副本是一个操作,那么从文档中应该可以清楚,原代码中的W1张量不是叶子张量。

    【讨论】:

    • 谢谢!我会在文档中遇到这个问题吗?我看了看,但我认为我没有找到这么清楚的东西。也许我没有找对地方。欣赏答案。
    • @Jibril 我添加了一个额外的讨论来解决你的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 2018-02-04
    • 2016-12-23
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    相关资源
    最近更新 更多