如何正确更新PyTorch中的权重?

如何正确更新PyTorch中的权重?,pytorch,backpropagation,gradient,Pytorch,Backpropagation,Gradient,我正试图根据这一点用Pytork实现梯度下降,但不知道如何正确地更新权重。这只是一个玩具示例,有两个线性层,隐藏层中有两个节点,还有一个输出 Learning rate = 0.05; target output = 1 我的代码如下: import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim class MyNet

我正试图根据这一点用Pytork实现梯度下降,但不知道如何正确地更新权重。这只是一个玩具示例,有两个线性层,隐藏层中有两个节点,还有一个输出

Learning rate = 0.05;
target output = 1

我的代码如下:

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim

    class MyNet(nn.Module):

    def __init__(self):
         super(MyNet, self).__init__()
         self.linear1 = nn.Linear(2, 2,  bias=None)
         self.linear1.weight = torch.nn.Parameter(torch.tensor([[0.11, 0.21], [0.12, 0.08]]))
         self.linear2 = nn.Linear(2, 1,  bias=None)
         self.linear2.weight = torch.nn.Parameter(torch.tensor([[0.14, 0.15]]))

    def forward(self, inputs):
         out = self.linear1(inputs)
         out = self.linear2(out)
         return out

    losses = []
    loss_function = nn.L1Loss()
    model = MyNet()
    optimizer = optim.SGD(model.parameters(), lr=0.05)
    input = torch.tensor([2.0,3.0])
    print('weights before backpropagation = ',   list(model.parameters()))
    for epoch in range(1):
       result = model(input )
       loss = loss_function(result , torch.tensor([1.00],dtype=torch.float))
       print('result = ', result)
       print("loss = ",   loss)
       model.zero_grad()
       loss.backward()
       print('gradients =', [x.grad.data  for x in model.parameters()] )
       optimizer.step()
       print('weights after backpropagation = ',   list(model.parameters())) 
结果如下:

    weights before backpropagation =  [Parameter containing:
    tensor([[0.1100, 0.2100],
            [0.1200, 0.0800]], requires_grad=True), Parameter containing:
    tensor([[0.1400, 0.1500]], requires_grad=True)]

    result =  tensor([0.1910], grad_fn=<SqueezeBackward3>)
    loss =  tensor(0.8090, grad_fn=<L1LossBackward>)

    gradients = [tensor([[-0.2800, -0.4200], [-0.3000, -0.4500]]), 
                 tensor([[-0.8500, -0.4800]])]

    weights after backpropagation =  [Parameter containing:
    tensor([[0.1240, 0.2310],
            [0.1350, 0.1025]], requires_grad=True), Parameter containing:
    tensor([[0.1825, 0.1740]], requires_grad=True)]
向后传递:让我们计算w5和w6(输出节点权重)

在我的示例中,Torch不会将损失乘以导数,因此更新后我们会得到错误的权重。对于输出节点,我们得到了新的权重w5,w6[0.1825,0.1740],而它应该是[0.174,0.169]

向后移动以更新输出节点(w5)的第一个权重,我们需要计算:
(预测目标)x(梯度)x(前一个节点的输出)x(学习率)=-0.809*1*0.85*0.05=-0.034
。更新重量
w5=0.14-(-0.034)=0.174
。但pytorch计算的新重量为0.1825。它忘记了乘以
(预测目标)=-0.809
。对于输出节点,我们得到了梯度-0.8500和-0.4800。但我们仍然需要将它们乘以损失0.809和学习率0.05,然后才能更新权重

这样做的正确方式是什么? 我们是否应该将'loss'作为参数传递给
backward()
,如下所示:
loss.backward(loss)


这似乎解决了问题。但是我在文档中找不到任何关于这方面的例子。

您应该将
.zero\u grad()
与optimizer一起使用,所以
optimizer.zero\u grad()
,而不是注释中建议的丢失或模型(虽然模型很好,但在IMO中不清晰或可读)

除了你的参数更新得很好,所以错误不在PyTorch这边

根据您提供的渐变值:

