ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

图解YOLOv5网络结构:从Backbone到Detect的完整拆解

图解YOLOv5网络结构:从Backbone到Detect的完整拆解 YOLOv5这个项目我前前后后啃了好几遍源码也拿它训练过几个自己的数据集。说实话网上讲YOLOv5结构文章不少但很多要么贴一堆公式让人劝退要么就丢一张大图让读者自己看。这次我用图解的方式把YOLOv5的结构彻底拆一遍从整体pipeline到每个模块的输入输出再到最后Detect层怎么从特征图变成预测框一条线讲清楚。不管你是准备拿YOLOv5训练自己的数据集还是想在Jetson Nano上做部署又或者只是想搞懂backbone、neck、head到底是什么这篇文章都能给你一个清晰的地图。内容会偏原理和理解很多是从源码里抠出来的细节读完之后你再去看官方yaml文件会感觉通透很多。1. YOLOv5的整体架构与设计思路1.1 从宏观到微观YOLOv5是如何组织一张图的理解YOLOv5的结构有个很取巧的方法先把整个网络当成一个黑盒搞清楚数据从进去到出来经历了哪些阶段再去盯着每个模块看内部结构。这个方法论不仅适用于YOLOv5也适用于绝大多数目标检测网络模型。我先用文字把这个黑盒画出来你脑海里先有个全局图输入图像(640x640x3) → Backbone(骨干网络) → Neck(特征融合) → Head(检测头) → 输出预测就是这么个三段式结构。Backbone负责提取特征Neck负责把不同尺度的特征进行融合Head负责最终分类和回归。后面我会对每一段单独拆开来讲。为什么要这样设计你想想人类是怎么看图的。我们看一张照片第一眼就能捕捉到整体轮廓——这是一条路有几辆车天气怎么样这是大尺度的全局信息。然后目光再聚焦到细节——这辆车是什么颜色车牌号是什么这是小尺度的局部信息。目标检测网络也需要同时拥有这两种能力既要知道图里大致有什么东西感受野要大又要知道东西具体在哪、具体长什么样分辨率要细。但卷积神经网络有个天然矛盾网络越深感受野越大但特征图分辨率越来越小位置信息丢失得越来越厉害。YOLOv5的Backbone就是在不断下采样把640x640的图压缩到20x20得到高语义、低分辨率特征Neck就是干融合的活把高层语义信息和底层空间细节拼在一起Head再在融合后的多尺度特征图上做预测兼顾了不同大小的目标。整个设计逻辑就是这样一环扣一环的不是随便堆层数。1.2 为什么选择YOLOv5作为分析和学习的对象我先坦白YOLOv5不是精度最高的检测器也不是速度最快的检测器但它是很多人入坑目标检测的喊“真香”的框架而且特别适合用来学结构。原因有几个第一代码可读性强结构清晰。官方仓库把整个网络拆成了yaml文件加模块化组件yaml文件是“搭积木的图纸”common.py里是“积木零件”yolo.py负责“把积木拼起来”这种设计哲学是Ultralytics版YOLO最大的优点之一。你去读其他一些检测框架的源码可能要在几个文件之间来回跳但YOLOv5的代码组织方式对初学者极其友好。第二工程化程度很高。它几乎把训练、验证、导出、部署所有环节都写好了甚至包含了自动anchor计算、超参数进化这些功能。这也解释了为什么“yolov5训练自己的数据集”这个搜索热词常年居高不下。第三它的结构演化史就是目标检测技术迭代的缩影。YOLOv5的Backbone里有CSPNet的思路Neck里有FPNPANet的思路Head里继承了YOLOv3的anchor-based检测方式你把它读透了也就把现代单阶段检测器主流的“套路”摸清了。之后你再去看YOLOv8、YOLOv9甚至其他系列都会轻松很多。有一点我得先说明YOLOv5官方仓库迭代很快不同版本结构细节有差异比如v5.0和v6.0的Backbone中第一层就从Focus换成了6x6卷积。这篇文章以目前常用的v6.0/v6.1/v7.0版本的默认yolov5s结构为基准来讲遇到差异我会特别指出来。2. 核心模块逐个拆解Backbone、Neck、Head2.1 Backbone特征提取的主干道Backbone是整个网络的地基。YOLOv5s的Backbone由这些模块构成一个最初的下采样卷积旧版本是Focus新版本是6x6卷积、多个CBS模块ConvBNSiLU、多个C3模块CSP瓶颈结构和一个SPPF模块。我说的“下采样”你可以理解为逐步压缩图像尺寸的过程。初始图像是640x640第一层之后变320x320之后160x160、80x80、40x40、20x20一共下采样了5次。这里有个非常重要的数字640除以2的5次方等于20YOLOv5最终的特征图尺寸是输入尺寸的1/32。你后面自己改输入尺寸的时候要保证能被32整除否则会报错或者模型形状不匹配这个细节我踩过坑。CBS模块CBS就是 Conv BatchNorm SiLU激活函数 三个操作打包在一起的组合模块。虽然名字很朴素但它是YOLOv5最基础的零件几乎到处都是。为啥不用ReLU而用SiLU因为SiLU函数是x乘以sigmoid(x)它不像ReLU在负数区域直接一刀切为0而是让非常小的负值有一个非常小的梯度这个微小差异在深层网络中会积累实测用SiLU收敛会更平稳一些。C3模块C3是YOLOv5从CSPNet借鉴后改造的核心模块。它把输入分成两个分支一个分支经过若干个Bottleneck残差瓶颈结构另一个分支直接绕过去最后concat拼接在一起。这个设计思路有点像高速公路的“分流汇合”一部分车走主路做深度变换一部分车走辅路保持不变最后在出口汇合。为什么要这么干如果不分流所有信息都必须经过串联的卷积层这既增加了参数量又可能导致梯度消失。CSP结构通过“旁路直连”让原始信息无损地传到后面主干分支只需要专注学习残差部分这样网络在加深的同时不至于难以优化计算量也能降下来。SPPF模块SPPF是Spatial Pyramid Pooling - Fast的缩写。官方在v5.0版本用SPPF替代了原来的SPP模块。两者的核心思想一致用不同尺寸的池化核并行处理获得不同感受野的特征然后把它们拼在一起。YOLOv5里的实现是连续用多个5x5的MaxPool串联等效于同时获得了5x5、9x9、13x13三种池化视野但计算量比SPP原版要小很多。在实际观察中SPPF对提升感受野范围、适应不同尺寸目标很有帮助尤其是当目标在图像中占据面积差异很大时这个模块的作用会比较明显。2.2 Neck特征融合的信息立交桥如果你只用一个20x20的特征图去做检测会发现小目标经常漏检因为20x20的分辨率太粗了小目标的那几个像素早就被“抹平”了。所以现代检测器都会加一个Neck模块把不同尺度的特征融合起来YOLOv5用的是FPN PANet结构。这一层我用一张文字图来表示Backbone输出 | 80x80(C3) ---- | | 40x40(C3) -------- FPN自顶向下融合 | | 20x20(C3) -------- PANet自底向上融合 | | Detect Head(3个尺度)FPNFeature Pyramid Network做的事情是自顶向下的把20x20这种高层特征上采样和40x40、80x80这种低层特征逐元素相加或concat这样子小目标的特征图里就混入了高层语义信息知道“我看到的可能是什么”。PANet在FPN的基础上又加了自底向上的路径把80x80的特征往下融合。这有啥用FPN只传递了语义信息往下走但位置信息从底层向高层的传递其实还不够充分。PANet相当于修了一条“反向匝道”让底层精确的位置信息也能流到高层检测头去。这条额外的路径对定位精度的提升是实打实的。YOLOv5的Neck里也有C3模块还多了一个叫C3的变体——在v6.0及以后Neck中用于融合的模块实际上是C3只不过部分版本里编写代码时会特别区分。你只需要知道Neck里的卷积结构比Backbone里更强调“融合”它处理的不是单张特征图而是两张甚至更多特征图的组合。2.3 Head从特征图到预测框的最后一步Head是整个网络预测输出的地方。YOLOv5的Detect层接收Neck输出的3个不同尺度的特征图分别是80x80、40x40、20x20对应检测小目标、中目标、大目标。每个尺度的Head做的事情是对每个网格位置输出3个anchor对应的预测结果分别是类别概率、目标置信度objectness以及边界框坐标x, y, w, h。如果一个特征图是80x80那就意味着把图像划分成了80x806400个网格每个网格又预测3个anchor一共19200个候选框。三个尺度加起来有25200个候选框。你想想一张图里通常就几个目标所以后处理阶段还要做NMS非极大值抑制把这25200个框过滤到最后几个真正有用的框。Detect层输出的shape计算方式如下对于80x80的特征图输出shape是[batch_size, 3, 80, 80, 85]其中3是anchor数85是4个坐标 1个置信度 80个类别COCO数据集。对于40x40是[batch_size, 3, 40, 40, 85]20x20是[batch_size, 3, 20, 20, 85]。三个shape不同所以实际代码里会通过view和permute把它们规整到同样的[batch_size, num_anchors, 85]格式再继续处理。Head里有两点最值得关注的设计细节第一anchor预设值。YOLOv5在训练前会自动用K-Means聚类的思想在数据集上计算anchor的宽高比。比如在COCO上的初始anchor是[10,13, 16,30, 33,23]等9组值各自分配给不同尺度的检测头。为什么不用固定的预设值因为不同数据集中目标的长宽比差异很大比如行人检测里目标普遍是瘦长的车牌识别里目标普遍是扁宽的用固定anchor效果会很差。第二解耦与耦合的问题。YOLOv5的Head是耦合的也就是分类和回归共享同一个特征层这在后期版本YOLOv8等中改成了解耦头。耦合头的好处是结构简单、计算量小但在某些精度要求极高的场景下分类和回归可能互相干扰。这个是结构演进的话题了先不展开你只需要知道YOLOv5这里选的是一条兼顾速度和易用性的路线。2.4 关键设计anchor机制、损失函数与数据增强Anchor机制YOLOv5依然是anchor-based的方法。它的训练阶段有一个“正样本匹配”的过程对于每个真实目标框计算它与9组anchor的宽高比如果比例在阈值范围内就认为这个anchor“配得上”这个目标对应的网格位置就需要去预测它。这个过程在源码里叫build_targets是整个训练逻辑里比较绕的部分。举个例子如果真实框的宽是100像素、高是50像素而某一组anchor是[116, 90]宽高比接近比例是0.86在0.25到4的阈值区间内那么这个anchor就是正样本。每个真实框可能会匹配到多个尺度的多个anchor这个“一真多匹”策略是YOLOv5能学得比较好的关键之一。损失函数YOLOv5的损失函数分成三部分分类损失、置信度损失、定位损失。分类损失用的是BCEWithLogitsLoss二分类交叉熵注意不是softmax多分类。每个类别都当成独立的二分类问题因为COCO数据集中一个目标可能同时是“人”和“行人”用sigmoid可以支持多标签分类。置信度损失同样用BCEWithLogitsLoss。这里有个细节YOLOv5给置信度损失加了一个平衡因子正样本区域和负样本区域的权重不同避免背景区域太多导致模型“躺平”只预测背景。定位损失用的是CIoU Lossv6.0之前是GIoU。CIoU同时考虑了重叠面积、中心点距离和长宽比三个维度比传统的IoU Loss收敛更稳定。我在训练自己的数据集时对比过用CIoU训练出来的框通常更紧实不容易出现那种“框住了但明显细节不够好”的情况。数据增强YOLOv5在训练时默认开启了Mosaic增强就是把4张图拼成1张图喂给网络。这个操作有多重好处增加了目标尺度的多样性、提高了小目标的样本量、变相增加了batch size的多样性。此外还有随机仿射变换、HSV色域增强、随机翻转等。但也要注意Mosaic增强在某些场景下可能帮倒忙。比如你做工业质检产品的位置和姿态几乎是固定的你硬要把图片拼在一起、随机旋转反而会让模型学到偏离实际的分布。我自己在做工业零件检测时就把Mosaic关闭了效果反而更好。这就是理解结构对实际调参的意义。3. 实操视角下的结构分析从yaml配置文件到实际训练建议3.1 一步步读懂yolov5s.yaml我当初刚接触YOLOv5时看到yaml文件里一堆数字也是一脸懵。后来发现这个东西其实是结构分析最好的入口。下面这个是加了详细注释的版本理解方式nc: 80 # 类别数COCO是80类自定义数据集要改成你自己的类别数 depth_multiple: 0.33 # 控制网络深度C3模块数量 width_multiple: 0.50 # 控制网络宽度通道数 anchors: # 9组anchor按从小到大排序分别分配给3个检测层 - [10,13, 16,30, 33,23] # P3/8小目标 - [30,61, 62,45, 59,119] # P4/16中目标 - [116,90, 156,198, 373,326] # P5/32大目标 backbone: - [-1, 1, Conv, [64, 6, 2, 2]] # 初始卷积输出64通道6x6核步长2 - [-1, 1, Conv, [128, 3, 2]] # P4/8输出128通道 - [-1, 3, C3, [128]] - [-1, 1, Conv, [256, 3, 2]] # P3/8 - [-1, 6, C3, [256]] - [-1, 1, Conv, [512, 3, 2]] # P4/16 - [-1, 9, C3, [512]] - [-1, 1, Conv, [1024, 3, 2]] # P5/32 - [-1, 3, C3, [1024]] - [-1, 1, SPPF, [1024, 5]] # SPPF模块输出1024通道 head: - [-1, 1, Conv, [512, 1, 1]] - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] # 与backbone第6层输出做concat - [-1, 3, C3, [512, False]] # 这里的False表示不使用shortcut # 后续还有类似的上采样和concat操作yaml文件里[-1, 1, Conv, [64, 6, 2, 2]]的意思是输入来自上一层-1表示上一层重复1次模块类型是Conv参数是输出通道64、卷积核大小6、步长2、padding 2。depth_multiple和width_multiple的作用就是让这一个yaml模板适配s/m/l/x不同大小比如深度乘0.33意味着C3里Bottleneck数量只有标准的1/3宽度乘0.5意味着通道数减半。为什么同一个yaml文件能用于s/m/l/x这其实是YOLOv5一种很取巧的设计。你在models/yolov5m.yaml等文件里看到的其实是一模一样的结构描述只是depth_multiple和width_multiple不同。比如yolov5m是0.67和0.75yolov5l是1.0和1.0yolov5x是1.33和1.25。这样做的好处是结构代码只维护一份模型大小通过参数统一控制。3.2 不同版本模型的结构差异对比我整理了一张表把YOLOv5s/m/l/x的关键指标列出来方便你在选型时有个感性认知数值基于COCO数据集不同版本会有细微差异模型depth_multiplewidth_multiple参数量(M)计算量(GFLOPs)推理速度(V100, ms)COCO mAPYOLOv5s0.330.507.317.02.437.4YOLOv5m0.670.7521.451.33.845.4YOLOv5l1.01.047.0115.46.149.0YOLOv5x1.331.2587.7218.89.950.7选择哪个版本完全取决于你的场景。Jetson Nano这种边缘设备上跑YOLOv5s都要喘口气你用YOLOv5x就是找不痛快反过来你离线做高精度检测追求极致mAPYOLOv5l和x才值得尝试。核心结论是不要一上来就追求大模型先在YOLOv5s把上跑通整个流程再根据性能瓶颈决定是否换更大的版本。3.3 结构理解对训练和部署的实际指导意义理解了结构之后你会发现很多训练时的“怪现象”其实都有了解释。为什么小目标检测效果差因为Backbone下采样5次之后20x20的特征图上一个小目标可能只有不到1个像素的信息量了。虽然Neck尝试把80x80的底层特征融合上来但信息损失已经发生。你要么提升输入分辨率比如从640提到1280要么在anchor匹配时对小目标更宽容一些。为什么遇到大尺寸图像会显存溢出输入分辨率是平方级影响显存占用的。640x640输入和1280x1280输入计算量差了4倍显存占用几乎也是4倍的关系。如果显存不够不要只想着换小模型先看看是不是输入分辨率定得虚高。为什么自定义数据集训练效果不理想一个高频原因是anchor没有重新计算。YOLOv5虽然训练时会自动算anchor但如果你数据集中目标尺寸极其单一或极端默认anchor的初始值离真实分布太远训练前期会浪费大量时间在“纠正”anchor上。建议用train.py里带--noautoanchor前先跑一下utils/autoanchor.py重新聚类。部署层面的指导意义更直接。如果你要把模型导出为TorchScript、ONNX或TensorRT你需要知道YOLOv5的Detect层在推理时会把训练用的解码操作剥离掉模型输出的已经是解码后的坐标。这意味着导出时要用model.model[-1].export True之类的操作避免把训练逻辑带进部署模型里。这也是很多人在TensorRT部署YOLOv5时遇到维度对不上的根本原因。4. 常见问题与排查技巧实录4.1 高频踩坑与解决方案速查表这些年接触YOLOv5的过程中我积累了一些高频问题的排查经验下面用表格整理出来方便你快速定位现象可能原因排查与解决思路训练loss下降但mAP不涨数据增强过强、anchor设置不合理、学习率过高关闭Mosaic或降低增强强度用autoanchor重新计算anchor适当降低初始学习率检测小目标效果极差输入分辨率低、小目标样本不足、anchor匹配不到提高输入尺寸如改为960或1280用切片方式训练检查anchor最小尺寸是否覆盖目标显存充足但训练OOM开启了缓存图片、batch size过大、图像尺寸不统一关闭--cache减小batch size设置统一的rect训练模式导出ONNX后输出维度不对Detec层的export标志未设置在导出脚本中设置model.model[-1].export TrueJetson Nano推理速度极慢模型尺寸过大、没有使用TensorRT换成yolov5s或更小用TensorRT做FP16量化输入尺寸可以降到544甚至416自定义数据集loss正常但检测不到目标标签格式错误、类别索引从0开始没对齐用utils/plots.py的标注可视化功能检查yolo格式是否正确训练到一半accident_loss变成nan学习率太大、图片存在全黑全白等极端样本、loss权重异常调低学习率删除极端样本检查标签是否有越界坐标4.2 判断结构哪里出问题的方法论结构理解如果只停留在“能看懂”层面意义不大真正厉害的是能用结构分析的思维去定位问题。我分享一个我常用的排查思路算是一个方法论。第一确定问题范围。先搞清楚是训练阶段出问题还是推理阶段出问题。训练阶段loss不降、mAP不升多半是数据或优化问题训练正常但推理结果差可能是预处理不一致、后处理有问题或模型转换出问题。第二验证每一层输出。如果怀疑网络结构有改动导致的问题你可以在PyTorch里注册hook逐层打印中间特征图的shape和数值分布。这是最直接的“结构排错法”。我曾经改过C3模块里的Bottleneck数量结果发现某层输出shape对不上用hook一查就定位到了是concat时通道数不匹配。第三用小规模数据快速验证。如果你改了结构强烈建议先用少量图片跑1-2个epoch看loss是否正常下降。不要一上来就全量训练那样你根本无法分辨是结构问题还是数据问题。第四可视化中间特征图。YOLOv5里其实没直接提供特征图可视化工具但你可以自己写一个小脚本把某一层的输出保存成图片。你会发现一个非常有用的现象浅层特征图对边缘、纹理响应强深层特征图对目标整体响应强。如果深层特征图什么都看不见或者全是噪声说明前面的结构很可能有问题。4.3 几个我亲历的经验性“旁门左道”以下内容属于我个人在实际项目中摸索出来的经验不一定出现在官方文档里但都是真金白银踩出来的关于SPPF和SPP的选择。如果你是自己在YOLOv5上魔改结构想用SPP替换SPPF精度差别不会很大但SPPF速度略快。Jetson Nano这种边缘设备上尽量保留SPPF能省一点是一点。关于C3模块中shortcut的使用。在C3模块中如果Bottleneck的输入输出通道数相同默认会开启残差连接如果通道数变化了shortcut会自动关闭。这是我读源码时发现的细节很多人忽略。如果你想魔改C3来减少参数量一定要注意这个通道数是否匹配的问题。关于图像尺寸的“32倍数”铁律。YOLOv5要求输入图片宽高都能被32整除原因是模型里有5次步长2的下采样2的5次方等于32。如果你用的尺寸是640x640自然满足但如果你图省事直接resize成800x600600不能被32整除很多操作会出问题。遇到shape mismatch时第一反应先检查这个。关于超参数文件的理解。hyp.scratch-low.yaml里有很多看似无关紧要的参数比如hsv_h、fl_gamma这些数据增强参数还有box、cls、obj三个loss的权重。调优时有个更稳妥的方向是让loss权重尽量平衡比单纯去调学习率更有效。我见过一个项目mAP卡在0.7上不去把obj loss权重从0.7调整到1.0后两天之内mAP就涨了3个点。关于模型部署时的检测头处理。YOLOv5的模型文件在训练时输出的预测结果shape是[batch, 25200, 85]以COCO为例这是已经把所有尺度和anchor展开后的结果。但如果你把它导出为ONNX再转TensorRT一定要确认导出的模型是不是包含了后处理NMS。官方仓库同时提供了带NMS和不带NMS的导出方式用--end2end或--nms之类的选项控制按需取用。部署时经常有人在这个环节纠结半天本质上还是因为对Detect层的输入输出关系理解不透。5. 从结构理解到实际项目落地的心得如果你读到这里说明你真的想把YOLOv5搞透而不只是跑通一个demo。在结束这篇文章之前我把自己从理解结构到完成项目落地这个过程的心得分享出来。消融实验一定要做。很多人在YOLOv5上做改进改完结构就直接训练效果变差了也不知道是哪个改动导致的。我的习惯是每次只改一个变量跑完一组实验后做对比。比如我想验证“把C3换成某种新模块”是否有效那我会保证其他代码完全不动用相同的数据、相同的超参数只替换这一个模块训练同样的轮次再对比mAP、参数量、推理速度三个指标。有了baseline原始YOLOv5s的结果每一次改动的影响才看得清楚。理解结构的最终目的是“裁剪”。在实际项目中我们用YOLOv5往往不是为了刷榜而是为了在某个硬件上跑起来。Jetson Nano上一个YOLOv5s模型用FP16推理大约能跑到10-15FPS左右如果业务要求30FPS你就需要考虑裁剪方案。哪些地方可以裁剪呢根据结构来分析减小width_multiple通道数、减少C3模块里的Bottleneck数量、去掉SPPF里最后一个池化层、缩小输入尺寸。这些都是结构层面的“手术”每条的影响范围在动手前就应该大致知道。这一步是画龙点睛一样的环节很多人以为裁剪就是换个小模型其实换模型和你主动从结构层面去修剪效果完全不同。关注结构变化带来的蝴蝶效应。比如你把Backbone的感受野改小了也许在某些数据集上精度反而上升了因为感受野太大看到太多背景噪声反而影响判断。又比如你把Neck里concat改成add参数量确实降了但不同尺度特征融合的方式变了小目标检测能力可能下降。这就是结构分析的价值——不是背下来每层的名字而是理解每个设计选择背后的权衡然后在自己的项目中做出最合理的取舍。YOLOv5整体的结构设计核心思路就四个字复用与权衡。它复用了CSPNet、FPN、PANet这些被验证过的思路又在速度、精度、易用性之间找准了自己的定位。真正弄懂YOLOv5之后你看YOLOv8那些“高大上”的新结构会发现核心思路依然一脉相承——解耦头、anchor-free、C2f模块这些改进都是在YOLOv5这套框架上做的增量优化。地基打牢了后续的学习都会顺理成章。最后再分享一个小技巧源码阅读不要只盯着一行一行看先把models/yolo.py里的parse_model函数读明白。这个函数是拆解结构与实际模型文件的“翻译官”它逐行解析yaml里的每个描述词然后把对应的模块实例化。读懂了这个函数你就不会再觉得“yaml和PyTorch模型是两个世界的东西”了整个YOLOv5的结构对你来说会是一张完全透明的地图。我在实际使用中的体会是所有对模型结构的自信最后都来源于对parse_model和common.py这两个文件的理解深度这是接下来你可以去攻克的最佳方向。
返回列表