Pytorch下grad详解 (V0.4+)

定义求导张量

pytorch中的张量(Torch/Variable)中包含了梯度grad属性,和grad_fn方法。其中,grad中保存了变量求导值,grad_fn保存计算符号,用于传播自动求导用的。
首先定义两个Torch,a,b。
张量在定义时默认requires_grad=False,即默认状态下该变量不参与求导。但是在定义张量的时候可以通过该参数来设置是否对该变量求导。

>>>import torch
>>>a = torch.randn(2,2)
tensor([[-1.5097,  0.1668],
        [ 1.3727, -1.1971]])
        
>>>b = torch.randn(2,2,requires_grad = True)
tensor([[ 1.5506,  0.3628],
        [ 0.3859, -0.1381]])
>>>a.requires_grad
False

>>>b.requires_grad
True       

向后传播时requires_grad的状态

若新的变量是由已有的变量推导而来(x = a +b),此时只要新变量(x)中所依赖变量(a, b)中任意一个变量的requires_grad = True。则新变量(x)的requires_grad值也为True
例如:

>>>a.requires_grad  # a 不可求导
False

>>>b.requires_grad  # b 可求导
True

>>>c.requires_grad  # c 不可求导
Flase

>>>x = a + b
>>>x.requires_grad  # x所依赖变量中b是可求导的,因此x也可求导
True

>>>y = a + c
>>>y.requires_grad  # y所依赖的所有变量requires_grad都为False,因此y不可求导
False

控制求导torch.no_grad()

变量可以在定义的时候通过requires_grad控制是否对其求导,也可以在定义后通过直接修改属性requires_grad=True来控制。
但是如果在向后传播时,通过可导变量来大规模推导出不可导的变量,则可以使用torch.no_grad()

注:Pytorch版本0.4及以上,volatile被弃用,使用with torch.no_grad()替代。
>>>a.requires_grad
True
>>>b.requires_grad
True

>>>with torch.no_grad():
>>>    x = a + b
>>>    y = a * b
>>>    z = a * (a + b)

>>>x.requires_grad
False

>>>y.requires_grad
False

>>>z.requires_grad
False

x, y, z变量所依赖的变量a, b 都是可求导的。如果是不加条件正常的向后传播,x, y, z应该均为可导(参考:控制求导torch.no_grad()),但是在这里,这三个变量都是定义在with torch.no_grad()之下,因此,新变量都不可求导。
要注意的是with torch.no_grad()在使用上有一些误区。
例如:

>>>a.requires_grad
True
>>>b.requires_grad
True
>>>x = a + b
>>>x.requires_grad
True

>>>with torch.no_grad():
>>>    c = torch.randn(2,2,requires_grad=True) 
>>>    x = a
>>>    y = a + c

>>>c.requires_grad  #  !!没有起作用
True
>>>x.requires_grad  #  !!没有起作用
True
>>>y.requires_grad  #  正常
False

注意! x = ac = torch.randn(2,2,requires_grad=True)其本质为赋值,而不是算数推理,因此torch.no_grad()不会生效。而y = a + c是推理,是一种运算,y求导会被冻结。
由上可以看出,torch.no_grad()会对下一级推理(运算)的求导冻结,但是赋值操作不算做推理运算,不受torch.no_grad()的约束。