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

时间:2018-12-30 06:30:53

标签: machine-learning neural-network pytorch gradient-descent

... char comma = ','; for (unsigned int i = 0; i < str.length(); i++) { // previously str[i] != ","; if(str[i] != comma) cout<<str[i]; else cout<<"\n"; } ... optimizer之间有何明确联系?

优化器如何知道在没有调用的情况下如何在loss上获得损耗的梯度?

-更多上下文-

当我将损失最小化时,我不必将梯度传递给优化器。

optimizer.step(loss)

6 个答案:

答案 0 :(得分:7)

比方说,我们定义了一个模型: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()

答案 1 :(得分:6)

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

假设我们有一个函数:z = 3 x^2 + y^3.
z w.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=1,y=2)中x和y的梯度

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)

答案 2 :(得分:5)

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

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

答案 3 :(得分:5)

也许这可以澄清loss.backwardoptim.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)

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

Optimizer只是遍历初始化时以及在张量具有requires_grad=True的所有地方接收到的参数(张量)列表,它减去存储在其.grad属性中的梯度值(乘以SGD的学习率)。它不需要知道计算梯度的损失是多少,它只想访问该.grad属性,以便可以执行x = x - lr * x.grad

请注意,如果我们在火车循环中执行此操作,我们会调用optim.zero_grad(),因为在每个火车步骤中我们都想计算新的梯度-我们不在乎来自前一批。梯度不清零会导致批次间的梯度累积。

答案 4 :(得分:3)

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

optimizer.step()根据parameter.grad

更新所有参数

答案 5 :(得分:-1)

简短答案:

loss.backward()#对我们设置的required_grad= True的所有参数进行渐变。参数可以是代码中定义的任何变量,例如h2hi2h

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