Pytorch中的梯度下降重分配



我在youtube上观看了一系列关于深度学习的教程,我遇到了一个让我很困惑的问题。

X = torch.tensor([1,2,3,4], dtype = torch.float32)
Y = torch.tensor([2,4,6,8], dtype = torch.float32)
w = torch.tensor(0.0, dtype = torch.float32, requires_grad=True)
def forward(x):
return w*x;
def loss(y, y_predicted):
return ((y-y_predicted)**2).mean()
print(f'Prediction before training: f(5) = {forward(5):.3f}')

learning_rate= 0.01
epoch = 20
for i in range(epoch):
y_pred = forward(X)
l = loss(Y, y_pred)
l.backward()
with torch.no_grad():
w = w - learning_rate * w.grad
# (w -= learning_rate * w.grad) # would not cause error in the following line

w.grad.zero_() #error : 'NoneType' object has no attribute 'zero_'
if i % 1 ==0:
print(f'weight : {w}, loss : {l}')

我真的很想知道">w = w - learning_rate * w.g grad";and ">w -= learning_rate * w.g grad";因为在我的经验中,这两者是一样的。谢谢!

正如评论中指出的那样,问题在于Pytorch如何计算/存储梯度。事实上,

w-= learning_rate * w.grad

是一个就地操作,它将使w保持其初始属性(requires_grad=True)。通常在Pytorch中,我们会避免就地操作,因为它可能会破坏Autograd使用的计算图(参见Pytorch论坛帖子)。

但对你来说,这是:

w = w - learning_rate * w.grad

不合适。因此,w被赋值给一个新的副本,并且由于torch.no_grad()语句,该副本将没有.grad属性。

虽然操作符在python中具有特定的行为,但对于pytorch框架并不总是如此。对于普通的python解释,我们可以看到A -= B的减法赋值与A = A - B语法是等价的。此外,每次想给变量赋值时都可以使用python操作符。事实上,我们在python中称之为语法糖,它可以用来简化代码。然而,pytorch框架的工作方式不同。

在您的示例中,新的w是在传播l.backward()之后分配的。因此,它没有grad值。此外,由于torch.no_grad()条件,w赋值没有requires_grad

现在,让我们考虑一下你的例子:

w = w - learning_rate * w.grad

pytorch框架中,它相当于:

w[...] = w - learning_rate * w.grad

虽然w[...] = w - learning_rate * w.gradw -= learning_rate * w.grad看起来是相等的,但它们并不遵循相同的结构。因此,他们促进不同的行为。一方面,w -= learning_rate * w.grad表达式被认为是一个原地操作。这意味着它直接改变了给定张量()的内容。w[...])不复制。另一方面,requires_grad=True属性使w保持其初始属性。因此,不更新它的状态。

最新更新