模型剪枝基础:什么是剪枝?结构化剪枝与非结构化剪枝的区别

各位同学好,今天我们来聊聊模型剪枝。说实话,我第一次接触这个概念时,脑子里想的是「修剪树枝」。后来发现,还真有点像——把模型里那些「不干活」的参数去掉,让模型更轻便。

我在做模型部署时,经常遇到这样的困境:模型精度明明很高,但放到手机上就是跑不动。要么太大,要么太慢。这时候,剪枝就成了我的救命稻草。

什么是模型剪枝?

模型剪枝,说白了就是把神经网络中不重要的连接或神经元去掉。你想想看,一个训练好的模型里,很多参数其实贡献很小。去掉它们,模型照样能工作,甚至还能跑得更快。

我习惯把剪枝比作「瘦身」。一个200斤的壮汉和150斤的运动员,谁更灵活?当然是后者。模型也是这个道理。

核心思想:在保持模型精度的前提下,减少参数量和计算量。

剪枝的依据是什么?通常看权重的大小。权重接近0的,说明这个连接不重要,可以砍掉。当然,也有更复杂的判断标准,比如基于梯度、基于激活值等。但最常用的,还是看权重绝对值。

非结构化剪枝:精细但麻烦

非结构化剪枝,是对单个权重进行操作。它会将小于某个阈值的权重直接置为0。

举个例子,一个4x4的权重矩阵:

原始权重:
[[0.8, 0.1, 0.5, 0.02],
 [0.3, 0.9, 0.01, 0.6],
 [0.05, 0.4, 0.7, 0.03],
 [0.2, 0.08, 0.6, 0.9]]

剪枝后(阈值0.1):
[[0.8, 0.0, 0.5, 0.0],
 [0.3, 0.9, 0.0, 0.6],
 [0.0, 0.4, 0.7, 0.0],
 [0.2, 0.0, 0.6, 0.9]]

看到没?那些小于0.1的权重全变成了0。这样做的好处是精度损失小,因为只砍掉了最不重要的连接。

但问题来了——这种稀疏矩阵,在普通硬件上很难加速。我曾经在一个项目里试过,剪枝后模型大小确实小了,但推理速度几乎没变。为什么?因为CPU和GPU对稀疏矩阵的支持很差,你想想看,一堆0夹杂着非0值,计算起来反而更慢。

我的经验:非结构化剪枝适合在支持稀疏计算的专用硬件上使用,比如某些NPU。在普通GPU上,效果往往不如预期。

结构化剪枝:粗暴但有效

结构化剪枝就不一样了。它不针对单个权重,而是直接砍掉整个通道、整个滤波器或整个层

比如在卷积层中,一个滤波器对应一个输出通道。结构化剪枝会判断哪些滤波器不重要,然后整个移除。

# 伪代码示例
# 假设有64个滤波器,我们想剪掉16个
# 计算每个滤波器的重要性(比如L1范数)
importance = [sum(abs(w)) for w in filters]
# 保留最重要的48个
keep_indices = argsort(importance)[-48:]
pruned_filters = filters[keep_indices]

这样做的好处很明显:剪枝后的模型结构是规整的,可以直接在现有硬件上加速。我做过测试,结构化剪枝后,推理速度能提升30%-50%,而且不需要特殊硬件支持。

但代价是什么?精度损失更大。你想想看,一刀切掉整个通道,肯定比只切几个权重来得猛。所以结构化剪枝通常需要配合微调(fine-tuning)来恢复精度。

两者的核心区别

对比维度 非结构化剪枝 结构化剪枝
剪枝粒度 单个权重 通道/滤波器/层
精度损失 较小 较大
加速效果 依赖硬件支持 直接有效
模型规整性 稀疏、不规则 规整、密集
部署难度 高(需特殊库支持) 低(通用框架即可)

实际项目中怎么选?

我个人习惯这样判断:

  • 目标设备是手机或嵌入式设备:选结构化剪枝。因为这些设备上的推理框架(如TFLite、CoreML)对稀疏矩阵支持很差。
  • 目标设备是专用AI芯片:可以试试非结构化剪枝。有些芯片专门优化了稀疏计算,能发挥出优势。
  • 精度要求极高:先用非结构化剪枝试试,如果精度损失在可接受范围内,再考虑是否转结构化。

避坑指南:我曾经在一个项目中,花了大量时间做非结构化剪枝,剪到了90%的稀疏度。结果部署时发现,推理速度反而慢了20%。原因就是硬件不支持稀疏加速。从那以后,我每次做剪枝前,都会先确认目标平台的硬件特性。

TensorFlow中的剪枝工具

TensorFlow提供了tensorflow_model_optimization库,里面集成了剪枝API。使用起来很简单:

import tensorflow_model_optimization as tfmot

# 定义剪枝参数
pruning_params = {
    'pruning_schedule': tfmot.sparsity.keras.ConstantSparsity(
        target_sparsity=0.5,  # 目标稀疏度50%
        begin_step=0,
        frequency=100
    )
}

# 对模型应用剪枝
pruned_model = tfmot.sparsity.keras.prune_low_magnitude(
    model, **pruning_params
)

# 训练时注意要使用回调
callbacks = [
    tfmot.sparsity.keras.UpdatePruningStep(),
    tfmot.sparsity.keras.PruningSummaries(log_dir='./logs')
]

pruned_model.fit(x_train, y_train, callbacks=callbacks)

这里要注意,prune_low_magnitude默认做的是非结构化剪枝。如果你想做结构化剪枝,需要自己实现或使用第三方库。

小结

嗯,今天的内容就到这里。总结一下:

  • 剪枝就是去掉不重要的参数,让模型更轻便
  • 非结构化剪枝精细但加速受限,适合专用硬件
  • 结构化剪枝粗暴但加速明显,适合通用平台
  • 选择哪种方式,取决于你的目标设备和精度要求

下一章,我会带大家动手实操,在TensorFlow中实现一个完整的剪枝流程。到时候你们会发现,其实剪枝没那么神秘,关键是要选对方法。

公众号:蓝海资料掘金营,微信deep3321