☰
遥感图像目标检测实战:RSod数据集YOLO训练与避坑指南
2026/9/26 2:11:32 网站建设 项目流程

简介:这份资源面向计算机视觉与遥感图像分析方向的研究者、学生及算法工程师,提供YOLO目标检测训练所需的RSod遥感数据集,覆盖飞机、游乐场、立交桥、油箱四类典型目标,可用于模型训练、验证与课程实验。压缩包共2000个文件,含936个xml标注、937个txt标签与127张jpg图像,xml记录边界框与类别信息,txt便于YOLO系列框架直接读取,整体约308.8MB,目录组织清晰。其中飞机类别含446张图像、4993架目标,油箱类别含165张图像、1586个目标,样本分布差异明显,适合开展类别不均衡与密集小目标检测的对比实验。已有101人学习下载,读者可据此快速搭建数据管线,完成标注解析、格式转换与模型评估,为遥感目标检测研究提供可直接复用的数据基础。

1. 遥感图像 RSod 数据集:为什么它值得你花一个下午跑通

如果你正在做遥感图像目标检测,大概率经历过这样的场景:网上找了一圈公开数据集,要么是 DIOR 那种几十 G 的巨无霸,下载完解压就得等半天;要么是 DOTA 那种带旋转框的,光格式转换就能耗掉一整天。而 RSod 这个数据集,恰好卡在一个很舒服的位置——体量适中、标注完整、同时提供 xml 和 txt 两种标签格式,拿来跑 YOLO 系列的训练和验证非常顺手。

这份资源是一个压缩包,里面包含遥感图像的原始图片,以及每张图对应的 xml 标注文件和已经转好的 txt 标签文件。xml 是 PASCAL VOC 格式,txt 是 YOLO 训练直接能吃的归一化坐标格式。也就是说,你拿到手之后不需要再写转换脚本,直接配好 data.yaml 就能开训。适合谁?刚入门遥感目标检测、想快速验证模型效果的人;需要一个小规模数据集做消融实验的研究者;以及想拿它做预训练模型微调的工程师。

2. 拆开压缩包:目录结构、标签格式与类别分布

2.1 解压后你会看到什么

拿到 .rar 文件后,Windows 下用 WinRAR 或 7-Zip 解压,Linux 下需要先装 unrar。解压后的目录结构通常是这样:

RSod/ ├── JPEGImages/ # 原始遥感图像,jpg 格式 ├── Annotations/ # PASCAL VOC 格式的 xml 标注 ├── labels/ # YOLO 格式的 txt 标签 └── ImageSets/ └── Main/ # 训练/验证集划分文件(部分版本有)

JPEGImages 里是遥感图像,分辨率不固定,常见在 1000×1000 左右。Annotations 里每个 xml 对应一张图,记录了每个目标框的类别和 bbox 坐标(xmin, ymin, xmax, ymax)。labels 里每个 txt 对应一张图,每行格式是class_id x_center y_center width height,坐标已经归一化到 0~1 之间。

注意:不同来源的 RSod 包目录名可能略有差异,有的把图片放在 images/ 下,有的把标签放在 labels/ 下。拿到手先ls或tree看一眼,别急着写脚本。

2.2 xml 和 txt 两种标签的对应关系

xml 是给人看的,txt 是给 YOLO 吃的。理解它们之间的转换逻辑,后面排查问题会快很多。xml 里的 bbox 是绝对像素坐标,txt 里是归一化后的中心点坐标加宽高。转换公式如下:

# xml -> yolo txt 转换核心逻辑 # bbox: xmin, ymin, xmax, ymax (绝对像素) # img_w, img_h: 图像宽高 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h

这段逻辑看着简单,但有两个容易翻车的地方:一是 xml 里可能存在 xmax 小于 xmin 的脏数据,二是归一化后坐标可能超出 [0,1] 范围。后面避坑章节会细说。

2.3 类别分布与典型目标

RSod 数据集通常包含几类典型的遥感目标:飞机、油罐、立交桥、操场等。具体类别数以你拿到的版本为准,常见是 4 类左右。每类目标的数量分布不均匀,飞机和油罐通常占大头,立交桥和操场相对少一些。这个分布特点意味着训练时容易出现类别不平衡,mAP 被多数类拉高、少数类拉胯的情况。

类别典型尺寸(像素)特点
飞机50~200方向多样,密集排列
油罐30~100圆形,背景干扰少
立交桥200~600尺度大,形状复杂
操场300~800纹理单一,边界模糊

这张表不是让你背的,是让你在调 anchor 和做数据增强时心里有数。小目标多的类别,anchor 要往小调;大目标多的,输入分辨率别设太低。

3. 从零跑通 YOLO 训练:环境、配置与启动命令

3.1 环境准备与依赖安装

