Pytorch- 在 loss.behind()和 Optimizer.step()之间的连接

optimizerloss之间的显式连接在哪里?

优化器如何知道在哪里得到的梯度的损失没有一个调用喜欢这个 optimizer.step(loss)

- 更多的背景-

当我将损失最小化时,我不需要将渐变传递给优化器。

loss.backward() # Back Propagation
optimizer.step() # Gardient Descent
46716 次浏览

在不深入研究 pytch 内部构成的情况下,我可以给出一个简单的答案:

回想一下,在初始化 optimizer时,您显式地告诉它应该更新模型的哪些参数(张量)。梯度是由张量本身“存储”的(它们有一个 grad和一个 requires_grad属性)一旦你调用 backward()的损失。在计算模型中所有张量的梯度之后,调用 optimizer.step()使优化器迭代所有应该更新的参数(张量) ,并使用它们内部存储的 grad来更新它们的值。

这个答案中可以找到更多关于计算图的信息以及存储在 pytorch 张量中的附加“梯度”信息。

通过优化器引用参数有时会引起麻烦,例如,当模型被移动到 GPU 之后初始化优化器时。 请确保您已经完成了设置您的模型 之前构造优化器。请参阅 这个答案了解更多细节。

当您调用 loss.backward()时,它所做的只是计算损失的梯度.r.t 损失中所有具有 requires_grad = True的参数,并将它们存储在每个参数的 parameter.grad属性中。

optimizer.step()基于 parameter.grad更新所有参数

假设我们定义了一个模型: model和丢失函数: criterion,我们有以下步骤:

pred = model(input)
loss = criterion(pred, true_labels)
loss.backward()

pred将有一个 grad_fn属性,该属性引用创建它的函数,并将它绑定回模型。因此,loss.backward()将有关于它正在使用的模型的信息。

尝试删除 grad_fn属性,例如:

pred = pred.clone().detach()

然后模型的梯度将是 None,因此权重不会得到更新。

并且优化器与模型绑定在一起,因为我们在创建优化器时传递 model.parameters()

简短的回答:

对设置为 required_grad= True的所有参数做梯度。参数可以是代码中定义的任何变量,如 h2hi2h

根据优化器函数 optimizer.step() # (以前在我们的代码中定义过) ,我们更新这些参数,最终得到最小的损失(错误)。

也许这会澄清一点 loss.backwardoptim.step之间的联系(尽管其他的答案都是切中要害的)。

# Our "model"
x = torch.tensor([1., 2.], requires_grad=True)
y = 100*x


# Compute loss
loss = y.sum()


# Compute gradient of the loss w.r.t. to the parameters
print(x.grad)     # None
loss.backward()
print(x.grad)     # tensor([100., 100.])


# MOdify the parameters by subtracting the gradient
optim = torch.optim.SGD([x], lr=0.001)
print(x)        # tensor([1., 2.], requires_grad=True)
optim.step()
print(x)        # tensor([0.9000, 1.9000], requires_grad=True)

loss.backward()requires_grad=True设置所有张量的 grad属性 损失为叶子的计算图(在这种情况下只有 x)。

优化器只是迭代初始化时接收到的参数列表(张量) ,在任何张量有 requires_grad=True的地方,它减去存储在 .grad属性中的梯度值(在 SGD 情况下简单地乘以学习速率)。它不需要知道计算出的梯度损失是多少,它只需要访问 .grad属性,这样就可以执行 x = x - lr * x.grad

注意 ,如果我们在一个 train 循环中执行这个操作,我们将调用 optim.zero_grad(),因为在每个 train 步骤中,我们想计算新的梯度-我们不关心前一批的梯度。不对梯度进行归零将导致不同批次之间的梯度累积。

有些答案解释得很好,但我想给出一个具体的例子来解释这种机制。

假设我们有一个函数: z = 3 x ^ 2 + y ^ 3。
Z. r.t. x 和 y 的更新梯度公式是:

enter image description here

初始值是 x = 1和 y = 2。

x = torch.tensor([1.0], requires_grad=True)
y = torch.tensor([2.0], requires_grad=True)
z = 3*x**2+y**3


print("x.grad: ", x.grad)
print("y.grad: ", y.grad)
print("z.grad: ", z.grad)


# print result should be:
x.grad:  None
y.grad:  None
z.grad:  None

然后计算当前值 x 和 y 的梯度(x = 1,y = 2)

enter image description here

# calculate the gradient
z.backward()


print("x.grad: ", x.grad)
print("y.grad: ", y.grad)
print("z.grad: ", z.grad)


# print result should be:
x.grad:  tensor([6.])
y.grad:  tensor([12.])
z.grad:  None

最后,使用 SGD 优化器根据公式更新 x 和 y 的值: enter image description here

# create an optimizer, pass x,y as the paramaters to be update, setting the learning rate lr=0.1
optimizer = optim.SGD([x, y], lr=0.1)


# executing an update step
optimizer.step()


# print the updated values of x and y
print("x:", x)
print("y:", y)


# print result should be:
x: tensor([0.4000], requires_grad=True)
y: tensor([0.8000], requires_grad=True)