pytorch后训练剪枝应使用torch.nn.utils.prune而非torch.ao.quantization;前者通过掩码实现稀疏化,支持非结构化与手动结构化剪枝,后者仅用于量化。

PyTorch后训练剪枝用 torch.nn.utils.prune 还是 torch.ao.quantization?
后训练剪枝(Post-Training Pruning)不依赖反向传播,只靠校准数据前向推理来评估重要性,因此和量化一样属于部署前优化手段。但别混淆:torch.ao.quantization 专用于量化(int8/fp16),不处理稀疏性;真正做剪枝的是 torch.nn.utils.prune 模块——它直接操作 nn.Module 的权重属性,插入稀疏掩码(mask),不改模型结构。
注意:PyTorch 1.12+ 把剪枝工具从 torch.nn.utils 移到了 torch.nn.utils.prune,旧文档里写的 prune.random_unstructured 等函数名没变,但 import 路径必须更新。
怎么对 Conv2d 或 Linear 层做全局结构化剪枝?
结构化剪枝(如剪整行/整列/整通道)比非结构化(随机置零)更利于硬件加速,但 PyTorch 原生 prune 模块默认只支持非结构化。要实现通道级剪枝(比如剪掉不重要的输出通道),得手动组合:
- 用
prune.ln_structured对每个Conv2d.weight按n=1, dim=0(即按输出通道维度)做 L1-norm 排序剪枝 - 对
BatchNorm2d.weight和bias同步剪相同索引的通道(否则 shape 不匹配) - 剪完后调用
prune.remove永久删除被剪参数(否则推理时仍保留 mask 和零值)
示例关键步骤:
prune.ln_structured(model.layer1.conv1, name='weight', amount=0.3, n=1, dim=0) prune.ln_structured(model.layer1.bn1, name='weight', amount=0.3, n=1, dim=0) prune.remove(model.layer1.conv1, 'weight') prune.remove(model.layer1.bn1, 'weight')
剪枝后模型变慢甚至报错?常见三类陷阱
剪枝不是“设个参数就完事”,实际落地常卡在这几个点:
-
prune默认只修改.weight,但有些层(如ConvTranspose2d)需同时处理.bias,否则 forward 报RuntimeError: mat1 and mat2 shapes cannot be multiplied - 剪枝后的模型无法直接用
torch.jit.trace导出:因为 mask 是动态属性,JIT 不识别;必须先prune.remove再 trace - 全局剪枝(
prune.global_unstructured)容易误伤小权重层(如最后分类层),建议按模块分组控制比例,比如 backbone 剪 40%,head 只剪 10%
验证剪枝效果不能只看稀疏率
prune.total_sparsity(model) 返回的只是零元素占比,和真实推理加速无关。真正要看的是:
- 用
torch.profiler测剪枝前后各层的self_cpu_time_total,尤其关注 conv 层是否下降 - 导出 ONNX 后用
onnxruntime跑 inference,对比 latency 和内存占用(稀疏权重在 CPU 上不一定快,GPU 需配合 cuSPARSE) - 校准数据集上 top-1 准确率下降超过 2% 就得调
amount或换剪枝策略(比如从ln_structured换成random_structured测试鲁棒性)
稀疏模型的权重本质还是 dense tensor,除非你用 torch.sparse 重写 forward,否则不会自动触发稀疏计算路径——这点最容易被忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











