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

wyyhbhjk  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(165)

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

Learning rate = 0.05;
target output = 1

https://hmkcode.github.io/ai/backpropagation-step-by-step/
Forward
Backward
代码如下:

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)]

正向传递值:

2x0.11 + 3*0.21=0.85 ->  
2x0.12 + 3*0.08=0.48 -> 0.85x0.14 + 0.48*0.15=0.191 -> loss =0.191-1 = -0.809

向后传球:让我们计算w5和w 6(输出节点权重)

w = w - (prediction-target)x(gradient)x(output of previous node)x(learning rate)  
w5= 0.14 -(0.191-1)*1*0.85*0.05= 0.14 + 0.034= 0.174  
w6= 0.15 -(0.191-1)*1*0.48*0.05= 0.15 + 0.019= 0.169

在我的例子中,Torch没有将损失乘以导数,所以我们在更新后得到了错误的权重。对于输出节点,我们得到了新的权重w5,w 6 [0.1825,0.1740],而它应该是[0.174,0.169]
向后移动以更新输出节点的第一个权重(w5),我们需要计算:(prediction-target)x(gradient)x(output of previous node)x(learning rate)=-0.809*1*0.85*0.05=-0.034。更新了权重w5 = 0.14-(-0.034)=0.174。但pytorch计算的是new weight = 0.1825。它忘了乘以(prediction-target)=-0.809。对于输出节点,我们得到梯度-0.8500和-0.4800。但是我们仍然需要将它们乘以损失0.809和学习率0.05,然后才能更新权重。
做这件事的正确方法是什么?我们是否应该将'loss'作为参数传递给backward(),如下所示:loss.backward(loss)
这似乎解决了它。但我在文档中找不到任何关于这方面的例子。

myzjeezk

myzjeezk1#

你应该使用.zero_grad()与优化器,所以optimizer.zero_grad(),而不是在评论中建议的损失或模型(虽然模型是好的,但它不是清晰或可读的IMO)。
除了你的参数更新得很好,所以错误不在PyTorch这边。
根据您提供的梯度值:

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

让我们将所有这些乘以你的学习率(0.05):

gradients_times_lr = [tensor([[-0.014, -0.021], [-0.015, -0.0225]]), 
                      tensor([[-0.0425, -0.024]])]

最后,让我们应用普通SGD(theta -= gradient * 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

应该做什么(使用PyTorch的结果):

w5 = 0.14 - (-0.85*0.05) = 0.1825

没有前一个节点的乘法,它是在幕后完成的(这就是.backprop()所做的-为所有节点计算正确的梯度),不需要将它们乘以前一个。
如果你想手动计算它们,你必须从损失开始(delta为1),然后一路向下反向传播(不要在这里使用学习率,这是另一回事!).
在计算完所有的权重之后,你可以将每个权重乘以优化器的学习率(或任何其他公式,例如:在此之后,您将获得正确的更新。

如何计算backprop

学习率**不是反向传播的一部分,在你计算所有梯度之前,不要去管它(它把不同的算法混淆在一起,优化过程和反向传播)。

1.总误差w.r.t.的导数输出

好吧,我不知道为什么你使用平均绝对误差(而在教程中它是均方误差),这就是为什么这两个结果不同。但我们还是听你的吧。

衍生物|y_true - y_pred| w.r.t.到y_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中一样。同样的想法也适用于W 6。
我认真地建议你不要混淆学习率和反向传播,你让你的生活变得更困难(在国际海事组织,反向传播并不容易,这是违反直觉的),这是两件独立的事情(不能强调这一点)。
This源代码很好,更循序渐进,有一个更复杂的网络概念(包括激活),所以如果你经历了所有这些,你可以更好地掌握。
此外,如果你真的热衷于(你似乎是),想知道更多的细节,计算其他优化器(比如,nesterov)的权重修正,这样你就知道为什么我们应该把这些想法分开。

相关问题