假设你用 Ultralytics 的 YOLOv8 或 YOLOv11,这是目前最省事的方案。Python 版本建议 3.8~3.10,太高了有些依赖轮子还没跟上。CUDA 版本根据你显卡驱动来,V100 上跑 CUDA 11.8 很稳。

# 创建虚拟环境 conda create -n rsod_yolo python=3.9 -y conda activate rsod_yolo # 安装 ultralytics(会自动装 torch 等依赖) pip install ultralytics # 验证安装 yolo checks

yolo checks会输出你的 torch 版本、CUDA 是否可用、GPU 型号。如果 CUDA 显示不可用,先别急着跑训练,把驱动和 torch 版本对齐再说。这一步偷懒,后面训练时 loss 不降你都不知道是数据问题还是环境问题。

3.2 数据集配置文件 data.yaml 怎么写

YOLO 训练需要一个 yaml 文件告诉它去哪里找图片和标签。在 RSod 根目录下新建rsod.yaml:

# rsod.yaml path: /home/user/RSod # 数据集根目录,改成你的实际路径 train: JPEGImages # 训练集图片目录(相对 path) val: JPEGImages # 验证集图片目录,小数据集可以先复用 nc: 4 # 类别数,根据你的版本改 names: # 类别名,顺序必须和 txt 里的 class_id 对应 0: airplane 1: oil_tank 2: overpass 3: playground

这里有个血泪经验:names的顺序不是随便写的,它必须和 txt 标签里 class_id 的映射关系一致。如果你拿到的 txt 是别人转好的,先打开几个文件看看 class_id 是 0 开始还是 1 开始,然后反推 names 的顺序。搞错了,模型训出来的类别全是乱的。

3.3 启动训练与关键参数解读

配置文件写好后,一行命令就能开训:

yolo detect train \ data=rsod.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ device=0

逐项说下参数含义。model=yolov8n.pt用的是 nano 版本预训练权重,小数据集上够用,想刷精度换yolov8m.pt或yolov8l.pt。imgsz=640是输入分辨率,遥感图像目标偏小的话可以提到 1024,但显存占用会翻倍。batch=16在 V100 上跑 640 分辨率很轻松,显存不够就降到 8。lr0=0.01是初始学习率,小数据集上可以降到 0.001 避免过拟合。device=0指定第一块 GPU。

训练过程中重点看三个指标:box_loss、cls_loss、mAP50。box_loss 不降说明 anchor 和你的目标尺寸不匹配;cls_loss 震荡说明类别不平衡或者学习率太大;mAP50 卡在某个值上不去,大概率是数据量不够或者标注有噪声。

3.4 训练完怎么看结果

训练结束后,runs/detect/train/目录下会有权重文件和结果图。weights/best.pt是验证集上表现最好的权重,results.png里画了 loss 和 mAP 曲线。先看曲线有没有过拟合迹象——训练 loss 一直降但验证 mAP 开始掉,那就是过拟合了,减少 epochs 或者加数据增强。

# 用训练好的权重做推理 yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=RSod/JPEGImages \ conf=0.25 \ save=True

conf=0.25是置信度阈值,遥感图像里小目标多,阈值设太高会漏检,设太低误检多。建议从 0.25 开始,根据实际效果微调。

4. 避坑与排查:xml 解析、标签对齐和训练崩溃的常见问题

4.1 xml 解析报错:文件编码和标签缺失

现象:用xml.etree.ElementTree解析时报ParseError,或者读出来的 bbox 全是 None。

原因:遥感数据集的 xml 有些是用 GBK 编码保存的,Python 默认按 UTF-8 读就会炸。另外有些 xml 里<object>标签不完整,缺<bndbox>或<name>。

解决:读文件时显式指定编码,解析前先检查关键标签是否存在。

import xml.etree.ElementTree as ET def parse_xml(xml_path): # 常见做法是先用二进制读,再尝试不同编码 with open(xml_path, 'rb') as f: raw = f.read() try: tree = ET.fromstring(raw.decode('utf-8')) except UnicodeDecodeError: tree = ET.fromstring(raw.decode('gbk')) for obj in tree.findall('object'): name = obj.findtext('name') bndbox = obj.find('bndbox') if bndbox is None: continue # 跳过不完整的标注 xmin = int(bndbox.findtext('xmin')) # ... 继续解析

4.2 标签对齐翻车:class_id 从 0 还是 1 开始

现象:训练完模型推理时,明明图里有飞机,却标成了油罐。

原因:xml 里的类别名是字符串,转 txt 时需要映射成数字。不同转换脚本有的从 0 开始编号,有的从 1 开始。你的 data.yaml 里 names 顺序如果和 txt 里的编号对不上,类别就全乱了。

解决:打开几个 txt 文件,看第一列的数字范围。如果是 0~3,names 就从 0 开始写;如果是 1~4,要么改 names,要么把 txt 里所有 class_id 减 1。我一般会写个脚本统计所有 txt 里出现的 class_id 集合,确认无误再开训。

