我正在尝试根据schema使用PyTorch实施梯度下降,但无法弄清楚如何正确地更新权重。这只是一个玩具示例,具有2个线性层,其中2个隐藏层中的节点和一个输出。
学习率= 0.05; 目标输出= 1
https://hmkcode.github.io/ai/backpropagation-step-by-step/
我的代码如下:
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)的第一权重,我们需要计算:(预测目标)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)。
这似乎可以解决。但是我在文档中找不到任何示例。
答案 0 :(得分:0)
您应该将.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(θ-=梯度* 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()
的工作-为所有节点计算正确的梯度),无需将它们乘以前一个。
如果要手动计算它们,则必须从损耗开始(增量为1)并一直反向传播(此处不使用学习率,这是另一回事了! )。
计算所有权重之后,您可以将每个权重乘以优化器的学习率(或与此有关的任何其他公式,例如动量),然后进行正确的更新。
学习率不是反向传播的一部分,请不要理会它,直到您计算所有的梯度(它将单独的算法,优化过程和反向传播混淆在一起)。
好吧,我不知道您为什么使用均值绝对误差(而在本教程中为均方误差),因此这两个结果都不同。但是,让我们选择。
|的导数y_true-y_pred | w.r.t.到y_pred为1 ,因此与损失不相同。更改为 MSE 可获得相等的结果(此处的导数为(1/2 * y_pred-y_true),但是我们通常将MSE乘以2以删除第一个乘法)。
在 MSE 情况下,您可以乘以损失值,但这完全取决于损失函数(不幸的是,您使用的教程没有指出这一点)。 / p>
您可能会从这里开始,但是...总误差w.r.t到w5的导数是h1的输出(在这种情况下为0.85)。我们将其乘以总误差w.r.t.输出(它是1!)并获得0.85,就像在PyTorch中所做的那样。同样的想法适用于w6。
我强烈建议您不要将学习速度与反向支持相混淆,这会使您的生活变得更加艰难(反向支持IMO并不容易,这很违反直觉),这是两件事(可以t强调一个)。
This的源代码很好,循序渐进,网络思想稍微复杂一些(包括激活),因此,如果您仔细阅读所有内容,将会更好地掌握。
此外,如果您真的很热衷(并且似乎是这样),要了解更多的来龙去脉,请计算其他优化程序的权重校正(例如Nesterov),因此您知道为什么我们应该将这些想法分开