PyTorch
    首先明确一点,有哪些hook?
    看到的有3个:

    • torch.autograd.Variable.register_hook (Python method, in Automatic differentiation package)
    • torch.nn.Module.register_backward_hook (Python method, in torch.nn)
    • torch.nn.Module.register_forward_hook

    第一个是registerhook,是针对Variable对象的,后面的两个:register_backward_hook和register_forward_hook是针对**nn.Module**这个对象的。
    其次,明确一下,为什么需要用hook?
    打个比方,有这么个函数:
    ![](https://cdn.nlark.com/yuque/__latex/04b3ac4e9f5b3d819832c8b4881bb00e.svg#card=math&code=x%20%5Cin%20%5Cmathbb%7BR%7D%5E%7B2%7D%2C%20%5Cquad%20y%3Dx%2B2%2C%20%5Cquad%20z%3D%5Cfrac%7B1%7D%7B2%7D%5Cleft%28y
    %7B1%7D%5E%7B2%7D%2By_%7B2%7D%5E%7B2%7D%5Cright%29&id=leGgY)
    2022-06-28-08-24-30-525983.png
    想通过梯度下降法求最小值。在PyTorch里面很容易实现,只需要:

    1. import torch
    2. from torch.autograd import Variable
    3. x = Variable(torch.randn(2, 1), requires_grad=True)
    4. y = x+2
    5. z = torch.mean(torch.pow(y, 2))
    6. lr = 1e-3
    7. z.backward()
    8. x.data -= lr*x.grad.data

    但问题是,如果想要求中间变量 y 的梯度,系统会返回错误。
    事实上,如果输入:

    1. type(y.grad)

    系统会提示:NoneType
    这个问题在PyTorch的论坛上有人提问过,开发者说是因为当初开发时设计的是,对于中间变量一旦它们完成了自身反传的使命,就会被释放掉
    因此,hook就派上用场了。简而言之,register_hook的作用是,当反传时,除了完成原有的反传,额外多完成一些任务。可以定义一个中间变量的hook,将它的grad值打印出来,当然也可以定义一个全局列表,将每次的grad值添加到里面去。

    1. import torch
    2. from torch.autograd import Variable
    3. grad_list = []
    4. def print_grad(grad):
    5. grad_list.append(grad)
    6. x = Variable(torch.randn(2, 1), requires_grad=True)
    7. y = x+2
    8. z = torch.mean(torch.pow(y, 2))
    9. lr = 1e-3
    10. y.register_hook(print_grad)
    11. z.backward()
    12. x.data -= lr*x.grad.data

    需要注意的是,register_hook函数接收的是一个函数,这个函数有如下的形式:

    1. hook(grad) -> Variable or None

    也就是说,这个函数是拥有改变梯度值的威力的!
    至于register_forward_hookregister_backward_hook的用法和这个大同小异。只不过对象从Variable改成了自己定义的nn.Module
    当训练一个网络,想要提取中间层的参数、或者特征图的时候,使用hook就能派上用场了。
    参考资料: