ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLOv5目标检测全解:从网络结构到训练调参与边缘部署

YOLOv5目标检测全解:从网络结构到训练调参与边缘部署 做目标检测的十有八九入门第一个跑通的模型就是YOLOv5。GitHub上的star数、教程数量、行业落地案例基本把这个框架顶到了“新手必学、老手常用”的位置。很多做毕设、做比赛、做工程项目的朋友也都是从YOLOv5开始接触“目标检测算法”这条线的。这篇文章我就把自己从原理到实战踩过的坑、总结的经验一次性倒出来从网络结构到损失函数从训练自己的数据集到边缘设备部署争取一篇讲透。刚开始接触YOLOv5的时候很多人容易把它当成一个“黑盒”——数据丢进去权重下载下来train.py一跑loss往下掉最后出个mAP就完事。但真到换数据集、换场景、调精度的时候不懂原理的人会特别被动。比如为什么小目标检测效果差为什么训练到一半损失变成nan为什么别人mAP能到90你只有60这些问题光靠调参是解决不了的你得先明白YOLOv5内部的工作机制。这篇文章适合谁准备入门目标检测的学生、刚转到算法岗的新人、以及已经跑通YOLOv5但想系统梳理一遍的朋友。我不会把每行源码都贴出来但会把核心模块的原理、代码层面的对应关系、训练时真正有用的参数讲清楚附带我实际项目里积累的排查方法和调优习惯。1. YOLOv5到底解决了什么问题1.1 从R-CNN到YOLO目标检测的两条技术路线目标检测的任务说起来很简单给一张图找出里面有哪些物体分别在哪框出来标上类别。但实现方式差别很大演化到今天主要有两条路线。一条是两阶段检测器代表就是R-CNN系列Fast R-CNN、Faster R-CNN。这类算法先通过区域提议网络生成一堆候选框再对每个候选框做分类和回归。思路很直观先猜可能哪里有物体再仔细确认。好处是精度比较高坏处是速度上不去一张图要过两遍网络实时性很难保证。另一条是单阶段检测器YOLOYou Only Look Once就是这条线的扛把子。它的核心思想是“一次看全图”把整张图划分成网格每个网格负责预测中心点落在该网格内的目标。这相当于把检测问题直接当成回归问题来做一个网络同时输出类别和位置。速度极快非常适合视频流、嵌入式设备这类实时场景。YOLOv5作为单阶段检测器继承了YOLO家族的快同时通过一系列工程和结构上的改进把精度也拉到了接近两阶段检测器的水平。这就是它能成为工业界“万金油”的根本原因也是为什么那么多人拿它做baseline、做二次开发。1.2 YOLOv5在YOLO家族里的位置YOLO从v1发展到v8中间还有v3、v4为什么v5反而讨论度最高这得从几个方面看。v1和v2太老结构简单但精度有限v3引入了多尺度预测和FPN特征金字塔让检测精度有了质变至今很多项目还在用v3的改进版。v4是Alexey Bochkovskiy在v3基础上做的优化引入了CSPNet和Mish激活函数但它的源码使用Darknet框架对新手不太友好配置环境能劝退一批人。YOLOv5是Ultralytics公司推出的工程化实现准确说它是一个“工程框架”而不是一篇论文官方一直没有发布正式的论文但它的代码质量和易用性远超前辈。开箱即用的训练脚本、丰富的数据增强、完善的部署生态、清晰的文档这些让YOLOv5迅速成了社区首选。我在长期项目里实测下来v5s模型在1080Ti上跑视频流可以稳在60FPS以上精度也比同等计算量下的v3高出一截。后来官方又推出了v8写得更工程化但v5的成熟度和资料数量依然是目前最合适的入门选择。2. 网络结构与核心机制拆解2.1 主干网络CSPDarknet特征提取的“骨架”YOLOv5的主干网络叫CSPDarknet思路源自CSPNetCross Stage Partial Network。你可以把它理解成“一个把特征图逐层变深变小的特征提取器”。图像输入后经过若干卷积和池化得到越来越抽象、通道数越来越多的特征图。CSPNet的核心贡献在于“跨阶段局部连接”把输入特征分成两部分一部分走正常的卷积堆叠另一部分直接和前面的特征拼接。这样做的目的是减少重复梯度计算降低参数量同时保证特征的复用性实测可以让网络在相同精度下减少约20%的计算量训练内存也降下来了。在YOLOv5的代码里你会看到BottleneckCSP和C3两种残差模块。v5早期版本用的是BottleneckCSP后面官方把部分模块换成了C3结构上简化了BN和激活函数的重复使用推理速度更快。这些细节对普通用户影响不大你只要知道它们是网络的基本积木块就行。主干网络最后会输出三种不同尺寸的特征图分别对应下采样8倍、16倍、32倍。也就是说输入是640x640时会得到80x80、40x40、20x20三张特征图。20x20的特征图感受野最大适合检测大物体80x80感受野最小细节最多适合检测小物体。这就是YOLOv5能同时处理不同大小目标的基础。2.2 颈部网络PANetFPN多尺度特征怎么融合有了三张不同尺寸的特征图接下来要做的是“融合”。为什么要融合因为大尺寸特征图虽然细节多但语义信息不够抽象小尺寸特征图语义强但丢失了太多细节。单独用任何一层效果都不好。YOLOv5的颈部网络采用的是FPNPAN结构。FPN是从上往下的路径把深层的高语义特征逐层传递到浅层让50x50这种小特征图也能拥有强语义信息主要帮助小目标检测。PANet则额外加了一条从下往上的路径把浅层的强定位信息反馈给深层帮助大目标定位更准。这个结构很像城市里修了两条方向相反的高架路信息可以双向流通。实际训练中多尺度融合的好处非常明显。我在做工业零件检测时同一张图里既有几像素的小螺丝也有占画面三分之一的大机箱如果只用单一特征图很难同时兼顾。YOLOv5的三层输出通过FPNPAN融合后小目标和大目标的召回率都有明显提升。2.3 检测头与Anchor预测框是怎么算出来的YOLOv5的检测头把每张特征图划分成网格。比如20x20的特征图就有400个网格每个网格对应原图上的一个区域。每个网格负责预测3个Anchor预设框YOLOv5在COCO数据集上总共聚出了9个不同大小和宽高比的Anchor分配到三层特征图上每层3个。Anchor的作用是给预测一个“初始参考框”。网络不直接回归目标的绝对宽高而是回归相对于Anchor的偏移量和缩放比。这样设计是因为“预测相对值”比“预测绝对值”容易得多。就像你让人猜一个箱子有多大直接说长宽高很难但说“比这个1米参考箱大0.2倍”就直观多了。每个网格最终输出一个向量包含x、y、w、h四个位置参数再加上类别概率和置信度。以80x80层为例输出形状就是80x80x255255对应3个Anchor乘以4180个类别数。这也是为什么换数据集时要重建Anchor因为不同数据集的物体形状分布差异很大预设框不合适训练收敛会明显变慢。2.4 损失函数与后处理训练和推理时的“裁判”YOLOv5的损失函数由三部分组成类别损失、置信度损失和边界框损失。边界框损失用的是CIoU Loss。CIoU相比早期使用的IoU Loss额外考虑了预测框和真实框的中心点距离、宽高比一致性。简单说它不仅关注两个框重叠了多少还关注框中心是否对齐、形状是否接近。这个设计在工程上非常实用比如检测车辆时如果预测框中心偏了但IoU很高普通损失函数可能给很小的惩罚CIoU却能准确反映这种“位置偏差”。置信度损失和类别损失用的都是二元交叉熵BCE分别判断“框里有没有物体”和“物体属于哪一类”。训练过程中不同损失项还有权重系数代码里的box_loss_gain默认是0.05cls_loss_gain默认是0.5这些数值经过大量实验调整保持默认通常是最稳妥的。推理阶段还有一个关键后处理操作——NMS非极大值抑制。因为同一目标周围会有大量预测框重叠NMS会把置信度最高的框保留下来把和它重叠度超过阈值的框都删掉这样每个目标最终只输出一个干净的框。我在做项目时经常需要调整NMS的IoU阈值默认0.45如果目标密集或互相遮挡就适当调低避免多个框叠在一起。3. 从零配置环境到训练自己的数据集3.1 环境配置的版本坑YOLOv5的环境配置是很多人入坑的第一道坎关键词搜索量居高不下。其实它依赖的东西并不复杂Python、PyTorch、CUDA外加几个常见库。问题的核心在于版本匹配。我的建议是直接用官方requirements.txt装依赖Python版本选3.8或3.9PyTorch版本跟着CUDA版本走。新手最容易犯的错是装了对不上号的PyTorch和CUDA导致torch.cuda.is_available()返回False代码也能跑但只能用CPU速度慢到你怀疑人生。安装命令本身不难git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果是NVIDIA显卡先确认驱动支持的最高CUDA版本再选对应的PyTorch安装命令比如CUDA 11.8就装pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118。没有独显的也可以用CPU跑模型能跑通但训练时间会非常漫长只建议用来验证代码流程。权重文件下载也是个容易卡住的地方。官网下载链接慢的话可以从国内镜像或别人分享的网盘拿权重文件本体是公开的只要校验一下大小和哈希一致就能用。3.2 手把手做一套自己的数据集标注格式与目录结构YOLOv5要求的数据集格式是每个图片对应一个同名的txt文件txt里每行代表一个目标格式为“类别id x_center y_center width height”注意这里的坐标全部是归一化到0到1的。标注工具我用过LabelImg、labelme、Roboflow最顺手的还是LabelImg因为它直接支持YOLO格式导出。标注的时候有几点经验密集小目标要放大图片再标否则坐标不准遮挡严重的目标尽量框出可见部分把遮挡部分也算进去容易导致训练时边界模糊类别编号从0开始和data.yaml里定义的顺序对应好。目录结构建议这样组织your_dataset/ images/ train/ val/ labels/ train/ val/然后写一个data.yaml内容大致是train: your_dataset/images/train val: your_dataset/images/val nc: 2 names: [cat, dog]这个文件就是告诉YOLOv5你的训练集在哪、验证集在哪、有几类、分别叫什么。一个小小的避坑点路径最好写绝对路径或相对YOLOv5根目录的路径否则运行时容易报AssertionError: train: No labels found八成就是路径没配对。3.3 训练参数与超参数理解别再把所有参数都拉满YOLOv5的训练命令看起来简单参数却不少。我先说几个出场率最高的。--img是输入图片尺寸默认640。尺寸越大能保留的细节越多小目标效果越好但显存和耗时都会上升。我一般建议从640起步如果小目标特别多再尝试800或960。--batch是批大小理论上越大越好但受显存限制。显存不够时优先减小batch不要硬上。如果你在8G显存的卡上训练yolov5sbatch设16通常问题不大设64大概率报CUDA out of memory。--epochs是训练轮数。很多人一上来就追求300轮其实小数据集100轮左右就已经收敛得很好。判断标准是看验证集的mAP是否还在持续上升如果连续二三十轮没有明显波动就早点停。--hyp是超参数文件路径默认是data/hyps/hyp.scratch.yaml。里面关键参数包括初始学习率lr0默认0.01、动量momentum默认0.937、权重衰减weight_decay默认0.0005还有mosaic数据增强概率等。新手建议先不动超参数文件用默认值训练一轮跑出baseline再说。上来就拉大学习率想加速收敛的大概率会看到loss炸掉。3.4 跑起来训练日志、指标含义与模型选型训练命令示例python train.py --data your_dataset.yaml --weights yolov5s.pt --img 640 --epochs 100 --batch 16跑起来以后你会在终端看到类似下面这样的输出Epoch gpu_mem box obj cls labels img_size 99/100 4.21G 0.0217 0.0264 0.0132 82 640上面这些box、obj、cls分别对应边界框损失、置信度损失、类别损失数值持续下降是正常的。真正要看的是每一轮结束后在验证集上计算的指标Precision精确率、Recall召回率、mAP0.5和mAP0.5:0.95。mAP0.5表示IoU阈值取0.5时的平均精度指标比较宽松适合快速判断模型效果mAP0.5:0.95是IoU从0.5到0.95按0.05步长取平均指标更严格更能反映定位精度。我做项目汇报时一般两个指标都看前者给业务方交代后者作为算法内部优化的衡量标准。模型选型方面YOLOv5有s/m/l/x四个版本精度依次提升、速度依次下降。还有一个很好的经验法则先跑v5s确认数据和流程没问题再根据耗时预算考虑换更大的模型。很多时候v5s效果已经够了不要盲目追求大模型。4. 常见问题与排查技巧实录4.1 训练不收敛或Loss变成nan这是刚接触目标检测的人最常遇到的问题之一很多人第一反应是调小学习率但问题往往没那么简单。我先列一个排查清单现象可能原因处理方式Loss刚开始很大然后骤降为0标签文件有问题检查txt里坐标是否越界类别id是否超出ncLoss不降反升学习率太大调低lr0从0.001开始重新训练Loss直接是nan数据存在异常值或显存溢出检查数据集里是否有损坏图片查看梯度是否爆炸mAP一直为0类别编号或配置文件对不上打开图片和标签可视化检查确认标注正确我遇到过最离谱的一次是数据集里混入了一张全黑图片标签还标了一堆目标训练出来的模型对真实图片完全无感。后来排查出来把问题图片删除后重新训练一切正常。所以养成习惯第一次训练前先写个脚本把标注结果画在图片上肉眼过一遍能省下大量Debug时间。4.2 小目标检测效果差的3个解决思路小目标检测是目标检测领域的长期难题红外小目标、航拍图像、监控远距离目标都是典型场景。YOLOv5的80x80特征图理论上已经能捕捉小目标但实际效果往往不理想。我的经验是优先做三件事第一提高输入分辨率。把--img从640提高到960或1280相当于给小目标更多像素去表达特征效果立竿见影但显存消耗也变大需要配合减小batch。第二开启多尺度训练。YOLOv5训练时本来就会按--multi-scale参数随机缩放输入尺寸如果你关闭了这个功能可以打开它让网络见过各种尺度下的目标泛化能力更强。代价是训练时间变长。第三把大图切块训练。如果目标本身就很小直接整图训练等于让网络在很小的区域里找特征。把原图切成多个有重叠的patch分别训练和推理最后再做结果合并这是很多遥感检测和医学图像检测项目中最实用的技巧。4.3 模型部署到Jetson Nano等边缘设备很多人训练完模型不是部署在服务器上而是要跑到Jetson Nano、树莓派、手机这种算力受限的设备上。YOLOv5在这方面的生态非常完善。最常用的路径是导出为TensorRT引擎。先在PC上将PyTorch模型导出为ONNX再到Jetson上转换成TensorRT的engine格式。精度损失可以控制在1%以内速度却能快上好几倍。以yolov5s为例在Jetson Nano上纯PyTorch推理大概只有1到2 FPS转成TensorRT的FP16模型后能到10 FPS左右基本满足轻量级实时检测需求。导出命令很简单python export.py --weights best.pt --include onnx engine --half --device 0注意--half表示使用FP16半精度推理速度更快但偶尔会有精度损失。如果模型对精度特别敏感可以先不开启对比一下结果再决定。另外TensorRT引擎是跟硬件绑定的在Jetson上生成的engine文件不能随便拷贝到另一款显卡上用换设备就得重新导出。4.4 训练时的“老朋友”显存不足与训练中断CUDA out of memory是每台训练机器上都出现过的报错。除了降低batch还有一个更优雅的解法开启梯度累积。YOLOv5没有直接暴露这个参数但可以通过修改训练脚本来实现或者直接把batch降到能跑的大小效果差别不会太大。训练中断是另一个让人抓狂的问题。YOLOv5默认每个epoch结束都会保存last.pt中断后直接加--resume参数续训就行python train.py --resume runs/train/exp/weights/last.pt我在公司训练一个大型数据集时经常跑到半夜断电有了断点续训第二天早上起来恢复训练就行不会白费之前的算力。另外建议保存best.pt后再额外复制一份到别的目录防止存放模型的磁盘满了导致保存失败。4.5 数据不平衡与类别数量悬殊自己做数据集难免遇到类别数量严重失衡的情况。比如检测两类物体A类有5000个标注框B类只有200个模型很容易把所有目标都预测成A类B类的召回率惨不忍睹。解决办法有几个一是在损失函数层面做处理YOLOv5的类别损失权重cls_pw参数可以在超参数文件里调整把样本少的类别权重加大二是做数据增强对B类图片做更多样的变换旋转、翻转、色彩抖动来扩充数量三是干脆先采用“欠采样”策略让A类和B类图像比例接近1比1。注意欠采样不是丢弃像素而是让每个epoch中模型见到的样本分布更均衡。我在做工厂瑕疵检测时就用过这个思路原本合格品和瑕疵品比例接近50比1强行训练出来的模型几乎检测不到瑕疵。后来把合格品图片做了欠采样、对瑕疵图片做了大量增强把比例拉到5比1mAP提升非常明显。5. 训练技巧与项目应用心得5.1 数据增强Mosaic和它背后的哲学YOLOv5训练时默认启用Mosaic数据增强这是个非常有意思的设计把4张训练图片随机缩放、拼接成一张新图片喂给网络。好处太多了一方面变相增大了batch的多样性另一方面因为拼接过程中目标被随机截断和缩放网络被迫学到更鲁棒的特征对遮挡、小目标都更友好。另外还有MixUp增强把两张图片按比例混合。我个人的习惯是默认配置下先跑一个baseline如果发现泛化性能不够再逐步增强数据增强的强度而不是上来就把所有增强都拉满。增强太猛会导致训练集分布和真实场景偏差太大反而让模型在验证集上表现变差。5.2 迁移学习为什么总是从COCO预训练权重开始YOLOv5官方提供的yolov5s.pt、yolov5m.pt这些权重都是在COCO数据集上预训练过的。COCO有80个类别、超过20万张图片模型从里面学到了非常丰富的通用视觉特征。当我们训练自己的数据集时把这些特征迁移过来通常比从头训练效果好得多收敛也快得多。尤其当你的数据集很小比如只有几百张图从头训练非常容易过拟合用预训练权重做微调几乎是唯一靠谱的选择。代码里默认就是这么设计的python train.py --data your_dataset.yaml --weights yolov5s.pt当你第二次训练同样的数据集比如想换个更大的模型别贪图方便继续用官方预训练权重。应该用上一次训练得到的best.pt来初始化因为它的特征分布已经接近你的数据效果会更好。这个细节很多人会忽略。5.3 用YOLOv5做具体项目的基本流程把前面的东西串起来我拿一个“基于YOLOv5的水果识别”项目举例完整流程是这样的第一步数据采集。拍或下载各种水果图片覆盖不同光照、角度、遮挡情况。第二步数据清洗。删掉模糊、严重遮挡、类别不清晰的图片。第三步标注。用LabelImg把苹果、香蕉、橙子框出来导出YOLO格式。第四步划分数据集。按8比1比1分成训练集、验证集、测试集注意同一场景不同帧的图片不要同时出现在训练和验证集里否则指标虚高。第五步配置data.yaml启动训练。第六步用测试集检验效果可视化预测结果。第七步是部署。把模型导出成ONNX或TensorRT接到摄像头或图片接口上。我通常在树莓派上用ONNX Runtime跑CPU推理精度尚可但速度一般硬件允许的话还是建议上一块推理卡或者NPU设备。5.4 多类别任务与改进方向YOLOv5虽然好用但也不是万能的。比如旋转目标检测遥感图像里的飞机、船舶常常是带角度的、密集小目标检测、视频中的时序信息利用这些任务直接套YOLOv5效果会打折。好在社区有很多改进方案可以参考比如加注意力机制SE、CBAM、CA、更换检测头、加入Transformer结构等等。我自己试过在YOLOv5里加SE注意力模块训练小目标数据集时mAP提升了一两个点。但注意任何改进都要在同等参数和计算量下做对比实验否则很难判断是结构改进带来的收益还是单纯因为模型变大了。还有一点值得提醒如果只是想把检测精度提上去先别急着改网络结构。优先把数据质量、标注一致性、超参数调优做扎实效果提升往往比改结构更明显。我在多个项目里反复验证过干净的数据集加合理的超参数能比脏数据集加大模型多出5个点以上的mAP。写在最后做了这么多年目标检测YOLOv5在我这始终是首选baseline。它可能不是每一项目标检测算法里精度最高的也不是速度最快的但它把“易用性”这三个字做到了极致。从这个模型入手你能完整地经历数据标注、模型训练、调优、部署全流程这套能力放到YOLOv8、YOLOv9以及其他检测模型上都依然适用。最后分享一个我在实际项目里的小习惯每次训练完不要只看mAP随机挑几张验证集图片把预测框和GT框一起画出来看。有时候指标不错但预测框边缘明显偏大或偏移这些问题从指标上看不出来却恰恰是业务方最在意的。模型不是跑完训练就结束了能把结果解释清楚、把边界情况处理好才算是真正掌握了目标检测。
返回列表