第三章 模型选型策略:轻量化网络与检测头
模型选型这事儿,说白了就是一场「精度」与「速度」的博弈。工业现场不像实验室,你跑个 200 帧的 GPU 服务器随便玩。到了嵌入式端,算力就那么点,还得保证实时性。我这些年踩过的坑,十有八九都跟选型有关。
今天咱们就聊聊,怎么在 MobileNet、ShuffleNet、EfficientNet-Lite 这些轻量化网络里挑合适的,以及检测头到底用 YOLOv5s 还是 NanoDet。
3.1 轻量化骨干网络:三选一
先看骨干网络。它负责提取特征,是整个模型的「眼睛」。眼睛不好使,后面检测头再牛也白搭。
| 网络 | 核心思想 | 参数量 | 适合场景 |
|---|---|---|---|
| MobileNetV3 | 深度可分离卷积 + SE 注意力 | ~2.5M | 通用场景,平衡性好 |
| ShuffleNetV2 | 通道重排 + 分组卷积 | ~1.5M | 极致低算力设备 |
| EfficientNet-Lite | 复合缩放 + 深度可分离 | ~4.0M | 精度优先的轻量场景 |
我的经验:别只看参数量。实际跑起来,MobileNetV3 在 RK3588 上能到 30ms,ShuffleNetV2 能到 22ms,但精度差了 2 个点。如果缺陷类型是细微划痕,我建议优先保精度。
3.1.1 MobileNetV3:最稳妥的选择
MobileNet 系列我用的最多。V3 版本加入了 SE 模块,能自动关注重要通道。工业缺陷检测里,很多缺陷只占图像很小区域,SE 机制正好派上用场。
举个例子。检测手机屏幕的微小气泡,普通卷积容易漏掉。MobileNetV3 的 SE 注意力会把「异常区域」的权重拉高,召回率能提升 3-5%。
# 伪代码:MobileNetV3 的 SE 模块
def se_block(x, ratio=4):
channels = x.shape[-1]
# 全局平均池化
squeeze = tf.reduce_mean(x, axis=[1,2])
# 两个全连接层做激励
excitation = Dense(channels // ratio, activation='relu')(squeeze)
excitation = Dense(channels, activation='sigmoid')(excitation)
# 逐通道加权
return x * tf.reshape(excitation, [-1, 1, 1, channels])
小技巧:如果你用 MobileNetV3-Large,建议把最后几层的 stride 从 2 改成 1。这样特征图分辨率不会降得太快,对小缺陷更友好。代价是推理速度慢 10% 左右,自己权衡。
3.1.2 ShuffleNetV2:极致速度党
ShuffleNetV2 的设计理念很直接——「内存访问成本」才是真瓶颈。它用通道重排代替密集的 1x1 卷积,计算量确实小。
但我得说句实话。我在一个轴承表面缺陷项目里试过 ShuffleNetV2,速度确实快,RK3399 上能跑到 18ms。但精度掉了 4 个点,有些细小的裂纹直接漏检了。
避坑指南:我曾经在检测 PCB 焊点缺陷时用了 ShuffleNetV2,结果误检率飙升。后来发现是分组卷积导致特征融合不充分,焊点的微小虚焊特征被「分组」割裂了。如果你检测的目标是细小、连续的缺陷,慎用 ShuffleNet。
3.1.3 EfficientNet-Lite:精度优先派
EfficientNet-Lite 是 Google 专门为移动端优化的版本。它用 NAS 搜索出来的结构,理论上效率最高。实际用下来,精度确实比 MobileNetV3 高 1-2 个点。
代价是什么?模型体积大,推理慢。在树莓派 4B 上,EfficientNet-Lite4 要 80ms,MobileNetV3 只要 35ms。所以它更适合算力稍好的设备,比如 Jetson Nano。
3.2 检测头选择:YOLOv5s vs NanoDet
骨干网络负责「看」,检测头负责「找」。检测头的设计直接影响定位精度和速度。
| 检测头 | 结构特点 | mAP@0.5 | 推理速度 |
|---|---|---|---|
| YOLOv5s | FPN + PAN 特征金字塔 | ~56% | ~25ms |
| NanoDet | ShuffleNetV2 + 轻量级 Head | ~52% | ~15ms |
3.2.1 YOLOv5s:工业场景的万金油
YOLOv5s 的检测头用了 FPN+PAN 结构。FPN 从上往下传语义,PAN 从下往上传位置。这种双向融合对多尺度缺陷特别有效。
我做过一个项目,检测金属表面的划痕和凹坑。划痕是长条形的,凹坑是圆形的,尺度差异很大。YOLOv5s 的 FPN 能同时抓住这两种特征,mAP 到了 58%。换成 NanoDet,凹坑还行,划痕的召回率掉了 7 个点。
# YOLOv5s 检测头核心结构
class Detect(nn.Module):
def __init__(self, nc=80, ch=()):
super().__init__()
self.m = nn.ModuleList([
nn.Conv2d(x, 256, 1) for x in ch # 三个检测头
])
def forward(self, x):
# x 是 FPN 输出的三个尺度特征
return [self.m[i](x[i]) for i in range(3)]
我的建议:如果你的缺陷类型超过 3 种,或者尺度变化大,直接上 YOLOv5s。它的特征金字塔是经过大量场景验证的,泛化能力更强。
3.2.2 NanoDet:极致轻量的选择
NanoDet 的设计思路是「能省则省」。它用 ShuffleNetV2 做骨干,检测头只用简单的卷积层,没有复杂的特征金字塔。
优点很明显——快。在 RV1126 上能跑到 10ms,比 YOLOv5s 快一倍。但代价是精度下降,尤其对重叠目标和小目标不友好。
避坑指南:我曾经在一个螺丝缺陷检测项目里用 NanoDet,结果两个挨着的螺丝经常只检出一个。后来发现是 NanoDet 的 NMS 阈值设得太低,导致重叠框被抑制了。如果你检测的目标密集排列,建议把 NMS 的 IoU 阈值从 0.5 调到 0.3。
3.3 选型决策流程
说了这么多,到底怎么选?我画了个决策图,你照着走就行。
我的习惯:每次选型前,先拿 MobileNetV3 + YOLOv5s 跑个 baseline。然后根据瓶颈(速度不够还是精度不够)再调整。这样至少有个参照系,不会盲目选型。
3.4 实战组合推荐
最后给几个我验证过的组合,直接抄作业也行。
| 设备 | 推荐组合 | 预期性能 | 适用缺陷 |
|---|---|---|---|
| RK3588 / Jetson Orin | EfficientNet-Lite3 + YOLOv5s | ~20ms, mAP 58% | 多尺度、复杂缺陷 |
| RK3399 / Jetson Nano | MobileNetV3-Large + YOLOv5s | ~30ms, mAP 55% | 通用工业缺陷 |
| RV1126 / 树莓派 4B | ShuffleNetV2 + NanoDet | ~15ms, mAP 50% | 简单、大尺度缺陷 |
| MCU / 低端 DSP | MobileNetV3-Small + NanoDet | ~40ms, mAP 45% | 粗检、有无判断 |
最后提醒一句:别迷信理论数据。我见过太多人拿着 paper 上的指标直接上线,结果现场一跑就崩。模型选型一定要结合你的实际硬件、实际缺陷样本、实际帧率要求。先跑起来,再优化,这才是工业界的玩法。