我有一个特殊的用例,我必须将推理和反向传播分开:我必须推断所有图像和切片输出为批,然后是批后传播批。我不需要更新我的网络权重。
我修改了教程的代码片段,以模拟我的问题:j是一个变量,用来表示由我自己的逻辑返回的索引,我想要一些变量的梯度。
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。
我的问题是:
outputs和outputs[1,:].unsqueeze(0)都发布了,所以第二个反向传播失败了。我说的对吗?retain_graph=True,代码会按照这个帖子运行得越来越慢吗?发布于 2018-12-19 09:58:13
outputs进行反向传播时,缓冲区将被释放,并且它将在接下来的时间(循环的下一次迭代)失败,因为这样的计算所必需的数据已经被删除了。retain_graph=True相比,您肯定需要更多的内存。outputs和labels形状,您应该能够一次计算所有outputs和labels的损失:
标准(输出、标签)
您必须跳过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
# 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看起来像:
[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分别进行反向调用:
# 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:
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,并比较结果:
# 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)让我们来看看结果:
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值进行求和,默认值为“平均”,这可能会导致略有不同的结果。
https://stackoverflow.com/questions/53843711
复制相似问题