gradients = [tensor([[-0.2800, -0.4200], [-0.3000, -0.4500]]), 
             tensor([[-0.8500, -0.4800]])]
让我们将它们全部乘以您的学习率(0.05):

最后,让我们应用普通SGD(θ-=梯度*lr),以获得与PyTorch中完全相同的结果:

parameters = [tensor([[0.1240, 0.2310], [0.1350, 0.1025]]),
              tensor([[0.1825, 0.1740]])]
您所做的是使用PyTorch计算的梯度,并将其与上一个节点的输出相乘,它不是这样工作的

你所做的:

w5= 0.14 -(0.191-1)*1*0.85*0.05= 0.14 + 0.034= 0.174  
w5 = 0.14 - (-0.85*0.05) = 0.1825
应该做什么(使用PyTorch的结果):

w5= 0.14 -(0.191-1)*1*0.85*0.05= 0.14 + 0.034= 0.174  
w5 = 0.14 - (-0.85*0.05) = 0.1825
不需要对上一个节点进行乘法,它是在幕后完成的(这就是
.backprop()
所做的-为所有节点计算正确的梯度),无需将它们与上一个节点相乘

如果您想手动计算它们,您必须从损失开始(增量为1)并一直向下反向支撑(这里不要使用学习率,这是另一回事!

计算完所有权重后,您可以将每个权重乘以优化器的学习率(或任何其他公式,例如动量),然后进行正确的更新

如何计算backprop 学习速率不是反向传播的一部分,在计算所有梯度之前不要使用它(它会将单独的算法、优化过程和反向传播混淆在一起)

1.总误差w.r.t.输出的导数 嗯,我不知道为什么要使用平均绝对误差(而在教程中是均方误差),这就是为什么这两个结果都不同。但是让我们按照你的选择

y|u true-y|u pred | w.r.t.对y|u pred的导数为1,因此与损失不同。更改为MSE以获得相等的结果(此处,导数为(1/2*y_pred-y_true),但我们通常将MSE乘以2以删除第一次乘法)

MSE情况下,您将乘以损失值,但这完全取决于损失函数(遗憾的是,您使用的教程没有指出这一点)

2.总误差w.r.t.w5的导数 你可以从这里走,但是。。。总误差w.r.t到w5的导数是h1的输出(本例中为0.85)。我们将其乘以总误差w.r.t.输出(它是1!)的导数,得到0.85,就像PyTorch中所做的那样。同样的想法也适用于w6

我郑重地建议你不要把学习率和backprop混为一谈,你正在让你的生活变得更艰难(backprop在我看来并不容易,相当违反直觉),这是两件不同的事情(不能强调这一点)

源代码很好,更循序渐进,有一个更复杂的网络概念(包括激活),所以如果你经历了所有这些,你可以更好地理解它


此外,如果你真的很想(而且你似乎很想)了解更多的细节,请计算其他优化器(比如nesterov)的权重修正值,这样你就知道为什么我们应该将这些想法分开。

你应该做
loss.zero\u grad()
而不是
model.zero\u grad()
。这是因为当你做
loss.backward()
,梯度会累积(累加),所以你应该在做另一个
loss.backward()
optimizer.step()
。实际上,loss.zero\u grad()会给我错误“张量”对象没有属性“零度”。在pytorch教程中,他们使用model.zero_grad()。但我的问题是,为什么我的模型在计算loss.backward()和optimizer.step()时没有正确地计算新的权重是的,对不起,
optimizer.zero\u grad()
是正确的。那么我们应该如何在这里应用增量规则呢?Pytorch在更新权重时所做的是w5=0.14-(-0.85*0.05)=0.1825(0.85是前一个节点的输出),但是我们在这里缺少了delta(预测-目标),即(0.191-1)。根据delta规则,它应该是:w=w-(预测目标)x(梯度)x(前一个节点的输出)x(学习率),因此正确的权重应该计算为:w5=0.14-(0.191-1)*1*0.85*0.05=0.14+0.034=0.174。这也是根据本教程编辑的
w5 = 0.14 - (-0.85*0.05) = 0.1825