【问题标题】:What does the parameter retain_graph mean in the Variable's backward() method?变量的backward()方法中的参数retain_graph是什么意思?
【发布时间】:2018-03-28 05:31:17
【问题描述】:

我正在浏览neural transfer pytorch tutorial,对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

来自the documentation

retain_graph (bool, optional) – 如果为 False,则用于计算的图 毕业生将被释放。请注意,几乎在所有情况下都设置此 不需要 True 选项,通常可以在很长时间内解决 更有效的方式。默认为 create_graph 的值。

因此,通过设置retain_graph= True,我们不会释放为向后传递的图形分配的内存。保留这个内存有什么好处,我们为什么需要它?

【问题讨论】:

    标签: neural-network conv-neural-network backpropagation pytorch automatic-differentiation


    【解决方案1】:

    @cleros 非常清楚retain_graph=True 的使用。本质上,它将保留计算某个变量所需的任何信息,以便我们可以对其进行反向传递。

    一个说明性的例子

    假设我们有一个如上所示的计算图。变量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(),我们必须在d.backward()中将参数retain_graph设置为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!
    

    更多有用的讨论可以找到here

    一个真实的用例

    目前,一个真正的用例是多任务学习,其中可能存在不同层的多个损失。假设您有 2 个损失:loss1loss2,它们位于不同的层。为了将loss1loss2 w.r.t 的梯度独立地反向传播到网络的可学习权重。在第一个反向传播损失中,您必须在 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
    

    【讨论】:

    • 为了避免使用retain_graph=True,你可以先使用loss = loss1 + loss2然后loss.backward()
    • @BrandonBrown 这两种方法在数学上是等价的吗?
    • @PrzemekD 我认为只要在将它们相加时不使用系数就可以了。
    • @MAsadAli 我会试试的。 ADAM 的每个副本都存储自适应学习率参数,这些参数表示损失函数在参数空间中的“平滑”程度。如果两个损失是不同的平滑度,则可能很难选择对两者都适用的值。 (1/2)
    • 假设损失 1 随您的参数快速变化,但幅度很小。你需要小步骤来优化它,因为它并不顺利。损失 2 变化缓慢,但幅度很大。 #2 将主导它们的总和,因此一个共享的 ADAM 将选择一个大的学习率。但是如果你把它们分开,ADAM 会为损失 #2 选择一个大的学习率,而为损失 #1 选择一个小的学习率。 (2/2)
    【解决方案2】:

    当您有多个网络输出时,这是一项非常有用的功能。这是一个完全虚构的示例:假设您想要构建一些随机卷积网络,您可以提出两个问题:输入图像是否包含猫,图像是否包含汽车?

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

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

    给定一张我们想要同时运行两个分支的图片,在训练网络时,我们可以通过多种方式进行。首先(这可能是这里最好的事情,说明这个例子有多糟糕),我们简单地计算两个评估的损失并将损失相加,然后反向传播。

    但是,还有另一种情况 - 我们希望按顺序执行此操作。首先我们想通过一个分支进行反向传播,然后通过另一个分支(我之前有过这个用例,所以它没有完全组成)。在这种情况下,在一个图上运行.backward() 也会破坏卷积层中的任何梯度信息,并且第二个分支的卷积计算(因为这些是唯一与另一个分支共享的)将不再包含图!这意味着,当我们尝试通过第二个分支进行反向传播时,Pytorch 将抛出错误,因为它找不到将输入连接到输出的图! 在这些情况下,我们可以通过简单地保留第一次反向传递的图来解决问题。然后该图将不会被消耗,而只会被不需要保留它的第一个反向传递消耗。

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

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

    【讨论】:

    • 谢谢,这非常有帮助!几个后续问题: 1. 如果所有后向传递都保留了图表会发生什么?这只是浪费内存还是会出现其他问题? 2. 在你的例子中,假设我们也在训练所有的卷积层。在第一次向后传递时,将为每一层计算它们的梯度。当我们运行第二次反向传递时,相同卷积层的梯度是否加在一起?
    • 添加了您对答案的评论的答案:-)
    • 这对我来说很有意义。即使您在最后一次向后传递时使用retain_graph=False 向后运行,看起来仍然是 isn't 共享的分支,例如首先运行的那个,仍然不会清理它的资源。在您的示例中,Conv -> Conv -> Conv 在共享分支中被释放,但-- FC - FC - FC - cat?
    猜你喜欢
    • 2016-10-17
    • 1970-01-01
    • 2016-03-20
    • 2017-11-14
    • 1970-01-01
    • 2021-03-04
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多