在学习图像迁移的时候,发现使用的 vgg19 模型需要保证模型参数不更新,所以使用了如下代码:

vgg19 = models.vgg19(pretrained=True).to(device)
vgg = vgg19.features
for param in vgg.parameters():
    param.requires_grad_(False)

  但是疑惑的是,requires_grad_(False) 的作用是让参数不被追踪。百思不解其意,故使用如下代码测试:

import torch

N, D = 1, 1
x1 = torch.Tensor([2]).requires_grad_(True)
y1 = torch.Tensor([2])
z1 = torch.Tensor([2])

# y2 = torch.Tensor([3])
# z2 = torch.Tensor([3])

y2 = torch.Tensor([3]).requires_grad_(False)
z2 = torch.Tensor([3]).requires_grad_(False)

print(y1, z1)
print(y2, z2)

a1 = x1 * y1
b1 = a1 + z1

a2 = b1 * y2
b2 = a2 + z2

b2.backward()
print(x1.grad)
print(y1.grad)
print(y2.grad)

发现requires_grad_(True)的作用是使得当前的参数可以被保存梯度,当使用backward()的时候,x1的梯度会被保存在grad中,如果y1,y2等没有使用requires_grad_(True)或使用requires_grad_(False),那么在backward()的时候会计算它的梯度,但是不会保存在grad里。可以节省内存开销。

  或许在backward()时候计算完当前参数的梯度,就会释放上一个计算的梯度,如果没有requires_grad_(True),就不会写进grad里。(猜测


更新(2021-05-12)
torch.autograd 的简要介绍Pytorch 中 autograd 以及 hook 函数详解

import torch
x = torch.tensor([1, 2], dtype=torch.float32, requires_grad=True)
a = torch.tensor([3, 4], dtype=torch.float32, requires_grad=True)
y = x * 2 + a
y.requires_grad_(True)
z = torch.mean(y)

z.backward()
print(x.grad, x.requires_grad)
print(a.grad, a.requires_grad)
print(y.grad, y.requires_grad)
print(z.grad, z.requires_grad)

#  以下结果需要设置a的requires_grad=False或取消这个参数。
#  tensor([1., 1.]) True
#  None False
#  None True
#  None True

经过以上两篇文章和代码,得到如下结论:

  1. requires_grad=True 的作用是让 backward 可以追踪这个参数并且计算它的梯度。最开始定义你的输入是 requires_grad=True ,那么后续对应的输出也自动具有 requires_grad=True ,如代码中的 yz ,而与 zx 求导无关联的 a ,其 requires_grad 仍等于 False。
  2. 如果在 zx 求导的过程中需要用到 zy 求导,而如果设置 y.requires_grad=False ,则会报错。就是因为只有 requires_grad=True 的参数才会参与求导,而在求导路径的中间设置相关参数不可求导,那么它就会报错。
  3. 那么如何取得参数的 grad :①如果你想取的参数是由 torch.tensor(requires_grad=True) 定义的,可以直接取它的 grad ;②如果你的参数是如 yz 这样计算出来的,那么根据编译器警告,需要定义 y.retain_grad() 就可以取得 y 的 grad ;③还有一个方法是使用钩子可以保存计算的中间梯度,在上面文章中可见。由此可知梯度的计算会在计算完成后遗弃,并且 requires_grad=False 的参数不计算它的梯度,如此可以减少内存使用和降低计算量。
  4. 当你在使用 Pytorch 的 nn.Module 建立网络时,其内部的参数都自动的设置为了 requires_grad=True ,故可以直接取梯度。而我们使用反向传播时,其实根据全连接层的偏导数计算公式,可知链式求导和 wb 的梯度无关,而与其中一个连接层的输出梯度有关,这也是为什么冻结了网络的参数,还是可以输出对输入求导。如下式:
    pytorch GCN参数共享_python
    pytorch GCN参数共享_深度学习_02
    pytorch GCN参数共享_python_03
  5. Torch 的 tensor 和 Tensor 使用有所不同:
torch.tensor([1, 2], dtype=torch.float32, requires_grad=True)
torch.Tensor([1, 2], dtype=torch.float32).requires_grad_(True)
  1. 叶子节点:只有定义的 Tensor 参数才是叶子节点;只有 requires_grad=True 和叶子节点 is_leaf=True 才有 grad 的值。
    参考博文:pytorch 张量 叶子节点说明    Pytorch学习笔记(一)——自动求导和叶子节点