1.查看计算图节点梯度
很多时候在BP过程中需要查看某个变量的梯度,来看看到底在梯度计算和计算图计算过程中发生了什么,如果是叶子节点,或是神经网络内部的节点,或者是loss等值还好,可以使用pytorch自带的 .grad 方法直接查看。
loss.retain_grad()
print(loss.grad)
但是如果想查看的中间变量是自定义函数计算过程中的非叶子节点,这些中间变量的梯度在函数执行后是不能在函数外查看的,就需要用hook的思想,把他勾出来记一下。方法如下:
# 先定义函数
grads = {}
def save_grad(name):
def hook(grad):
grads[name] = grad
return hook
# 在想查看的位置添加register_hook(函数内部或这个中间变量出现的地方)
t3.register_hook(save_grad('t3')) # 保存t3的梯度
# 最后输出这个中间变量梯度
print("[grads]t3:", grads['t3'])
2.修改计算图节点梯度
有些时候想人为修改某个节点的梯度,例如在BP过程中,某个节点的梯度出现了不理想值,需要修改,可以考虑这个方法(注意!!慎用,该方法相当于直接改回传梯度,会影响整体梯度回传,当梯度不理想时,建议认真查原因,而不是人为强制修改回传梯度)
# 定义修改函数
def hook_fn(grad):
grad = torch.zeros_like(grad)
return grad
# 在需要修改梯度的中间变量的后边添加这个register_hook,
# BP时会直接执行上边自定义的梯度修改函数,这里我对s2中间变量的i[0]位置的数的梯度进行修改。没错,甚至可以改一个1*n tensor中某个位置的梯度,这取决于代码逻辑。
s2[i[0]].register_hook(hook_fn)
3.(常用)修改梯度
如果担心梯度爆炸之类的发生,推荐使用clip_grad进行梯度剪裁
loss.backward() # 反向传播求梯度
nn.utils.clip_grad_norm_(model.parameters(), max_norm=args.clip) # 进行梯度裁剪