📚推荐阅读
面试官:“剪枝了解吗?那你能说说结构化剪枝和非结构化剪枝的区别吗?”
剪枝虽然是老话题,但如果你真能讲清楚它的核心思想 + 工程取舍,那还是比较难的,所以今天我们就来彻底梳理一下这个问题。
所有相关源码示例、流程图、模型配置与知识库构建技巧,我也将持续更新在Github:AIHub,欢迎关注收藏!
一、为什么要剪枝?
剪枝通过移除模型中冗余或不重要的权重/神经元,减少参数量和计算量。比如一个 ResNet 可能有上千万个参数,但真正决定输出结果的那部分其实占比并不高。所以我们可以通过剪枝去掉这些“冗余权重”,在几乎不掉精度的情况下,让模型计算量更少、速度更快。
剪枝包括:
- 非结构化剪枝:移除单个权重(稀疏化矩阵),压缩率高但硬件利用率低。
- 结构化剪枝:移除整个通道/卷积核/注意力头,硬件加速友好。
剪枝能直接减少模型参数量和计算量(FLOPs),并且可以与量化结合,进一步压缩。
二、剪枝的核心逻辑
剪枝通常有两步:
- 评估重要性(哪些参数重要、哪些可删);
- 删除不重要的部分,并微调恢复精度。
而“哪些部分”要删,就决定了我们剪枝的粒度,也就是今天的重点:结构化剪枝(Structured Pruning)和非结构化剪枝(Unstructured Pruning)
三、非结构化剪枝(Unstructured Pruning)
非结构化剪枝,也叫“稀疏剪枝(Sparse Pruning)”。它是最自由、最细粒度的剪枝方式。
思想很简单,就是对每个参数单独评估,觉得小的就砍。
比如:
如果我们设定一个阈值 0.01,小于这个值的参数就直接剪掉变成 0:
非结构化剪枝精度损失小(因为只删不重要的参数),控制精细,理论上稀疏率越高,模型越小。但是由于硬件不擅长处理稀疏矩阵,实际加速效果差,并且存储时需要额外记录索引,带来开销。
所以非结构化剪枝更像是数学上变稀疏,而不是真正变快。适合研究或轻量化探索,不太适合直接部署。
四、结构化剪枝(Structured Pruning)
结构化剪枝更接近“工程落地”的思路,思想是直接剪掉整个卷积核、通道、甚至层结构。
比如我们在 CNN 中:
- 剪通道(Channel Pruning):删掉一整列 feature map;
- 剪卷积核(Filter Pruning):删除整个 filter;
- 剪层(Layer Pruning):直接去掉某些层。
这时候模型的结构会真正变小,计算图也简化了,剪完后模型不仅参数变少,推理速度也会显著提升。
结构化剪枝可以实现真正的加速,并且部署简单(不需要特殊稀疏硬件),和 TensorRT、OpenVINO、ONNX 等框架兼容性好。
缺点就是粒度大,删多了容易掉精度,剪完结构变动大,微调成本高。
结构化剪枝就比较适合部署导向的优化,比如在边缘设备、移动端上跑 CNN。
面试如果还追问你“那你知道有哪些剪枝策略吗”,可以简单提一下:
- 基于权重(Magnitude Pruning):删掉权值小的参数;
- 基于BN通道(BN Scaling Pruning):看 BatchNorm 的缩放系数;
- 基于梯度或敏感度:看参数对损失的贡献;
- 基于L1/L2正则:通过约束让模型自己“变稀疏”;
- 动态剪枝(Dynamic Pruning):在推理时动态决定是否剪掉某些路径。
不需要细讲,只要提到思路 + 分类,面试官就知道你有体系。
五、工程实践
在工程实践中,剪枝的步骤通常为:
- 训练完整模型;
- 根据权重或通道重要性剪枝;
- 微调恢复精度;
- 导出 ONNX → TensorRT 部署。
比如 PyTorch 就内置了 torch.nn.utils.prune 模块:
import torch.nn.utils.prune as prune
# 对卷积层进行非结构化剪枝
prune.l1_unstructured(model.conv1, name='weight', amount=0.3)
# 对卷积层进行结构化剪枝(按通道)
prune.ln_structured(model.conv1, name='weight', amount=0.3, n=2, dim=0)
结构化剪枝出来的模型直接可以导出推理,非结构化的要么稀疏化存储,要么重新稠密化。
关于深度学习和大模型相关的知识和前沿技术更新,请关注公众号 coting!