4.3 训练中 BN 崩溃:batch size 太小惹的祸

现象:训练几个 epoch 后 loss 突然变成 NaN,报错信息里有BatchNorm相关字样。

原因:YOLO 的 backbone 里有大量 BatchNorm 层,batch size 太小时(比如 2 或 4),一个 batch 内的统计量不稳定,running_mean 和 running_var 跑飞了。

解决:把 batch 调到 8 以上,显存不够就降 imgsz 或者用梯度累积。如果实在只能用小 batch,可以把 BN 换成 GroupNorm,但这需要改模型结构,不如直接换小一号的模型。

4.4 验证集 mAP 为 0:路径和缓存问题

现象:训练 loss 正常下降,但验证集 mAP 一直是 0。

原因:data.yaml 里 val 路径写错了,或者 YOLO 缓存了旧的标签文件。Ultralytics 会在数据集目录下生成.cache文件,你改了标签但缓存没更新,它还在用旧数据。

解决:先确认 val 路径下的图片和标签能一一对应,然后删掉labels.cache和JPEGImages.cache,重新跑。这个坑我踩过不止一次,明明标签改对了,mAP 就是不动,删缓存立马正常。

4.5 显存溢出:imgsz 和 batch 的平衡

现象:训练启动就报CUDA out of memory。

原因:imgsz 和 batch 乘积太大,或者模型选得太大。V100 16G 显存跑 yolov8l + imgsz=1024 + batch=16 肯定爆。

解决:先降 batch 到 8,还爆就降 imgsz 到 640。或者换 yolov8s/yolov8n。遥感图像目标普遍偏小,imgsz 不建议低于 640,再低小目标就成几个像素了,模型根本学不到。

5. 进阶技巧:用 xml 做数据清洗和难例挖掘

5.1 从 xml 里统计目标尺寸分布

txt 标签虽然训练方便,但做数据分析时 xml 更直观,因为它是绝对坐标,不用反归一化。我一般会写个脚本统计所有目标的宽高分布,用来决定 anchor 尺寸和 imgsz。

import xml.etree.ElementTree as ET import glob import numpy as np widths, heights = [], [] for xml_path in glob.glob('RSod/Annotations/*.xml'): tree = ET.parse(xml_path) for obj in tree.findall('object'): bndbox = obj.find('bndbox') w = int(bndbox.findtext('xmax')) - int(bndbox.findtext('xmin')) h = int(bndbox.findtext('ymax')) - int(bndbox.findtext('ymin')) widths.append(w) heights.append(h) print(f"宽度分布: 中位数={np.median(widths):.0f}, 90分位={np.percentile(widths, 90):.0f}") print(f"高度分布: 中位数={np.median(heights):.0f}, 90分位={np.percentile(heights, 90):.0f}")

如果 90 分位宽度超过 300 像素,说明大目标不少,imgsz 别设太低。如果中位数只有 40 像素,那就是小目标为主,考虑用 P2 小目标检测层或者切片推理。

5.2 用 xml 交叉验证 txt 标签的正确性

txt 是别人转好的,但你不知道转的时候有没有出错。最稳妥的办法是拿 xml 重新转一遍,和现有 txt 做对比。

def xml_to_yolo(xml_path, img_w, img_h, class_map): tree = ET.parse(xml_path) lines = [] for obj in tree.findall('object'): name = obj.findtext('name') if name not in class_map: continue cls_id = class_map[name] bndbox = obj.find('bndbox') xmin = float(bndbox.findtext('xmin')) ymin = float(bndbox.findtext('ymin')) xmax = float(bndbox.findtext('xmax')) ymax = float(bndbox.findtext('ymax')) # 裁剪到图像边界内 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(img_w, xmax), min(img_h, ymax) xc = (xmin + xmax) / 2 / img_w yc = (ymin + ymax) / 2 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") return lines

跑完之后逐行对比,差异超过 0.01 的就人工看一眼。这个步骤花不了多少时间,但能帮你排除掉一批脏标签。从那以后我每次拿到别人转好的标签,都强制走一遍交叉验证,宁可多花半小时,也不想训到一半发现标签是错的。

5.3 难例挖掘:用验证结果反查标注质量

模型训完之后,用 best.pt 在验证集上跑推理,把置信度低但实际有目标的图挑出来。这些图要么是标注漏了,要么是目标太模糊模型学不会。打开对应的 xml 看一眼,如果确实有目标没标,补上;如果是目标本身就看不清,考虑从训练集里剔除。

# 保存推理结果到 txt,方便后续分析 yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=RSod/JPEGImages \ conf=0.1 \ save_txt=True \ save_conf=True

conf=0.1故意设低,让模型多报一些框出来。然后对比预测框和真实标签,漏检多的类别重点补标注,误检多的类别看看是不是背景太相似。这个迭代过程做两三轮,mAP 通常能涨几个点。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询