多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

YOLOv5模型结构深度解析:从CSPDarknet到PANet的代码级拆解

YOLOv5模型结构深度解析:从CSPDarknet到PANet的代码级拆解 1. 项目概述从“黑盒”到“白盒”的必经之路当你拿到一个像YOLOv5这样的成熟目标检测框架第一反应可能是直接跑通训练脚本看看效果。这没错但如果你想真正掌握它进行二次开发、模型优化或者仅仅是理解为什么它这么快、这么好用那么深入其模型结构就是绕不开的一步。很多人把深度学习模型当作一个“黑盒”输入数据得到结果中间过程不甚了了。但对于一个想进阶的开发者或研究者来说把“黑盒”变成“白盒”是能力提升的关键。YOLOv5的模型结构正是这个“白盒”的核心。它不像一些论文里的模型只有一张抽象的示意图和一堆数学公式。YOLOv5的代码是开源的、工程化的结构清晰但细节繁多。解析它的模型结构意味着我们要从代码层面理解每一层网络是如何搭建的数据是如何流动的特征是如何从原始图像一步步被提炼成边界框和类别置信度的。这个过程能让你真正看懂models/yolo.py和models/common.py里那些类与函数在做什么而不仅仅是机械地调用--weights yolov5s.pt。为什么是YOLOv5因为它代表了当前工业界落地最广泛的一类目标检测模型在精度、速度和易用性上取得了极佳的平衡。它的模型结构设计蕴含了许多实用的工程技巧比如CSPNet思想、SPPF模块、自适应锚框计算等。理解这些不仅有助于你用好YOLOv5更能让你将这些设计思想迁移到自己的项目中。无论是想在Jetson Nano、RK3588这类边缘设备上部署还是想针对“南方湖底鱼类”这种特定场景优化数据集训练对模型结构的深刻理解都是你进行有效改动的基础。本文我就带你一起像拆解一台精密仪器一样把YOLOv5的模型结构拆开来看个明白。2. 模型结构总览与设计哲学YOLOv5的模型结构是一个典型的单阶段目标检测器One-Stage Detector其核心设计哲学可以概括为“在有限的计算资源下实现多尺度特征的高效融合与检测”。整个模型可以清晰地划分为几个部分Input输入预处理、Backbone骨干特征提取网络、Neck特征融合网络和Head检测头。官方提供了从YOLOv5n纳米级到YOLOv5x超大级等不同大小的模型它们共享同一套结构设计主要通过调整网络的深度层数和宽度通道数来权衡速度与精度。2.1 BackboneCSPDarknet 的精髓Backbone的任务是从输入图像中提取多层次的特征。YOLOv5的Backbone基于CSPDarknet53这是对原始Darknet的改进。其核心是CSPCross Stage Partial connections结构。我刚开始看代码时对C3模块在common.py中有点困惑它和CSP是什么关系其实C3模块就是YOLOv5对CSP结构的具体实现。为什么用CSP传统网络的前向传播梯度信息可能会在深层次网络中逐渐稀释或爆炸。CSP结构将特征图在通道维度上拆分成两部分一部分通过一个密集的残差块Bottleneck进行深层次变换另一部分则直接通过一个短路连接shortcut。最后再将两部分合并。这样做的好处有两个一是减轻了梯度信息重复因为只有一部分特征经历了复杂变换二是显著减少了计算量FLOPS因为短路路径不参与复杂计算却能将原始特征信息直接传递到后面。在代码里C3模块通过c1, c2, n, shortcut, groups, expansion等参数控制其行为其中n就代表了Bottleneck的重复次数这是调节模型深度的关键。除了C3Backbone中另一个关键模块是SPPFSpatial Pyramid Pooling - Fast。它替代了YOLOv3中的SPP模块。SPPF通过串联多个最大池化层kernel size5来实现类似空间金字塔池化的效果捕获不同尺度的上下文信息但计算效率更高。你可以把它理解为一个“快速多尺度特征提取器”能让网络对目标的大小变化更鲁棒。2.2 NeckPANet的路径聚合如果Backbone是向下挖掘特征那么Neck就是负责将这些不同深度的特征进行融合和再加工。YOLOv5的Neck采用了PANetPath Aggregation Network的结构。简单说它包含两条路径一条是自底向上的特征金字塔和FPN类似将深层的高语义特征上采样后与浅层的高分辨率特征融合另一条是自顶向下的再细化将融合后的浅层特征再次下采样与更深层的特征进行二次融合。这个过程在代码中体现为一系列Conv,Upsample,Concat和C3操作的组合。数据流就像是在不同尺度的特征层之间建立起了“高速公路”让浅层的定位信息和深层的语义信息能够充分交互。最终Neck会输出三个不同尺度的特征图例如对于640x640输入可能是80x80, 40x40, 20x20分别用于检测小、中、大目标。这种多尺度预测是YOLO系列能有效处理不同大小目标的关键。2.3 Head解耦的检测与分类YOLOv5的Head相对“轻量”它接收Neck输出的多尺度特征图并通过一个Conv2d层直接预测每个网格grid cell的输出。这里有一个重要的细节YOLOv5的Head是耦合的Coupled即一个卷积层同时输出边界框坐标4维、物体置信度1维和分类概率C维C为类别数。这与一些“解耦头”Decoupled Head设计不同。耦合头的优势是速度更快结构更简单而解耦头通常能带来更高的精度但会增加一些计算开销。YOLOv5为了速度与精度的平衡选择了耦合头。Head输出的张量形状为[B, N, H, W]其中B是batch sizeN是每个锚点anchor的预测维度41CH和W是特征图的高和宽。后续的损失计算和推理后处理非极大值抑制NMS都是基于这个输出进行的。注意在阅读代码时务必区分“模型定义”和“模型构建”。models/yolo.py中的Model类主要负责根据配置文件*.yaml解析并搭建计算图。而模型的实例化、权重加载通常在训练脚本train.py中完成。先看懂Model类的__init__和forward方法是理解结构的第一步。3. 代码级深度解析从YAML到PyTorch Module理解了宏观结构我们深入到代码层面。YOLOv5的模型结构定义非常清晰采用了“配置文件驱动”的方式。核心文件是models/yolo.py和models/common.py。3.1 配置文件*.yaml的奥秘每个YOLOv5模型如yolov5s.yaml都对应一个YAML配置文件。这个文件用列表的形式逐层定义了网络的结构。我们以yolov5s.yaml的一部分为例# YOLOv5 by Ultralytics, AGPL-3.0 license # Parameters nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, C3, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, C3, [256, False]], # 17 (P3/8-small) [-1, 1, Conv, [256, 3, 2]], [[-1, 14], 1, Concat, [1]], # cat head P4 [-1, 3, C3, [512, False]], # 20 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, C3, [1024, False]], # 23 (P5/32-large) [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) ]每一行定义了一个层或模块格式通常是[from, number, module, args]。from: 该层的输入来自哪一层。-1表示上一层-2表示上上层[6, 4]表示来自第6层和第4层的拼接Concat。number: 该模块重复的次数。但注意最终次数是number * depth_multiple。这是模型缩放的关键。module: 模块名对应common.py中定义的类如Conv,C3,SPPF等。args: 初始化该模块的参数列表例如对于Conv可能是[out_channels, kernel_size, stride]。depth_multiple和width_multiple是YOLOv5模型缩放的核心。depth_multiple控制模块的重复次数如C3中的nwidth_multiple控制卷积层的输出通道数。yolov5s的这两个系数较小0.33和0.5因此模型更小更快yolov5x的系数为1.0和1.25模型更大更精确。这种设计让你能用一个配置文件通过调整两个系数就得到一系列不同规模的模型非常优雅。3.2 Model类的构建过程在yolo.py中Model类继承自nn.Module。它的__init__方法会解析上述YAML文件。解析与存储首先读取YAML将nc类别数、depth_multiple、width_multiple等超参数保存然后分别解析backbone和head部分的层定义列表。构建模型字典Model类定义了一个self.model字典OrderedDict用于存储所有层的索引和模块实例。同时它维护一个self.save列表记录哪些层的输出需要被保存下来以供后续层进行拼接from字段指定了多个输入源时。逐层构建核心是一个循环遍历每一层定义。对于每一行[from, number, module, args]根据module名字从common.py中获取对应的类。根据width_multiple调整args中与通道数相关的参数通常是第一个。如果number 1说明需要重复该模块如多个C3串联。此时会根据depth_multiple计算实际重复次数n并可能使用nn.Sequential来堆叠。实例化模块并添加到self.model中。如果该层的输出需要被后续层引用即其索引出现在后面某层的from列表中则将其索引加入self.save。注册前向钩子为了处理复杂的多输入拼接YOLOv5没有使用简单的顺序前向传播。它在forward方法中动态地根据self.save和每一层的from参数从存储的中间特征字典中获取输入然后执行当前层再将输出存入字典。这相当于实现了一个轻量级的计算图。3.3 核心模块common.py详解common.py定义了所有的基础模块。理解它们是读懂结构的基础。Conv类这是最基础的卷积块通常包含一个卷积层、一个批归一化层BN和一个SiLU激活函数YOLOv5用SiLU替代了LeakyReLU。它实现了“卷积BN激活”的标准模式并支持自动填充autopad以保持特征图尺寸。class Conv(nn.Module): def __init__(self, c1, c2, k1, s1, pNone, g1, actTrue): super().__init__() # 计算填充保持输出尺寸 if p is None: p autopad(k, p) # 卷积层 self.conv nn.Conv2d(c1, c2, k, s, p, groupsg, biasFalse) # 批归一化 self.bn nn.BatchNorm2d(c2) # 激活函数默认为SiLU self.act nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) def forward(self, x): return self.act(self.bn(self.conv(x)))Bottleneck类标准的残差瓶颈块包含两个Conv层和一个可选的shortcut连接。它是C3模块的组成部分。class Bottleneck(nn.Module): def __init__(self, c1, c2, shortcutTrue, g1, e0.5): super().__init__() c_ int(c2 * e) # 隐藏层通道数 self.cv1 Conv(c1, c_, 1, 1) # 1x1卷积降维 self.cv2 Conv(c_, c2, 3, 1, gg) # 3x3卷积 self.add shortcut and c1 c2 # 是否添加快捷连接 def forward(self, x): return x self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))C3类这是YOLOv5的核心模块实现了CSP结构。它将输入分成两部分在代码中通过两个卷积路径实现一部分通过多个Bottleneck块另一部分直接通过一个卷积最后拼接起来。class C3(nn.Module): def __init__(self, c1, c2, n1, shortcutTrue, g1, e0.5): super().__init__() c_ int(c2 * e) self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c1, c_, 1, 1) self.cv3 Conv(2 * c_, c2, 1) # 最后的融合卷积 # 构建多个Bottleneck self.m nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e1.0) for _ in range(n))) def forward(self, x): # 路径1经过多个Bottleneck y1 self.m(self.cv1(x)) # 路径2直接卷积 y2 self.cv2(x) # 拼接并融合 return self.cv3(torch.cat((y1, y2), dim1))实操心得C3模块中的shortcut参数控制内部的Bottleneck是否使用快捷连接。在Neck部分进行特征融合时通常会将shortcut设为False因为输入来自不同层的拼接直接相加可能不合适。SPPF类快速空间金字塔池化。它通过串联三个kernel_size5的MaxPool2d层来实现多尺度池化比原版SPP并行多个不同尺寸池化速度更快。class SPPF(nn.Module): def __init__(self, c1, c2, k5): super().__init__() c_ c1 // 2 self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c_ * 4, c2, 1, 1) self.m nn.MaxPool2d(kernel_sizek, stride1, paddingk // 2) def forward(self, x): x self.cv1(x) y1 self.m(x) y2 self.m(y1) y3 self.m(y2) # 拼接四个不同“感受野”的特征 return self.cv2(torch.cat([x, y1, y2, y3], 1))Concat类非常简单就是torch.cat的封装用于按指定维度拼接多个输入张量。Detect类检测头。它不对输入做复杂变换只是用一个卷积层将输入特征图映射到最终的预测维度(41nc) * na其中na是锚框数量。锚框anchors信息在这里被注册为缓冲区buffer。它的forward方法输出一个列表包含三个尺度的预测张量。4. 前向传播数据流追踪理解了模块和构建过程我们手动追踪一下一个640x640的RGB图像是如何经过模型变成预测框的。假设我们以yolov5s为例。输入预处理图像被缩放到640x640并归一化到[0,1]。形状为[1, 3, 640, 640]Batch1。Backbone (CSPDarknet)第一层Conv/2: 输出变为[1, 32, 320, 320]因为width_multiple0.564*0.532下采样2倍。经过一系列Conv下采样和C3特征提取后在三个关键层代码中的第4、6、9层左右输出多尺度特征我们记为P3/8、P4/16、P5/32。它们的尺寸大致是[1, 128, 80, 80],[1, 256, 40, 40],[1, 512, 20, 20]通道数为近似值具体由配置和width_multiple决定。P5还会经过SPPF模块增强。注意这些中间特征图会被存入self.save指定的字典中等待Neck使用。Neck (PANet)自底向上上采样路径从最深的P5开始先经过一个Conv减少通道数然后Upsample通常用最近邻插值放大2倍与来自Backbone的P4进行Concat拼接。拼接后的特征通过一个C3模块进行融合处理得到新的特征N4。同样的过程重复N4上采样后与P3拼接再经C3处理得到N3。N3是用于检测小目标的特征图分辨率最高。自顶向下下采样路径从N3开始经过一个Convstride2进行下采样与N4拼接再经C3处理得到M4。M4用于检测中目标。同样M4下采样后与P5或经过处理的P5特征拼接经C3处理得到M5。M5用于检测大目标分辨率最低。至此Neck输出了三个精心融合后的特征图N3, M4, M5。Head (Detect)Detect层接收[N3, M4, M5]作为输入。对每个输入特征图应用一个独立的Conv2d层。这个卷积层的核数量是na * (5 nc)其中na是该特征图对应的锚框数量默认为3。因此它将特征图从[B, C, H, W]变换为[B, na*(5nc), H, W]。最后将这个张量重塑reshape为[B, na, H*W, 5nc]再转置为[B, na*H*W, 5nc]。三个尺度的输出被拼接成一个列表。例如对于80类COCO检测三个尺度的输出维度可能分别是N3:[1, 3*(580), 80, 80]- reshape -[1, 3, 6400, 85]- view -[1, 19200, 85]M4:[1, 3*(580), 40, 40]-[1, 3, 1600, 85]-[1, 4800, 85]M5:[1, 3*(580), 20, 20]-[1, 3, 400, 85]-[1, 1200, 85]最终模型输出一个包含三个张量的列表每个张量的形状为[1, N, 85]其中N是该尺度预测框的总数na*H*W85维包含[tx, ty, tw, th, obj_conf, class_conf_1, ..., class_conf_80]。在训练和推理时会根据锚框将这些偏移量解码为真实的边界框坐标。重要提示数据流中所有的尺寸变化下采样、上采样都必须保证对齐否则Concat操作会失败。YOLOv5通过精心设计卷积的stride和padding以及使用Upsample的固定缩放因子确保了这一点。在修改网络结构时这是需要特别注意的地方。5. 模型缩放与自定义结构修改YOLOv5的模型缩放机制非常灵活理解它你就能轻松创建适合自己硬件和任务的新模型。5.1 深度与宽度缩放原理缩放由depth_multiple深度系数和width_multiple宽度系数控制在yolo.py的parse_model函数中实现。宽度缩放作用于每一层的输出通道数。当解析到Conv、C3等模块的args时如果该参数是通道数通常是列表的第一个元素则将其乘以width_multiple并取整到最接近的8的倍数为了GPU计算效率。例如yolov5s.yaml中第一层Conv的args是[64, 6, 2, 2]width_multiple0.5那么实际输出通道数就是int(round(64 * 0.5 / 8)) * 8 32。深度缩放作用于模块的重复次数number。最终重复次数n max(round(number * depth_multiple), 1) if number 1 else number。例如Backbone中某个C3的number9在yolov5s中depth_multiple0.33那么实际堆叠的C3模块数量就是max(round(9*0.33), 1) 3。通过调整这两个系数你可以得到从yolov5n极小到yolov5x极大的系列模型。如果你想为RK3588或RDK X5这类算力较强的嵌入式平台设计模型可以尝试介于s和m之间的系数如果是在算力极其有限的设备上可以尝试比n更小的系数。5.2 如何自定义模型结构有时预定义模型不能满足需求比如你想加入注意力机制、更换Backbone、或者修改Neck的连接方式。这时就需要自定义YAML文件。步骤一复制并修改YAML复制一份models/yolov5s.yaml重命名为my_yolov5.yaml。然后按需修改修改nc为你的类别数。调整depth_multiple和width_multiple。在backbone和head列表中增、删、改层。示例添加一个SE注意力模块假设你想在Backbone的最后一个C3后面加一个SESqueeze-and-Excitation注意力模块。首先你需要在common.py中实现SE类或从别处导入。class SE(nn.Module): def __init__(self, c1, reduction16): super().__init__() self.avgpool nn.AdaptiveAvgPool2d(1) self.fc nn.Sequential( nn.Linear(c1, c1 // reduction, biasFalse), nn.ReLU(inplaceTrue), nn.Linear(c1 // reduction, c1, biasFalse), nn.Sigmoid() ) def forward(self, x): b, c, _, _ x.size() y self.avgpool(x).view(b, c) y self.fc(y).view(b, c, 1, 1) return x * y.expand_as(x)然后在my_yolov5.yaml的backbone部分添加一行backbone: # ... 前面的层 ... [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 第9层 [-1, 1, SE, [1024]], # 新增的第10层SE注意力步骤二注册新模块修改yolo.py中的parse_model函数或者在运行前确保你的SE类已经被导入到common.py的命名空间中。更规范的做法是在common.py中定义好YOLOv5会自动发现它。步骤三使用新模型训练在训练命令中指定你的配置文件python train.py --cfg models/my_yolov5.yaml --weights --data coco128.yaml --epochs 100注意事项通道数对齐新增或删除层时必须确保前后层的输入输出通道数匹配。from参数指向的层其输出通道数必须与当前层args中定义的输入通道数或由前一层的输出通道数推断一致。尺寸对齐上采样、下采样、拼接操作必须保证特征图的空间尺寸H, W一致才能进行。仔细计算每一层的stride和padding。保存与加载自定义结构后保存的模型权重.pt文件包含了新的结构定义。在加载时必须使用相同的模型类定义否则会报错。6. 结构相关的训练与部署陷阱理解了结构最终是为了用好模型。在训练和部署中有几个与模型结构紧密相关的“坑”需要特别注意。6.1 锚框Anchors与模型结构的匹配YOLOv5在训练开始时会默认使用K-means算法在你的数据集上重新聚类生成一组新的锚框尺寸这个过程称为“自适应锚框计算”。这组锚框是针对特定输出特征图尺度的。如果你修改了模型结构特别是改变了Neck输出特征图的数量或下采样率stride那么预设的锚框组数na和每组的尺寸就必须相应调整。问题假设你删除了一个检测头比如只保留两个尺度的输出但配置文件中Detect层的anchors参数还是三组或者特征图尺度变了比如从/8, /16, /32变成了/4, /8, /16而锚框尺寸是针对原尺度聚类的这会导致训练时目标与锚框匹配错乱严重影响精度。排查训练时关注日志输出的“AutoAnchor”结果。如果出现大量Best possible recall (BPR) is less than 0.98的警告说明锚框与当前数据/结构匹配不佳。解决修改结构后最好在自定义数据集上关闭自动锚框计算--noautoanchor先跑几个epoch让模型自己学习偏移量。或者手动计算新结构对应尺度下的锚框。YOLOv5的utils/autoanchor.py提供了相关函数你可以用自己的数据集针对新的输出层尺度重新运行K-means聚类。6.2 模型剪枝与量化对结构的影响为了在Jetson Nano、K230、RK3588等边缘设备上部署常需要对模型进行剪枝移除不重要的通道或层和量化将FP32权重转换为INT8等低精度。这些操作与模型结构强相关。剪枝常见的通道剪枝Channel Pruning会移除卷积层中某些输出通道以及下一层卷积中对应的输入通道。这直接改变了模型的宽度通道数。关键点YOLOv5中的C3、Concat等模块涉及特征图的拼接剪枝时必须确保拼接前后的通道对齐否则网络会断裂。需要使用支持结构化剪枝并能处理复杂连接如残差、拼接的工具。量化Post-Training Quantization (PTQ) 或 Quantization-Aware Training (QAT)。关键点YOLOv5中的SiLU激活函数、Upsample插值操作对量化比较敏感。在TensorRT或ONNX Runtime上部署时需要测试量化后的精度损失。特别注意模型中的自定义操作如果你添加了需要确保有对应的量化实现。实操建议在部署前先将原始PyTorch模型导出为ONNX格式。使用models/export.py脚本时注意--dynamic参数是否支持动态batch size和opset_version算子集版本。导出ONNX后用Netron工具可视化检查模型结构是否与预期一致特别是所有节点和连接是否正确。6.3 自定义数据集的预处理对齐模型结构的第一层是输入。YOLOv5默认的输入分辨率是640x640训练时采用了Mosaic数据增强等。当你训练自己的数据集比如“南方湖底鱼类”时需要确保推理时的预处理与训练时一致。尺寸如果你用--img-size 640训练部署时也必须将图像缩放到640x640保持长宽比的填充或拉伸。归一化YOLOv5默认的归一化是img / 255.0缩放到[0,1]。在部署到TensorRT等平台时预处理管道必须完全复现这个操作。通道顺序PyTorch模型通常期望输入为[B, C, H, W]且是RGB顺序。从OpenCV读取的图像是BGR顺序和[H, W, C]布局需要进行转换。一个常见的部署错误是预处理不一致导致模型性能急剧下降。我的经验是将训练数据加载和预处理的代码片段单独保存在部署时严格复用或者使用ONNX Runtime/TensorRT提供的预处理库来保证一致性。7. 从结构理解出发的调优思路最后基于对模型结构的理解分享几个实用的调优方向这比盲目调参更有效。感受野与目标尺寸如果你的数据集中目标普遍偏大或偏小可以调整Backbone和Neck。对于小目标可以尝试使用更浅层更高分辨率的特征图进行检测如在PANet中增加一个来自更早层的特征或者减少下采样次数但这会大幅增加计算量。对于大目标确保深层特征如SPPF后的特征有足够大的感受野。特征融合方式PANet的“双向融合”已经很强但仍有改进空间。可以实验添加注意力机制如CBAM、ECA-Net到融合路径上让网络更关注有用的特征通道和空间位置。通常加在C3模块之后或Concat之前。检测头优化YOLOv5的耦合头速度快但精度有提升空间。可以尝试替换为解耦头Decoupled Head即用几个独立的卷积层分别预测分类和回归。这通常会带来1-2个点的mAP提升但会轻微增加推理时间。社区已有许多将YOLOX解耦头移植到YOLOv5的方案。激活函数与归一化YOLOv5默认使用SiLUSwish激活和BatchNorm。可以尝试Mish激活可能提升精度但更耗计算或将BatchNorm替换为GroupNorm在小batch size下更稳定。这些改动需要在common.py的Conv等模块中修改。轻量化对于边缘部署可以考虑使用更高效的模块如将部分C3替换为GhostBottleneck来自GhostNet或者使用深度可分离卷积Depthwise Separable Conv来构建轻量版C3。核心思想是在保持特征提取能力的同时大幅减少参数和计算量。记住任何结构修改都需要通过充分的实验来验证。从一个预训练模型开始进行微调Fine-tuning通常比从头训练一个结构大改的模型更快、更稳定。理解YOLOv5的模型结构就像是拿到了这座大厦的建筑图纸之后无论是装修、加固还是加盖新楼层你都能心中有数手中有术。
返回列表