PyTorch 3. autograd
autograd
- 动态图:运算与搭建同时进行
- 静态图:先搭建图,后运算
计算图只能backward一次,若想再次使用则必须设置retain_graph=True
torch.autograd.grad(outputs,
inputs,
grad_outputs=None,
retain_graph=None,
create_graph=False)
功能:求取梯度
outputs:用于求导的张量,如loss
inputs: 需要梯度的张量
create_graph: 创建导数计算图,用于高阶求导
retain_graph: 保存计算图,反向传播需要缓存一些中间结果,反向传播之后,这些缓存就会被清空,可通过指定这个参数不清空缓存,用来多次反向传播。
grad_outputs:多梯度权重
例子
# 创建tensor的时候指定requires_grad
a = torch.randn(3,4,requires_grad=True)
# 或者
a = torch.randn(3,4).requires_grad_()
# 或者
a = torch.randn(3,4)
a.requires_grad=True
如果某一个tensor需要求导,那么与该tensor有直接关联的tensor的requires_grad会自动设置为True
叶子节点:由用户创建的variable属于叶子节点,对应的grad_fn是None
auto_grad具体操作
在PyTorch实现中,autograd会随着用户的操作,记录生成当前variable的所有操作,并由此建立一个有向无环图。用户每进行一次操作,相应的计算图就会发生改变。更低层的实现中,图中记录了操作Function,每一个变量在图中的位置可通过其grad_fn属性在图中的位置推测得到。在反向传播过程中,autograd沿着这个图从当前变量(根节点z)溯源,可以利用链式求导法则计算所有叶子节点的梯度。 每一个前向传播操作的函数都有与之对应的反向传播函数用来计算输入的各个variable的梯度,这些函数的函数名通常以Backward结尾。
推理环节
有时候,我们不希望autograd对tensor求导,认为求导需要缓存许多中间结构,增加额外的显存开销,那么我们可以关闭自动求导。对于不需要反向传播的情形(如测试推理),关闭自动求导可实现一定程度的速度提升,并节省约一半的显存。
tensor.data
如果我们想要修改tensor的数值,但是又不希望被autograd记录,那么可以对tensor.data进行操作
d = a.data.sigmoid_() #sigmoid_ 是一个inplace操作,会改变a自身的值
autograd.grad和hook
在反向传播过程中饭非叶子节点的导数计算完之后即被清空。若想查看这些变量的梯度,有两种方法:
- 使用autograd.grad函数
- 使用hook
x = torch.ones(3, requires_grad=True)
w = torch.rand(3, requires_grad=True)
y = x * w
# y依赖于w,而w.requires_grad=True
z = y.sum()
z.backward()
计算完后,由于y是非叶子节点,所以y的梯度为空
- 第一种获取中间变量梯度的方法
torch.autograd.grad(z,y)
- 第二种获取中间变量梯度的方法
使用hook,hook是一个函数,输入是梯度,不应该有返回值
def variable_hook(grad):
print('y的梯度:',grad)
x = torch.ones(3, requires_grad=True)
w = torch.rand(3, requires_grad=True)
y = x*w
#注册hook
hook_handle = y.register_hook(variable_hook)
z = y.sum()
z.backward()
# 除非每次都用用hook,否则用完之后要移除hook
hook_handle.remove()
扩展autograd
class Sigmoid(Function):
@staticmethod
def forward(ctx, x):
output = 1/(1+t.exp(-x))
ctx.save_for_backward(output)
return output
@staticmethod
def backward(ctx, grad_output):
output,_ = ctx,saved_tensors
grad_x = output *(1-output)*grad_output
return grad_x