3、剪枝分类(上):非结构化剪枝 vs 结构化剪枝,细粒度剪枝 vs 粗粒度剪枝

好,咱们进入剪枝的分类环节。说实话,我刚入行那会儿,看到各种剪枝名词——非结构化、结构化、细粒度、粗粒度——头都大了。后来踩过几次坑才明白,这些分类其实是在回答两个核心问题:“剪哪里”“怎么剪”

今天咱们就把这两个问题彻底讲透。

3.1 非结构化剪枝 vs 结构化剪枝

先看第一个维度:剪枝的粒度。说白了,就是一次操作能干掉多大一块参数。

3.1.1 非结构化剪枝:最细的刀法

非结构化剪枝,也叫细粒度剪枝。它直接对单个权重或单个神经元下手。比如一个卷积核里有 9 个权重,我觉得其中 3 个不重要,就把它们置零。

这种剪枝方式,精度损失通常最小。为什么?因为你可以针对每个权重单独评估重要性,只干掉那些真正“没用”的。

我个人的习惯是,在模型探索阶段先用非结构化剪枝试试水。它能快速告诉你:这个模型到底有多少冗余。

核心特点:
  • 剪枝单位:单个权重或神经元
  • 结果:权重矩阵变得稀疏(很多 0)
  • 硬件友好度:低(需要稀疏计算库支持)
  • 精度保留:通常较好

代码实现其实很简单:

import torch
import torch.nn.utils.prune as prune

# 对一个卷积层做非结构化剪枝
model = torch.nn.Conv2d(3, 64, kernel_size=3)
prune.l1_unstructured(model, name='weight', amount=0.5)
# 这会干掉 50% 的权重(绝对值最小的那些)

嗯,这里要注意:非结构化剪枝后的模型,直接跑在普通硬件上,速度基本没提升。因为那些 0 权重还在参与计算,只是值变成了 0。你需要专门的稀疏矩阵乘法库(比如 NVIDIA 的 cuSPARSE)才能吃到加速红利。

我曾经踩过的坑: 在手机上部署一个非结构化剪枝后的模型,结果推理速度反而慢了。因为手机 CPU 没有稀疏计算优化,0 权重照样参与乘法,白忙活一场。

3.1.2 结构化剪枝:大刀阔斧

结构化剪枝就不一样了。它一次干掉一整块结构——比如一个卷积核、一个通道、甚至一整层。

你想想看,如果一个卷积核的所有权重都很小,那留着它干嘛?直接连根拔起。这样剪完之后,模型的结构还是规整的,通道数变少了而已。

我建议在部署阶段优先考虑结构化剪枝。因为它对硬件太友好了——剪完直接跑,不需要任何特殊库支持。

核心特点:
  • 剪枝单位:卷积核、通道、层
  • 结果:模型变“瘦”了(通道数减少)
  • 硬件友好度:高(直接加速)
  • 精度损失:通常比非结构化大一些

代码示例:

# 对卷积层做结构化剪枝(剪掉整个卷积核)
# 这里用 L1 范数评估每个卷积核的重要性
def prune_conv_filter(conv_layer, amount=0.3):
    # 计算每个卷积核的 L1 范数
    l1_norm = torch.sum(torch.abs(conv_layer.weight.data), dim=(1,2,3))
    # 找到要保留的索引
    num_keep = int(conv_layer.out_channels * (1 - amount))
    _, indices = torch.topk(l1_norm, num_keep)
    # 只保留这些卷积核
    conv_layer.weight.data = conv_layer.weight.data[indices]
    conv_layer.bias.data = conv_layer.bias.data[indices]
    conv_layer.out_channels = num_keep
一个小技巧: 结构化剪枝后,记得同步调整下一层的输入通道数。不然维度对不上,模型直接报错。我刚开始做的时候,这个问题卡了我一整天。

3.2 细粒度剪枝 vs 粗粒度剪枝

第二个维度,其实跟上面有重叠,但视角不同。它更关注剪枝的决策粒度

3.2.1 细粒度剪枝:逐个评估

细粒度剪枝,就是每个参数单独决定去留。非结构化剪枝就是典型的细粒度。

它的好处是灵活。你可以保留 99% 的精度,同时干掉 50% 的参数。但代价是——模型结构变得不规则,像一块千疮百孔的奶酪。

我记得有一次,为了在学术论文里刷 SOTA,我用了极细粒度的剪枝策略。精度确实保住了,但 reviewer 问了一句:“你这模型能实际部署吗?” 嗯,问得好。

3.2.2 粗粒度剪枝:批量处理

粗粒度剪枝,就是以组、块、通道为单位做决策。结构化剪枝就是粗粒度的代表。

它的好处是规整。剪完之后模型结构清晰,硬件利用率高。但代价是——精度损失可能更大。因为你要么留一整块,要么全干掉,没有中间状态。

我个人的经验是:粗粒度剪枝更适合做“大比例压缩”。比如目标是从 100M 参数压到 10M,那用粗粒度一刀切下去,效果反而好。因为细粒度剪枝到这种比例,模型结构已经乱得没法看了。

两种粒度的对比:
维度 细粒度 粗粒度
决策单位 单个权重 通道/卷积核/层
精度保留 优秀 良好
硬件加速 需要特殊支持 直接生效
适用场景 学术研究、精度优先 工业部署、速度优先

3.3 怎么选?我的实战建议

说了这么多,到底怎么选?我给大家一个简单的决策流程:

  1. 先问目标:你是要发论文,还是要落地?
  2. 发论文:用非结构化/细粒度剪枝,精度高,指标好看。
  3. 要落地:用结构化/粗粒度剪枝,省心省力,直接加速。
  4. 如果两者都要:先做粗粒度剪掉大部分冗余,再用细粒度微调。
我的一个习惯: 在项目初期,我会先用非结构化剪枝跑一遍,看看模型的理论压缩极限在哪。然后根据这个上限,再设计结构化剪枝的方案。这样心里有底,不会盲目。

说白了,没有绝对的好坏。非结构化剪枝像手术刀,精准但麻烦;结构化剪枝像砍刀,粗暴但高效。关键看你的场景需要什么。

下一节,咱们聊聊剪枝的具体算法——怎么判断一个权重重不重要。那才是真正的技术活。