Backpropagation 如何计算多个图像的损耗,然后反向传播平均损耗并更新网络权重

Backpropagation 如何计算多个图像的损耗,然后反向传播平均损耗并更新网络权重,backpropagation,pytorch,loss,Backpropagation,Pytorch,Loss,我正在做一个批量大小为1的任务,即每个批量只包含1个图像。因此,我必须进行手动批处理:当累积损失的数量达到一个数字时,对损失进行平均,然后进行反向传播。 我的原始代码是: real\u batchsize=200 对于范围(1,5)内的历元: net.train() 总损耗=变量(torch.zeros(1.cuda(),需要梯度=真) iter_计数=0 对于enumerate(train_loader)中的batch_idx(输入,目标): 输入,目标=变量(input.cuda()),变量

我正在做一个批量大小为1的任务,即每个批量只包含1个图像。因此,我必须进行手动批处理:当累积损失的数量达到一个数字时,对损失进行平均,然后进行反向传播。 我的原始代码是:

real\u batchsize=200
对于范围(1,5)内的历元:
net.train()
总损耗=变量(torch.zeros(1.cuda(),需要梯度=真)
iter_计数=0
对于enumerate(train_loader)中的batch_idx(输入,目标):
输入,目标=变量(input.cuda()),变量(target.cuda())
输出=净(输入)
损失=F.nll\U损失(输出、目标)
总损失=总损失+损失
如果batch\u idx%real\u batchsize==0:
iter_计数+=1
平均损耗=总损耗/实际批量大小
ave_loss.backward()
optimizer.step()
如果iter_计数%10==0:
打印(“历元:{},迭代:{},丢失:{}”。格式(历元,
国际热核聚变实验堆计数,
平均损失数据[0]))
总损失数据为零
optimizer.zero_grad()
此代码将给出错误消息

RuntimeError:第二次尝试向后遍历图形,但缓冲区已被释放。第一次向后调用时指定retain_graph=True

我试过以下方法

第一条路(失败) 我读了一些关于这个错误消息的帖子,但不能完全理解它。将
ave_loss.backward()
更改为
ave_loss.backward(retain_graph=True)
防止错误消息,但丢失不会改善即将变为
nan的性能

第二条路(失败) 我还尝试更改
total\u loss=total\u loss+loss.data[0]
,这也将防止出现错误消息。但损失总是一样的。所以一定是出了什么问题

第三条道路(成功) 按照中的说明,对于每个图像的丢失,我将丢失除以
real\u batchsize
,然后将其反向投影。当输入图像的数量达到
real\u batchsize
时,我使用
optimizer.step()
更新一个参数。随着训练过程的进行,损失逐渐减少。但是训练速度真的很慢,因为我们对每幅图像都进行了反向支撑

我的问题 在我的案例中,错误消息意味着什么?还有,为什么第一条路和第二条路不起作用?如何正确地编写代码,以便我们能够在每个
real\u batchsize
图像中反向投影梯度,并更新梯度一次,从而加快训练速度?我知道我的代码几乎是正确的,但我只是不知道如何更改它。

这里遇到的问题与PyTorch如何在不同的过程中累积梯度有关。(有关类似问题的另一篇帖子,请参见) 那么,让我们看看当您有以下形式的代码时会发生什么:

loss_total = Variable(torch.zeros(1).cuda(), requires_grad=True)
for l in (loss_func(x1,y1), loss_func(x2, y2), loss_func(x3, y3), loss_func(x4, y4)):
    loss_total = loss_total + l
    loss_total.backward()
在这里,当不同迭代中的
loss\u total
具有以下值时,我们进行反向传递:

total_loss = loss(x1, y1)
total_loss = loss(x1, y1) + loss(x2, y2)
total_loss = loss(x1, y1) + loss(x2, y2) + loss(x3, y3)
total_loss = loss(x1, y1) + loss(x2, y2) + loss(x3, y3) + loss(x4, y4)
因此,当您每次在
total_loss
上调用
.backward()
时,实际上您在
loss(x1,y1)
上调用了
.backward()
四次!(在
丢失时(x2,y2)
三次,以此类推)

将其与另一篇文章中讨论的内容结合起来,即为了优化内存使用,PyTorch将在调用
.backward()
(从而破坏连接
x1
y1
x2
y2
等的渐变)时释放附加到变量的图形,您可以看到错误消息的含义—您多次尝试对丢失进行反向传递,但在第一次传递后,底层图形被释放。(当然,除非指定
retain\u graph=True

至于您尝试过的具体变体: 第一种方法:在这里,您将永远累积(即,总结-再次,请参阅另一篇文章)渐变,它们(可能)相加为
inf
。 第二种方法:在这里,通过执行
loss.data
,删除
变量
包装,从而删除梯度信息(因为只有变量包含梯度),将
损失
转换为张量。 第三种方法:在这里,您只需对每个
xk,yk
元组执行一次传递,因为您立即执行backprop步骤,从而避免了上述问题


解决方案:我没有测试过它,但从我收集的信息来看,解决方案应该非常简单:在每个批次开始时创建一个新的
total_loss
对象,然后将所有损失相加到该对象中,然后在最后执行一个backprop步骤

每次参数更新后,创建一个新变量
total_loss
解决了这个问题。太棒了!那么你能接受这个答案吗?非常感谢。