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

Machine learning pytorch-loss.backward()和optimizer.step()之间的连接,machine-learning,neural-network,pytorch,gradient-descent,Machine Learning,Neural Network,Pytorch,Gradient Descent,哪里是优化器和丢失之间的显式连接 优化器如何知道在没有像下面这样的调用的情况下从何处获取损失梯度 -更多上下文- 当我最小化损失时,我不必将梯度传递给优化器 loss.backward() # Back Propagation optimizer.step() # Gardient Descent 在不深入研究Pytork内部的情况下,我可以提供一个简单的答案: 回想一下,在初始化优化器时,您明确地告诉它应该更新模型的哪些参数(张量)。一旦在丢失时调用backward(),渐变由张量本身“存储

哪里是
优化器
丢失
之间的显式连接

优化器如何知道在没有像下面这样的调用的情况下从何处获取损失梯度

-更多上下文-

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

loss.backward() # Back Propagation
optimizer.step() # Gardient Descent

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

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

有关计算图和pytorch张量中存储的附加“梯度”信息的更多信息,请参见

优化器引用参数有时会导致问题,例如,在初始化优化器后将模型移动到GPU时。
在构建优化器之前,请确保已完成模型设置。有关更多详细信息,请参见。

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


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

假设我们定义了一个模型:
模型
,损失函数:
标准
,我们有以下步骤序列:

pred = model(input)
loss = criterion(pred, true_labels)
loss.backward()
pred
将具有一个
grad\u fn
属性,该属性引用创建它的函数,并将它绑定回模型。因此,
loss.backward()

尝试删除
grad\u fn
属性,例如:

pred = pred.clone().detach()
然后模型梯度将为
None
,因此权重将不会得到更新

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

简短回答:

loss.backward() # Back Propagation
optimizer.step() # Gardient Descent
loss.backward()
#对我们设置的
必需的所有参数执行梯度调整_grad=True
。参数可以是代码中定义的任何变量,如
h2h
i2h


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

也许这将稍微澄清
损失.backward
optim.step
之间的联系(尽管其他答案都是中肯的)

loss.backward()
在损失为叶的计算图中(在这种情况下仅
x

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


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

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

假设我们有一个函数:z=3x^2+y^3。
z w.r.t 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
然后计算当前值中x和y的梯度(x=1,y=2)

最后,使用SGD optimizer根据以下公式更新x和y的值:


@艾琳这不是一个微不足道的联系。。。人们可能会期望
optimizer.step
获得
loss.backward()
作为参数。然而,这一切都发生在“幕后”…那么optimizer.step()如何从loss.backward()中获得渐变值呢。这个答案似乎没有回答“连接”的机制。优化器引用了模型参数。但损失函数是完全独立的。它看起来不像是对模型或优化器的引用。@cfeng loss函数完全不是它自己的!它是单个巨大计算图中的最后一片叶子,该图从模型输入开始,包含所有模型参数。此图针对每个批次进行计算,并在每个批次上生成单个标量数。当我们做
loss.backward()
时,反向传播的过程从损失开始,经过它的所有父代,一直到建模输入。图中的所有节点都包含对其父节点的引用。
loss
计算与网络无关的两个张量之间的损耗。
loss.backward()
如何知道它需要为哪个网络引用和计算
parameter.grad
。@AzizAlfoudari查看我的澄清尝试的答案:)。它不应该是“那么模型梯度将不会得到更新”。因为loss.backward()更新了梯度?@zwithouta,谢谢,这是一个很好的观点。我更新了我的答案。我喜欢这种“动手”的解释来理解事情。谢谢,这对我来说更有意义!
# 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
# 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)