第三章 模型选型策略:轻量化网络与检测头

模型选型这事儿,说白了就是一场「精度」与「速度」的博弈。工业现场不像实验室,你跑个 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 选型决策流程

说了这么多,到底怎么选?我画了个决策图,你照着走就行。

模型选型决策流程 开始选型 算力 < 1 TOPS? (如 RV1109、MCU) 是 否 ShuffleNetV2 + NanoDet EfficientNet-Lite + YOLOv5s 缺陷尺度变化大? (如同时有划痕和凹坑) 是 否 MobileNetV3 + YOLOv5s MobileNetV3 + NanoDet 提示:实际项目中建议至少跑两个组合,用实际数据说话

我的习惯:每次选型前,先拿 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 上的指标直接上线,结果现场一跑就崩。模型选型一定要结合你的实际硬件、实际缺陷样本、实际帧率要求。先跑起来,再优化,这才是工业界的玩法。