模型剪枝是深度学习模型优化技术之一,其核心目的是减少模型的参数数量和计算复杂度,以提高模型在资源受限环境中的部署效率和运行速度,剪枝问题可以形式化定义为:
image-20240715215130751
其中L为损失函数,x为输入,W为原始权重,Wp为剪枝后的权重,而Wp的范数为剪枝后的权重中的非零值,N剪枝目标设置的非零值的个数。
剪枝可以在模型生命周期的不同阶段进行,包括训练前、训练中、训练后,以及通过增量或动态的方式进行。训练前剪枝通过减少初始参数数量来降低模型的复杂度,但可能需要特别的初始化和训练策略来维持性能。训练中剪枝允许模型逐步适应剪枝操作,但需要精心设计的剪枝策略以避免性能损失。训练后剪枝则基于训练完成的模型来识别不重要的参数,通常需要重训练或微调来补偿可能的性能下降。增量剪枝结合了训练中和训练后剪枝的优点,通过分阶段剪枝为模型提供了更多适应机会。动态剪枝则在模型推理时根据输入数据动态调整剪枝程度,以提高特定任务的效率,但实现更为复杂。
模型剪枝的策略尽可能保持或恢复模型性能且尽可能减少参数数目。常见的剪枝策略包括基于权重幅度的剪枝,它进行权重排序,移除那些权重较小的连接,通过阈值选择进行剪枝;正则化策略通过鼓励权重稀疏性,使得模型在训练过程中自然形成稀疏权重矩阵,从而为剪枝提供依据。另外,结构化剪枝如过滤器剪枝易于在硬件上实现,但可能不如非结构化剪枝灵活。非结构化剪枝可以提供更大的压缩空间,但可能需要特定硬件或软件支持来实现加速。
结构化剪枝
结构化剪枝通过移除模型中的整个结构单元来实现稀疏化,而不破坏数据的存储结构。结构化剪枝有多种粒度,每种粒度针对模型的不同层次进行操作:
过滤器剪枝(Filter Pruning),这种剪枝方法通过移除整个过滤器(或称为通道)来减少模型的参数数量和计算量。由于每个过滤器与后续层的每个神经元都相连,移除一个过滤器可以显著减少网络的宽度,从而降低计算复杂度。过滤器剪枝是一种粗粒度剪枝,因为它以过滤器为单位进行操作,而过滤器通常包含多个权重。这种方法对于硬件友好,因为它保持了数据的存储结构,不会破坏卷积层的数据对齐性。
核剪枝(Kernel Pruning):核剪枝涉及到移除卷积核中的某些部分,这可能是一行、一列或者是核中的某些区域。与过滤器剪枝相比,核剪枝的粒度更细,因为它不移除整个过滤器,而是过滤器内部的部分权重。核剪枝可能需要特定的硬件支持来实现有效加速,因为它打破了卷积运算的标准流程。
向量级剪枝(Vector-Level Pruning):向量级剪枝是指移除卷积核中的整行或整列权重,这种剪枝方法的粒度介于过滤器剪枝和核剪枝之间。它通常用于减少卷积核的尺寸,但保持了卷积层的大部分结构。
细粒度剪枝(Fine-Grained Pruning):细粒度剪枝,或称为非结构化剪枝,涉及单个权重的移除。这种剪枝方法可以提供最大的压缩比,因为它允许模型在非常细的粒度上减少参数。然而,由于剪枝后权重的分布变得不规则,这可能会导致计算过程中的缓存命中率下降,从而影响运行效率。因此,细粒度剪枝通常需要特定硬件支持,以便有效利用稀疏性。
未命名文件(17).png
Exploring the granularity of sparsity in convolutional neural networks [Mao et al., CVPR-W]
剪枝工具
在深度学习框架如Pytorch和TensorFlow中也提供了剪枝的工具,如Torch-Pruning是开源的PyTorch工具箱,专门用于结构化剪枝,以优化和压缩深度学习模型。Torch-Pruning通过构建依赖图(Dep Graph)自动处理层之间的依赖关系,确保剪枝操作不会破坏模型的结构,示例代码如下:

1
2
3
4
5
6
7
8
import torch_pruning as tp
model= ...
DG = tp.DependencyGraph()
DG.build_dependency(model, example_inputs=torch.randn(1, 3, 224, 224))
strategy = tp.strategy.L1Strategy()
pruning_idxs = strategy(model.conv1.weight, amount=0.4) # 假设剪枝40%
pruning_plan = DG.get_pruning_plan(model.conv1, tp.prune_conv, idxs=pruning_idxs)
pruning_plan.exec()

TensorFlow的剪枝API是TensorFlow Model Optimization Toolkit的一部分,它提供了一种在训练过程中减少模型权重的方法,从而创建一个稀疏的模型。具体如tensorflow_model_optimization.sparsity.keras.prune_low_magnitude是用于实现剪枝操作的函数,它会移除模型中幅度较低的权重。

1
2
3
4
5
6
7
8
9
10
11
12
13
import tensorflow_model_optimization as tfmot
model = ...
# 定义剪枝计划
pruning_schedule = tfmot.sparsity.keras.PolynomialDecay(
initial_sparsity=0.0,
final_sparsity=0.5,
begin_step=2000,
end_step=4000
)
# 应用剪枝
model_for_pruning = tfmot.sparsity.keras.prune_low_magnitude(model, pruning_schedule=pruning_schedule)
# 继续训练剪枝后的模型
model_for_pruning.fit(...)