我要开一个超市,超市里面卖的是“图像分类、语义分割、模型量化剪枝与压缩
@TOC
1. 需求与问题:我要开一家智能超市
假设我想开一家24小时无人智能超市。核心需求是:
- 商品识别:顾客拿商品时,摄像头能自动识别拿了什么,并计入账单。
- 货架监控:检测商品是否缺货、摆放是否整齐,甚至分析顾客的购物行为。
- 边缘部署:所有计算必须在本地边缘设备(比如Jetson Nano或者树莓派)上实时完成,不能依赖云端(延迟和隐私问题)。
- 模型优化:设备资源有限(算力、内存),需要把模型压缩到足够小,同时保持精度。
2. 目标检测:用YOLO识别商品
首先,商品识别是个典型的目标检测问题。我们需要在图像中框出每个商品,并给出类别(比如可口可乐、薯片)。
为什么选YOLO?
因为需要实时性。YOLO(You Only Look Once)是单阶段检测器,一次前向就能同时预测边界框和类别,速度极快。像Faster R-CNN这种两阶段检测器虽然精度可能更高,但速度慢,不适合实时场景。
YOLO的细节理解
YOLO的核心思想是把图像划分成网格,每个网格负责预测中心点落在该网格内的物体。每个网格预测多个边界框(anchor-based),以及每个框的置信度和类别概率。
以YOLOv5为例,它的网络结构包括:
- Backbone:CSPDarknet,利用Cross Stage Partial结构减少计算量,增强梯度流动。
- Neck:PANet(路径聚合网络),通过自顶向下和自底向上的特征金字塔,融合不同尺度的特征,提升小物体检测能力。
- Head:三个不同尺度的输出头,分别对应大、中、小物体,每个输出头预测边界框偏移量、置信度和类别。
源码层面的体会
读过YOLOv5源码的话,会注意到几个关键点:
- 损失函数:用了CIoU Loss作为边界框回归损失,综合考虑重叠面积、中心点距离和长宽比;分类损失用BCEWithLogitsLoss;置信度损失也是BCE,但正负样本平衡通过focal loss或样本权重处理。
- 数据增强:Mosaic、MixUp等,极大丰富了训练样本,尤其是小物体。
- 自适应anchor:训练前会基于数据集自动计算最佳anchor尺寸,而不是硬编码。
回到超市例子:我们需要训练YOLO模型来识别数百种商品。由于商品外观差异可能很小(比如不同口味的薯片包装),我们可能还要引入细粒度分类,但YOLO的类别特征提取足够,关键在于数据采集和标注。
3. 语义分割:用mmsegmentation分析货架
除了检测商品,我们还想知道货架的哪些区域被商品覆盖,哪些是空的。这需要像素级的理解——语义分割。
比如,我们想区分“货架层板”、“商品区域”、“空隙区域”,以便自动补货。或者,分析顾客在货架前的停留区域,这需要分割出顾客与货架的交互区域。
为什么用FCN、UNet?
- FCN(Fully Convolutional Network)是语义分割的奠基之作,它将全连接层替换为卷积层,输出与输入相同尺寸的密集预测。但FCN的上采样是简单的反卷积,容易丢失细节。
- UNet引入跳跃连接,将编码器的高分辨率特征与解码器的上采样特征融合,特别适合医学图像分割,但对于自然图像也有很好的细节保留能力。在超市场景中,我们需要精确分割商品边缘,UNet的跳跃连接很有优势。
mmsegmentation框架
mmsegmentation是一个模块化的语义分割工具箱,它把数据集、模型、训练策略都解耦成配置文件。比如,我们想用UNet,只需要在配置文件中指定model=dict(type='UNet'),并配置backbone(如ResNet50)、解码器、损失函数等。源码层面,mmsegmentation的模型都继承自BaseSegmentor,实现了encode_decode、loss等接口,方便扩展。
在超市例子中,我们可以用mmsegmentation训练一个UNet,输入货架图像,输出每个像素的类别(货架、商品、空隙)。但直接训练可能数据量不够,可以用mmsegmentation提供的预训练模型进行微调。
4. 模型部署:从训练到边缘设备
模型训练好了,需要部署到超市的Jetson Nano上。这里涉及两个关键问题:
- 模型格式转换(PyTorch -> ONNX -> ncnn)
- 硬件适配(ARM CPU + GPU)
mmdeploy的作用
mmdeploy是OpenMMLab的部署工具,它支持将mmsegmentation、mmdetection等训练的模型一键导出为ONNX、TensorRT、ncnn等格式。它通过定义一系列转换器(比如ONNXRewriter)来重写PyTorch算子,确保导出后的模型能被目标后端正确执行。
例如,我们用mmsegmentation训练了UNet,只需运行:
1 | |
mmdeploy会自动处理动态轴、算子兼容等问题,生成ONNX模型。
ONNX作为中间表示
ONNX(Open Neural Network Exchange)是一种开放的模型格式,它把网络表示为一个计算图。ONNX本身不执行推理,但可以被各种推理引擎(如ONNX Runtime、TensorRT、ncnn)加载。为什么要转ONNX?因为不同框架(PyTorch、TensorFlow)训练出的模型,可以通过ONNX统一,然后分发到不同硬件平台。
ncnn部署到手机/边缘设备
ncnn是腾讯开源的轻量级推理框架,专门优化了ARM架构(手机、嵌入式设备)。它支持FP32、FP16、INT8等多种精度,并利用Neon指令集加速。ncnn的模型格式是.param和.bin,我们可以通过ONNX2ncnn工具将ONNX模型转换为ncnn格式。
在超市设备上,我们需要:
- 将ONNX模型转换为ncnn格式:
onnx2ncnn model.onnx model.param model.bin - 在C++代码中加载模型,进行推理。
但ncnn部署时,可能会遇到算子不支持的问题。比如UNet中的一些上采样操作(双线性插值)在ncnn中可能没有直接对应,需要手动实现或替换。mmdeploy在转换时会自动处理这些,比如把双线性上采样替换为ncnn支持的Interp层。
5. 模型压缩:用mmrazor做量化、剪枝
部署到Jetson Nano后,发现模型推理速度不够(比如需要30fps,但实际只有15fps),内存占用也大。这时就需要模型压缩。
量化
量化是把模型权重和激活从FP32降低到INT8,从而减少内存占用和加速计算(利用INT8指令)。但直接量化可能导致精度下降,需要量化感知训练(QAT)或后训练量化(PTQ)。
mmrazor是OpenMMLab的模型压缩工具,它支持剪枝、量化、知识蒸馏等。比如,我们可以用mmrazor对训练好的UNet进行量化感知训练:
- 在模型中插入伪量化节点(fake quant),模拟量化误差,让模型在训练中适应低精度。
- mmrazor提供了
Quantizer类,可以自动替换Conv、Linear等层为量化版本,并管理量化参数(scale、zero point)。
在超市场景中,我们可能把YOLO和UNet都量化到INT8,速度能提升2-3倍,内存减少4倍,基本满足实时要求。
剪枝
剪枝是去除不重要的神经元或通道,减少模型参数量。比如,我们可以对YOLO的卷积层进行通道剪枝,根据BN层的gamma值(缩放因子)判断通道重要性,把gamma小的通道剪掉。
mmrazor支持多种剪枝算法,比如基于L1范数的通道剪枝。它会自动分析模型结构,生成一个更窄的模型,然后进行微调恢复精度。
在超市例子中,我们可以先剪枝再量化,得到更小的模型,甚至可以部署到更低端的设备(如树莓派Zero)。
6. 串联整个流程:从需求到落地的完整体系
现在,我们把所有技术点串起来,用超市的例子展示一个完整的工作流:
- 需求分析:智能超市需要实时商品检测和货架分析 → 目标检测+语义分割。
- 数据准备:采集超市货架图像,标注商品边界框和像素级类别。
- 模型选型:
- 检测:YOLOv5(速度快,适合实时)。
- 分割:UNet(细节好,适合边缘分割)。
- 训练与调优:
- 用YOLOv5源码训练检测模型,理解损失函数、anchor机制、数据增强。
- 用mmsegmentation训练UNet,理解其配置文件结构、模块化设计,并能修改源码添加自定义损失(比如边界感知损失)。
- 部署准备:
- 用mmdeploy将两个模型导出为ONNX,并解决算子兼容问题(比如YOLO的Focus层、UNet的上采样层)。
- 将ONNX转为ncnn格式,在Jetson Nano上测试性能。
- 性能瓶颈:发现速度不达标 → 引入模型压缩。
- 模型压缩:
- 用mmrazor对UNet进行通道剪枝(根据BN gamma),然后微调。
- 再用mmrazor进行量化感知训练,转为INT8。
- 同样对YOLO做量化和剪枝。
- 最终部署:把压缩后的ncnn模型部署到设备,实现实时推理。
- 持续迭代:收集新数据,通过增量学习或知识蒸馏更新模型(mmrazor也支持蒸馏)。
7. 深度细节展示
为了证明你真的掌握了,还需要深入一些技术细节,比如:
- YOLOv5的anchor分配策略:它采用了跨网格的匹配规则,即计算GT与所有anchor的宽高比,如果比例在阈值内,就分配正样本。这比传统的IoU匹配更灵活。
- UNet的跳跃连接实现:在mmsegmentation中,UNet的decoder通过
UpConv(转置卷积)上采样,然后与encoder对应层的输出拼接。源码中需要处理好不同尺度的对齐。 - ONNX导出时的动态轴:部署时输入尺寸可能变化,ONNX导出要设置动态轴(
dynamic_axes),ncnn也支持动态输入,但需要确认。 - ncnn的优化技巧:比如用
ncnn::Optimizer进行算子融合(Conv+BN融合),或者手动编写汇编优化Neon指令。 - 量化中的校准:后训练量化需要校准数据集来统计激活值的范围,mmrazor会提供校准工具。
- 剪枝后的模型重参数化:剪枝后需要把原模型的权重映射到新结构,mmrazor会自动生成一个更小的网络并拷贝权重。
8. 总结
通过这个超市的例子,我们把YOLO(检测)、语义分割(UNet/FCN)、mmsegmentation、mmdeploy(ONNX/ncnn)、mmrazor(量化/剪枝)全部串联起来,并且每一步都有具体的问题驱动:为什么要检测?为什么要分割?为什么要部署?为什么要压缩?而且每个技术点都深入到源码层面,比如损失函数、算子转换、量化校准等。


