
很多刚接触目标检测的朋友都有类似的困惑网上教程一堆但真正从零开始跑通一个完整流程从标注数据到训练出自己的模型再到实际部署中间总会卡在某个奇怪的地方。我前前后后做了不少检测项目从早期的经典方案到后来的YOLO系列踩过的坑加起来能绕训练集两圈。今天就把基于YOLOv5s的训练全流程从头到尾拆开讲一遍这算是一篇经验向的长文记录适合刚入门想跑通一个项目或者跑通了但对参数和数据流程还比较模糊的朋友。先说结论YOLOv5s是一个速度和精度比较均衡的轻量检测模型显存需求和模型体积都控制得不错哪怕是之前出的一些入门级消费级显卡也能带得动。它解决的问题很直接——给定一张图片或一段视频模型能实时告诉你里面有什么物体以及它们各自的位置用矩形框标出来。这套能力延伸出去就是工业质检、安防监控、交通流量统计、无人零售、农业病虫害识别等一系列真实场景。这篇文章我会按照实际项目推进的顺序来写数据怎么准备、环境怎么搭、配置文件怎么改、训练时怎么观察和调参、训练完怎么评估最后是导出部署和实战检测。中间会穿插大量实操细节和踩坑记录基本属于直接能当操作手册用的那种。1. 数据准备决定上限的关键环节1.1 从哪搞数据怎么整理目录结构很多人一上来就跑去下载现成的公开数据集这没问题COCO、VOC这类经典数据集确实很适合用来跑通流程。但如果是做自己的业务场景比如检测某种型号的螺丝缺陷、识别果园里的特定病虫害公开数据集基本帮不上忙得自己采集和整理。我的建议是第一轮先用公开数据集或者网上能找到的相关数据把整个流程跑通确保代码、环境、训练链路都没问题第二轮再开始收集自己场景的数据精标一批训练一个微型版本试试效果。这样能避免“全家都准备好了才发现环境有问题”的情况。无论数据来源是哪里最终都要整理成YOLOv5能直接读的目录结构。推荐下面这种组织方式dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml也就是说图片和标签分开存放train/val/test三个集合在images和labels下都各有一份。图片是jpg、png这类常见格式标签是txt文件每个图片文件名对应一个同名txt标签文件。图片放images/train/xxx.jpg标签就放labels/train/xxx.txt。文件夹划分比例我一般用8:1:1也就是训练集占八成验证集和测试集各占一成。如果你的数据量特别大几万张以上可以适当调成9:0.5:0.5。数据量少几百张的时候建议先用训练集验证集的划分跑通流程测试集可以暂时从验证集里匀一点或者干脆等数据攒多了再补上。1.2 标注工具选型和格式转换标注是数据准备里最耗时最枯燥的环节。我常用的标注工具是LabelImg和X-AnyLabeling前者是老牌工具界面简单支持Pascal VOC格式和YOLO格式导出后者功能更强一些支持辅助标注和自动标注适合数据量大的场景。说实话如果项目里需要标几千张图纯手工标注是真的会标到崩溃。我个人的经验是先用预训练好的通用检测模型比如YOLOv8n或者YOLOv5s官方权重对图片做一次预标注生成粗略的框然后人工去修正和筛选。这样能把标注时间压缩到原来的三分之一左右尤其是目标类别比较常见人、车、动物等的情况下效果非常明显。标注完导出的时候建议优先导出YOLO格式的txt文件省去后面转换的麻烦。如果标的时候用的是VOC格式xml文件那就需要写个脚本转成YOLO格式。这里贴一个常用的转换脚本import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_file, class_names, output_dir): tree ET.parse(xml_file) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) txt_name os.path.splitext(os.path.basename(xml_file))[0] .txt with open(os.path.join(output_dir, txt_name), w) as f: for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_names: continue cls_id class_names.index(cls_name) box obj.find(bndbox) x_min float(box.find(xmin).text) y_min float(box.find(ymin).text) x_max float(box.find(xmax).text) y_max float(box.find(ymax).text) # 转换为YOLO格式中心点x、中心点y、宽度、高度都归一化到0~1 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n)这里最需要注意的是坐标归一化YOLO格式的坐标是相对于图片宽度和高度的比例值范围在0到1之间不是像素坐标。很多人第一次写转换脚本忘掉除以图片宽高导致训练出来的模型完全没法用损失函数直接不收敛。1.3 数据质量筛选与清洗数据量不是唯一重要的指标数据质量同样关键。我在多次实践中总结出几个必须检查的点标签是否准确框有没有包含太多背景或者切掉了目标的一部分类别是否均衡如果某个类别只有几十张另外的类别有几千张训练出来的模型对少数类会非常不友好图片是否清晰模糊、严重过曝、分辨率过低的图片该删就删是否有重复或近似重复的图片会无意中放大某张图对训练的影响注意网上有很多公开的爬虫数据集拿来做预实验可以但商用前必须确认数据来源的授权情况。数据合规问题在真实项目中非常关键别等上线了再返工。2. 环境搭建版本兼容是最大的坑2.1 Python环境与PyTorch安装YOLOv5官方推荐Python 3.8我用的是Python 3.9和3.10都跑过没有任何问题。环境管理建议用conda隔离干净不污染系统Python环境。conda create -n yolov5 python3.9 conda activate yolov5PyTorch的安装要注意CUDA版本。先用命令查一下自己的显卡驱动支持到哪个版本nvidia-smi查看右上角的CUDA Version然后去PyTorch官网选择对应版本安装。比如CUDA 11.8对应的安装命令pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这里的关键点在于显卡驱动里的CUDA Version和PyTorch的CUDA版本不是一回事前者是驱动支持的向上兼容版本后者是运行时所需的版本。只要驱动版本不低于PyTorch需要的CUDA版本就能正常用。2.2 克隆YOLOv5仓库与安装依赖git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果网速慢可以加上-i https://pypi.tuna.tsinghua.edu.cn/simple使用清华镜像源。这里有个很多人忽略的点requirements.txt里默认安装的是torch和torchvision的最新稳定版但如果你前面已经按CUDA版本装好了特定版本的PyTorch再执行pip install -r requirements.txt可能会把PyTorch覆盖掉装成CPU版本或者不匹配的版本。解决方法是先执行pip install -r requirements.txt --no-deps或者干脆手动安装除torch/torchvision之外的其他依赖包pip install opencv-python matplotlib seaborn pandas tqdm pyyaml requests scipy装完之后跑一下自带的检测脚本验证环境python detect.py --weights yolov5s.pt --source data/images/bus.jpg如果能在runs/detect/exp下看到标注好目标的输出图片说明环境和基础推理链路没问题了。2.3 准备预训练权重YOLOv5s的预训练权重可以直接从官方GitHub的README里下载也可以直接用代码自动下载python -c from utils.downloads import attempt_download; attempt_download(yolov5s.pt)第一次会自动下载到项目根目录。预训练权重的价值在于它在COCO数据集上已经学到了大量通用视觉特征边缘、纹理、形状等这些底层特征迁移到你的任务上能大幅缩短训练时间、提高收敛后的精度。特别是数据量不够多的时候用预训练权重和不用的差距非常大——前者可能几千轮就能到90%以上的mAP后者吭哧吭哧几百轮还在原地打转。3. 模型配置理解参数的含义再动手3.1 YOLOv5s网络结构特点YOLOv5s是整个YOLOv5系列里最轻量的版本之一。整个系列按网络深度和宽度系数分为n、s、m、l、x几个档位数字越小模型越小、速度越快、精度相对低一些。s版本的深度倍率是0.33宽度倍率是0.50通俗讲就是每层通道数和网络层数都比标准版本做了缩减但精度仍然有保证速度也很快非常适合在边缘设备或者实时场景跑。网络结构主要分三个部分Backbone负责提取特征核心是CSP结构跨阶段局部网络把特征图拆成两路一路走卷积提取另一路直接拼接减少重复梯度计算Neck负责特征融合典型的是PANet路径聚合网络自顶向下传递语义信息再自底向上传递位置信息让不同尺寸的目标都能得到充分表达Head负责最终预测输出三个不同尺度的特征图分别对应小目标、中目标和大目标的检测理解这三部分有助于排查训练问题。比如小目标检测效果差大概率是Backbone的特征提取不够细或者训练图片的输入分辨率不够大。3.2 修改数据配置文件data.yaml在data/目录下新建一个data.yaml内容格式如下train: /absolute/path/to/dataset/images/train val: /absolute/path/to/dataset/images/val test: /absolute/path/to/dataset/images/test nc: 2 names: [person, car]这里有几个需要注意的细节train、val、test的路径可以是绝对路径也可以是相对路径。如果是相对路径YOLOv5会相对于项目根目录来解析。我有一次因为路径写错训练开始后一直提示找不到图片排查了半天才发现是路径少了一层目录。nc是类别总数必须和names列表的长度一致否则会报错。注意names列表的顺序很重要。训练时模型输出的是类别的索引0, 1, 2...推理时根据这个索引去names里找对应的类别名称。如果你标注时类别顺序是[car, person]推理时显示的结果就会张冠李戴。3.3 训练参数详解YOLOv5的训练入口是train.py核心参数包括python train.py \ --weights yolov5s.pt \ --data data.yaml \ --img 640 \ --batch 16 \ --epochs 100 \ --workers 4 \ --device 0逐个解释一下--weights初始化权重用yolov5s.pt可以加载COCO预训练权重用空字符串或者--weights 表示从头训练--img训练输入图片分辨率默认640x640。提高分辨率能提升小目标检测能力但显存占用会显著增大--batch批大小一次喂给模型的图片数量。显存不够的时候就调小这个值--epochs训练轮数我一般先从100轮开始跑观察收敛情况再决定是否继续--workers数据加载的进程数Windows下不建议设置太大否则容易卡在数据加载阶段--device指定GPU编号多卡可以用0 1这种方式CPU则用cpu另外还有两个常用参数--patience用于早停验证集指标连续多少轮不提升就停止训练我通常设置50--resume用于断点续训训练中断后可以用--resume runs/train/exp来恢复之前的训练状态。3.4 显存不足的应对策略显存不够是训练中最常见的问题尤其当你用的是6GB或8GB显存的显卡时。我实际测过YOLOv5s在默认的640分辨率下batch 16大约需要6~7GB显存如果开一些额外的训练增强选项会更高。解决方法依次是降低batch大小从16降到8或4降低输入分辨率从640降到512或416开启梯度累积YOLOv5官方代码不支持直接开启但可以通过设置--batch为-1自动检测最大批大小其中第2种方法对显存的缓解非常明显因为显存占用和分辨率是平方关系从640降到512显存大约是原来的0.64倍。如果已经用了最低配还是不够只剩最后一条路加内存条开启swap交换空间但这会明显拖慢训练速度只作为应急手段。4. 训练过程看着损失下降的漫长时光4.1 训练启动与日志解读执行训练命令后会有日志输出。核心看的指标有这几个box_loss边界框回归损失衡量预测框和真实框的偏差obj_loss目标置信度损失衡量模型判断“这个位置有没有目标”的能力cls_loss分类损失衡量模型判断“这个目标属于哪一类”的能力mAP0.5IoU阈值为0.5时的平均精度mAP0.5:0.95IoU阈值从0.5到0.95步长0.05的平均精度更严格训练初期loss会快速下降这是正常现象因为模型在快速适应数据分布。训练后期loss下降变慢曲线趋于平缓说明模型逐渐收敛。如果损失曲线出现突然的尖峰或者大幅波动常见原因包括学习率偏大、batch太小、数据中有异常标注。训练过程中YOLOv5会在每个epoch结束后跑一遍验证集更新验证集上的mAP和损失曲线。完成全部epoch后runs/train/exp目录下会保存训练过程中的所有信息runs/train/exp/ ├── weights/ │ ├── best.pt │ └── last.pt ├── results.csv ├── results.png ├── confusion_matrix.png ├── PR_curve.png └── ...best.pt是验证集上指标最好的权重last.pt是最后一轮的权重。实际使用中优先选择best.pt不用犹豫。4.2 训练曲线怎么看训练结束后打开results.png里面包含多个子图box_loss、obj_loss、cls_loss、precision、recall、mAP等指标随epoch的变化曲线。我一般重点看两个东西第一验证集loss是否有反弹。如果验证集loss开始上涨而训练集loss还在下降说明模型过拟合了这时候就应该提前停止或者增加数据增强、增大数据量。YOLOv5默认开启了多项数据增强马赛克增强、随机翻转、颜色抖动等适度情况下过拟合会晚一些出现。第二mAP曲线是否还在上升趋势。如果最后阶段mAP曲线还在稳定向上走说明还没完全收敛可以在last.pt基础上用--resume多训练一些epoch。这里贴一下我个人的判断标准mAP0.5达到90%以上项目可用性很高mAP0.5在80%~90%基本可用某些类别可能还需要针对性优化mAP0.5低于80%先检查数据质量问题再考虑调参4.3 训练中断与断点续训训练过程中可能会因为断电、显存溢出、系统重启等原因中断。YOLOv5的断点续训做得比较友好python train.py --resume runs/train/exp或者简写python train.py --resume它会自动找到最近一次的训练目录并恢复。恢复时学习率、epoch进度、优化器状态都会被还原相当于从中断的地方继续跑。注意断点续训要求训练环境尤其是代码版本和之前一致。如果你是先跑了老版本代码后来又拉取了更新的YOLOv5仓库直接--resume大概率会报错。换个代码版本后别硬续训老老实实重新跑一轮更靠谱。5. 模型评估别只看mAP一个数字5.1 核心评估指标解读mAP是目标检测里最常用的指标但只看mAP数字会漏掉很多问题。mAP0.5表示IoU阈值为0.5时的平均精度。IoU指的是预测框和真实框的交并比两个框重合越多IoU越大。0.5的阈值意味着预测框和真实框有50%以上的重叠就认为是正确检测这个标准相对宽松。mAP0.5:0.95则是把IoU阈值从0.5到0.95每隔0.05取一次共10个阈值分别计算后取平均。这个指标更严格对定位精度的要求更高。举个例子某个模型的mAP0.5是92%mAP0.5:0.95只有65%。这说明模型能大致检测出目标的位置但框的边界不够精确。这在很多应用场景里是不够的——比如工业质检框偏了哪怕几个像素就可能影响判断。还需要同时看Precision精确率和Recall召回率。Precision衡量的是一张图里模型检测出来的结果有多少是正确的Recall衡量的是所有真实目标中有多少被检测出来了。这两个指标往往是此消彼长的关系具体业务里要根据实际需求调整置信度阈值漏检比误检更严重就适当降低阈值误检比漏检更严重就提高阈值。5.2 混淆矩阵分析训练完成后会生成confusion_matrix.png这是一个非常有用的诊断工具。矩阵的行代表真实类别列代表预测类别对角线上的数值越高越好。重点关注两类错误一是某个真实类别被误判为另一个类别说明这两个类别视觉上太相似需要补充更多区分性数据或者考虑增加类别内部的数据多样性。二是大量目标被检测成“background”说明模型漏检严重。这种情况下可以尝试降低置信度阈值、提高输入分辨率或者检查是否有大量小目标没有被充分训练。5.3 实际推理测试用训练好的权重做推理测试python detect.py \ --weights runs/train/exp/weights/best.pt \ --source /path/to/test/images \ --conf-thres 0.6 \ --iou-thres 0.5 \ --save-txt \ --save-conf--conf-thres控制置信度阈值低于这个值的检测结果会被过滤掉。--save-txt可以把检测结果保存为txt标签文件格式和训练标签一致适合后续做批量分析。--save-conf会在txt里附带每个框的置信度分数。我在实际测试时有个习惯把置信度阈值调低到0.25跑一遍把所有置信度在0.25到0.7之间的检测结果单独导出人工扫一遍这些“低置信度检测框”。这个方法看起来笨但能非常直观地暴露出模型的短板是漏检、误检还是定位不准一目了然。6. 实战检测从模型到可用工具6.1 视频流实时检测拿训练好的模型跑视频实时检测python detect.py \ --weights runs/train/exp/weights/best.pt \ --source 0 # 用摄像头--source 0表示使用电脑默认的摄像头也可以传视频文件路径或RTSP流地址。这个命令会实时弹出检测窗口画面上每个目标都有一个框和类别标签。如果想优化实时性可以把--img调小到416或320帧率会有明显提升代价是小目标检出率下降。如果模型推理速度还是不够可以考虑下一个方案模型导出。6.2 模型导出ONNX与TensorRTYOLOv5支持把训练好的PyTorch模型导出为多种格式。导出ONNX格式python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --img 640 \ --batch 1导出后的.onnx文件可以部署到各种端侧环境也可以用ONNX Runtime做CPU推理。如果目标平台是NVIDIA GPU还可以进一步导出TensorRT引擎文件python export.py \ --weights runs/train/exp/weights/best.pt \ --include engine \ --img 640 \ --batch 1TensorRT是NVIDIA的推理加速引擎能把模型算子做融合和优化推理速度相比PyTorch原生推理能有2到5倍的提升。我测试过在同样的显卡上YOLOv5s的TensorRT版本比PyTorch版本能快出不少差距还是很明显的。6.3 用Python API做批量检测如果需要在业务代码里集成检测能力可以直接调用YOLOv5的Python接口import torch # 加载模型 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, force_reloadTrue) # 推理单张图片 results model(/path/to/image.jpg) results.print() results.show() results.save() # 获取检测结果数据框 df results.pandas().xyxy[0] print(df)results.pandas().xyxy[0]返回的是一个DataFrame每一行是一个检测框列包含xmin、ymin、xmax、ymax、confidence、class、name。拿到这个以后就可以按业务逻辑继续处理了裁剪目标区域、计数、跟踪、判断异常等。这里有个实际项目里的细节如果你的业务有“同类别目标不能重叠超过一定比例”这种约束需要自己写后处理逻辑。YOLOv5的NMS非极大值抑制只保证同类目标内部不严重重叠不会保证不同类别之间不重叠很多业务规则是要在检测结果之上再做一层过滤的。6.4 多场景部署概要如果要把模型部署到树莓派、Jetson Nano这一类的嵌入式设备上有几个建议优先选用n版本而不是s版本n版本模型文件更小、推理速度更快代价是精度会损失一些导出为TensorRT或者ONNX格式后再部署不要直接跑PyTorch权重输入分辨率降到320或416显著减少计算量如果设备没有GPU可以考虑用量化后的INT8模型速度提升非常明显但精度会有一定下降需要实测对比如果是部署到服务端用户并发量大的场景建议用ONNX Runtime或者TensorRT来承载推理PyTorch原生的Python推理路径虽然写起来方便但吞吐量往往达不到要求。我自己做过的服务端方案里用ONNX Runtime GPU跑YOLOv5s单卡并发能力和延迟都比PyTorch方式好不少。7. 常见问题与排查技巧实录7.1 训练Loss不降或直接NaNLoss不降优先检查数据本身。打开几张训练图片和对应的txt标签一起可视化确认标注框是否正确、类别索引是否越界、坐标是否归一化。标签文件和图片不对应比如图片里有人但txt里是空的也会导致模型学不到东西。Loss变成NaN绝大多数情况是学习率太大导致梯度爆炸把学习率降低一个数量级比如从0.01降到0.001重新跑。另外检查训练集中是否有全黑或全白的异常图片这种图片会让模型的BatchNorm统计量跑偏严重时也会导致NaN。7.2 验证集mAP低但训练集很高这是典型的过拟合现象。解决办法优先顺序是增加数据量包括数据增强、降低模型复杂度从m版本换到s版本、提高正则化强度YOLOv5里的--label-smoothing 0.1可以试一下。如果数据量几千张、模型是xl版本这种情况几乎必然出现别纠结换小模型或者加数据。7.3 小目标检测效果差小目标检测是目标检测的老大难问题。实际项目中我验证过的可行手段包括提高输入分辨率--img 1280这是最直接的改进方式使用更大感受野的模型版本把图片切片/瓦片化把大图切成小块分别检测最后合并结果第三种方案在遥感、航拍这类大尺寸图片场景里尤其有效。代价是推理时间成倍增加需要根据业务在速度和精度之间取平衡。7.4 推理时误检一大堆误检多的时候第一件事是查看检测结果的置信度分布。如果大量误检框的置信度集中在0.3~0.5之间说明模型对真实目标和干扰物的区分度不够。解决方案提高--conf-thres阈值检查训练数据里是否缺少包含干扰物的“负样本”图片即图上没有目标但场景和正样本类似针对性地采集一些难负样本加入训练集让模型见过“长得很像目标但不是目标”的东西。写在最后从数据准备到实战检测YOLOv5s的全流程说复杂也不算复杂说简单也绝对不简单。真正跑通一个项目之后回头看最大的感受是模型架构本身已经非常成熟了真正决定项目成败的往往是数据质量和细节处理。标注是否规范、坐标是否归一化、类别顺序是否统一、验证集划分是否合理这些看起来不起眼的小事实际带来的影响比很多人想象中大得多。如果你也正准备开始一个目标检测项目我的建议是别急着追求最好的模型和最炫的部署方案先跑通一个最小可行版本有一批标注好的数据、有一个能出结果的权重然后再一步步迭代优化。这个过程本身就会让你对目标检测有一个完整且扎实的理解。