ARTICLE DETAIL

资讯详情

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

RSOD遥感数据集YOLO训练实战:从XML转换到小目标调优

RSOD遥感数据集YOLO训练实战:从XML转换到小目标调优 简介本资源为面向目标检测学习者的RSOD遥感检测数据集采用PASCAL VOC标注格式覆盖飞机、油箱、运动场和立交桥四类目标适合计算机、电子信息工程、数学等专业学生用于课程设计、期末大作业或毕业设计也可供算法入门者练手YOLO训练与评估。压缩包共1912个文件含976张jpg图像与936个xml标注文件图像与标注一一对应整体约308.63MB解压后可直接投入训练流程省去自行标注与格式转换的环节。目前已有579人学习下载说明该数据集在遥感小目标检测场景中具备一定参考价值。读者可据此快速搭建数据加载、模型训练与指标验证的完整链路并结合清晰的代码思路与注释理解参数化配置的调整方式为后续更换类别或扩充数据提供可复用的工程模板。1. 遥感目标检测落地RSOD 数据集与 YOLO 训练的第一道坎拿到一个标注好的遥感数据集比拿到一个没标注的航拍压缩包要省心得多但省心不等于能直接跑出结果。RSOD 遥感检测数据集通常包含 1000 张左右图像和对应的 XML 标注文件覆盖飞机、油罐、立交桥、操场等典型遥感目标标注格式是 PASCAL VOC 风格的 XML。很多人第一次做遥感目标检测卡住的地方不是模型结构而是「XML 里的坐标到底怎么变成 YOLO 能吃的 txt」以及「遥感图像的小目标为什么一上来就掉点」。这篇笔记就围绕 RSOD 数据集 YOLO 这条链路把目录结构、格式转换、训练参数、验证方法和踩坑记录一次讲透。适合刚接触遥感目标检测的工程师也适合手里已经有这套数据、想快速跑通 baseline 的人。2. RSOD 数据集拆开看目录、XML 结构与类别分布2.1 解压后先别急着训练先确认三件事拿到.rar之后第一步不是装环境而是解压后把目录结构看清楚。RSOD 这类数据集的常见组织方式有两种一种是images/和annotations/分开图像和 XML 同名不同后缀另一种是按类别分子目录每个子目录里图像和 XML 混放。两种都能用但转换脚本写法不同。先执行# 查看解压后的顶层结构确认图像和标注是否分离 find ./RSOD -maxdepth 2 -type d | sort # 统计图像数量确认是否与标注数量一致 find ./RSOD -name *.jpg -o -name *.png | wc -l find ./RSOD -name *.xml | wc -l图像数和 XML 数必须一致如果不一致说明有图像漏标或者 XML 命名不匹配。遥感数据集里这种情况不少见尤其是从多个来源拼凑的版本。数量对不上时不要硬转先按文件名求差集把没有对应 XML 的图像挑出来。import os img_dir ./RSOD/images xml_dir ./RSOD/annotations imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir)} xmls {os.path.splitext(f)[0] for f in os.listdir(xml_dir)} print(只有图像没有标注:, imgs - xmls) print(只有标注没有图像:, xmls - imgs)这段脚本用集合差集找出不配对的文件。参数上只需要改img_dir和xml_dir如果你的数据集是图像和 XML 同目录把两个路径都指向同一目录即可。差集结果里如果出现大量文件优先怀疑扩展名大小写问题比如.JPG和.jpg被当成不同文件。2.2 XML 里到底存了什么一次解析看清字段PASCAL VOC 格式的 XML 核心字段就几个filename、size下的width和height、以及每个object下的name和bndbox。遥感数据集的坑在于bndbox的坐标可能是浮点数也可能是越界值甚至有的标注工具会写出xmin xmax的情况。import xml.etree.ElementTree as ET def parse_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) objs [] for obj in root.findall(object): name obj.find(name).text box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) objs.append((name, xmin, ymin, xmax, ymax)) return w, h, objs w, h, objs parse_xml(./RSOD/annotations/00001.xml) print(图像尺寸:, w, h) for o in objs: print(o)解析时把坐标统一转成float因为后续要判断越界和做归一化。size字段必须读不能假设所有图像都是 640×640遥感图像的尺寸往往不统一。如果size缺失就得用 PIL 或 OpenCV 重新读图像拿宽高否则归一化会全错。2.3 类别分布决定你的训练策略RSOD 的类别通常不多但分布极不均衡。飞机和油罐可能占大多数立交桥和操场样本很少。训练前先统计每个类别的框数量from collections import Counter counter Counter() for xml_file in os.listdir(xml_dir): _, _, objs parse_xml(os.path.join(xml_dir, xml_file)) for o in objs: counter[o[0]] 1 for k, v in counter.most_common(): print(k, v)如果某个类别少于 50 个框直接按默认参数训练大概率会掉点。常见做法是过采样少数类图像或者在损失里给少数类更高权重。YOLO 本身没有直接的类别权重参数但可以通过复制少数类图像到训练集来近似。注意验证集不要过采样否则指标会虚高。3. XML 转 YOLO txt转换脚本与四个边界坑3.1 转换逻辑归一化坐标和类别索引YOLO 的标注格式是每行class_id x_center y_center width height全部归一化到 0 到 1。转换时最容易错的是x_center和width的计算以及类别索引要从 0 开始。import os import xml.etree.ElementTree as ET classes [aircraft, oiltank, overpass, playground] class_to_id {c: i for i, c in enumerate(classes)} def convert(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w float(size.find(width).text) h float(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_to_id: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 裁剪越界坐标 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) if xmax xmin or ymax ymin: continue xc (xmin xmax) / 2.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_to_id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue name os.path.splitext(xml_file)[0] convert(os.path.join(xml_dir, xml_file), f./labels/{name}.txt)classes列表必须和后续训练时的data.yaml完全一致顺序错了模型学到的类别就是乱的。坐标裁剪放在归一化之前避免出现负数或大于 1 的值。if xmax xmin这行是后悔药遥感标注里偶尔会有零宽框不跳过会让训练直接报 NaN。3.2 四个边界坑越界、零宽、类别名空格、文件名不匹配第一个坑是越界坐标。遥感图像拼接时边缘目标常被裁掉一半标注框会超出图像范围。不裁剪的话归一化后坐标大于 1YOLO 虽然不报错但损失计算会异常。第二个坑是零宽或零高框。有些标注工具在目标极小时会写出xmin xmax这种框没有面积必须跳过。第三个坑是类别名带空格。比如oil tank和oiltank会被当成两个类。转换前统一做strip()和替换空格。第四个坑是文件名不匹配。图像是0001.jpgXML 是0001.xml但标签输出成了0001.txt而 YOLO 找标签时是按图像路径替换后缀。如果图像在images/而标签在labels/目录结构必须符合 YOLO 的约定否则训练时找不到标签全部当背景处理指标直接归零。3.3 生成 data.yaml 和目录划分YOLO 训练需要data.yaml指定路径和类别。目录按images/train、images/val、labels/train、labels/val组织。# 按 8:2 划分先建目录 mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 简单划分示例实际用 Python 更可控 ls RSOD/images | head -800 | while read f; do cp RSOD/images/$f dataset/images/train/ cp labels/${f%.*}.txt dataset/labels/train/ done# data.yaml path: ./dataset train: images/train val: images/val nc: 4 names: [aircraft, oiltank, overpass, playground]nc必须等于names长度names顺序必须和转换脚本里的classes一致。path用相对路径时训练命令要在data.yaml同级目录执行否则路径解析会出错。4. YOLO 训练 RSOD参数怎么设、指标怎么看4.1 环境配置与最小训练命令YOLO 现在主流用 Ultralytics 版本安装和训练命令都很短。遥感图像分辨率通常比 COCO 大但 RSOD 的 1000 张图尺寸不一训练时统一 resize 到 640。pip install ultralytics yolo detect train \ data./data.yaml \ modelyolov8n.pt \ imgsz640 \ epochs100 \ batch16 \ lr00.01 \ patience20 \ projectrsod_run \ namebaselinemodel选yolov8n还是yolov8s取决于显存。1000 张图用 nano 足够跑通 baseline想提点再换 s。imgsz640是起点遥感小目标多时可以试 1024但显存翻倍。patience20表示 20 轮没提升就早停避免过拟合。lr00.01是默认值数据量小的时候可以降到 0.001。4.2 遥感小目标为什么掉点三个可调参数遥感图像里飞机、油罐在 640 分辨率下可能只有十几个像素YOLO 的 stride 8 特征图勉强能覆盖。掉点的常见原因和对策第一imgsz太小。把 640 提到 1024 或 1280小目标召回率会明显上升但显存和训练时间增加。如果显存不够用batch8配合梯度累积。第二数据增强过猛。YOLO 默认的mosaic和mixup对小目标不友好拼接后目标更小。可以关掉mixup把mosaic概率降到 0.5。yolo detect train \ data./data.yaml \ modelyolov8s.pt \ imgsz1024 \ batch8 \ mosaic0.5 \ mixup0.0 \ epochs150第三锚框不匹配。YOLOv8 是无锚框设计但特征层分配仍受目标尺寸影响。小目标多时可以尝试yolov8s而不是 nano因为 s 的通道数更多浅层特征表达能力更强。4.3 看指标mAP50 和 mAP50-95 哪个先动训练日志里重点看metrics/mAP50(B)和metrics/mAP50-95(B)。遥感数据集上mAP50 通常先涨mAP50-95 涨得慢因为小目标的定位精度难做高。如果 mAP50 到 0.7 以上但 mAP50-95 只有 0.3说明框的位置还不够准可以检查标注质量或者把imgsz再提一档。验证时用yolo detect val \ modelrsod_run/baseline/weights/best.pt \ data./data.yaml \ imgsz1024验证的imgsz最好和训练一致否则指标会掉。如果验证时显存不够可以调小batch但imgsz不要随便改。5. 避坑与排查RSOD 训练里最常见的五条血泪经验5.1 现象训练 loss 一直不降mAP 接近 0原因标签路径不对YOLO 把所有图像当背景。检查labels/train下是否有 txt文件名是否和图像一一对应。YOLO 找标签的规则是图像路径把images替换成labels后缀换成.txt。如果目录结构是images/train/0001.jpg标签必须在labels/train/0001.txt。解决用yolo detect train时加verboseTrue看日志里train和val的样本数。如果样本数为 0就是路径问题。5.2 现象训练到一半 loss 变 NaN原因标注里有零宽框或越界坐标归一化后出现除零或极大值。遥感数据集里这种脏标注很常见。解决在转换脚本里加裁剪和跳过逻辑转换完再跑一遍校验统计每个 txt 的行数和坐标范围。# 校验所有标签坐标是否在 0 到 1 之间 for txt in os.listdir(./labels/train): with open(f./labels/train/{txt}) as f: for line in f: parts line.strip().split() vals [float(x) for x in parts[1:]] assert all(0 v 1 for v in vals), f{txt} 坐标越界5.3 现象验证集 mAP 很高但实际推理漏检严重原因训练集和验证集划分时同一张图像的不同裁剪块被分到了两边导致数据泄漏。遥感数据集如果是从大图裁出来的必须按原图划分不能按裁剪块随机分。解决划分前先按原始图像分组同一组只进训练或只进验证。如果数据集没有分组信息至少保证文件名前缀相同的进同一侧。5.4 现象训练速度极慢GPU 利用率低原因imgsz设得太大或者batch太小导致频繁读写。遥感图像本身分辨率高如果原图是 4000×4000dataloader 解码就耗掉大量时间。解决训练前把图像统一 resize 到目标尺寸附近比如 1024×1024存成新目录。这样 dataloader 不用每次解码大图。workers可以设成 CPU 核数的 2 倍但不要超过 16。5.5 现象某个类别始终检测不到原因该类样本太少或者类别名在data.yaml和转换脚本里不一致。比如转换时写的是oiltankdata.yaml里写的是oil_tank模型学到的类别索引对不上。解决先统计每个类别的框数少于 50 的做过采样。再核对data.yaml的names和转换脚本的classes是否逐字一致包括大小写。6. 进阶技巧用验证集反推标注质量与模型上限跑通 baseline 之后别急着换模型。RSOD 这种 1000 张规模的数据集模型上限往往由标注质量决定而不是网络结构。我一般会做一件事把验证集里 mAP 最低的 20 张图挑出来用best.pt推理把预测框和真实框画在同一张图上肉眼比对。from ultralytics import YOLO import cv2 model YOLO(rsod_run/baseline/weights/best.pt) results model.predict(./dataset/images/val, saveTrue, conf0.25) # 对单张图看预测和真实标注的差异 img cv2.imread(./dataset/images/val/00001.jpg) with open(./dataset/labels/val/00001.txt) as f: for line in f: cid, xc, yc, bw, bh map(float, line.split()) h, w img.shape[:2] x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(gt_vis.jpg, img)绿色是真实框YOLO 保存的预测图里是彩色框。对比之后常见三种情况真实框本身偏了、真实框漏标了、预测框定位不准。前两种是标注问题换模型解决不了第三种才值得调imgsz或换更大的模型。还有一个习惯训练前把data.yaml的names抄在便签上转换脚本、验证脚本、推理脚本三处逐字核对。我翻车过两次都是因为playground写成了play_ground模型训练正常但推理时类别名对不上排查了半天。遥感数据集类别少但正因如此一个类别错位就会让整体 mAP 掉一大截。最后说一个判断值不值得继续投入的标准如果验证集 mAP50 在 0.75 以上mAP50-95 在 0.45 以上这套 RSOD 数据 YOLO 的 baseline 就算立住了可以往上叠注意力机制或换更大模型。如果 mAP50 卡在 0.5 以下先回去查标注别急着改网络。希望帮到你。本文还有配套的精品资源点击获取
返回列表