pytorch中正向传播和反向传播的钩子Hook

本文介绍了PyTorch中如何使用钩子(Hook)来监控和调试深度学习模型。通过在模块的前向计算前后及反向传播后注册钩子函数,可以动态地查看和修改输入、输出及梯度,帮助定位NaN或Inf等异常值,加速调试过程。文中提供了一个实例,展示了如何打印模块输入、输出和梯度的形状,以展示钩子的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在可视化特征图时遇到了钩子的问题,这里记录学习一下相关知识~


在某些情况下,我们需要对深度学习模型的前向计算和反向传播的行为做一定的修改。比如,我们想要观察深度学习模型的某一层中出现的异常值(NaN或者Inf),找出这些异常值的来源,或者想要对模块输出的张量做一定的修改。在这种情况下,可以通过在模块中引入钩子Hook来动态修改模块的行为。

一、钩子的概念

pytorch中的深度学习模型是由一个个子模块组合而成的,每个子模块有一定的输入(可以是张量和其他的python原生数据类型),并且会通过计算给出对应的输出。同样,在反向传播过程中,输入的是下一层梯度的输出,对应输出的是上一层的梯度和权重对应的梯度。钩子的引入就是为了能够在正向计算的前后和反向计算之后,对输入/输出的张量进行读取或者修改,以达到最终修改模块行为的目的。对于一个模块而言,可以有三种类型的钩子,分别在前向计算之前、前向计算之后和反向传播之后执行

二、模块执行之前的前向计算钩子

# 定义nn.Module的一个实例模块
module = ...
def hook(module, input):
    # 对模块权重或者输入进行操作的代码
    # 函数结果可以返回修改后的张量或者None
    return input
handle = module.register_forward_pre_hook(hook)

首先定义一个nn.Module的模块实例,然后定义一个钩子函数,这个函数有2个参数:模块本身和模块的输入(即forward方法的输入参数)。定义好这个钩子函数后,通过register_forward_per_hook来注册这个钩子函数,这样,在调用这个模块之前,会先进行钩子函数的调用,对模块和模块的输入做一定的修改,然后把修改后的参数传入模块中计算。注册钩子函数会返回一个句柄handle,通过调用这个句柄的remove方法可以移除这个钩子函数。

三、模块执行之后的前向计算钩子

# 定义nn.Module的一个实例模块
module = ...
def hook(module, input, output):
    # 对模块权重或者输入/输出进行操作的代码
    # 函数结果可以返回修改后的张量或者None
    return output
handle = module.register_forward_hook(hook)

该钩子函数有三个参数:module,input,output,分别是模块本身,模块的输入参数和输出参数。同样,这个钩子的注册函数会返回一个句柄,通过调用这个句柄的remove可以对这个钩子函数进行移除。

四、模块执行之后的反向传播钩子

# 定义nn.Module的一个实例模块
module = ...
def hook(module, grad_input, grad_output):
    # 对模块权重或者输入/输出梯度进行操作的代码
    # 函数结果可以返回修改后的张量或者None
    return output
handle = module.register_backward_hook(hook)

这个钩子的输入参数分别代表模块本身、输入的梯度和输出的梯度。注意,如果输入的参数有多个张量,输入梯度和输出梯度可能是一个元组,其中包含输入的每个张量的梯度。由于pytorch的模块可以输入张量和其他的python数据类型作为forward的参数,所以反向传播中grad_input和前向传播中的Input未必一一对应。

五、用例展示

最后用一个实例来展示一下如何使用钩子函数打印模块的输入和输出。首先定义模块前执行的钩子、模块后执行的钩子、以及梯度的钩子,然后分别在模块中注册这些钩子,接着执行前向计算。可以看到,模块在前向计算时分别调用了钩子函数,打印了输入张量的形状。然后进行反向传播计算,模块成功地打印了梯度的形状。当然,也可以把钩子函数换成更复杂的,比如检查模块输入/输出是否存在NaN, Inf这些值等,可以大大加快debug的速度。

import torch
import torch.nn as nn

def print_pre_shape(module, input):
    print('模块前钩子')
    print(module.weight.shape)
    print(input[0].shape)

def print_post_shape(module, input, output):
    print('模块后钩子')
    print(module.weight.shape)
    print(input[0].shape)
    print(output[0].shape)

def print_grad_shape(module, grad_input, grad_output):
    print('梯度钩子')
    print(module.weight.grad.shape)
    print(grad_input[0].shape)
    print(grad_output[0].shape)

conv = nn.Conv2d(16, 32, kernel_size=(3,3))
handle1 = conv.register_forward_pre_hook(print_pre_shape)
handle2 = conv.register_forward_hook(print_post_shape)
handle3 = conv.register_forward_backward_hook(print_grad_shape)

input = torch.randn(4, 16, 128, 128, requires_grad=True)

ret = conv(input)
ret = ret.sum()
ret.backward()

以上代码会输出下面的结果:

模块前钩子

torch.Size([32, 16, 3, 3])

torch.Size([4, 16, 128, 128])

模块后钩子

torch.Size([32, 16, 3, 3])

torch.Size([4, 16, 128, 128])

torch.Size([32, 126, 126])

梯度钩子

torch.Size([32, 16, 3, 3])

torch.Size([4, 16, 128, 128])

torch.Size([4, 32, 126, 126])

### Nginx 文件名逻辑漏洞(CVE-2013-4547) #### 漏洞概述 Nginx 文件名逻辑漏洞(CVE-2013-4547)允许攻击者通过精心构造的 URL 请求来绕过访问控制并读取或执行受限资源。此漏洞的根本原因在于 Nginx 错误地解析了带有特定编码字符的 URL,从而导致文件路径处理不当[^1]。 #### 影响范围 该漏洞影响多个版本的 Nginx,在某些配置下可能导致未经授权的文件访问甚至远程代码执行。具体受影响的版本包括但不限于: - Nginx 1.4.x 版本系列 - Nginx 1.5.x 版本系列 (部分) 当 Web 应用程序部署于上述版本之上时,可能存在潜在风险[^3]。 #### 复现过程 为了验证这一漏洞的存在,可以通过上传一个看似无害但实际上包含恶意 PHP 代码的图片文件 `phpinfo.jpg` 来测试。一旦成功上传,攻击者能够修改 HTTP 请求中的参数使服务器错误解释文件扩展名,进而触发命令注入行为[^4]。 ```bash curl -X POST http://example.com/upload.php \ -F "file=@/path/to/phpinfo.jpg" ``` 随后发送如下请求可尝试利用漏洞: ```http GET /uploads/phpinfo.jpg%00.php?cmd=id HTTP/1.1 Host: example.com ``` 如果存在漏洞,则返回的结果会显示当前用户的 ID 信息。 #### 安全修复措施 针对 CVE-2013-4547 的防护手段主要包括以下几个方面: - **升级至最新稳定版**:官方已发布更新解决此问题,建议立即应用最新的安全补丁以消除隐患[^2]。 - **手动修补源码**:对于无法即时升级的情况,可以从官方网站下载专门为此漏洞准备的安全补丁,并按照指引完成编译安装流程。 - **加强输入校验**:无论何时都应严格过滤用户提交的数据,特别是涉及文件操作的部分,防止非法字符进入内部处理环节。 - **启用 WAF 防护**:Web Application Firewall 能够识别异常模式并阻止可疑流量到达应用程序层面上游位置。 综上所述,及时采取适当行动可以有效降低遭受此类攻击的风险。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值