首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >逐批推理所有图像并逐批反向传播

逐批推理所有图像并逐批反向传播
EN

Stack Overflow用户
提问于 2018-12-19 02:23:50
回答 1查看 1.2K关注 0票数 6

我有一个特殊的用例,我必须将推理和反向传播分开:我必须推断所有图像和切片输出为批,然后是批后传播批。我不需要更新我的网络权重。

我修改了教程的代码片段,以模拟我的问题:j是一个变量,用来表示由我自己的逻辑返回的索引,我想要一些变量的梯度。

代码语言:javascript
复制
for epoch in range(2):  # loop over the dataset multiple times

    for i, data in enumerate(trainloader, 0):
        # get the inputs
        inputs, labels = data
        inputs.requires_grad = True

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)

        for j in range(4): # j is given by external logic in my own case

            loss = criterion(outputs[j, :].unsqueeze(0), labels[j].unsqueeze(0))

            loss.backward()

            print(inputs.grad.data[j, :]) # what I really want

我发现了以下错误:

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

我的问题是:

  1. 根据我的理解,问题的出现是因为第一个向后传播,整个outputsoutputs[1,:].unsqueeze(0)都发布了,所以第二个反向传播失败了。我说的对吗?
  2. 在我的例子中,如果我设置了retain_graph=True,代码会按照这个帖子运行得越来越慢吗?
  3. 有更好的方法来实现我的目标吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-12-19 09:58:13

  1. 是的你是对的。当您第一次(第一次迭代)已经通过outputs进行反向传播时,缓冲区将被释放,并且它将在接下来的时间(循环的下一次迭代)失败,因为这样的计算所必需的数据已经被删除了。
  2. 是的,图形越来越大,所以它可能会变慢,这取决于GPU (或CPU)的使用和您的网络。我曾经使用过它一次,它要慢得多,但是这在很大程度上取决于您的网络架构。但是,与没有retain_graph=True相比,您肯定需要更多的内存。
  3. 根据您的outputslabels形状,您应该能够一次计算所有outputslabels的损失: 标准(输出、标签) 您必须跳过j-loop,这也会使您的代码更快。也许你需要重塑(重新塑造)。view)您的数据,但这应该可以正常工作。 如果您由于某种原因不能这样做,您可以手动总结张量上的损失,并在循环后调用backward。这应该也很好,但比上面的解决方案要慢。 因此,您的代码看起来是这样的:init丢失张量丢失= torch.tensor(0.0) #,如果您在范围(4)中为j使用一个,则移动到GPU:#对每一个j损失+=标准(outputsj,:.unsqueeze(0),labelsj.unsqueeze(0)) #.#向后调用累计损失--获取梯度loss.backward() #,因为现在只对输出调用一次#您不应该有任何错误,并且不必使用retain_graph=True。

编辑:

损失的积累和稍后的反向调用完全等价,下面是一个有和不累积损失的小例子:

首先创建一些数据data

代码语言:javascript
复制
# w in this case will represent a very simple model
# I leave out the CE and just use w to map the output to a scalar value
w = torch.nn.Linear(4, 1)
data = [torch.rand(1, 4) for j in range(4)]

data看起来像:

代码语言:javascript
复制
[tensor([[0.4593, 0.3410, 0.1009, 0.9787]]),
 tensor([[0.1128, 0.0678, 0.9341, 0.3584]]),
 tensor([[0.7076, 0.9282, 0.0573, 0.6657]]),
 tensor([[0.0960, 0.1055, 0.6877, 0.0406]])]

让我们首先像您所做的那样,对每个迭代j分别进行反向调用:

代码语言:javascript
复制
# code for directly applying backward
# zero the weights layer w
w.zero_grad()
for j, inp in enumerate(data):
    # activate grad flag
    inp.requires_grad = True
    # remove / zero previous gradients for inputs
    inp.grad = None
    # apply model (only consists of one layer in our case)
    loss = w(inp)
    # calling backward on every output separately
    loss.backward()
    # print out grad
    print('Input:', inp)
    print('Grad:', inp.grad)
    print()
print('w.weight.grad:', w.weight.grad)

这是打印出的每一个输入和相应的梯度和梯度的模型resp。在我们简化的情况下,图层w

代码语言:javascript
复制
Input: tensor([[0.4593, 0.3410, 0.1009, 0.9787]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.1128, 0.0678, 0.9341, 0.3584]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.7076, 0.9282, 0.0573, 0.6657]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.0960, 0.1055, 0.6877, 0.0406]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

w.weight.grad: tensor([[1.3757, 1.4424, 1.7801, 2.0434]])

现在,我们不是每次迭代j都向后调用一次,而是累积值并调用和上的backward,并比较结果:

代码语言:javascript
复制
# init tensor for accumulation
loss = torch.tensor(0.0)
# zero layer gradients
w.zero_grad()
for j, inp in enumerate(data):
    # activate grad flag
    inp.requires_grad = True
    # remove / zero previous gradients for inputs
    inp.grad = None
    # apply model (only consists of one layer in our case)
    # accumulating values instead of calling backward
    loss += w(inp).squeeze()
# calling backward on the sum
loss.backward()

# printing out gradients 
for j, inp in enumerate(data):
    print('Input:', inp)
    print('Grad:', inp.grad)
    print()
print('w.grad:', w.weight.grad)

让我们来看看结果:

代码语言:javascript
复制
Input: tensor([[0.4593, 0.3410, 0.1009, 0.9787]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.1128, 0.0678, 0.9341, 0.3584]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.7076, 0.9282, 0.0573, 0.6657]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

Input: tensor([[0.0960, 0.1055, 0.6877, 0.0406]], requires_grad=True)
Grad: tensor([[-0.0999,  0.2665, -0.1506,  0.4214]])

w.grad: tensor([[1.3757, 1.4424, 1.7801, 2.0434]])

当比较结果时,我们可以看到两者是相同的。

这是一个非常简单的例子,但是我们可以看到,在每个张量上调用backward(),然后对张量进行求和,然后调用backward(),在输入和权重的结果梯度方面都是等价的。

当您一次对所有j 's使用CE时,如3中所述,您可以使用标志reduction='sum'来存档与上面相同的行为,并对CE值进行求和,默认值为“平均”,这可能会导致略有不同的结果。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53843711

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档