首页 文章

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

提问于
浏览
2

我正在做一个批量大小为1的任务,即每个批次只包含1个图像 . 所以我必须进行手动配料:当累计损失的数量达到一个数字时,平均损失然后做反向传播 . 我原来的代码是:

real_batchsize = 200

for epoch in range(1, 5):
    net.train()

    total_loss = Variable(torch.zeros(1).cuda(), requires_grad=True)

    iter_count = 0
    for batch_idx, (input, target) in enumerate(train_loader):

        input, target = Variable(input.cuda()), Variable(target.cuda())
        output = net(input)

        loss = F.nll_loss(output, target)

        total_loss = total_loss + loss

        if batch_idx % real_batchsize == 0:
            iter_count += 1

            ave_loss = total_loss/real_batchsize
            ave_loss.backward()
            optimizer.step()

            if iter_count % 10 == 0:
                print("Epoch:{}, iteration:{}, loss:{}".format(epoch,
                                                           iter_count,
                                                           ave_loss.data[0]))
            total_loss.data.zero_() 
            optimizer.zero_grad()

此代码将给出错误消息

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

我试过以下方式,

第一种方式(失败)

我阅读了一些关于此错误消息的帖子,但无法完全理解 . 更改 ave_loss.backward()ave_loss.backward(retain_graph=True) 阻止错误消息,但损失不会改善很快变为 nan .

第二种方式(失败)

我也试图改变 total_loss = total_loss + loss.data[0] ,这也会阻止错误信息 . 但损失总是一样的 . 所以一定有问题 .

第三种方式(成功)

按照this post中的说明,对于每个图像的丢失,我将损失除以 real_batchsize 并将其推回 . 当输入图像的数量达到 real_batchsize 时,我使用 optimizer.step() 进行一次参数更新 . 随着训练过程的进行,损失正在缓慢减少 . 但是训练速度非常慢,因为我们为每个图像提供反向支持 .

我的问题

在我的情况下,错误消息意味着什么?另外,为什么第一种方式和第二种方式不起作用?如何正确编写代码,以便我们可以对每个 real_batchsize 图像进行反向渐变,并更新渐变一次,以使训练速度更快?我知道我的代码几乎是正确的,但我只是不知道如何改变它 .

1 回答

  • 3

    您在这里遇到的问题与PyTorch如何在不同的传递上累积渐变有关 . (有关类似问题的另一篇文章,请参阅here)让我们看看当您拥有以下形式的代码时会发生什么:

    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_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() 四次! (和 loss(x2, y2) 三次,等等) .

    将其与其他帖子中讨论的内容相结合,即为了优化内存使用,PyTorch将在调用 .backward() 时释放附加到变量的图形(从而破坏连接 x1y1x2y2 等的渐变),你可以看看错误信息是什么意思 - 你尝试多次向后传递一次丢失,但是在第一次传递之后基础图被释放了 . (当然,除非指定 retain_graph=True

    至于你尝试过的具体变化:第一种方式:在这里,你将永远积累(即总结 - 再次,见另一篇文章)渐变,与它们(可能)加起来 inf . 第二种方式:在这里,通过执行 loss.dataloss 转换为张量,删除 Variable 包装器,从而删除梯度信息(因为只有变量保持渐变) . 第三种方式:在这里,你只需要通过每个 xk, yk 元组,因为你立即执行一个backprop步骤,完全避免了上述问题 .

    解决方案:我还没有测试过,但是根据我收集的内容,解决方案应该非常简单:在每个批处理的开头创建一个新的 total_loss 对象,然后将所有损失加总到该对象中,然后执行一个最终的backprop步骤在末尾 .

相关问题