
简介一套面向深度学习目标检测初学者的YOLO项目源码包基于YOLOv5与YOLOv8在三角洲行动数据集上训练“摸金”检测模型。数据集包含3万张图片其中1万张完成全身标注项目完整覆盖数据准备、标注、配置文件创建、YOLO安装、训练、评估与实测推理全流程适合希望掌握YOLO实际训练方法的开发者参考。压缩包共70个文件约19.45MB含jpg示例图片、txt标签文件、Python训练/检测脚本、yaml配置文件、yolov8n.pt预训练权重等目录结构清晰便于对照学习。已有2085人学习下载人气可观。通过该资源可了解大规模数据集的整理与标注方式学习如何通过合理设置超参数提升检测精度并借助现成脚本快速复现训练和预测过程。1. 用 YOLO 训练一个三角洲目标检测数据集值不值当亲自下场做目标检测的朋友应该都听过 YOLO 训练自己数据集的说法但真正拿到一个三角洲数据集时大多数人第一反应是把压缩包解压后直接丢进训练脚本然后等一个看起来漂亮的 mAP。三角洲影像有几个特点目标小、密度高、背景复杂、类别不均匀这些恰好是 YOLO 最容易被数据集“牵着鼻子走”的场景。围绕“YOLO训练三角洲数据集[项目源码]”这个方向我在这篇笔记里把数据清洗、训练参数、验证指标到导出落地一条线讲清楚。适合手里有一批无人机或卫星影像、想自己训一个检测器的工程师也适合被 mAP 不错但实拍一测就漏检卡住的研究者。你会看到坑大多不在模型结构而在数据和阈值。2. 先别急着训练把三角洲数据整理成 YOLO 能吃的格式三角洲数据集最常见的原生态标注是class x1 y1 x2 y2这种四点坐标或者旋转框跟 YOLO 需要的class x_center y_center width height归一化格式不是一回事。直接把两种格式混在一起训练轻则框偏重则训练直接崩掉。所以拿到项目源码后第一件事不是看模型而是看数据目录和标注格式。2.1 把标注批量转成 YOLO 格式坐标归一化与路径校验我一般会先写一个转换脚本把原始标注转成 YOLO 的 txt 格式。转换的核心是记住一点YOLO 的框坐标是相对于图片宽高的比值不是像素值。比如原始标注里一个框是x1100, y1200, x2300, y2400图片宽高是1280x720那么中心点就是(200, 300)归一化后 x 是200 / 1280y 是300 / 720宽高同理。# convert_to_yolo.py把三角洲数据集的矩形标注转成 YOLO 格式 import os import glob from PIL import Image def convert(size: tuple, box: tuple): # size 是 (width, height)box 是 (x1, y1, x2, y2) dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[2]) / 2.0 * dw y (box[1] box[3]) / 2.0 * dh w (box[2] - box[0]) * dw h (box[3] - box[1]) * dh return (x, y, w, h) img_dir ./images label_dir ./labels os.makedirs(label_dir, exist_okTrue) for img_path in glob.glob(img_dir /*.jpg): w, h Image.open(img_path).size src_txt img_path[:-4] .txt # 原始标注与图片同名 dst_txt os.path.join(label_dir, os.path.basename(img_path)[:-4] .txt) with open(dst_txt, w) as f: for line in open(src_txt): parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) box tuple(map(float, parts[1:5])) # 只取前四个坐标 cx, cy, bw, bh convert((w, h), box) f.write(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n)这段代码里我特意只转换前四个坐标因为很多三角洲数据集的原始标注后面还带旋转角或其他字段。转换后要顺手检查一遍有没有类别 id 是负数或超出类别总数的有没有宽或高小于等于 0 的。还有一个容易踩的边界归一化之后坐标仍在 0 到 1 之外说明原始标注可能裁切到图片外面了这种样本最好直接删掉而不是保留到训练集里让模型去学一个永远学不会的框。2.2 半自动清洗标注尺度过小、误标与类别错位的可视化检查转换完格式不等于数据能直接拿来训练。三角洲场景里的目标经常只有二三十像素标注框在可视化时几乎看不见人工核对非常吃力。我的做法是生成一张“标注密度热图”把每张图上的框画出来统计每张图的标注数量、目标平均尺寸、类别占比再抽检那些目标特别小或者数量特别多的图片。这里有一个非常实用的脚本可以快速找出可疑样本标注面积小于 32x32 像素的框占比过高、某张图出现超过 100 个框、类别分布明显偏斜的图片都值得重新看一眼。# check_labels.py扫描 YOLO 格式标注输出可疑样本清单 import glob import cv2 import numpy as np MIN_PIXEL 32 # 小于这个像素数的框标记为可疑 MAX_BOXES 100 # 单张图框数超过这个值标记为可疑 for txt in glob.glob(./labels/*.txt): img_path ./images/ txt.split(/)[-1][:-4] .jpg img cv2.imread(img_path) if img is None: print(f[missing] {img_path}) continue h, w img.shape[:2] boxes [] for line in open(txt): parts line.strip().split() if len(parts) 5: continue cls, cx, cy, bw, bh int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) x1, y1 int((cx - bw / 2) * w), int((cy - bh / 2) * h) x2, y2 int((cx bw / 2) * w), int((cy bh / 2) * h) boxes.append((cls, x1, y1, x2, y2)) if len(boxes) MAX_BOXES: print(f[dense] {img_path} - {len(boxes)} boxes) small_count sum(1 for _, x1, y1, x2, y2 in boxes if (x2 - x1) MIN_PIXEL or (y2 - y1) MIN_PIXEL) if small_count / max(len(boxes), 1) 0.3: print(f[small] {img_path} - {small_count}/{len(boxes)} small boxes)这段脚本只做两件事读标注、判可疑。但它的价值不是把可疑样本删掉而是给你一个复核优先级。目标过小如果是普遍现象那不是标注错了而是真实场景如此后面训练要靠增强和多尺度来解决如果只有个别图片出现大量小框那大概率是标注时把同一目标重复框了需要手动合并。还有一种常见问题是类别错位比如把“车辆”标成“船只”单纯靠坐标检查看不出来需要抽出标注图和原图叠在一起做抽检我一般抽 20 张密集图人工过一遍比随机抽 200 张效果更好。2.3 针对性数据增强mosaic、旋转增强与光学变换的参数建议三角洲数据集的增强策略和普通交通数据集不太一样。这里的目标颜色与背景接近船只与水面灰度对比低建筑影子还会造成伪目标。所以增强的重点是光学变换加几何变换的配合而不是盲目堆强度。YOLOv8 自带的增强参数已经够用但有几个参数需要根据数据情况调。mosaic 增强在多目标小目标的场景下很有效它把四张图拼成一张让模型在小目标上更鲁棒。建议训练初期把mosaic开到 1.0最后一个 epoch 前关掉或降低到 0.3因为 mosaic 生成的图片和真实分布有差异收尾阶段用接近真实分布的图微调能让模型稳定下来。参数三角洲场景建议值普通目标检测默认值说明mosaic1.0前中期0.3收尾1.0四图拼接提升小目标泛化hsv_h0.0150.015色相扰动别太大水面颜色失真会误检hsv_s0.70.7饱和度增强对低对比船只很有用degrees300.0无人机视角方向随机旋转扰动必不可少fliplr0.50.5水平翻转scale0.50.5缩放到 0.5 倍相当于模拟不同飞行高度translate0.10.1平移扰动目标是保持目标在画面中的位置多样性mixup0.20.0混合两个样本的像素对背景复杂场景有缓解作用旋转增强这里要提一个只有遥感/航拍场景才会遇到的坑如果你后期要用旋转框评估或者做旋转框检测几何增强时要把旋转角度记录到标注里否则框和目标的朝向就对不上。如果项目只是普通水平框检测degrees30不会带来太大麻烦因为水平框在旋转后仍然能框住目标只是框内多余背景变多。3. 训练阶段的选型与参数让预训练权重、超参、损失函数各就各位数据准备好后进入选型环节。三角洲数据集的项目源码如果只给了一套固定脚本千万别拿过来就训。YOLO 系列从 v5 到 v8、v11、v13结构一直在变但一个基本原则没变模型参数量要和目标尺寸、数据规模匹配。三角洲数据集通常只有几千张图目标小而密直接用 YOLOv8x 或 YOLO11x 这种大模型大概率严重过拟合。3.1 用哪种 YOLO、多大输入、什么预训练权重选型矩阵先把预训练权重的事说清楚。很多人上来就问“yolo预训练模型下载”其实预训练权重的价值不是让你从零开始而是让模型先学会通用特征提取。对三角洲数据集来说COCO 预训练权重是合理的起点因为 COCO 里包含船只、汽车、人等常见类别特征提取层基本可以直接复用。如果数据集中有大量红外或 SAR 影像预训练权重帮助会变小因为域差异太大这时候可以尝试从头训练反而更干净。模型参数量输入尺寸适用数据规模三角洲场景判断YOLOv8n3.2M640数千张小目标密集时会漏检慎用YOLOv8s11.2M640数千张最稳妥的起点YOLOv8m25.9M640/1024一万张以上训练集够大时可用YOLOv8l/x43.7M/68.2M1024两万张以上数据少时过拟合明显YOLO11s9.4M640数千张与 v8s 性能接近部署生态略新输入尺寸的选择比模型选择更关键。三角洲目标小640 输入下很多目标只有十几个像素特征图上一个点都占不到。我一般会把imgsz从 640 提升到 1024代价是显存占用和训练时间增加但对小目标的召回率提升非常明显。如果你用的是单卡 12G 显存YOLOv8s 1024 输入 batch 8 是刚能跑的配置再大就得换混合精度或减小 batch。3.2 训练启动一份可平移到任何数据集的 setup 与 train 脚本完整训练流程不只是一条命令而是先建数据配置再选预训练权重最后调超参。数据配置用 YAML 文件描述类别和路径类别名要和标注里的 id 一一对应顺序错了训练不报错但评估时混淆矩阵会非常难读。# delta_dataset.yaml path: /data/delta train: images/train val: images/val nc: 5 names: 0: ship 1: vehicle 2: building 3: person 4: bridge训练命令我通常写成一段 bash把数据、预训练权重、超参一次性定死。注意cacheTrue这个参数如果数据集不大把图片缓存进内存能省很多时间。yolo detect train \ data/data/delta/delta_dataset.yaml \ model/weights/yolov8s.pt \ epochs80 \ imgsz1024 \ batch8 \ lr00.01 \ lrf0.01 \ cos_lrTrue \ optimizerauto \ cacheTrue \ valTrue这里的model/weights/yolov8s.pt使用的是下载好的预训练权重这个权重本质上是一个迁移起点。训练过程中 YOLOv8 会自动加载它的结构参数但最后几层检测头会重置。如果预训练权重本身就包含了类似船只的类别收敛会更快如果完全不相关也不要慌前十个 epoch 的 loss 下降慢是正常的。关于损失函数YOLOv8 的损失由三部分组成box_loss边界框回归损失默认用 CIoU 配合 DFL、cls_loss分类损失BCE 变体、dfl_loss分布焦点损失用于边界框距离预测。训练日志里的cls_loss和dfl_loss在三角洲这类小目标场景下尤其值得关注。cls_loss降不下去往往代表相似类别被混淆比如船只和车辆在低分辨率下纹理接近dfl_loss偏高代表边界框不够贴合目标边缘模糊时容易这样。3.3 三个关键超参数batch、学习率、epoch 之间的联动关系教一个血泪教训不要把 batch、学习率、epoch 当成三个独立参数来调。它们之间的联动关系决定了训练是“快速收敛”还是“看似收敛实则泛化差”。先说 batch 和学习率。官方默认配置里batch 16 对应 lr 0.01这个组合在 COCO 上没问题。但你把 batch 调到 8 以后如果还保持 lr 0.01梯度估计的噪声变大模型容易在训练前期震荡表现为 mAP 曲线像锯齿。一个常用的线性缩放规则是batch 减半学习率也减半。也就是 batch 8 时用 lr0 0.005。如果你用更大 batch 比如 32lro 可以试着提到 0.02但前提是数据集规模也够大。epoch 的选择不能只看训练 loss要看验证 loss 和 mAP 的收敛曲线。三角洲数据集的训练集如果只有几千张80 个 epoch 可能在第 50 个 epoch 时已经过拟合。要打开results.png看验证集 mAP 是否还在上升如果连续 20 个 epoch 没有明显改善就可以提前停掉。还有一点值得注意不要最后一个 epoch 才选 best.ptYOLO 默认保存best.pt是根据验证集 mAP 选的不是根据训练 loss 选的这个机制本身就会缓解过拟合但前提是你开了valTrue。4. 训练三角洲数据集的常见问题与避坑记录YOLO 训练过程中的报错其实很少更多是“不报错但结果不对”。这一章把最常遇到的几个问题按现象、原因、解决三步拆开全是实操里反复出现的记录。坑 1训练 loss 正常下降但验证集 mAP 一直趴在 0.3 以下现象是训练日志里 loss 曲线很漂亮平滑下降但每个 epoch 结束后的验证 mAP 就是上不去。很多人这时候会去调模型结构其实最可能的原因是训练前期梯度不稳定也就是所谓的“yolo训练中bn崩溃”。BatchNorm 层在训练初期统计均值方差时如果学习率过大或数据分布不稳定噪声会累积放大导致特征分布偏移之后无论怎么训练 mAP 都回不来。解决方法是做三件事把初始学习率调到 0.005 或更低检查是否加载了预训练权重从零训练时 BN 层更需要保守开启学习率 warmup。YOLOv8 自带 warmup 机制但如果你自定义训练脚本一定要对齐这一点。我在一个项目里遇到过类似情况调低学习率后 mAP 从 0.25 涨到 0.5中间没有任何其他改动。坑 2混淆矩阵热力图里每一行加起来不是 100%训练完用yolo detect val生成的混淆矩阵看热力图时很多人发现每一行的数字之和不是 1于是怀疑计算有问题。这里分两种情况。第一整体精度和召回率本来就小于 1混淆矩阵对角线占总数比例不可能达到 100%这是正常的第二YOLO 的混淆矩阵里还有一个“背景”类别漏检的目标会被归到背景那一列而背景那一列的值不会显示在对角线上。如果你强行打印归一化矩阵发现行和不是 1那是因为背景类没有被算进分母或者你用的是按像素计算的语义混淆矩阵而不是按目标框计算的检测混淆矩阵。解决办法是理解而不是修改看混淆矩阵时先确认它的横轴是预测类别纵轴是真实类别然后只关注对角线相对值是否集中在某几个类上。如果特定两类的混淆比例过高比如“船”和“漂浮物”互相错认这才是真正需要处理的类别混淆。坑 3小目标大量漏检但大目标检测效果很好这是三角洲数据集的经典问题。训练完后跑一眼验证集发现大船、大桥都检得很好但远处的小船几乎全漏。原因有两层一是输入分辨率不够小目标在特征图上只有一个点二是 anchor 或标签分配策略对小目标不友好。YOLOv8 是 anchor-free 结构没有 anchor 尺寸的概念但标签分配里仍有一个尺度匹配的过程小于一定像素的目标不会被有效分配。解法优先级先把imgsz从 640 提到 1024这一步在小目标场景下带来的提升往往比换模型还大。然后看增强参数里的scale如果缩放过强会让小目标变得更小建议把scale降到 0.3。最后检查数据里的标签分布如果标注时小目标大量被漏标模型会把小目标当成背景学习这时需要回到标注清洗步骤而不是调模型。坑 4训练到一半 loss 变成 NaN模型权重直接作废loss 突然变成 NaN常见于训练中后期是梯度爆炸的表现。三角洲数据集里如果存在某些极端样本比如整张图都是水面没有目标或者某张图的标注框宽度恰好是 0这都会成为梯度爆炸的导火索。我在一个项目里排查了很久最后发现是清洗脚本里没有过滤掉width0的框导致那一批样本的损失函数输入了无效值。解决方法是三个地方一起堵数据清洗阶段过滤掉所有面积为零或小于 1 像素的框训练时开启梯度裁剪YOLO 命令行工具里加nbs64做累积梯度把混合精度关闭几轮看是否复现。多数情况下数据里那个坏样本才是元凶模型结构本身很稳定。坑 5训练集 mAP 很高换成同场景新图直接打回原型三角洲场景的影像来源不同不同飞行高度、不同光照、不同水质颜色都会让模型泛化变差。训练集和验证集如果来自同一个航次模型很容易学到“那个颜色区域就是船”这种伪特征。这也是为什么三角测数据集的验证集划分不能简单随机抽样而应按航次或区域划分。解决方法是把按时间或位置划分数据保证验证集与训练集的重叠率尽量低。如果你的数据集是项目源码里自带划分也要检查一下是不是按文件名随机划分的如果是建议自己重新划分一次。泛化问题不会在训练时暴露只会在你换新数据测试时暴露。5. 训练完怎么验证有没有训明白mAP、混淆矩阵与失败样本复盘训练不是终点验证才是把模型从“能跑”变成“能用”的关键一步。很多人只看一个 mAP50 就交付了但三角洲这类小目标密集场景mAP50 高不代表实拍效果好因为小目标在 IoU 阈值 0.5 时容易虚高。这一章专门讲验证指标怎么读、怎么定位问题。5.1 从 mAP50 到 mAP50:95阈值口径影响你的判断mAP50 是把预测框与真实框的 IoU 大于 0.5 就算检测成功mAP50:95 则要求在 0.5 到 0.95 之间按 0.05 步长各算一次再取平均。两者差距在三角洲数据集上可能非常悬殊。因为小目标框只要稍微偏移几个像素IoU 就会从 0.7 掉到 0.3mAP50:95 特别敏感。我在参数表里通常这样评估指标大目标场景三角洲小目标场景说明mAP500.70.6只能说明目标被大致框住mAP50:950.50.25小目标场景这个值很难非常高召回率0.80.7漏检比错检更致命误检率0.20.3背景比目标多误检会偏高如果你在验证集上看到 mAP50 有 0.8但 mAP50:95 只有 0.2那说明模型确实能定位到目标但框的边界不够准。这时优先调的是输入分辨率和后处理 NMS 阈值而不是继续加训练时间。5.2 读懂归一化混淆矩阵为什么“总合不唯一”读混淆矩阵时先确认输出的格式。YOLO 的 val 命令运行后会生成confusion_matrix.png和confusion_matrix_normalized.png两张图。前者是绝对数量后者是归一化比例。你看到“每一行总合不唯一”时先判断自己看的是哪一张绝对数量那张行和本来就是变量归一化那张行和理论上接近 1。另一个常见原因是背景类的存在。归一化混淆矩阵会预测类别与真实类别的对应关系但漏检目标不会凭空消失它们被算入最后一列或最后一行。如果你定义的数据集只有 5 类训练时检测头里会有 6 类输出多出来的那个就是背景。所以行和不是 1多半是你把背景行也算进去了或者模型把背景预测成某个前景类并在矩阵里产生了非对称计数。真正要看的不是总合而是哪些行偏离对角线最严重。如果“车辆”这一行里有 30% 被预测成“船只”说明这两类特征在影像上高度相似。解决方法是增加这两个类别的样本量或者在增强阶段对这个类别组合做更多 mixup。5.3 用错误样本重训还是调阈值排错优先级验证阶段生成的val_batch_pred.jpg会画出预测框我最常做的事是从里面挑出所有错误样本按漏检和误检分开统计。漏检样本的比例如果超过误检说明模型容量或输入分辨率不够重训时优先提高imgsz误检如果超过漏检说明背景中某些纹理被当成了目标这时优先调的是置信度阈值而不是重训。置信度阈值的影响在小目标场景很容易被低估。默认conf0.25对普通目标合理但三角洲小目标往往在低置信度区间把阈值降到 0.1 会显著提升召回率代价是误检增加。我一般会在验证集上扫一遍conf0.1到0.5把 PR 曲线打出来看哪个点最合适。这个过程不用重训只是后处理参数选择但它产生的效果有时比换模型更明显。6. 从验证集走向落地导出、推理与迭代调优的收尾建议验证指标达标后就要把模型从 PyTorch 环境里搬出去落到实际推理链路里。这一步我建议只做两件事导出 ONNX 并在目标端部署再定义一个伪标注迭代流程把新数据回收进训练集。6.1 导出 ONNX绕开 Python 推理的三种坑导出命令很简单但有几个坑要提前知道。第一导出时输入尺寸必须和训练时一致否则动态尺寸会引入额外处理逻辑第二opset版本建议固定为 12 或 13太高版本在部分推理框架里兼容性差第三导出后一定要用 ONNX Runtime 跑一遍验证集确认推理结果和 PyTorch 输出一致。yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz1024 opset12导出完成后用 Python 做一次快速验证import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(best.onnx) img cv2.imread(boat.jpg) img cv2.resize(img, (1024, 1024)) blob img[:, :, ::-1].transpose(2, 0, 1)[None] / 255.0 out sess.run(None, {sess.get_inputs()[0].name: blob.astype(np.float32)})[0]注意这里的预处理是反序 BGR 转 RGB除以 255 归一化input 是 NCHW 格式漏掉任何一步结果都会偏移。ONNX 的模型输出是一个二维数组每一行是[x1, y1, x2, y2, conf, cls_id, cls_conf]需要对它做 NMS非极大值抑制后才能得到最终框。如果你接的是 TensorRT 或者 OpenVINONMS 部分可能要自己写这也是部署时最常出问题的地方。6.2 半自动标注迭代把测试集里的新框补回训练集训练完成后我一般会把模型扔到最新的实拍数据上跑一遍用高置信度预测结果生成伪标注然后人工抽检后加入训练集这是最常见的数据闭环玩法。关键不是“标注多准”而是“怎么挑哪些框可以自动进训练集”。我的标准是置信度大于 0.8、框宽高大于 20 像素、与已有标注无重叠的框直接合并进训练数据置信度在 0.5 到 0.8 之间的批量人工过一遍再决定。这种迭代方式对三角洲这种小目标场景特别友好因为第一批模型找到的小目标很多是人工标注时漏掉的把它们补回数据集比重新标注更省时间。伪标注时要注意类别平衡如果模型对某个类别误检率高那一类的伪标注就要更多人工审核不能全信。最后收一个我自己的习惯每次改成新数据分布后我固定会重跑一遍第 2 章的清洗脚本和第 5 章的混淆矩阵检查哪怕只是调了拍摄高度。目标尺寸分布一变之前的训练参数就要重新验证。这套流程跑下来虽然不那么“智能”但每一步都能解释为什么这么做也比闷头调模型结构靠谱得多。希望帮到你也祝你的三角洲模型在验证和实地测试中都能站稳。本文还有配套的精品资源点击获取