4、剪枝概念入门:定义、类型、粒度与动机
在TinyML的工程实践中,模型压缩是让神经网络在微控制器(MCU)上“跑得动、跑得快、跑得省”的关键前置步骤。剪枝(Pruning)作为最经典的模型压缩技术之一,其核心思想非常直观:删除神经网络中不重要的连接、通道甚至整个层,从而减少模型参数量、计算量和存储体积。本章将系统梳理剪枝的定义、两大类型(结构化与非结构化)、三种粒度(权重级、通道级、层级),并深入剖析剪枝的工程动机,为后续章节中“剪枝前后模型文件大小对比”的实操奠定理论基础。
剪枝的本质是在“模型容量”与“模型精度”之间寻找最优平衡点。对于TinyML而言,剪枝不是可选项,而是部署前的必要工序——因为典型的MCU仅有几十KB到几百KB的Flash存储,而一个未经压缩的MobileNetV2模型大小就超过13MB。
4.1 剪枝的定义与核心原理
剪枝(Pruning)源于神经科学的“突触修剪”概念——人脑在发育过程中会清除不常用的神经连接,以提高信息传递效率。在深度学习中,剪枝指通过某种重要性评估准则,识别并移除神经网络中对最终输出贡献较小的参数(权重、通道或层),从而获得一个更紧凑、更高效的子网络。
其核心原理基于一个经验观察:训练好的神经网络中存在大量冗余参数。这些参数的值通常接近零,或者与其他参数高度相关,对模型输出的影响微乎其微。移除它们不会显著改变模型的预测结果,但能直接减少模型的存储空间和推理计算量。
剪枝的一般流程包含四个步骤:
- 训练一个基线模型:通常是一个过参数化的模型,精度略高于目标要求。
- 评估参数重要性:常用方法包括权重绝对值、梯度幅值、激活值统计、Hessian矩阵等。
- 移除不重要的参数:根据预设的剪枝率(如30%、50%)删除低重要性参数。
- 微调(Fine-tune)恢复精度:对剪枝后的稀疏模型进行少量epoch的训练,让剩余参数适应新的网络结构。
4.2 剪枝的两大类型:结构化与非结构化
根据剪枝后参数在内存中的排列方式,剪枝可分为非结构化剪枝(Unstructured Pruning)和结构化剪枝(Structured Pruning)。两者的核心区别在于:剪枝后是否保持了原始张量的规则形状。
| 对比维度 | 非结构化剪枝 | 结构化剪枝 |
|---|---|---|
| 剪枝单位 | 单个权重(Weight) | 通道(Channel)、滤波器(Filter)、层(Layer) |
| 稀疏模式 | 不规则稀疏,零值随机分布 | 规则稀疏,整块删除 |
| 存储压缩 | 需稀疏索引,实际节省有限 | 直接减少张量维度,节省明显 |
| 硬件加速 | 需专用稀疏计算库(如DeepSparse) | 通用硬件可直接受益 |
| 精度恢复 | 较容易,微调效果好 | 较困难,需谨慎选择剪枝通道 |
| TinyML适用性 | 低(MCU无法高效处理不规则稀疏) | 高(CMSIS-NN等库直接支持) |
非结构化剪枝的典型做法是:设定一个阈值(如0.01),将绝对值低于该阈值的权重置为零。这种方法能达到极高的稀疏度(如90%以上),但产生的稀疏矩阵需要额外的索引信息来记录非零值的位置,在MCU上反而可能增加存储开销和计算复杂度。
结构化剪枝则直接删除整个通道或滤波器。例如,在卷积层中,如果某个滤波器的重要性得分较低,就将其整个移除,同时删除下一层对应的输入通道。这样张量的形状保持规则,不需要特殊的数据结构,可以直接利用CMSIS-NN、TensorFlow Lite Micro等标准推理库加速。
4.3 剪枝粒度:权重级、通道级与层级
剪枝粒度决定了“一次删除多大块”的参数。粒度越细,剪枝的灵活性越高,但管理成本也越高;粒度越粗,压缩率越低,但部署越简单。以下是三种主要粒度的详细分析:
4.3.1 权重级剪枝(Weight-level Pruning)
这是最细粒度的剪枝,直接对单个权重进行操作。每个权重独立评估,不相关的权重被置零。优点在于精度损失最小,因为可以精确地只删除那些对输出影响最小的参数。缺点是需要存储稀疏矩阵的索引,且无法直接减少计算量(除非硬件支持稀疏计算)。在TinyML中,权重级剪枝通常作为“预剪枝”步骤,后续再配合量化使用。
4.3.2 通道级剪枝(Channel-level Pruning)
通道级剪枝以卷积层的输出通道(或滤波器的数量)为基本单位。评估每个通道的重要性(如通过BN层的缩放因子γ、通道激活值的L1范数等),删除不重要的通道及其对应的输入特征图。这种粒度的剪枝直接减少了卷积层的计算量和输出特征图的尺寸,是TinyML中最常用的剪枝方式。
# 伪代码示例:基于BN层γ值的通道剪枝
for layer in model.conv_layers:
gamma = layer.bn.weight.data # BN层缩放因子
threshold = percentile(gamma, 30) # 剪枝30%的通道
mask = gamma > threshold
# 保留mask为True的通道,重建卷积层
new_conv = Conv2d(in_channels, sum(mask), kernel_size)
new_conv.weight.data = layer.conv.weight.data[mask]
4.3.3 层级剪枝(Layer-level Pruning)
层级剪枝直接删除整个层或层组。这种粒度最粗,通常用于删除冗余的残差块、过渡层或重复的卷积块。例如,在ResNet中,如果某个残差块的输出与输入非常接近(即恒等映射),可以将其整体删除。层级剪枝的压缩率最高,但风险也最大,一旦删错层,精度会急剧下降。
| 粒度 | 删除单位 | 压缩率 | 精度影响 | 部署难度 | 典型应用 |
|---|---|---|---|---|---|
| 权重级 | 单个权重 | 高(可达90%+) | 低 | 高(需稀疏库) | 学术研究、GPU部署 |
| 通道级 | 滤波器/通道 | 中(30%~70%) | 中 | 低(标准推理库) | TinyML、移动端部署 |
| 层级 | 整个层/块 | 极高(可超80%) | 高 | 低 | 极简模型、特定任务 |
4.4 剪枝的动机:为什么TinyML必须剪枝?
在TinyML场景下,剪枝的动机远不止“减小模型体积”这么简单。以下是五个核心驱动力:
- Flash存储限制:主流MCU(如STM32F4系列)的Flash容量为512KB~2MB,而一个MobileNetV2模型(FP32)需要13.4MB。即使转换为INT8也需要3.4MB,剪枝是唯一能将模型塞进MCU的手段。
- RAM占用约束:推理时的中间特征图(Activations)会占用大量RAM。通道级剪枝直接减少特征图的通道数,从而降低RAM峰值。例如,一个224×224×64的特征图在FP32下占用1.2MB,剪枝50%通道后仅需0.6MB。
- 功耗与电池寿命:剪枝减少计算量(MACs),直接降低MCU的动态功耗。对于电池供电的传感器节点,每减少1M次乘加运算,可延长数小时的待机时间。
- 推理延迟:在实时控制或语音唤醒场景中,推理延迟必须控制在10ms以内。剪枝后的模型计算量减少,推理速度可提升2~5倍。
- 缓解过拟合:剪枝本身是一种正则化方法。删除冗余参数后,模型的有效容量降低,泛化能力反而可能提升,尤其在数据量有限的TinyML应用中。
4.5 常见误区与总结复盘
在实际项目中,工程师常对剪枝存在以下误解,需要特别警惕:
误区二:剪枝后不需要微调。剪枝会破坏原有的权重分布,必须进行微调(通常需要原训练数据量的10%~20%)来恢复精度。跳过微调会导致精度大幅下降。
误区三:所有层都使用相同的剪枝率。不同层对剪枝的敏感度不同。浅层(靠近输入的层)通常更敏感,应使用较低的剪枝率;深层可以剪更多。建议使用逐层敏感度分析来确定每层的剪枝率。
误区四:剪枝和量化是互斥的。实际上两者是互补的。剪枝减少参数数量,量化减少每个参数的位宽。先剪枝后量化是TinyML的标准流程。
总结复盘:剪枝是TinyML模型压缩的基石技术。理解其定义、类型和粒度,是后续进行“剪枝前后模型文件大小对比”实验的前提。在实际操作中,应遵循“训练→评估重要性→剪枝→微调→验证”的闭环流程,并根据目标硬件(MCU型号、Flash/RAM容量)选择合适的剪枝类型和粒度。对于绝大多数TinyML项目,通道级结构化剪枝是最佳起点——它在压缩率、精度保持和硬件兼容性之间取得了最佳平衡。