4、模型轻量化技术:模型剪枝原理与实践、权重量化(INT8/FP16)、知识蒸馏入门、轻量化网络结构(MobileNet、ShuffleNet)
好,咱们进入第四章。这一章,我打算聊聊模型轻量化。
你想想看,一个ResNet-152跑在服务器上,显存占几个G,推理一次几十毫秒,这都没问题。但要是把它塞进一个只有2MB内存、主频1GHz的摄像头模组里呢?直接卡死。所以,轻量化不是可选项,是必选项。
我个人习惯把轻量化技术分成四类:剪枝、量化、蒸馏、以及直接设计轻量网络。咱们一个一个来。
4.1 模型剪枝:给网络“瘦身”
剪枝,说白了就是去掉那些不重要的连接或通道。神经网络里有很多参数,其实贡献很小。去掉它们,模型变小,速度变快,精度损失却不大。
剪枝分两种:非结构化剪枝和结构化剪枝。
- 非结构化剪枝:把单个权重置为0。优点是压缩率高,但依赖稀疏计算库,硬件加速效果一般。我在项目中试过,实际推理速度提升有限,因为CPU和GPU对稀疏矩阵的支持并不完美。
- 结构化剪枝:直接剪掉整个通道或卷积核。这是工业界的主流做法。剪完后模型结构变“瘦”,不需要特殊硬件支持,直接跑就快。
核心原则:剪枝不是乱剪。通常按“L1范数”或“L2范数”对卷积核排序,值小的优先剪掉。每次剪一点,然后微调恢复精度,再剪一点。这叫“迭代式剪枝”。
我曾经在一个行人检测项目里,把MobileNetV2的通道数剪掉30%,精度只掉了0.5%,但推理速度提升了近40%。嗯,这里要注意:剪枝比例超过50%后,精度会断崖式下跌。所以别贪心。
4.2 权重量化:用更少的比特表示参数
量化,就是把模型参数从32位浮点数(FP32)变成更低位宽的表示。最常见的是INT8和FP16。
为什么能这么做?因为神经网络对噪声有一定容忍度。你想想看,人眼看一张图,差几个像素值根本看不出来。网络也一样。
| 量化类型 | 位宽 | 模型体积缩小 | 推理加速 | 精度损失 |
|---|---|---|---|---|
| FP32(基准) | 32 bit | 1x | 1x | 无 |
| FP16 | 16 bit | 约2x | 1.5-2x | 几乎无 |
| INT8 | 8 bit | 约4x | 2-4x | 0.5-2% |
我个人建议:如果边缘设备支持FP16(比如NVIDIA Jetson系列),优先用FP16。精度几乎无损,部署最简单。如果设备只支持INT8(比如大部分手机NPU、树莓派的某些加速棒),那就得做INT8量化。
避坑指南:我曾经在量化一个语义分割模型时,发现输出结果全是黑的。排查了半天,原来是激活值的分布太集中,量化时信息全丢了。解决办法是:先做“校准”,用一小批真实数据跑一遍,统计出激活值的min/max,再去做量化。很多框架(如TensorRT、ONNX Runtime)都支持这个功能。
4.3 知识蒸馏:大模型教小模型
知识蒸馏,说白了就是“师生学习”。用一个大的、精度高的教师模型,去指导一个小的、轻量的学生模型学习。
怎么教?不是直接让学生模仿教师的最终输出,而是模仿“软标签”。
- 硬标签:类别概率,比如[0, 1, 0]。
- 软标签:教师模型输出的概率分布,比如[0.1, 0.8, 0.1]。这里面包含了教师对“相似类别”的理解。
蒸馏的损失函数通常是:L = α * 硬标签损失 + (1-α) * 软标签损失。其中软标签损失用的是KL散度。
我记得有一次,我需要把一个YOLOv5s(小模型)部署到一款低端芯片上。直接训练,mAP只有68%。后来我用YOLOv5m(中等模型)做教师,蒸馏训练学生,mAP直接提到了72%。效果很明显。
注意:蒸馏不是万能的。如果教师模型本身就不准,那教出来的学生也好不到哪去。另外,学生模型的结构不能太“弱”,否则学不动。我一般建议学生模型的参数量至少是教师的1/10以上。
4.4 轻量化网络结构:MobileNet 与 ShuffleNet
前面说的剪枝、量化、蒸馏,都是对现有模型做“改造”。但如果你从零开始设计,为什么不直接用轻量化的网络结构呢?
这里我重点讲两个:MobileNet 和 ShuffleNet。
4.4.1 MobileNet:深度可分离卷积
MobileNet的核心思想,是把标准卷积拆成两步:
- 深度卷积:每个通道单独做卷积,不跨通道。
- 逐点卷积:用1x1卷积把通道信息融合起来。
这样做的好处是:计算量大幅下降。标准卷积的计算量是 K*K*C_in*C_out*H*W,而深度可分离卷积是 K*K*C_in*H*W + C_in*C_out*H*W。当K=3时,计算量大约只有原来的1/8到1/9。
MobileNetV2还引入了“倒残差结构”和“线性瓶颈”,进一步提升了效率。V3则加入了NAS(神经架构搜索)来寻找最优结构。
4.4.2 ShuffleNet:通道混洗
ShuffleNet的思路更巧妙。它用分组卷积来减少计算量,但分组卷积有个问题:不同组之间信息不流通。怎么办?
ShuffleNet提出“通道混洗”操作。就是把不同组的通道打乱重排,让信息在组间流动。你想想看,这就像洗牌一样,把不同花色的牌混在一起。
ShuffleNetV2更进一步,提出了四条实用准则:
- 输入输出通道数相等时,内存访问成本最低。
- 过多的分组卷积会增加内存访问。
- 网络碎片化(多分支)会降低并行度。
- 逐元素操作(如ReLU、Add)虽然计算量小,但内存访问成本不可忽视。
我的选择建议:如果设备对内存带宽敏感(比如很多嵌入式芯片),优先考虑ShuffleNetV2。如果设备有较好的GPU或NPU加速,MobileNetV3通常表现更好。我在一个智能门锁项目里,用ShuffleNetV2做人脸检测,在1.2GHz的Cortex-A7上跑到了30fps,效果很满意。
好了,这一章的内容就这些。总结一下:剪枝去掉冗余,量化压缩位宽,蒸馏传承知识,轻量网络从源头优化。四者可以组合使用,比如先剪枝再量化,或者用蒸馏训练一个轻量网络。具体怎么选,取决于你的硬件和精度要求。