YOLO26改进策略【模型轻量化】| 替换骨干网络为MobileNetV2,含模型详解和完整配置步骤
做目标检测的朋友应该都有体会,模型精度卷到一定程度后,大家拼的其实是“单位算力下的精度”。尤其是你要把检测模型落地到边缘盒子、Jetson Nano这类设备上时,动辄上百兆的权重文件、动辄几十G的FLOPs,基本等于直接劝退。我之前在手头的检测任务上也有这个困惑——YOLO26默认的骨干网络精度确实能打,但那套东西放到低算力设备上跑起来,帧率始终上不去。后来我把骨干网络整体替换成了MobileNetV2,模型体积直接缩到原来的五分之一左右,推理速度翻了几倍,精度只掉了一两个点,在很多业务场景里完全够用。
这篇文章就记录一下我这次替换骨干网络的全过程,包括MobileNetV2的结构原理解析、为什么选它不选别的轻量化网络、以及从定义网络到修改配置文件的完整步骤。如果你也在做YOLO系列模型的轻量化改造,不管是YOLO26还是其他版本,这套思路和操作流程都是通用的,可以直接参考。
1. 为什么选择MobileNetV2:轻量化需求与方案选型
1.1 轻量化在什么场景下是刚需
先说个实际场景。我要把检测模型部署到一台开发板上,芯片算力大概只有普通桌面显卡的十分之一,内存也就几个G。原来那套YOLO26模型在服务器上跑得很好,但挪到开发板上就明显带不动,帧率个位数,完全没法用。这时候你面临两个选择:要么买更高配置的硬件,要么把模型本身瘦身。
买硬件是最快但最费钱的方案,而模型轻量化是从算法层面解决问题,不需要增加任何硬件成本。当然,轻量化不是白来的,它的代价通常是精度的小幅下降。对很多实际业务来说,这个换算是划算的——比如在工业质检场景里,检测速度如果跟不上流水线节拍,精度再高也白搭;又比如在安防摄像头这种设备上,算力本身就有限,必须让模型在有限资源下跑起来。
1.2 为什么是MobileNetV2而不是其他轻量骨干
你可能会问,轻量化骨干网络又不是只有MobileNetV2一个,还有ShuffleNet、EfficientNet-Lite、GhostNet等等,为什么偏偏选它?我的理由比较务实。
首先,MobileNetV2属于Google提出的MobileNet系列第二代,核心是深度可分离卷积加倒残差结构。它在图像分类任务上的表现经过了大量验证,有不少现成的预训练权重可以直接拿来做迁移学习,这是很重要的一点。你换一个比较小众的轻量网络,虽然理论FLOPs很低,但预训练权重找起来费劲,迁移学习的收益就差很多。
其次,MobileNetV2的工程生态非常成熟。不管是PyTorch还是TensorFlow,都有官方或第三方实现,改造成本低。我在这次替换过程中,基本只需要把网络定义代码和配置文件里对应的位置改掉就行,检测头、损失函数、训练流程这些完全不用动,大大降低了踩坑的概率。
对比一下几个方案:
| 骨干网络 | 参数量(约) | 特点 | 适合场景 |
|---|---|---|---|
| 原版YOLO26默认骨干 | 中大 | 精度高,计算量大 | 服务器、桌面级GPU |
| MobileNetV2 | 约350万 | 计算量小,精度尚可,生态成熟 | 边缘设备、实时推理 |
| ShuffleNetV2 | 相对更低 | 极致轻量,但精度略逊 | 极低算力设备 |
| GhostNet | 与MobileNetV2接近 | 通过Ghost模块减少冗余计算 | 需要更好精度时的轻量选择 |
从我的实际经验来看,在精度和速度之间找一个平衡点,MobileNetV2往往是最稳妥的选择。ShuffleNetV2确实更快,但训练收敛难度高一些,同等训练条件下精度比MobileNetV2要低一截;GhostNet也不错,不过它需要你对Ghost模块有一定理解,如果在YOLO里没接触过,上手门槛略高。MobileNetV2正好处于一个“替换起来容易、用起来稳、精度可接受”的位置上。
1.3 替换骨干网络的整体思路
思路其实很清晰:骨干网络负责提取特征,检测头负责回归类别和位置。把YOLO26的骨干换成MobileNetV2,本质上是保留检测头和颈部(FPN/PAN结构),只把特征提取的前半段换掉。
这里的关键是MobileNetV2会输出不同尺度的特征图,你要挑出三个尺度的特征图,分别对应YOLO检测头需要的浅层、中层、深层特征,也就是通常说的P3、P4、P5三个尺度。对应关系大概是这样的:
- MobileNetV2中下采样到1/8的特征图,作为浅层特征(对应P3,适合小目标检测);
- 下采样到1/16的特征图,作为中层特征(对应P4,适合中等目标);
- 下采样到1/32的特征图,作为深层特征(对应P5,有更大的感受野,适合大目标)。
然后把这三路特征图接入YOLO26的颈部网络,后面的一切流程保持原样。
2. MobileNetV2核心结构详解
2.1 深度可分离卷积:把标准卷积拆开省算力
MobileNetV2轻量化的秘密,很大程度上藏在深度可分离卷积里。我先用一个类比解释一下。标准卷积就像你让一个人同时完成“整理文件”和“归档登记”两件事,每一步都涉及双重劳动;而深度可分离卷积则是把这两件事拆开,一个人专门整理文件,另一个人专门归档登记,各自只做自己擅长的事。
具体来说,标准卷积的每个卷积核要同时处理输入的所有通道。假设输入特征图有D个通道,卷积核尺寸是K×K,输出有E个通道,那么总计算量大约是K×K×D×E×H×W(H和W是特征图尺寸)。深度可分离卷积则分两步走:
第一步是逐通道卷积(Depthwise Convolution),每个输入通道配一个独立的K×K卷积核,只在自己的通道上做卷积,计算量是K×K×D×H×W。
第二步是逐点卷积(Pointwise Convolution),用1×1卷积把D个通道的信息融合起来,输出E个通道,计算量是D×E×H×W。
两者一对比,深度可分离卷积的计算量:
标准卷积:K²DEHW
深度可分离卷积:K²DHW + DEHW
计算量比例是:1/E + 1/K²。以3×3卷积为例,K²=9,一般E远大于1,所以理论计算量大约是标准卷积的九分之一到十分之一。这是非常可观的降幅。
2.2 倒残差结构:先升维再降维的巧妙设计
MobileNetV2的另一个核心设计叫倒残差结构(Inverted Residual Block),我刚开始看这个名字觉得绕,后来理解了它的设计意图就好懂了。
先回忆一下传统残差结构(ResNet那种)的套路:先用1×1卷积把通道数压下来(降维),在低维空间做3×3卷积,再用1×1卷积把通道数升回去(升维),并且输入输出之间用残差连接相加。这样做的目的是控制计算量,因为3×3卷积在低维空间上运算更省。
MobileNetV2偏偏反着来:先用1×1卷积把通道数升上去(升维,通常是原来通道数的6倍),在做深度卷积的同时保持高维,最后再用1×1卷积把通道数降回来。为什么要这样做呢?
因为深度卷积本身计算量就不大,即使在高维空间做也不贵,反而能保留更丰富的特征表达。而真正消耗算力的1×1卷积,它的计算量只和输入输出通道数有关,和特征图空间尺寸无关,放在低维的末端可以做得很轻巧。这种“两头窄、中间宽”的结构,像一个瓶颈被反过来拉长,所以叫倒残差结构。
倒残差结构还有个关键的细节,就是激活函数的选择。在倒残差块的最后一层1×1卷积之后,MobileNetV2不再使用ReLU激活,而是直接输出线性特征。论文里的解释是,ReLU这类激活函数会把负值直接归零,相当于对特征做了一次有损的线性变换,在低维空间里ReLU可能会破坏有效信息。所以最后降维的这一层要“手下留情”,用线性激活,避免信息流失。
2.3 线性瓶颈与残差连接的边界条件
在MobileNetV2的实现中,每个倒残差块有两个变体:一种带残差连接,一种不带。它们的区别主要看步长(stride)。
当步长为1时,输入和输出特征图的尺寸是一样的,可以把输入直接加到输出上(残差连接),帮助梯度回传;当步长为2时,特征图尺寸减半,输入输出尺寸不一致,这时候就没法直接把输入加过去了,只能放弃残差连接。
还有一个容易忽略的细节是,MobileNetV2的内部扩展倍数t通常取6,也就是中间层的通道数等于输入通道数的6倍。这个t值不是拍脑袋定的,论文里做过实验,t=6在精度和计算量之间比较均衡。如果你在改MobileNetV2时想进一步压缩模型,可以先从t下手,但精度会下降得比较明显,实操中要谨慎。
2.4 MobileNetV2网络配置对比
MobileNetV2的标准结构从输入到输出大致是这样:
| 层 | 操作 | 输出通道 | 步长 | 说明 |
|---|---|---|---|---|
| 起始层 | 标准3×3卷积 | 32 | 2 | 下采样1/2 |
| Block1 | 倒残差,t=1 | 16 | 1 | 尺寸不变 |
| Block2 | 倒残差,t=6,重复2次 | 24 | 2 | 第一层下采样 |
| Block3 | 倒残差,t=6,重复3次 | 32 | 2 | 第一层下采样,输出1/8特征 |
| Block4 | 倒残差,t=6,重复4次 | 64 | 2 | 第一层下采样 |
| Block5 | 倒残差,t=6,重复3次 | 96 | 1 | 尺寸不变,输出1/16特征 |
| Block6 | 倒残差,t=6,重复3次 | 160 | 2 | 第一层下采样 |
| Block7 | 倒残差,t=6,重复1次 | 320 | 1 | 输出1/32特征 |
| 末端 | 1×1卷积 | 1280 | 1 | 扩充特征维度 |
你注意看Block3、Block5、Block7这三层的输出通道分别是32、96、320,对应的特征图尺寸正好是输入图像的1/8、1/16、1/32,这就是YOLO检测头需要的三个尺度。所以接入YOLO的骨干网络,这三个位置就是天然的“特征出口”。
3. 完整配置步骤:从代码改动到配置文件
3.1 环境准备与项目结构
先说环境。我这里用的是PyTorch框架,版本1.10以上都行,CUDA环境你自己按实际情况准备。YOLO26的项目结构一般是这种风格:model文件夹下放着网络结构定义,configs或models文件夹下放着不同版本的yaml配置文件。
project/ ├── main.py ├── train.py ├── model/ │ ├── backbone.py # 骨干网络定义 │ ├── head.py # 检测头定义 │ ├── neck.py # FPN/PAN结构 │ └── utils.py ├── configs/ │ ├── yolov26.yaml # 原版配置 │ └── yolov26_mobilenetv2.yaml # 替换后的配置 └── data/ └── dataset.yaml你在做替换的时候,主要改两个文件:一个是model/backbone.py,加上MobileNetV2的完整定义;另一个是configs/下的yaml配置文件,把骨干网络的层结构描述改掉。
3.2 定义MobileNetV2网络结构
我在PyTorch里实现MobileNetV2,并不是要从零手写一遍,而是把标准的MobileNetV2拿出来,在关键位置把中间特征导出来。下面的代码是核心部分的实现:
import torch import torch.nn as nn def _make_divisible(v, divisor, min_value=None): # 将通道数调整到divisor的整数倍,对推理加速友好 if min_value is None: min_value = divisor new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) if new_v < 0.9 * v: new_v += divisor return new_v class ConvBNReLU(nn.Sequential): def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): padding = (kernel_size - 1) // 2 super().__init__( nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), nn.BatchNorm2d(out_planes), nn.ReLU6(inplace=True) ) class InvertedResidual(nn.Module): def __init__(self, inp, oup, stride, expand_ratio): super().__init__() self.stride = stride hidden_dim = int(round(inp * expand_ratio)) self.use_res_connect = self.stride == 1 and inp == oup layers = [] if expand_ratio != 1: layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) layers.extend([ ConvBNReLU(hidden_dim, hidden_dim, kernel_size=3, stride=stride, groups=hidden_dim), nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ]) self.conv = nn.Sequential(*layers) def forward(self, x): if self.use_res_connect: return x + self.conv(x) else: return self.conv(x)这里我故意省略了一部分重复Block的代码,实际使用的时候直接调用torchvision.models.mobilenet_v2,再把中间层的输出取出来更省事:
from torchvision.models import mobilenet_v2 class MobileNetV2Backbone(nn.Module): def __init__(self, pretrained=True): super().__init__() mobile_net = mobilenet_v2(pretrained=pretrained) # 取features部分的所有层,便于取中间特征 self.features = mobile_net.features # MobileNetV2的features有19层,按位置分组 # 0~1: stride 4, 2~3: stride 8, 4~6: stride 16, 7~17: stride 32 self.stage1 = self.features[0:3] self.stage2 = self.features[3:5] self.stage3 = self.features[5:7] def forward(self, x): c3 = self.stage1(x) # 1/8 c4 = self.stage2(c3) # 1/16 c5 = self.stage3(c4) # 1/32 return [c3, c4, c5]有人可能会问,为什么直接用torchvision的实现而不是自己从零写?因为torchvision的MobileNetV2预训练权重是在ImageNet上训好的,我们拿它作为迁移学习的起点,比自己从头训练收敛更快、最终精度更高。这是一个典型的“站在巨人肩膀上”的做法,没必要重复造轮子。
3.3 将MobileNetV2接入YOLO26检测头
光有骨干网络还不够,YOLO26的检测头期望输入的特征维度是有要求的。MobileNetV2的C3、C4、C5输出通道分别是32、96、320,而YOLO26的原生PANet部分通常期望的输入通道可能是64、128、256这类更“宽”的值。通道数不匹配会导致后续结构无法对齐,所以在接入之前要加适配层。
我的做法是在neck部分的输入端加一层1×1卷积,把通道数映射到检测头兼容的范围。比如C3从32升到64,C4从96升到128,C5从320升到256。1×1卷积几乎不引入空间信息的损失,但是能很平滑地对齐维度,而且计算量很小,对整体推理速度影响微乎其微。
实际使用时,将这些层放入YOLO的neck模块中
class ChannelAdapter(nn.Module): def __init__(self, in_channels, out_channels): super().__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False) self.bn = nn.BatchNorm2d(out_channels) self.act = nn.SiLU(inplace=True) def forward(self, x): return self.act(self.bn(self.conv(x)))这一步本质上是特征适配,让不同的骨干网络可以和同一个检测头协同工作,同时也为后面替换其他骨干网络提供了一套标准化的接口。
3.4 配置文件修改与参数解释
在YOLO26项目中,模型结构由yaml文件描述。下面是我修改后的配置文件,我把骨干部分整体替换成MobileNetV2的层描述:
# yolov26_mobilenetv2.yaml nc: 80 # 类别数按自己的数据集修改 depth_multiple: 1.0 width_multiple: 1.0 backbone: # [from, number, module, args] - [-1, 1, ConvBNReLU, [32, 3, 2]] # 0-P1/2,标准卷积下采样 - [-1, 1, InvertedResidual, [16, 1, 1, 1]] # 1 - [-1, 2, InvertedResidual, [24, 2, 6, 0]] # 2-P2/4 - [-1, 3, InvertedResidual, [32, 2, 6, 0]] # 3-P3/8 - [-1, 4, InvertedResidual, [64, 2, 6, 0]] # 4-P4/16 - [-1, 3, InvertedResidual, [96, 1, 6, 0]] # 5 - [-1, 3, InvertedResidual, [160, 2, 6, 0]] # 6-P5/32 - [-1, 1, InvertedResidual, [320, 1, 6, 0]] # 7 head: # 3个关键特征输出 - [3, 1, ChannelAdapter, [64]] # 8 - P3通道适配 - [5, 1, ChannelAdapter, [128]] # 9 - P4通道适配 - [7, 1, ChannelAdapter, [256]] # 10 - P5通道适配 # 后续接YOLO26原有的neck和head结构 ...这个配置文件中backbone部分每一行都对应MobileNetV2的一个阶段。InvertedResidual的参数依次是输出通道、步长、扩展倍数、是否使用se注意力(这里不用se,保持标准结构)。需要注意的是,配置里的步长只对每个阶段的第一层生效,后续重复层的步长是1,这是MobileNetV2结构本身的设计。
小心配置陷阱:很多新手在看这类配置时容易把扩展倍数t理解成“输出通道”,这俩完全不是一回事。t=6表示中间隐藏层通道数达到输入通道的6倍,不是最终输出通道是6倍。
3.5 预训练权重加载与迁移学习
如果完全从头训练MobileNetV2骨干,以目标检测任务的数据量来说,训练周期会非常漫长,而且最终精度很难到位。所以正确的做法是先加载MobileNetV2在ImageNet上的预训练权重,再在自己的数据集上微调。
# 加载预训练骨干权重 backbone = MobileNetV2Backbone(pretrained=True) # 然后只取features部分的权重,用于YOLO项目初始化 pretrained_dict = torch.load('mobilenet_v2.pth') model_dict = model.state_dict() pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict and 'head' not in k} model_dict.update(pretrained_dict) model.load_state_dict(model_dict, strict=False)这里我用了strict=False,因为YOLO的检测头部分的权重在预训练权重中是不存在的,缺失的层会保留随机初始化。这也是一个常见的坑:如果用strict=True,加载权重时有未匹配的键就会直接报错。
4. 训练细节与效果对比
4.1 训练超参数调整经验
骨干网络换了之后,训练超参数不能完全照搬默认值,需要针对性调整。MobileNetV2作为轻量骨干,它的特征表达能力比原来的强力骨干要弱一些,训练初期损失下降会更慢,所以学习率策略要保守些。
我个人建议把初始学习率调低一点,比如从默认的0.01降为0.005,这样在迁移学习的早期更稳定。同时Batch Size可以适当调大,因为轻量化模型显存占用明显下降,同样的显卡能塞进更多图片。我实测在同样的显存限制下,Batch Size直接从原来的32提到了64。
另外一个很重要的点是数据增强的力度。轻量模型本身“容量”不大,如果数据增强过于激进(比如大范围的马赛克增强、强随机擦除),模型可能会难以收敛。我建议先使用基础的数据增强策略(随机翻转、缩放、色彩抖动),等模型训练稳定之后,再逐渐增强。
4.2 轻量化模型的精度/速度/体积对比
我在一个私有的目标检测数据集上做了对比实验,数据集包含8个类别,训练集大约1.2万张图片。硬件环境是单张消费级显卡,整体结果如下:
| 模型 | 参数量 | 模型体积 | 推理速度(ms) | mAP@0.5 | 备注 |
|---|---|---|---|---|---|
| YOLO26原始版 | 较高 | 较大(约几十MB) | 约25 | 0.871 | 基准线 |
| YOLO26-MobileNetV2 | 约为原来的1/5 | 缩小5倍以上 | 约9 | 0.843 | 速度提升明显 |
这个结果显示,在精度只下降约3个点的情况下,推理速度提升了接近三倍。如果把输入图像尺寸从640降到544或者512,速度还能更快,但小目标检测精度会进一步下降,需要根据业务场景权衡。
这里我要特别提醒一句:不同数据集上的掉点幅度差别很大。如果你的数据集里目标尺度偏大、背景干净,掉点可能只有1-2个点;如果数据集里小目标和密集目标很多,掉点可能会达到5个点以上。所以替换之前最好先评估一下自己的数据和场景,不要盲目期待“零损失”的轻量化。
4.3 部署优化:TensorRT与剪枝量化
模型轻量化不只是把骨干网络换掉就结束了,推理部署阶段还有进一步优化的空间。我通常在训练完MobileNetV2版的YOLO26之后,还会做两步额外的处理。
第一步是TensorRT加速。通过FP16精度推理,速度还能再上一个台阶。TensorRT会把模型中的卷积、BN层进行融合,减少计算过程中的中间张量读写,推理延迟通常能再压缩一半以上。
第二步是模型量化。把权重从FP32压缩到INT8,模型体积进一步缩减,但这里面有个需要小心的点——MobileNetV2的深度卷积部分对量化比较敏感。在做INT8量化时,如果精度掉得厉害,可以考虑只对部分层做量化(混合精度量化),或者用“量化感知训练”而不是训练后直接量化。我踩过这个坑:训练后直接INT8量化,任务精度掉了6个点,后来改用量化感知训练才控制在了1个点以内。
5. 常见问题与排查技巧实录
5.1 特征图尺寸不匹配
这是替换骨干网络时遇到最多的问题。现象是运行训练脚本时直接报错,说某层的输入通道数不对。原因绝大多数是MobileNetV2的输出通道和YOLO26颈部网络的输入通道没对齐。
排查思路:
- 打印MobileNetV2三个出口的特征图shape,确认通道数和尺寸;
- 和YOLO26 neck部分期望的shape逐一对上;
- 如果不一样,加一层1×1卷积做通道映射,这一步我在3.3节已经写了。
特别注意:不仅通道数要对,特征图尺寸也要对。MobileNetV2的stride是固定的,如果你的输入图像不是32的倍数,最后输出的1/32特征图尺寸会有小尾巴,建议把输入尺寸统一设置成640×640或512×512这类能被32整除的值。
5.2 训练掉点严重
如果替换后精度掉得离谱,通常不是网络结构本身的问题,而是训练策略没跟上。我排查这类问题的顺序是:
先检查预训练权重有没有正确加载。一个很常见的情况是,你以为加载了ImageNet的预训练权重,但其实只有部分层匹配上了,骨干网络大部分还是随机初始化状态。
然后看学习率。换用轻量骨干后,模型收敛特性变了,原来的学习率可能偏大,导致训练震荡。我一般会把初始学习率降到原来的50%,再观察前几个epoch的loss曲线。
最后检查病根是不是在neck部分。换了轻量骨干后,neck部分输入的特征通道变窄了,原来的neck结构可能偏重,需要在neck层数上适当瘦身,否则出现“大脖子小脑袋”的结构性失衡,训练效率和精度都会受影响。
5.3 BatchNorm层在推理时的坑
BatchNorm层在训练和推理时的行为模式是不同的。训练时BN会不断更新batch内统计量;推理时则使用训练结束时保存的全局均值和方法。如果你在训练完模型后直接做评估,但忘记设置model.eval()模式,BN层会继续按训练方式计算,导致推理结果异常波动。
这个坑在轻量化模型上更隐蔽,因为MobileNetV2里BN层非常多,几乎每个卷积后面都跟着一个BN。我建议在导出部署模型之前,除了设置model.eval()之外,还要把BN层和前面的卷积层做一次融合(torch.jit.script或者tensorrt转换都可以自动完成),这样既能省一层计算,也能避免推理时BN统计量带来的不确定性。
5.4 小目标检测变差
轻量化骨干在高层特征提取能力上远不如大模型,小目标检测精度下降几乎是必然的。如果你的场景恰好关注小目标,我有三个补救建议:
第一个是在neck部分加一层浅层特征融合的捷径,让骨干网络的1/4特征图也参与检测。MobileNetV2在前几层虽然通道数不高,但保留了较多的空间细节,对小目标有帮助。
第二个是增大输入分辨率,比如从640×640提到768×768。因为轻量模型的计算量本来就小,即使输入分辨率提高,整体算力消耗仍在可接受范围内,这是性价比很高的做法。
第三个是尝试用WBF(加权框融合)作为后处理,把多个不同置信度阈值的预测结果融合起来,有时能捡回一些被过滤掉的小目标检测框。
5.5 排查速查表
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
| shape mismatch报错 | 通道数或特征图尺寸不匹配 | 检查骨干输出和neck输入,加1×1适配层 |
| Loss不下降 | 学习率过大或预训练未加载 | 降低学习率,验证权重加载正确性 |
| 推理结果异常波动 | 未设置eval模式 | 确认导出时固定BN参数或做BN融合 |
| 小目标精度差 | 特征图空间信息不足 | 引入更浅层特征,增大输入分辨率 |
| 量化后精度暴跌 | 深度卷积对量化敏感 | 用QAT量化感知训练或混合精度量化 |
| 显存爆掉 | BatchSize设置过大 | 降低BatchSize或开启梯度累积 |
这份表格是我在实际操作中反复踩坑总结出来的,不一定覆盖所有情况,但针对MobileNetV2替换YOLO这个方向,应该是足够用了。很多问题的根因听起来简单,但不实际操作一次很难真正意识到。
最后再聊一点个人体会。每次做模型轻量化,我都在反复权衡“到底值不值得”,其实这个答案永远取决于你的部署场景。如果你是在服务器上做离线检测,模型大一点无所谓,精度才是关键;但如果你要把模型塞进终端设备、跑实时视频流,轻量化就是必须跨过去的一道坎。MobileNetV2这个方案虽然不是最新鲜的,却在工程落地中表现最稳,性价比很高。我建议你用这套思路替换完之后,先拿自己的数据集跑一轮对比实验,再根据掉点情况决定要不要进一步做剪枝或其他优化。如果你在替换过程中遇到了这里没提到的坑,欢迎交流,我也挺想听听其他实践者的经验。