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

问题描述 投票:0回答:2

我正在尝试根据此 schema 使用 PyTorch 实现梯度下降,但无法弄清楚如何正确更新权重。这只是一个玩具示例,有 2 个线性层,隐藏层有 2 个节点,一个输出。

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和w6(输出节点权重)

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,w6 [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,然后才能更新权重。

这样做的正确方法是什么? 我们是否应该将“损失”作为参数传递给

backward()
,如下所示:
loss.backward(loss)
.

这似乎可以解决问题。但我在文档中找不到任何关于此的示例。

pytorch backpropagation gradient
2个回答
3
投票

您应该将

.zero_grad()
与优化器一起使用,所以
optimizer.zero_grad()
,而不是评论中建议的损失或模型(虽然模型很好,但在我看来不清楚或不可读)。

除了你的参数更新得很好,所以错误不在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 -= 梯度 * 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)并一路向下反向传播(这里不要使用学习率,这是一个不同的故事!)。

计算完所有这些之后,您可以将每个权重乘以优化器学习率(或与此相关的任何其他公式,例如动量),然后您就可以得到正确的更新。

如何计算反向传播

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

1.总误差的导数输出

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

| 的导数y_true - y_pred | y_true - y_pred | w.r.t.到 y_pred 是 1,所以 它不 与损失相同。更改为 MSE 以获得相同的结果(此处,导数将为 (1/2 * y_pred - y_true),但我们通常将 MSE 乘以 2 以删除第一个乘法)。

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

2.总误差的导数w5

您可能可以从这里开始,但是...总误差对 w5 的导数是 h1 的输出(在本例中为 0.85)。我们将其乘以总误差的导数。输出(它是 1!)并获得 0.85,如 PyTorch 中所做的那样。 w6 也有同样的想法。

我认真建议你不要将学习率与反向传播混淆,你会让你的生活变得更加困难(在我看来,反向传播并不容易,相当违反直觉),而这是两件不同的事情(怎么强调都不够)。

这个来源很好,更一步一步,有一点更复杂的网络想法(包括激活),所以如果你完成所有内容,你可以更好地掌握。

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


0
投票

在使用上述代码训练模型期间,我尝试不更新第二层(线性2)的第一个权重参数(0.14)。其他权重将根据梯度下降进行更新。我怎样才能做到这一点?我正在通过以下方式尝试这一点。但它正在更新模型的所有权重。

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.5, 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)
        
        with torch.no_grad():
                model.linear2.weight[0, 0] = .14

        model.zero_grad()
        loss.backward()
        print('gradients =', [x.grad.data  for x in model.parameters()] )
        optimizer.step()
        print('weights after backpropagation = ',  list(model.parameters())) 
© www.soinside.com 2019 - 2024. All rights reserved.