57

我知道从梯度计算中排除计算元素的两种方法backward

方法一:使用with torch.no_grad()

with torch.no_grad():
    y = reward + gamma * torch.max(net.forward(x))
loss = criterion(net.forward(torch.from_numpy(o)), y)
loss.backward();

方法二:使用.detach()

y = reward + gamma * torch.max(net.forward(x))
loss = criterion(net.forward(torch.from_numpy(o)), y.detach())
loss.backward();

这两者有区别吗?两者都有好处/缺点吗?

4

3 回答 3

79

tensor.detach()创建一个与不需要 grad 的张量共享存储的张量。它将输出与计算图分离。所以没有梯度会沿着这个变量反向传播。

包装器with torch.no_grad()暂时将所有requires_grad标志设置为 false。torch.no_grad表示任何操作都不应该构建图形。

不同之处在于,它仅指调用它的给定变量。with另一个影响语句中发生的所有操作。此外,torch.no_grad将使用更少的内存,因为它从一开始就知道不需要渐变,因此不需要保留中间结果。

从这里了解更多关于这些之间的差异以及示例。

于 2019-06-29T12:39:00.243 回答
33

detach()

一个没有的例子detach()

from torchviz import make_dot
x=torch.ones(2, requires_grad=True)
y=2*x
z=3+x
r=(y+z).sum()    
make_dot(r)

在此处输入图像描述

绿色的最终结果r是 AD 计算图的根,蓝色是叶张量。

另一个例子detach()

from torchviz import make_dot
x=torch.ones(2, requires_grad=True)
y=2*x
z=3+x.detach()
r=(y+z).sum()    
make_dot(r)

在此处输入图像描述

这与以下内容相同:

from torchviz import make_dot
x=torch.ones(2, requires_grad=True)
y=2*x
z=3+x.data
r=(y+z).sum()    
make_dot(r)

但是,x.data是旧的方式(符号),x.detach()是新的方式。

有什么区别x.detach()

print(x)
print(x.detach())

出去:

tensor([1., 1.], requires_grad=True)
tensor([1., 1.])

所以 x.detach()是一种删除方法requires_grad,你得到的是一个新的分离张量(从 AD 计算图分离)。

火炬.no_grad

torch.no_grad实际上是一个类。

x=torch.ones(2, requires_grad=True)
with torch.no_grad():
    y = x * 2
print(y.requires_grad)

出去:

False

来自help(torch.no_grad)

当您确定时,禁用梯度计算对推理很有用 | 你不会调用 :meth: Tensor.backward()。它会减少内存| 否则会有的计算消耗requires_grad=True。|
| 在这种模式下,每次计算的结果都会有 | requires_grad=False,即使输入有requires_grad=True.

于 2020-09-23T01:54:20.267 回答
7

一个简单而深刻的解释是,使用的with torch.no_grad()行为就像一个循环,其中写入的所有内容都将requires_grad设置参数,False尽管是暂时的。因此,如果您需要停止从某些变量或函数的梯度进行反向传播,则无需指定任何其他内容。

然而,torch.detach()顾名思义,只需将变量从梯度计算图中分离出来。但是,当必须为有限数量的变量或函数提供此规范时,例如使用此规范。通常在神经网络训练结束后显示损失和准确性输出时,因为在那一刻,它只消耗资源,因为它的梯度在结果显示期间无关紧要。

于 2020-07-06T08:34:16.373 回答