资讯详情 NWPU VHR-10遥感数据集YOLO训练实战:从标注转换到TensorRT加速
📅 2026/10/7 8:54:13
简介本资源为NWPU VHR-10遥感目标检测数据集的完整标注版本面向计算机、电子信息工程、数学等专业的学生及算法入门者可直接用于YOLO目标检测模型的训练与课程设计、期末大作业、毕业设计等场景。数据集覆盖飞机、轮船、储罐、棒球场、网球场、篮球场、地面跑道、港口、桥梁和车辆共10个类别每张图像均配有对应的XML标注文件省去自行标注的繁琐环节。压缩包共1600个文件包含800张jpg图像与800个xml标注文件整体约73.71MB图像与标注一一对应目录结构清晰便于直接接入YOLO训练流程或转换为其他格式。目前已有1395人学习下载配套代码采用参数化编程参数修改方便注释详细编程思路清晰可帮助读者快速完成数据加载、类别统计与模型训练验证是遥感检测方向较为实用的入门与实战素材。1. 遥感目标检测落地为什么我盯上了这份 NWPU VHR-10 已标注数据做遥感目标检测的同学大概都有过这种体验模型结构调了一整天结果卡在数据上——要么找不到带标注的图要么标注格式和 YOLO 对不上要么类别定义模糊飞机和跑道混在一起。我最近拆了一份 NWPU VHR-10 的已标注数据包800 张遥感图像配套 XML 标注文件覆盖飞机、轮船、储罐、棒球场、网球场、篮球场、地面跑道、港口、桥梁、车辆共 10 个类别。这不是那种需要自己写爬虫、手动框选的半成品而是可以直接喂给 YOLO 训练流程的成品数据。适合谁做课程设计、期末大作业、毕业设计的学生以及需要快速验证检测算法改进效果的工程师。遥感场景和自然图像不一样目标小、方向任意、背景复杂这份数据正好能把这些坑暴露出来。下面我从数据本身讲到训练落地再到实际踩过的坑一步步拆开。2. NWPU VHR-10 数据解剖10 类标注的分布与 YOLO 适配逻辑2.1 数据目录结构与标注格式拿到压缩包解压后常见的目录组织是这样的一个images文件夹放 800 张 JPG 图像一个annotations文件夹放同名的 XML 文件。XML 是 PASCAL VOC 格式每个文件对应一张图里面记录了每个目标的类别名和边界框坐标xmin, ymin, xmax, ymax。文件名像000084.jpg、000311.jpg这种纯数字编号没有多余前缀省去了重命名的麻烦。先确认一下目录层级用一条命令就能看清# 查看解压后的目录结构确认图像和标注是否一一对应 find ./NWPU_VHR-10 -maxdepth 2 -type d # 统计图像数量和XML数量两者应该相等 ls ./NWPU_VHR-10/images/*.jpg | wc -l ls ./NWPU_VHR-10/annotations/*.xml | wc -l逻辑说明find只看两层目录避免输出太多文件wc -l统计数量如果图像和 XML 数量不一致说明有缺失需要先补齐再往下走。参数上没什么要调的重点是确认配对关系。2.2 10 个类别的分布特点与检测难点这 10 个类别在遥感图像里的尺度差异很大。车辆和轮船往往只有几十个像素储罐和飞机稍大棒球场、网球场、篮球场这类场地目标能占到图像的三分之一。这种尺度跨度对 YOLO 的 anchor 设计是个考验。我一般会先跑一遍统计脚本看看每个类别的实例数量和平均框面积import os import xml.etree.ElementTree as ET from collections import defaultdict # 统计每个类别的实例数量和平均边界框面积 class_stats defaultdict(lambda: {count: 0, area_sum: 0.0}) ann_dir ./NWPU_VHR-10/annotations for xml_file in os.listdir(ann_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) area (xmax - xmin) * (ymax - ymin) class_stats[name][count] 1 class_stats[name][area_sum] area for name, stat in sorted(class_stats.items()): avg_area stat[area_sum] / stat[count] if stat[count] 0 else 0 print(f{name}: 实例数{stat[count]}, 平均面积{avg_area:.1f} 像素)逻辑说明用ElementTree解析 XML遍历每个object节点提取类别名和边界框坐标累加实例数和面积。参数上area是像素面积能帮你判断哪些类别属于小目标。如果车辆的平均面积低于 32×32就得考虑在 YOLO 里增加小目标检测层或者调整输入分辨率。2.3 从 VOC XML 到 YOLO TXT转换脚本与类别映射YOLO 训练需要的是每张图对应一个 TXT 文件每行格式为类别索引 中心x 中心y 宽 高且坐标要归一化到 0~1。类别索引从 0 开始按你定义的类别顺序来。下面这个脚本我用了很多次直接改路径就能跑import os import xml.etree.ElementTree as ET # 类别列表顺序要和训练时的 data.yaml 一致 classes [airplane, ship, storage_tank, baseball_diamond, tennis_court, basketball_court, ground_track_field, harbor, bridge, vehicle] def convert_annotation(xml_path, txt_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并计算中心点、宽高 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) # 假设图像尺寸已知实际可从图像读取 img_w, img_h 640, 640 # 这里需要根据实际图像尺寸修改 ann_dir ./NWPU_VHR-10/annotations out_dir ./NWPU_VHR-10/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(ann_dir): if xml_file.endswith(.xml): txt_file xml_file.replace(.xml, .txt) convert_annotation(os.path.join(ann_dir, xml_file), os.path.join(out_dir, txt_file), img_w, img_h)逻辑说明classes列表的顺序决定了 TXT 里的类别索引必须和后续data.yaml里的names完全一致否则训练时类别会错位。img_w和img_h要换成每张图的实际尺寸如果所有图像尺寸统一直接写死就行如果不统一得用 OpenCV 或 PIL 逐张读取。归一化后的坐标保留 6 位小数足够YOLO 官方也是这个精度。3. YOLO 训练配置从 data.yaml 到超参数调优3.1 数据集划分与 data.yaml 编写800 张图不算多按 7:2:1 划分训练集、验证集、测试集比较稳妥。划分时要注意类别均衡别让某个类别在验证集里一个实例都没有。我一般写个简单脚本随机划分然后生成 YOLO 需要的train.txt、val.txt、test.txt每行是图像的绝对路径或相对路径。import os import random # 按 7:2:1 划分数据集生成 YOLO 所需的 txt 列表 img_dir ./NWPU_VHR-10/images all_imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.seed(42) # 固定随机种子保证可复现 random.shuffle(all_imgs) n len(all_imgs) train_imgs all_imgs[:int(0.7 * n)] val_imgs all_imgs[int(0.7 * n):int(0.9 * n)] test_imgs all_imgs[int(0.9 * n):] for name, subset in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: with open(f./NWPU_VHR-10/{name}.txt, w) as f: for img in subset: f.write(os.path.join(img_dir, img) \n)逻辑说明random.seed(42)保证每次划分结果一致方便复现实验。路径写绝对路径或相对路径都行YOLO 的data.yaml里会指定path根目录。划分比例不是死的如果类别实例太少可以适当增加训练集比例。接着写data.yaml# NWPU VHR-10 数据集配置 path: ./NWPU_VHR-10 train: train.txt val: val.txt test: test.txt nc: 10 names: [airplane, ship, storage_tank, baseball_diamond, tennis_court, basketball_court, ground_track_field, harbor, bridge, vehicle]参数说明nc是类别数必须和names长度一致。path是根目录train、val、test是相对路径。如果图像和标签不在同一目录YOLO 默认会去images同级找labels所以目录结构最好是images/train和labels/train这种镜像结构。这份数据原始结构可能不是这样需要手动整理一下。3.2 YOLOv8 训练命令与关键参数YOLOv8 是目前比较稳的选择Ultralytics 的接口也简洁。假设你已经装好了ultralytics包一条命令就能启动训练# 使用 YOLOv8n 预训练权重在 NWPU VHR-10 上微调 yolo detect train \ modelyolov8n.pt \ data./NWPU_VHR-10/data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ device0 \ project./runs/nwpu \ nameyolov8n_exp1参数说明model选yolov8n.pt是因为数据量不大小模型不容易过拟合imgsz640是遥感检测的常用输入尺寸再大显存吃不消batch16在 8GB 显存上比较稳显存小就降到 8lr00.01是初始学习率微调时通常比从头训练小一个数量级patience20表示验证集指标 20 轮不提升就早停省时间。device0指定第一块 GPU没 GPU 就写cpu但速度会慢很多。训练过程中重点看mAP50和mAP50-95两个指标。遥感小目标多mAP50-95通常比自然图像低不少别一看数字低就以为模型崩了。如果mAP50在 0.6 以上说明基本可用低于 0.4 就得检查数据转换和类别映射有没有错。3.3 推理验证与结果可视化训练完先别急着导出模型用验证集跑一遍推理看看实际检测效果# 在验证集上评估模型性能 yolo detect val \ model./runs/nwpu/yolov8n_exp1/weights/best.pt \ data./NWPU_VHR-10/data.yaml \ imgsz640 \ batch16 \ conf0.25 \ iou0.5参数说明conf0.25是置信度阈值低于这个值的框会被过滤iou0.5是 NMS 的 IoU 阈值控制重叠框的合并程度。遥感目标密集时iou可以适当调低到 0.4避免漏检相邻目标。跑完会输出每个类别的精确率、召回率和 mAP重点看车辆、轮船这些小目标的召回率如果明显偏低说明模型对小目标欠拟合。想直观看看检测效果可以用predict模式# 对单张图像或整个文件夹做推理保存可视化结果 yolo detect predict \ model./runs/nwpu/yolov8n_exp1/weights/best.pt \ source./NWPU_VHR-10/images/000084.jpg \ conf0.25 \ saveTrue \ project./runs/nwpu/predict逻辑说明source可以指向单张图、文件夹或视频。saveTrue会把带框的图存到project目录下。检查可视化结果时重点看有没有把棒球场误判成篮球场这两个类别在遥感图像里纹理相似容易混淆。4. 避坑与排查遥感检测训练中常见的 5 个翻车点4.1 类别名大小写不一致导致标注丢失现象转换脚本跑完TXT 文件里某些类别的行数为零训练时这些类别完全学不到。原因XML 里的类别名可能是Airplane或airplane而你的classes列表写的是airplaneif name not in classes直接跳过了。解决在转换脚本里加一行name name.lower().strip()统一转小写并去空格。转换完再统计一遍每个类别的实例数和原始 XML 对比确认没丢。4.2 图像尺寸不统一导致坐标归一化错误现象训练 loss 一直不降或者检测框位置明显偏移。原因脚本里写死了img_w640, img_h640但实际图像尺寸可能是 500×500 或 1024×1024归一化坐标全错了。解决用 PIL 或 OpenCV 逐张读取图像尺寸传给转换函数。如果图像尺寸差异大建议先统一 resize 到 640×640再重新生成标注。4.3 训练集和验证集类别分布不均现象验证集 mAP 波动很大某些类别时好时坏。原因随机划分时没考虑类别均衡某个类别可能全被分到训练集验证集里一个实例都没有。解决划分前先统计每个类别的实例列表按类别分层抽样。简单做法是每个类别按比例抽取保证验证集里每个类别至少有 5~10 个实例。4.4 小目标漏检严重现象车辆、轮船的召回率低于 0.3大目标检测正常。原因YOLO 默认的 anchor 和特征层对小目标不友好遥感图像里小目标像素太少。解决把imgsz从 640 提高到 1024或者改用 YOLOv8 的 P2 检测层需要修改模型配置。另外数据增强里加上mosaic1.0和scale0.5增加小目标的出现频率。4.5 XML 文件编码问题导致解析失败现象转换脚本报ParseError某些 XML 文件读不了。原因XML 文件可能包含 BOM 头或非 UTF-8 编码。解决用ET.parse时指定编码或者先用open(xml_path, r, encodingutf-8-sig)读入字符串再解析。批量处理时加个 try-except把出错的文件名打印出来单独处理。5. 进阶技巧用 TensorRT 加速推理与 mAP 验证训练完的 PyTorch 模型在推理时速度一般如果要做部署或者批量测试导出 TensorRT 引擎能快不少。YOLOv8 支持直接导出# 导出 TensorRT 引擎FP16 精度适合 RTX 系列显卡 yolo export \ model./runs/nwpu/yolov8n_exp1/weights/best.pt \ formatengine \ halfTrue \ imgsz640 \ device0参数说明halfTrue启用 FP16 精度速度提升明显精度损失通常在 1% 以内imgsz640要和训练时一致否则检测框会错位。导出完成后会生成.engine文件用yolo detect predict时把model指向这个文件即可。验证 TensorRT 引擎的精度有没有掉最直接的方法是在验证集上跑一遍val对比 PyTorch 模型的 mAP# 用 TensorRT 引擎在验证集上评估对比精度 yolo detect val \ model./runs/nwpu/yolov8n_exp1/weights/best.engine \ data./NWPU_VHR-10/data.yaml \ imgsz640 \ batch16如果 mAP 掉了超过 2 个百分点检查一下half和imgsz是否和训练配置匹配。另外TensorRT 引擎和显卡架构绑定换显卡后需要重新导出。还有一个容易被忽略的点遥感图像的通道顺序。有些数据集保存时是 RGB有些是 BGRYOLO 默认按 RGB 处理。如果推理结果颜色异常或者检测框偏移用 OpenCV 读图后加一行cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转一下。我一般会在推理脚本里固定这个操作避免换数据集时翻车。从那以后我每次拿到新的遥感数据集都强制走一遍「统计类别分布 → 检查图像尺寸 → 转换标注 → 可视化抽查」这四步确认无误再开训练。希望帮到你。本文还有配套的精品资源点击获取