ARTICLE DETAIL

资讯详情

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

基于DOTA数据集的YOLO训练实战:预训练参数、源代码与文档说明

基于DOTA数据集的YOLO训练实战:预训练参数、源代码与文档说明 简介本资源面向计算机、电子信息工程、数学等专业的大学生及算法初学者提供基于DOTA数据集的YOLO目标检测完整训练方案可用于课程设计、期末大作业与毕业设计。压缩包共18个文件约517KB包含6个Python脚本、2个cfg网络配置、2个sh训练脚本、1个names类别文件、1个data数据配置及README说明文档等覆盖数据转换、模型配置、训练与测试全流程。资源内含预训练参数与运行结果代码采用参数化编程注释清晰关键参数可方便修改并已通过测试运行验证。读者可据此掌握DOTA遥感数据集的格式转换、YOLOv3与YOLOv3-tiny两种配置的训练部署方法理解目标检测从数据准备到推理输出的完整链路同时获得可复用的脚本工具与排错思路。目前已有1323人学习下载适合希望快速上手YOLO实战的读者参考。1. DOTA 数据集 YOLO 训练这套组合到底解决什么问题遥感图像里的目标检测和自然图像完全不是一回事。DOTA 数据集里的飞机、舰船、储油罐、棒球场动辄几百像素宽长宽比能到 1:10而且全是俯视视角、任意朝向。拿 COCO 上训出来的 YOLO 权重直接推理框会歪、会漏、会把一排飞机当成一个目标。这套「基于 DOTA 数据集的 YOLO 训练 预训练参数 源代码 文档说明」要解决的就是把通用 YOLO 改造成能吃 DOTA 格式、能出旋转框、能复现的训练工程。适合两类人一类是刚拿到 DOTA 数据、想跑通第一个 baseline 的算法工程师另一类是手里有遥感业务需求、需要一套能改能调的代码骨架的开发者。下面按数据准备、配置、训练、验证、避坑的顺序讲透。2. DOTA 数据集的格式转换与 YOLO 训练前处理2.1 DOTA 原始标注长什么样为什么不能直接喂给 YOLODOTA 的标注文件是labelTxt目录下的.txt每行格式是x1 y1 x2 y2 x3 y3 x4 y4 category difficult。四个点是旋转矩形的四个角按顺时针排列difficult为 1 表示难样本训练时通常忽略。问题在于标准 YOLO 只认水平框格式是class cx cy w h归一化到 0-1。所以中间必须做一次转换而且转换方式决定了你后面走哪条技术路线。常见做法有两条路。第一条是「旋转框路线」用DOTA_devkit里的poly2rbox把四点转成cx cy w h angle配合 MMRotate 或 YOLO 的旋转框分支训练。第二条是「水平框路线」用四点算外接水平矩形直接转成 YOLO 格式简单但会引入背景冗余密集场景下框会重叠。我一般建议先跑通水平框路线拿到 baseline再上旋转框。下面给的是水平框转换脚本因为它最容易复现。2.2 用 Python 把 DOTA 四点标注转成 YOLO 格式import os import cv2 import numpy as np # DOTA 类别列表顺序必须和训练时的 data.yaml 一致 CLASSES [plane, ship, storage-tank, baseball-diamond, tennis-court, basketball-court, ground-track-field, harbor, bridge, large-vehicle, small-vehicle, helicopter, roundabout, soccer-ball-field, swimming-pool] def dota_to_yolo(label_path, img_w, img_h, out_path, skip_difficultTrue): 把单个 DOTA labelTxt 转成 YOLO txt lines_out [] with open(label_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 10: continue coords list(map(float, parts[:8])) cls_name parts[8] difficult int(parts[9]) if skip_difficult and difficult 1: continue if cls_name not in CLASSES: continue xs coords[0::2] ys coords[1::2] # 四点外接水平矩形 xmin, xmax min(xs), max(xs) ymin, ymax min(ys), max(ys) # 归一化并转 cx cy w h cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h cls_id CLASSES.index(cls_name) lines_out.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines_out)) def batch_convert(img_dir, label_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for name in os.listdir(label_dir): if not name.endswith(.txt): continue stem name[:-4] img_path None for ext in (.png, .jpg, .tif): p os.path.join(img_dir, stem ext) if os.path.exists(p): img_path p break if img_path is None: continue img cv2.imread(img_path) h, w img.shape[:2] dota_to_yolo(os.path.join(label_dir, name), w, h, os.path.join(out_dir, stem .txt)) if __name__ __main__: batch_convert(./DOTA/train/images, ./DOTA/train/labelTxt, ./DOTA/train/labels)逻辑说明dota_to_yolo逐行读标注先过滤difficult1和不在类别表里的行再用四点坐标的 min/max 算外接矩形最后除以图像宽高做归一化。batch_convert负责遍历目录、匹配图像尺寸。参数上skip_difficult控制是否丢弃难样本做 baseline 时建议 True想冲精度再设 False。CLASSES的顺序是硬约束写错一位训练出来的类别就全乱。2.3 大图切分DOTA 原图 4000×4000 必须切DOTA 的原始图像普遍在 4000×4000 左右直接缩到 640 训练小目标small-vehicle会缩成几个像素等于白送。标准做法是切分成 1024×1024 带 overlap 的子图overlap 一般设 200。切分时要注意跨子图的目标会被切断需要在切分脚本里做「保留中心点在子图内的目标」策略否则同一个目标在两张子图里各出现一半标注就废了。# 用 DOTA_devkit 的 split 工具常见调用方式 python DOTA_devkit/split.py \ --src ./DOTA/train/images \ --dst ./DOTA/train/split \ --subsize 1024 \ --gap 200subsize是子图边长gap是重叠像素。切完记得同步切标注再跑一遍 2.2 的转换脚本。这一步的坑在于切分后图像数量会膨胀 20 倍以上磁盘和显存都要提前算好。3. 预训练参数怎么选从 COCO 权重到 DOTA 微调3.1 为什么不能从零训预训练权重到底带来什么遥感数据集标注成本极高DOTA 全量也就两万多张图从零训 YOLO 大概率欠拟合。预训练权重的作用是提供通用的边缘、纹理、形状特征让模型在少量数据上快速收敛。常见做法是加载 COCO 预训练的 YOLO 权重替换检测头再在 DOTA 上微调。这里有个反直觉的点COCO 的类别和 DOTA 完全不重叠但骨干网络的特征依然有效所以「换头不换骨干」是标准操作。选权重时看三点一是骨干规模n/s/m/l/x 里遥感小目标多我一般从 m 起步显存够就上 l二是输入分辨率COCO 权重是 640 训的DOTA 切图后建议 1024需要改imgsz三是 anchorYOLOv5/v8 系列是 anchor-free 或自适应 anchor不用手动改但老版本 v3/v4 必须重新聚类 anchor。3.2 训练配置文件的必调参数# data/dota.yaml path: ./DOTA/split train: images/train val: images/val nc: 15 names: [plane, ship, storage-tank, baseball-diamond, tennis-court, basketball-court, ground-track-field, harbor, bridge, large-vehicle, small-vehicle, helicopter, roundabout, soccer-ball-field, swimming-pool]# 训练命令以 YOLOv8 为例 yolo detect train \ modelyolov8m.pt \ datadata/dota.yaml \ imgsz1024 \ epochs100 \ batch8 \ lr00.01 \ lrf0.01 \ warmup_epochs3 \ cos_lrTrue \ cacheTrue \ pretrainedTrue \ device0参数说明imgsz1024是遥感场景的关键640 会丢小目标batch8是 1024 分辨率下 16G 显存的保守值显存不够就降到 4 并开ampTruelr00.01是微调的常见起点比从零训的 0.01 略低更稳lrf0.01控制余弦退火的终点学习率warmup_epochs3让前几个 epoch 慢慢升学习率避免预训练权重被大梯度冲垮cacheTrue把图像缓存到内存切图后数据量大这个能省不少 IO 时间。3.3 冻结策略前 N 层冻不冻微调时常见两种策略。一是全量微调所有层都更新适合数据量够切图后上万张的情况。二是冻结骨干前若干层只训检测头和后几层适合数据少、怕过拟合。YOLOv8 里可以用freeze参数yolo detect train modelyolov8m.pt datadata/dota.yaml \ imgsz1024 epochs100 batch8 freeze10freeze10表示冻结前 10 层。我的经验是DOTA 切图后数据量通常够先全量微调跑一轮看 mAP如果验证集掉得厉害再考虑冻结。冻结太多反而限制模型适应遥感域。4. 训练过程监控与验证指标怎么看4.1 loss 曲线的正常形态和异常信号YOLO 训练输出三个 lossbox_loss、cls_loss、dfl_loss。正常形态是前 10 个 epoch 快速下降之后缓慢收敛。要警惕的信号box_loss 震荡不降多半是学习率太大或标注框有问题cls_loss 突然飙升检查类别 id 是否越界dfl_loss 长期高位说明框回归没学好可能是 imgsz 太小或 anchor 不匹配。我一般会在第 20、50、80 epoch 各存一次权重方便回滚。4.2 mAP 在 DOTA 上的解读陷阱DOTA 官方评测用的是旋转框 mAP如果你走的是水平框路线mAP 会虚高因为水平框把背景也算进 IoU 了。看指标时注意mAP50在密集小目标场景下参考价值有限重点看mAP50-95small-vehicle和large-vehicle这两类的 AP 差距能反映模型对小目标的敏感度如果plane的 AP 很高但ship很低多半是舰船长宽比大、水平框回归不准。# 验证命令 yolo detect val modelruns/detect/train/weights/best.pt \ datadata/dota.yaml imgsz1024 batch8验证时imgsz必须和训练一致改成 640 验证会得到偏低的 mAP别被这个数字骗了。5. 避坑与排查DOTA YOLO 训练里最容易翻车的 5 个点5.1 类别顺序不一致导致全盘错乱现象训练 loss 正常下降但验证时所有预测框的类别都是乱的mAP 接近 0。原因data.yaml里的names顺序和转换脚本里的CLASSES顺序不一致或者和预训练权重的类别映射冲突。解决把类别列表抽成一个单独的classes.py转换脚本和 yaml 都从它导入杜绝手写两遍。5.2 切图后标注没同步出现空标签现象训练时大量图像没有对应 label 文件YOLO 会警告No labels found模型学不到东西。原因切图脚本只切了图像标注文件没跟着切或者切完没重新跑转换。解决切图流程写成一条链——切图 → 切标注 → 转 YOLO 格式 → 校验每张图都有对应 txt。校验脚本很简单遍历 images 目录检查 labels 目录同名文件是否存在且非空。5.3 显存溢出OOM的三种触发方式现象训练中途报 CUDA out of memory。原因一imgsz1024batch8在 16G 卡上本来就紧加上cacheTrue把数据全塞内存容易崩。原因二DOTA 切图后有些子图目标特别多单张图的 loss 计算峰值显存高。原因三验证阶段 batch 没调小。解决先降 batch 到 4开ampTrue混合精度cache改成ram或关掉验证时单独设val_batch2。5.4 小目标 AP 始终上不去现象small-vehicle的 AP 卡在 0.3 以下怎么调都上不去。原因切图尺寸太大导致小目标缩得太小或者 anchor 尺度不匹配。解决把subsize从 1024 降到 512让 small-vehicle 在子图里占比更大或者用 YOLOv8 的 P2 检测头增加一个更高分辨率的特征层代价是显存和推理时间上升。5.5 预训练权重加载了但没生效现象训练日志显示加载了yolov8m.pt但收敛速度和从零训差不多。原因pretrainedTrue和modelyolov8m.pt同时用时某些版本会以model为准但只加载骨干检测头随机初始化如果学习率没调低随机头的大梯度会把骨干也带偏。解决确认日志里Transferred X/Y items的数字Y 应该接近总层数微调时lr0设小一点0.001~0.01warmup 拉长到 5 个 epoch。6. 进阶技巧用旋转框和 TTA 把 DOTA 精度再抬一档水平框跑通之后想再往上走旋转框是绕不开的。DOTA 里舰船、桥梁、大型车辆的长宽比极大水平框的 IoU 天然吃亏。常见做法是切到 MMRotate 框架用poly2rbox把四点转成cx cy w h angle配 Rotated Faster R-CNN 或旋转框版 YOLO。转换时角度定义要统一DOTA devkit 用的是le90角度范围 -90 到 0MMRotate 默认也是le90但有些实现用le135混用会让框转 90 度。# poly2rbox 的核心逻辑简化版 def poly2rbox(polys): rboxes [] for poly in polys: poly np.array(poly).reshape(-1, 2) # 用 OpenCV 最小外接矩形 (cx, cy), (w, h), angle cv2.minAreaRect(poly.astype(np.float32)) # 统一到 le90角度限制在 [-90, 0) if w h: w, h h, w angle 90 if angle 0: angle - 90 rboxes.append([cx, cy, w, h, angle]) return np.array(rboxes)逻辑说明cv2.minAreaRect返回的角度范围是 [0, 90)需要手动归一化到 le90。w h时交换宽高并补 90 度保证长边始终是 w。这个细节不处理训练时角度回归会震荡。另一个提精度的技巧是 TTA测试时增强。推理时把图像旋转 90、180、270 度各跑一遍再把旋转框转回原角度做 NMS 融合。代价是推理时间翻 4 倍但 mAP 通常能涨 1~2 个点。我一般只在最终提交或验收时开 TTA日常调试不开。最后说个我踩过的坑DOTA 的验证集和测试集划分官方给的train/valsplit 里同一张大图切出来的子图可能同时出现在训练和验证集导致验证 mAP 虚高。正确做法是按原始大图划分再各自切图。这个细节不注意线下 0.8 线上 0.5 的落差能让你怀疑人生。希望帮到你。本文还有配套的精品资源点击获取
返回列表