简介:面向目标检测开发者的YOLO实战源码包,基于YOLOv5与YOLOv8训练三角洲行动数据集,专注于“摸金”目标的检测,覆盖数据集准备、标注、配置文件创建、模型训练、评估到实际检测的完整链路,适合深度学习入门与进阶学习者按需参考。压缩包共包含70个文件,主要有大量图片样本、文本标注与说明、训练与检测脚本、数据集配置和预训练权重文件,整体体积仅为19.45MB,小巧易用。目前已有2023人在线学习与下载。资源中提供训练脚本、检测演示脚本、数据生成工具、示例测试图片,以及可运行的目录结构和标注样例,能够帮助快速复现从数据整理到模型评估的完整流程,并从中理解大型数据集的规范组织、YOLO系列参数调整与效果验证思路,对实际开展目标检测项目具有直接参考价值。 最近在整理一份用于目标检测训练的三角洲数据集,配合YOLO系列做了一套完整训练方案,顺手把项目源码也整理打包了。这个项目做下来最大的感受是:真正让模型效果拉开差距的,往往不是网络结构本身,而是你在数据准备和训练参数上投入了多少精力。这篇文章就把我从数据检查、格式转换、训练参数设置,到实际跑训练时踩过的坑完整梳理一遍,适合正在折腾 YOLO 训练自己数据集的读者参考,尤其是有遥感场景检测需求的朋友。
所谓“三角洲数据集”,圈内一般指的是在河流入海口、沿海冲积平原这类区域拍摄的遥感影像检测数据,目标通常包含舰船、港口设施、大型储罐、堆场等。这类数据集和常规 VOC/COCO 风格数据有个明显区别:目标尺度跨度大、背景纹理复杂、小目标占比高,而且很多目标带有旋转方向性。拿它来训练 YOLO,其实是把通用目标检测框架往遥感垂直场景里落地的典型过程,涉及标注格式转换、类别平衡、超参数调整、训练稳定性控制一整套问题。
1. 项目整体设计与思路解读
1.1 为什么选 YOLO 来处理这类影像数据
YOLO 系列从 v5 到 v8 再到 v11,已经成了目标检测领域的事实标准之一。对于遥感场景下的舰船、储罐检测,YOLO 的优势不只是速度快,更关键的是它在小目标检测和密集排列目标上的表现经过大量工业项目验证。相比 Faster R-CNN 这类两阶段方法,YOLO 的 anchor-free 设计(v8 之后)让检测头对不同尺寸目标的适应能力更强,配合多尺度训练,能明显提升小目标的召回率。
在这个项目里,我选用的是 YOLOv8 作为主训练框架。原因不复杂:Ultralytics 官方仓库维护活跃,训练接口统一,文档齐全,而且自带数据增强、EMA、混合精度等一系列工程化能力。你不需要自己写 NMS、写评估脚本,只需准备好数据集,写一个 YAML 配置文件,就能跑通完整的训练-评估-导出流程。
1.2 技术选型:YOLOv8 还是 YOLOv5,怎么取舍
很多人纠结到底用 v5 还是 v8。我的判断标准很简单:新项目无脑选 v8,老项目为了和已有代码兼容才留在 v5。
v5 的优势是社区资料多,很多老牌工具箱(比如某些 RK3588 部署工具链)对 v5 的 ONNX 导出支持更成熟。v8 则在训练稳定性和检测精度上做了不少改进,比如 C2f 模块替代了 C3,Decoupled Head 让分类和回归任务不再互相干扰,训练时的 loss 曲线也更平滑,不容易出现 v5 上那种剧烈震荡。
这个项目里我最终确认用 v8 还有一个原因:后续要导出的 ONNX 模型要接入到自研的推理服务里,v8 的检测头输出格式更统一,后处理解析起来更省事。如果你手头有现成的 v5 项目要改数据集训练,也可以参照本文思路,训练命令和配置文件几乎通用。
1.3 项目源码结构总览
整理后的源码仓库按业务逻辑拆分,不追求花哨,但保证任何人拿到都能直接跑:
delta_train/ ├── data/ # 数据集放置目录 │ ├── images/ # train/val/test 子目录 │ └── labels/ # 与 images 对应的 txt 标签 ├── configs/ │ └── delta.yaml # 数据集配置文件 ├── scripts/ │ ├── convert_format.py # VOC/coco -> yolo 格式转换脚本 │ ├── split_dataset.py # 训练/验证集划分脚本 │ ├── train.sh # 一键训练脚本 │ └── export_onnx.sh # 模型导出脚本 ├── docs/ │ └── 训练日志样例.md └── requirements.txt这个结构是经过好几轮项目沉淀下来的:数据和代码分离,脚本独立成文件,避免把逻辑全部堆在命令行参数里,方便后续做自动化训练时直接调用 Python 函数。
2. 数据集准备与预处理要点
2.1 明确标注格式,避免最隐蔽的坑
拿到三角洲数据集的第一件事不是直接跑训练,而是确认标注格式。很多开源数据集给的是 PASCAL VOC 的 XML 或者 COCO 的 JSON,而 YOLO 系列训练要求的是每个图片对应一个 TXT 文件,里面每行代表一个目标,格式为:
class_id x_center y_center width height注意这里全部是归一化坐标,都用 0 到 1 之间的小数表示。x_center 和 y_center 是目标框中心点相对图片宽高的比例,width 和 height 是目标框宽高相对图片宽高的比例。
我最开始踩过一个坑:从网上某份数据转换脚本拷过来直接用,结果它把 Polygon 标注按外接矩形写进 YOLO,没有做坐标裁剪,导致部分框超出图片边界。YOLO 训练时对这类异常框容忍度很低,会出现 loss 不降、mAP 异常低的现象。所以坐标合法性检查必须放在训练前,推荐写一个脚本扫描所有标签,检查 x_center、y_center、w、h 是否在 [0,1] 区间内,超出就打印出来。
2.2 三种格式转换的实操细节
如果你手里的数据是 VOC 或 COCO 格式,转换脚本的核心逻辑很直接:读取 XML 或 JSON 中每个目标的 bbox 坐标,做一次坐标归一化,写入 TXT。需要注意difficult和occluded这类属性,一般直接忽略不写进 YOLO 标签,避免引入噪声。
这里给一个我用着还算稳定的 COCO JSON 转 YOLO TXT 的核心代码片段:
import json import os def coco_to_yolo(coco_json_path, output_label_dir): with open(coco_json_path, "r", encoding="utf-8") as f: coco = json.load(f) # 建立 image_id -> 图片信息的映射 images = {img["id"]: img for img in coco["images"]} for ann in coco["annotations"]: img = images[ann["image_id"]] img_w, img_h = img["width"], img["height"] # COCO 的 bbox 是 [x, y, width, height],左上角为原点 x, y, w, h = ann["bbox"] x_center = (x + w / 2.0) / img_w y_center = (y + h / 2.0) / img_h x_center = max(0, min(1, x_center)) y_center = max(0, min(1, y_center)) w = max(0, min(1, w / img_w)) h = max(0, min(1, h / img_h)) label_name = img["file_name"].rsplit(".", 1)[0] + ".txt" with open(os.path.join(output_label_dir, label_name), "a") as f: f.write(f"{ann['category_id'] - 1} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n")这里有个关键细节:COCO 的 category_id 往往不是从 0 开始的,而 YOLO 要求类别 ID 必须从 0 连续编号。转换时一定要做category_id - 1或者建一个映射字典,否则类别错位会让模型训练彻底跑偏。
2.3 数据集划分:别让同类目标扎堆
划分训练集和验证集时,我强烈建议按图片目录或按视频帧来源划分,而不是直接随机打乱。遥感数据集经常出现同一架次拍摄的影像,相邻帧高度相似,如果随机划分,验证集里会出现大量训练集的“近亲”,导致验证分数虚高,实际部署效果却很差。
实际操作中,我按场景来源对图片做了分组,确保同一来源的图片全部进训练集或全部进验证集,再用split_dataset.py脚本生成train.txt和val.txt。另外,验证集比例我设在 15%~20% 之间,目标类别越多,验证集尽量留足,保证每个类别在验证集里都有一定数量的样本可供评估。
3. 训练配置与核心参数解析
3.1 数据集 YAML 和模型配置
YOLOv8 训练前需要一份数据配置文件delta.yaml,内容很简洁,但每一项都不能写错:
path: /path/to/delta_train/data train: images/train val: images/val nc: 4 names: 0: ship 1: storage_tank 2: terminal 3: container_yardpath是整个数据集根目录的绝对路径,train和val是相对它的子目录。很多人会在这里踩坑:路径写错或者子目录名不匹配,训练程序直接报错找不到图片。
类别顺序一旦确定,后续所有脚本和推理代码都要沿用,中间不要随意增删类别,否则会导致标签索引错位,排查起来非常痛苦。
3.2 关键超参数的计算与选择
训练参数决定了模型能否正常收敛,这里给出我实测下来比较稳的一套配置:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| model | yolov8s.pt | 从 s 版本预训练权重起步,速度和精度平衡 |
| imgsz | 640 | 若小目标多可尝试 1280,但训练时间约翻倍 |
| batch | 16 | 12GB 显存可稳定运行 |
| epochs | 300 | 配合早停,实际多数在 150~200 轮内稳定 |
| optimizer | SGD | 精度略高,收敛稳定 |
| lr0 | 0.01 | 初始学习率 |
| mosaic | 1.0 | YOLOv8 自带 mosaic 增强,前 10 轮自动关闭 |
有读者问过 batch 怎么算。这里给一个粗略估算方法:YOLOv8 训练显存占用和batch * imgsz * imgsz近似成正比。12GB 显存跑 640 分辨率、batch 16 比较稳,如果调到 1280 分辨率,batch 降到 4~6 才不爆显存。显存不够时优先减 batch,不要优先减分辨率,因为分辨率对检测精度的直接影响更大。
3.3 断点续训与增量训练实操
训练过程中途断了是常态,别慌,YOLOv8 默认每个 epoch 结束都会保存last.pt,接着训练只需要在启动命令里把model参数指向last.pt:
yolo detect train model=/path/to/runs/detect/train/weights/last.pt data=delta.yaml epochs=300 imgsz=640 batch=16增量训练在工程里也常用,比如先用公开遥感数据预训练,再用三角洲数据集微调。做法是先把预训练权重作为model参数传入,然后设置freeze=10冻结前 10 层,只训练检测头。等检测头收敛后,再解冻全部层做几轮完整微调,这样能避免模型在少量新数据上发生灾难性遗忘。
4. 训练过程中的常见问题与排查实录
4.1 训练前后参数量不一致
网络上有不少人在问 YOLO 训练一开始统计的参数量和训练完后得到的参数量不一致,我在这个项目里也碰过。先说结论:这通常不是 bug,而是统计口径变了。
训练开始时打印的参数量来自模型定义时的结构,而训练结束后用torchinfo或ultralytics自带的summary重新统计时,如果模型启用了 EMA(指数移动平均)权重、或者你手动对检测头做了结构替换,统计结果就会不同。另外,AMP 混合精度训练时,PyTorch 会自动为某些层插入转换节点,也会让参数统计上下浮动。
排查思路:先确认训练过程中没有修改过 YAML 结构文件;再比较best.pt和初始权重里model.state_dict()的键名和形状是否一一对应。如果只是总量上的小幅差异,完全不影响推理,不用过度纠结。
4.2 loss 不降或者验证 mAP 一直为 0
这类问题我先按优先级排查数据:
- 打开几张训练图片,把标签框可视化画出来,确认坐标没有错位。
- 检查类别 ID 是否从 0 开始连续编号。
- 确认所有图片能被 OpenCV 正常读取,损坏图片会直接污染训练。
- 确认标签文件里的类别 ID 最大值不超过 YAML 里
nc-1。
如果数据没问题,再考虑超参数:降低lr0到 0.001,或者把优化器换成 AdamW。有些数据集上 SGD 收敛慢,AdamW 能更快跳出初始震荡。注意换优化器后训练轮数可能要相应增加,因为 AdamW 的权重衰减策略和 SGD 不一样。
4.3 验证分数低但训练 loss 很低,过拟合怎么应对
三角洲数据集样本量如果只有几千张,YOLOv8 很容易出现过拟合,典型表现是训练 loss 降到很低,但验证集 mAP 上不去。我常用的手段按优先级排序:
- 调大
mosaic、mixup等增强强度,让模型见到更多语义变化。 - 增加
weight_decay(默认 0.0005 可尝试调到 0.001)。 - 在
augment参数里开启hsv_h、hsv_s等颜色扰动,模拟不同光照条件。 - 如果还是过拟合,优先考虑找更多数据或者用公开预训练权重做迁移学习,不要盲目加深网络,网络越大过拟合越快。
5. 源码脚本使用与后续部署扩展
5.1 一键训练脚本,减少重复操作
整理源码时我把训练启动命令封装成了train.sh,方便批量跑实验:
#!/bin/bash source /opt/conda/etc/profile.d/conda.sh conda activate yolo_env cd /path/to/delta_train python scripts/check_labels.py --label_dir data/labels/train python -m yolo detect train \ model=yolov8s.pt \ data=configs/delta.yaml \ epochs=300 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/detect \ name=delta_sgd_640脚本里先跑了一个check_labels.py做标签合法性校验,再启动训练。这样每次换数据集训练时,至少不会带着异常标签跑半天才发现问题。
5.2 模型导出与部署端适配
训练完成后,把best.pt导出成 ONNX 是一个常见动作,方便后续接入 Qt 应用或边缘设备:
yolo export model=runs/detect/delta_sgd_640/weights/best.pt format=onnx imgsz=640 opset=12导出后的 ONNX 模型可以继续转成 OpenVINO、TensorRT 等格式,对应部署场景差异很大。这个项目里我用 ONNX Runtime 做过 CPU 端推理,单张 640 图片耗时约 30ms 左右。如果你要在 RK3588 这类边缘设备上部署,通常先用rknn-toolkit2转成 RKNN 格式,转换时注意要指定与训练一致的输入分辨率,否则精度会下降。
顺带提一句,训练好的模型也可以配合 X-AnyLabeling 这类工具做智能标注,用模型先出框再人工修正,能明显提升标注效率。这是一个很实用的扩展方向:第一轮训练得到粗模型,标注第二批数据时直接用它来初标,把人工从画框变成改框,时间能省一半以上。
6. 跑完这个项目的几点实在心得
这套流程走下来,我最想强调的一点是:别在一开始就迷信大模型。先用yolov8n或yolov8s把数据和流程跑通,确认 loss 曲线正常、验证集 mAP 有合理表现后,再升级到yolov8m或yolov8l。大模型对小数据集不友好,收敛慢、过拟合风险高,而且不一定带来精度收益。
第二点心得是:训练日志里前几个 epoch 的 loss 参考价值不大。YOLOv8 默认开了 mosaic 增强,前 10 轮会逐渐关闭,这个阶段 loss 波动剧烈很正常。我习惯等 50 个 epoch 之后再评估模型趋势,而不是一开始看到 loss 下降慢就急着调参。
还有一个小细节:如果训练过程中重启了很多次,runs/detect目录下会积累大量实验记录,建议每次训练都用name=实验名区分,方便回溯对比。别小看这一步,做多组实验对比时,规范命名能省下大量整理时间。
最后再分享一个习惯:每次训练完,我会把训练指标曲线(results.png)和验证集上的预测可视化图直接归档到项目的docs/目录。这些资料不只在写报告时有用,后续模型迭代时,对比它们能快速判断新改动到底是变好还是变坏。这一步看着不起眼,但做多了就知道有多省心。
本文还有配套的精品资源,点击获取