3、结构化裁剪:权重、神经元与通道的取舍之道
模型裁剪做到一定程度,你会发现一个有意思的现象:不是所有参数都值得保留。有些权重几乎不起作用,有些神经元一直在“摸鱼”,有些通道对整个任务贡献微乎其微。结构化裁剪,说白了就是把这些冗余的部分系统地找出来、干掉。
我个人习惯把结构化裁剪分成三个层次:权重级、神经元级、通道级。每个层次解决的问题不同,适用的场景也不一样。咱们一个一个说。
3.1 权重级裁剪:最细粒度的“瘦身”
权重级裁剪是最直观的。训练完成后,模型里会有大量接近零的权重。这些权重对最终输出的影响极小,直接置零也不会明显掉点。
具体做法其实很简单:设定一个阈值,低于阈值的权重全部干掉。但这里有个坑——阈值怎么定?
我早期做实验时,直接设了个全局阈值 0.01,结果模型精度直接崩了。后来才发现,不同层的权重分布差异很大。全连接层的权重通常比较集中,而卷积层的权重分布更分散。统一阈值对某些层太严,对另一些层又太松。
权重级裁剪的优点是实现简单,缺点是稀疏不规则。裁剪后的权重矩阵变成稀疏矩阵,需要专门的硬件或库才能加速。如果你用的是普通 CPU 或 GPU,稀疏矩阵的运算效率反而可能下降。
3.2 神经元级裁剪:更彻底的“砍人”
权重级裁剪只是把单个连接砍掉,但神经元还在。神经元级裁剪更狠——直接删除整个神经元。这样一来,网络结构就变窄了。
判断一个神经元是否重要,常用的指标是 激活值统计。如果一个神经元在大部分输入下输出都接近零,那它基本就是个“摆设”。
我记得有个项目,模型里有个全连接层有 1024 个神经元。我统计了验证集上的激活值,发现超过 300 个神经元的平均激活值不到 0.01。砍掉这些神经元后,模型大小直接减了 30%,精度只掉了 0.2%。
怎么判断神经元的重要性?我常用两种方法:
- 平均激活值法:统计每个神经元在验证集上的平均激活值,低于阈值的删除。
- APoZ 指标:统计激活值为零的比例,比例越高说明神经元越“懒”。
神经元级裁剪的好处是结构规整,裁剪后的模型可以直接用现有框架加速。但要注意,删除神经元会改变下一层的输入维度,需要同步调整权重矩阵的尺寸。
3.3 通道级裁剪:卷积层的“主战场”
通道级裁剪主要针对卷积层。每个卷积核对应一个输出通道,如果某个通道不重要,直接删掉整个卷积核和对应的特征图。
通道的重要性评估,我一般用 BN 层的缩放因子。BN 层有个参数 γ(gamma),它控制着每个通道的缩放比例。训练完成后,γ 值接近零的通道,说明它对后续层的影响很小。
具体做法是:
- 在训练时给 BN 层的 γ 加上 L1 正则化,让不重要的通道 γ 趋近于零。
- 训练完成后,统计所有通道的 γ 值。
- 设定一个阈值,γ 低于阈值的通道直接删除。
我曾经在一个图像分类项目里试过通道裁剪。原始模型有 64 个通道,裁剪后剩 40 个,推理速度提升了 1.8 倍,精度只掉了 0.5%。微调 10 个 epoch 后,精度甚至回到了原始水平。
3.4 三种裁剪方式的对比
这三种方式各有优劣,我整理了一张对比表:
| 裁剪方式 | 粒度 | 加速效果 | 精度影响 | 硬件友好度 | 适用场景 |
|---|---|---|---|---|---|
| 权重级 | 单个连接 | 低(需稀疏计算) | 小 | 低 | 全连接层、小模型 |
| 神经元级 | 单个神经元 | 中 | 中 | 中 | 全连接层、RNN |
| 通道级 | 整个通道 | 高 | 较大 | 高 | 卷积层、大模型 |
从表中能看出来,粒度越细,精度损失越小,但硬件加速越难。反过来,粒度越粗,加速效果越明显,但精度风险也更高。
3.5 结构化裁剪的整体流程
说了这么多,咱们把整个流程串起来。下面这张图展示了结构化裁剪的完整链路:
整个流程其实就三步:评估 → 决策 → 执行。评估阶段确定哪些部分不重要,决策阶段定阈值或比例,执行阶段做删除或置零。最后别忘了微调,这一步能挽回不少精度损失。
3.6 避坑指南
做结构化裁剪这些年,我踩过不少坑。挑几个典型的说说:
- 一次性裁剪太多:我曾经一次裁掉 50% 的通道,结果模型直接废了。后来改成渐进式裁剪,每次裁 10%,边裁边微调,效果好了很多。
- 忽略层间差异:浅层卷积核通常编码低级特征(边缘、纹理),裁多了影响大。深层编码高级语义,可以多裁一些。我一般浅层裁 10-20%,深层裁 30-50%。
- 不微调直接部署:裁剪后的模型参数分布变了,不微调精度大概率会掉。我建议至少微调 5 个 epoch,学习率设为原始训练时的 1/10。
结构化裁剪的核心,说白了就是在精度和效率之间找平衡。没有银弹,每种方法都有适用场景。我的建议是:先从通道级裁剪入手,效果不够再考虑神经元级,最后才碰权重级。这样能最快看到收益,也最容易落地。