pytorch - loss.backward()和optimizer.step()之间的连接

Aer*_*rin 14 machine-learning neural-network gradient-descent pytorch

哪个optimizerloss?之间有明确的联系?

优化器如何知道在没有通话的情况下从哪里获得损失的梯度optimizer.step(loss)

- 更多背景 -

当我最小化损失时,我没有必要将渐变传递给优化器.

loss.backward() # Back Propagation
optimizer.step() # Gardient Descent
Run Code Online (Sandbox Code Playgroud)

Sha*_*hai 16

在不深入研究pytorch内部的情况下,我可以提供一个简单的答案:

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

  • @Aerin 这不是一个微不足道的联系......人们会期望 `optimizer.step` 将 `loss.backward()` 作为参数。然而,这一切都发生在“幕后”…… (8认同)
  • 那么optimizer.step()是如何从loss.backward()获取梯度值的。看来这个答案还没有回答“连接”的机制。优化器参考模型参数。但损失函数完全是独立的。它看起来不像模型或优化器的参考。 (5认同)
  • @mofury 简而言之,这个问题并不那么容易回答。粗略地说,首先,可以调用损失函数类的实例,例如“nn.CrossEntropyLoss”的实例,并返回一个“Tensor”。这很重要,这个“Tensor”对象有一个“grad_fn”属性,其中存储了它派生的张量。而这些张量也有这样的一个 prop,这样“backward”函数就可以通过这样的 prop 进行反向传播,最终得到我们想要在模型中优化的参数。你可以参考这个:https://pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html。 (4认同)
  • @cfeng 损失函数根本不是独立的!它是单个巨大计算图中的最后一片叶子,该计算图以模型输入开始并包含所有模型参数。该图针对每个批次进行计算,并在每个批次上产生单个标量。当我们执行“loss.backward()”时,反向传播过程从损失开始,并遍历其所有父级,一直到对输入进行建模。图中的所有节点都包含对其父节点的引用。 (3认同)
  • @MonaJalal 你在验证代码中哪里看到了优化器?优化器与验证/测试无关 (2认同)

Aka*_*all 14

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

pred = model(input)
loss = criterion(pred, true_labels)
loss.backward()
Run Code Online (Sandbox Code Playgroud)

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

尝试删除grad_fn属性,例如:

pred = pred.clone().detach()
Run Code Online (Sandbox Code Playgroud)

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

并且优化器是和模型绑定的,因为我们model.parameters()在创建优化器的时候就通过了。


pse*_*vin 10

也许这会稍微澄清loss.backward和之间的联系optim.step(尽管其他答案是重点)。

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

# Compute loss
loss = y.sum()

# Compute gradients of the parameters w.r.t. the loss
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)
Run Code Online (Sandbox Code Playgroud)

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

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

请注意,如果我们在训练循环中执行此操作,我们会调用,optim.zero_grad()因为在每个训练步骤中我们都想计算新的梯度——我们不关心上一批的梯度。不将 grads 归零会导致跨批次的梯度累积。

  • 我喜欢这种“动手”的解释来理解事物。谢谢,对我来说更有意义! (3认同)

小智 8

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

假设我们有一个函数:z = 3 x^2 + y^3。
z wrt x 和 y 的更新梯度公式为:

在此处输入图片说明

初始值为 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
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

# 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
Run Code Online (Sandbox Code Playgroud)

最后,使用 SGD 优化器根据公式更新 x 和 y 的值: 在此处输入图片说明

# 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)
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解释以及代码。谢谢。 (3认同)

小智 6

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

optimizer.step() 根据更新所有参数 parameter.grad

  • “loss”是计算与网络无关的两个张量之间的损失。“loss.backward()”如何知道需要引用和计算“parameter.grad”的网络? (2认同)