我试图根据这个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)
。
这似乎解决了它。但我在文档中找不到任何关于这方面的例子。
1条答案
按热度按时间myzjeezk1#
你应该使用
.zero_grad()
与优化器,所以optimizer.zero_grad()
,而不是在评论中建议的损失或模型(虽然模型是好的,但它不是清晰或可读的IMO)。除了你的参数更新得很好,所以错误不在PyTorch这边。
根据您提供的梯度值:
让我们将所有这些乘以你的学习率(0.05):
最后,让我们应用普通SGD(theta -= gradient * lr),以获得与PyTorch中完全相同的结果:
你所做的是把PyTorch计算的梯度乘以前一个节点的输出,这不是它的工作方式!.
你做了什么:
应该做什么(使用PyTorch的结果):
没有前一个节点的乘法,它是在幕后完成的(这就是
.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)的权重修正,这样你就知道为什么我们应该把这些想法分开。