参数keep_graph在变量的backward()方法中意味着什么?
conv-neural-network
neural-network
pytorch
9
0

我正在阅读神经传递pytorch教程 ,并对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(布尔型,可选)–如果为False,将释放用于计算grad的图形。请注意,几乎在所有情况下都不需要将此选项设置为True,并且通常可以以更有效的方式解决它。默认为create_graph的值。

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

参考资料:
Stack Overflow
收藏
评论
共 2 个回答
高赞 时间 活跃

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

一种实现方法是让网络共享卷积层,但随后具有两个并行分类层(原谅我糟糕的ASCII图,但这应该是三个卷积层,然后是三个完全连接的层,其中一层用于猫还有一个用于汽车):

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

给定一张我们想要在两个分支上都运行的图片,在训练网络时,我们可以通过几种方式来实现。首先(这可能是最好的事情,说明示例有多糟糕),我们仅在两个评估中计算损失并求和,然后反向传播。

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

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

至于多次向后传递所发生的情况:如您所料,pytorch通过就地添加梯度(将其添加到变量的/ parameters .grad属性中)来累积梯度。这可能非常有用,因为这意味着循环遍历一个批处理并一次处理一次,最后累积渐变,将执行与进行完整批处理更新相同的优化步骤(该步骤只会将所有渐变汇总为好)。尽管完全批处理的更新可以更多地并行化,因此通常是更可取的,但是在某些情况下,批处理计算要么非常,非常难以实现,要么根本不可能。但是,使用这种累积量,我们仍然可以依靠批处理带来的一些稳定特性。 (如果不提高性能)

收藏
评论

@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!

这里可以找到更多有用的讨论。

一个真实的用例

现在,一个真正的用例是多任务学习,其中您可能会遇到多个损失,这些损失可能在不同的层次上。假设你有2负: loss1loss2和他们居住在不同的层次。为了梯度backprop loss1loss2 WRT到您的网络的可学习的重量独立。在第一个向后传播的损失中,必须在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
收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号