在变量的 back()方法中,保留_graph 参数意味着什么?

我正在浏览 神经传导火炬教程,对使用 retain_variable感到困惑(不推荐使用,现在称为 retain_graph)。代码示例显示:

class ContentLoss(nn.Module):


def __init__(self, target, weight):
super(ContentLoss, self).__init__()
self.target = target.detach() * weight
self.weight = weight
self.criterion = nn.MSELoss()


def forward(self, input):
self.loss = self.criterion(input * self.weight, self.target)
self.output = input
return self.output


def backward(self, retain_variables=True):
#Why is retain_variables True??
self.loss.backward(retain_variables=retain_variables)
return self.loss

来自 文件

Keep _ graph (bool,可选)-如果为 False,则用于计算的图 毕业生将被释放。请注意,在几乎所有的情况下设置此 选择真是不需要,往往可以在一个工作周围的很多 默认值为 create _ graph。

因此,通过设置 retain_graph= True,我们不会在向后传递时释放分配给图的内存。保留这些记忆的好处是什么,我们为什么需要它们?

56816 次浏览

当您有一个以上的网络输出时,这是一个非常有用的特性。这里有一个完全虚构的例子: 假设您想构建一个随机卷积网络,您可以问两个问题: 输入图像是否包含一只猫,以及图像是否包含一辆汽车?

一种方法是建立一个共享卷积层的网络,但是后面有两个并行的分类层(请原谅我的糟糕的 ASCII 图,但是这应该是三个传送层,后面是三个完全连接的层,一个用于猫,一个用于汽车) :

                    -- FC - FC - FC - cat?
Conv - Conv - Conv -|
-- FC - FC - FC - car?

给定一个我们希望同时运行两个分支的图片,在训练网络时,我们可以通过几种方式来完成。首先(这可能是这里最好的事情,说明这个例子有多糟糕) ,我们只是计算两个评估的损失,并对损失进行求和,然后反向传播。

然而,还有另一种情况-在这种情况下,我们希望按顺序做这件事。首先,我们希望通过一个分支进行后台支撑,然后再通过另一个分支进行后台支撑(我以前有过这个用例,所以它不是完全编造的)。在这种情况下,在一个图上运行 .backward()也会破坏卷积层中的任何渐变信息,而第二个分支的卷积计算(因为这是唯一与另一个分支共享的计算)将不再包含图了!这意味着,当我们尝试通过第二个分支进行后台支持时,Pytch 将抛出一个错误,因为它找不到将输入和输出连接起来的图形! 在这些情况下,我们可以通过简单地保留图的第一个向后通道来解决这个问题。然后,图将不会被消耗,而只会被不需要保留它的第一个向后传递消耗。

编辑: 如果在所有向后传递过程中都保留图形,那么附加到输出变量的隐式图形定义将永远不会被释放。这里可能也有一个用例,但我想不出一个。因此,一般来说,您应该确保最后一次向后传递通过不保留图形信息来释放内存。

至于多次向后传递会发生什么: 正如您猜测的那样,pytch 通过就地添加渐变(添加到变量的/参数 .grad属性中)来积累渐变。 这是非常有用的,因为它意味着循环处理一个批处理,一次处理一个批处理,在最后累积渐变,将做相同的优化步骤做一个完整的批处理更新(这只是总结了所有的渐变)。尽管完全批处理的更新可以并行化得更多,因此通常更可取,但在某些情况下,批处理计算要么非常、非常难以实现,要么根本不可能实现。然而,使用这种积累,我们仍然可以依赖于批处理带来的一些很好的稳定性能。(如果没有业绩收益)

@ cleros 非常关注 retain_graph=True的使用。本质上,它将保留任何必要的信息来计算某个变量,这样我们就可以对它进行向后传递。

举个例子

enter image description here

假设我们有一个如上所示的计算图。变量 de是输出,a是输入。比如说,

import torch
from torch.autograd import Variable
a = Variable(torch.rand(1, 4), requires_grad=True)
b = a**2
c = b*2
d = c.mean()
e = c.sum()

当我们做 d.backward(),这是罚款。计算结束后,默认情况下将释放计算 d的图的各个部分以节省内存。因此,如果我们执行 e.backward(),将弹出错误消息。为了执行 e.backward(),我们必须将参数 retain_graph设置为 d.backward()中的 True,即,

d.backward(retain_graph=True)

只要在反向方法中使用 retain_graph=True,就可以随时进行反向操作:

d.backward(retain_graph=True) # fine
e.backward(retain_graph=True) # fine
d.backward() # also fine
e.backward() # error will occur!

更有用的讨论可以找到 给你

一个真正的用例

现在,一个真正的用例是多任务学习,在这个学习过程中,您可能在不同的层上有多个损失。假设你有2个损失: loss1loss2,他们居住在不同的层。为了独立支撑 loss1loss2的梯度到你网络的可学习权重。你必须在 backward()方法中使用 retain_graph=True在第一个反向传播的损失。

# suppose you first back-propagate loss1, then loss2 (you can also do the reverse)
loss1.backward(retain_graph=True)
loss2.backward() # now the graph is freed, and next process of batch gradient descent is ready


optimizer.step() # update the network parameters