gpt4 book ai didi

python - Pytorch - 推断所有图像并逐批反向传播

转载 作者:太空宇宙 更新时间:2023-11-03 11:14:51 25 4
gpt4 key购买 nike

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

我修改了 cifar10_tutorial 的片段进入以下模拟我的问题: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: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time.

我的问题是:

  1. 根据我的理解,问题的出现是因为第一个反向传播整个outputsoutputs[1,:].unsqueeze(0)已发布,因此第二次反向传播失败。我说得对吗?

  2. 在我的例子中,如果我设置 retain_graph=True,代码会根据这个 post 运行得越来越慢吗? ?

  3. 有没有更好的方法来实现我的目标?

最佳答案

  1. 是的,你是对的。当您已经第一次(第一次迭代)通过输出反向传播时,缓冲区将被释放,并且它将在接下来的时间(循环的下一次迭代)失败,因为此计算所需的数据已被删除。

  2. 是的,图形变得越来越大,因此它可能会变慢,具体取决于 GPU (或 CPU) 使用情况和您的网络。我曾经用过一次,但速度要慢得多,但这在很大程度上取决于您的网络架构。但是使用 retain_graph=True 肯定会比不使用时需要更多的内存。

  3. 根据您的outputslabels 形状,您应该能够计算所有outputs 的损失一次标签:

    criterion(outputs, labels)

    你必须跳过 j-loop 然后,这也会使你的代码更快。也许您需要 reshape (resp.view)您的数据,但这应该可以正常工作。

    如果您出于某种原因不能这样做,您可以手动计算张量的损失并在循环后调用 backward。这应该也能正常工作,但比上面的解决方案慢。

    所以你的代码看起来像这样:

    # init loss tensor
    loss = torch.tensor(0.0) # move to GPU if you're using one

    for j in range(4):
    # summing up your loss for every j
    loss += criterion(outputs[j, :].unsqueeze(0), labels[j].unsqueeze(0))
    # ...
    # calling backward on the summed loss - getting gradients
    loss.backward()
    # as you call backward now only once on the outputs
    # you shouldn't get any error and you don't have to use 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)

这是每个输入的打印输出以及模型的相应梯度和梯度。 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,而是累加这些值并在总和上调用 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()是就输入和权重的结果梯度而言是等效的。

当您按照3. 中的描述同时对所有j 使用CE 时,您可以使用标志reduction= 'sum' 将与上述相同的行为归档为对 CE 值求和,默认为 'mean',这可能会导致略有不同的结果。

关于python - Pytorch - 推断所有图像并逐批反向传播,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53843711/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com