3. 结构剪枝基础
结构剪枝的目标不是简单“删掉一些权重”,而是从网络结构中移除整组参数,使模型在通用硬件上真正获得推理加速。与稀疏剪枝产生大量零值不同,结构剪枝直接改变张量形状,因此其粒度选择决定了压缩率、精度损失和部署收益之间的平衡。本节围绕通道、滤波器、层、块四种基本粒度展开,并给出硬件友好性的判断依据。
一、四种基本剪枝粒度的概念
在卷积神经网络中,一个卷积层的权重张量通常为 [C_out, C_in, K_h, K_w]。不同剪枝粒度对应删除该张量中不同维度的整组元素:
- 通道剪枝(Channel Pruning):删除输入通道或输出通道。若删除输出通道,则同时移除对应滤波器及其产生的特征图;若删除输入通道,则需同步裁剪上一层输出通道,保持连接一致。
- 滤波器剪枝(Filter Pruning):以整个卷积核为单位删除,即删除某个
C_out索引对应的全部[C_in, K_h, K_w]权重。它本质上是输出通道剪枝的一种实现视角。 - 层剪枝(Layer Pruning):删除整个网络层或残差阶段中的若干层,粒度最粗,压缩率最高,但对精度和特征表达能力影响也最大。
- 块剪枝(Block Pruning):以结构块为单位删除,例如删除一个残差块、一个 Bottleneck 模块或 Transformer 中的多头注意力块。它兼顾了较大压缩率和结构完整性。
二、粒度选择与硬件友好性
粒度越细,可选剪枝组合越多,精度通常越容易保持,但硬件加速收益越依赖稀疏计算库;粒度越粗,张量形状变化越明显,越容易被通用推理引擎直接加速。下图展示了四种粒度从细到粗的层级关系及其对硬件的影响:
从图中可以看出,粒度越粗,硬件友好性通常越高,但精度风险也越大。实际工程中常采用“先粗后细”或“混合粒度”策略:先删除冗余层或块,再对关键层做通道级微调。
三、方法流程与参数示例
结构化剪枝的典型流程包括:重要性评估、剪枝掩码生成、结构重排、微调恢复。以通道剪枝为例,常用 L1 范数衡量滤波器重要性:
# 计算每个输出通道的 L1 范数
importance = torch.sum(torch.abs(conv.weight), dim=(1, 2, 3))
# 按比例保留重要性最高的通道
keep_ratio = 0.7
k = int(conv.out_channels * keep_ratio)
keep_idx = torch.topk(importance, k).indices.sort().values
# 裁剪权重与 BN 参数
new_weight = conv.weight[keep_idx]
new_bn_weight = bn.weight[keep_idx]
new_bn_bias = bn.bias[keep_idx]
剪枝后必须同步调整下一层输入通道,否则张量维度不匹配。对于残差连接,还需保证主分支与 shortcut 分支输出通道一致,必要时对 shortcut 也做对应裁剪。
四、案例与场景:不同粒度的选择
| 粒度 | 典型对象 | 压缩率 | 精度风险 | 适用场景 |
|---|---|---|---|---|
| 通道剪枝 | 卷积层输入/输出通道 | 中 | 低 | 需要精细压缩、有微调预算 |
| 滤波器剪枝 | 整个卷积核 | 中高 | 中 | 通用推理引擎部署 |
| 块剪枝 | 残差块、Bottleneck | 高 | 中高 | 深层网络、Transformer |
| 层剪枝 | 整层或整阶段 | 很高 | 高 | 冗余明显的浅层或重复阶段 |
例如在 ResNet-50 上,若目标是部署到通用 GPU,优先删除部分残差块中的 Bottleneck 通道,再对剩余层做滤波器剪枝,通常比直接层剪枝更稳。若目标是极致压缩且允许重新训练,可先删除冗余阶段,再逐层恢复。
五、误区与复盘
常见误区一:只裁剪权重而不调整下一层输入通道,导致推理时维度报错。
常见误区二:忽略 BN 层参数同步裁剪,造成统计量错位,精度骤降。
常见误区三:认为通道剪枝一定比层剪枝快。若推理引擎不支持稀疏张量,通道剪枝可能只减少参数量而不减少实际延迟。
复盘建议:每次剪枝后记录三组指标——参数量、FLOPs、实际推理延迟。只有三者同时下降,才说明剪枝真正落地。对于硬件友好性,优先选择能改变张量形状的粗粒度剪枝,再辅以通道级微调。
结构剪枝的核心不是追求最大压缩率,而是在精度、速度和部署成本之间找到可复现的平衡点。理解通道、滤波器、层、块四种粒度的差异,是后续实现剪枝算法和硬件适配的基础。