
简介本资源为面向YOLO系列算法学习者的卫星遥感图像目标检测数据集适合从事遥感图像识别、地理信息分析及深度学习目标检测的开发者与研究人员使用可直接用于模型训练、验证与测试。压缩包共2000个文件包含1280个VOC格式xml标注文件与720个YOLO格式txt标注文件整体约59.44MB并附带data.yaml配置文件已划分好训练与验证集兼容yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流版本。YOLO格式采用类别索引与归一化中心点、宽高坐标便于直接读取训练VOC格式则保留原始XML结构方便转换与复核。两种标注分别存放于独立文件夹文件名末尾标注部分类别名称便于按类别检索与管理。目前已有110人学习下载适合希望快速上手遥感目标检测任务、验证算法效果或进行二次开发的读者参考使用。1. 遥感图像目标检测为什么总在“小目标”上翻车1825 张带标签数据集的真实价值拿到一份卫星遥感图像物体检测数据集很多人第一反应是直接套 YOLOv8 训练脚本跑一遍然后看着 mAP 曲线沾沾自喜。但真正做过遥感检测的人都知道卫星图像和自然图像根本不是一个物种——目标尺度差异极大、方向任意、背景极其复杂一张 1024×1024 的图里可能同时存在几十米长的飞机和几米宽的汽车。这份 1825 张图像带标签的 YOLO 格式数据集核心价值不在于“数量大”而在于它把遥感场景下最典型的几类目标飞机、舰船、储油罐、车辆等用统一的 YOLO 标注格式整理好了省去了从原始影像到可训练数据之间最耗时的清洗和转换环节。如果你正在做遥感目标检测的落地验证或者想找一个真实场景练手 YOLO 训练全流程这份数据集能让你把精力集中在模型调优而不是数据整理上。但前提是你得先搞清楚遥感图像和常规图像在训练策略上的本质差异否则再好的数据集也救不了你的 mAP。2. 从压缩包到可训练YOLO 格式遥感数据集的目录结构与校验方法2.1 遥感数据集的 YOLO 标注格式到底长什么样YOLO 格式的标注文件是每张图片对应一个.txt文件每行代表一个目标格式为class_id x_center y_center width height其中坐标全部是相对于图像宽高的归一化值0~1 之间。遥感图像的特殊之处在于目标通常很小归一化后的宽高可能只有 0.01 甚至更小这就导致在训练时正样本匹配非常困难。拿到这份 1825 张图像的数据集后第一件事不是急着训练而是先确认标注文件和图像文件是否一一对应、类别 ID 是否从 0 开始连续编号、有没有空标注文件混在里面。常见的目录结构是这样的dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml但很多从公开渠道拿到的遥感数据集并不会预先划分好 train/val/test而是所有图像和标注混在一起。这时候你需要自己写脚本做划分同时保证图像和标注同步移动。2.2 用 Python 脚本完成数据集完整性校验与划分下面这段脚本做三件事检查图像和标注是否配对、统计每个类别的目标数量、按 8:1:1 划分训练集验证集测试集。import os import random import shutil from pathlib import Path from collections import Counter # 数据集根目录根据实际情况修改 ROOT Path(dataset) IMG_DIR ROOT / images LBL_DIR ROOT / labels # 收集所有图像文件常见遥感图像格式 img_exts {.jpg, .jpeg, .png, .tif, .tiff} images [p for p in IMG_DIR.rglob(*) if p.suffix.lower() in img_exts] paired, missing_label, empty_label [], [], [] class_counter Counter() for img_path in images: # 标注文件与图像同名只是后缀换成 .txt lbl_path LBL_DIR / img_path.relative_to(IMG_DIR).with_suffix(.txt) if not lbl_path.exists(): missing_label.append(str(img_path)) continue lines lbl_path.read_text().strip().splitlines() if len(lines) 0: empty_label.append(str(img_path)) continue for line in lines: cls_id int(line.split()[0]) class_counter[cls_id] 1 paired.append((img_path, lbl_path)) print(f配对成功: {len(paired)}) print(f缺失标注: {len(missing_label)}) print(f空标注文件: {len(empty_label)}) print(f类别分布: {dict(sorted(class_counter.items()))}) # 按 8:1:1 划分 random.seed(42) random.shuffle(paired) n len(paired) n_train int(n * 0.8) n_val int(n * 0.1) splits { train: paired[:n_train], val: paired[n_train:n_train n_val], test: paired[n_train n_val:] } for split_name, items in splits.items(): img_out ROOT / split / images / split_name lbl_out ROOT / split / labels / split_name img_out.mkdir(parentsTrue, exist_okTrue) lbl_out.mkdir(parentsTrue, exist_okTrue) for img_path, lbl_path in items: shutil.copy2(img_path, img_out / img_path.name) shutil.copy2(lbl_path, lbl_out / lbl_path.name) print(f{split_name}: {len(items)} 张)这段脚本的关键逻辑在于先做配对检查再划分避免把没有标注的图像混进训练集导致训练时找不到标签而报错。random.seed(42)保证每次划分结果一致方便复现。类别统计那一步能帮你快速判断数据集是否存在严重的类别不平衡——遥感数据集里“背景类”往往占据绝对多数如果某个类别的目标数少于 50 个训练时基本学不到有效特征需要考虑过采样或数据增强。参数方面ROOT指向解压后的数据集根目录img_exts集合覆盖了遥感领域常见的 GeoTIFF 和普通图像格式。如果你的数据集里图像和标注不在同一级目录下需要根据实际结构调整IMG_DIR和LBL_DIR的相对路径。2.3 data.yaml 的写法与类别名映射划分完成后需要生成 YOLO 训练所需的data.yamlpath: ./split train: images/train val: images/val test: images/test names: 0: plane 1: ship 2: storage_tank 3: vehicle 4: bridge这里names的键必须从 0 开始连续且与标注文件里的class_id严格对应。遥感数据集常见的坑是类别名用了中文或者包含空格YOLO 训练时读取 yaml 会直接报编码错误。另外path建议用相对路径方便整个数据集目录迁移到不同机器上。3. 用 YOLOv8 在遥感数据集上跑通训练环境配置与关键参数3.1 Anaconda 环境配置与 ultralytics 安装遥感图像训练对显存的要求比常规图像高因为输入分辨率通常要设得更大1024 或 1280否则小目标在特征图上只剩几个像素检测头根本抓不住。我一般用 Anaconda 建一个独立环境避免和系统里的其他 CUDA 版本打架。conda create -n yolo_remote python3.10 -y conda activate yolo_remote # 安装 PyTorch根据你的 CUDA 版本选择对应命令 # CUDA 11.8 的情况 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralytics pip install ultralytics # 验证安装 yolo checksyolo checks会输出当前环境检测到的 CUDA 版本、GPU 型号和 ultralytics 版本号。如果这里显示 CPU only说明 PyTorch 装成了 CPU 版本需要卸载重装。遥感训练没有 GPU 基本跑不动1024 分辨率下 batch size 设为 8 就需要至少 8GB 显存。3.2 训练命令与遥感场景下的参数调整yolo detect train \ data./split/data.yaml \ modelyolov8s.pt \ epochs150 \ imgsz1024 \ batch8 \ device0 \ workers4 \ patience30 \ lr00.001 \ lrf0.01 \ mosaic0.5 \ scale0.3 \ degrees0.0 \ fliplr0.5 \ flipud0.5 \ projectruns/remote \ nameexp1逐项说明这些参数为什么这样设。imgsz1024是遥感检测的底线低于这个值小目标基本消失。batch8是在 8GB 显存下的保守值显存够可以加到 16。patience30表示 30 个 epoch 没有提升就早停遥感数据集通常 100~150 epoch 就能收敛。lr00.001比默认的 0.01 小一个数量级因为遥感图像预训练权重和实际数据分布差异大学习率太大会导致损失震荡。数据增强部分需要特别注意degrees0.0关掉旋转增强因为遥感目标本身就有任意方向再随机旋转会让模型对方向的建模更加混乱flipud0.5打开上下翻转遥感图像从卫星俯拍上下翻转不改变目标的语义mosaic0.5比默认的 1.0 低因为 mosaic 四图拼接会让本就很小的目标变得更小过度使用反而有害。3.3 训练过程监控与 mAP 曲线解读训练启动后runs/remote/exp1/目录下会生成results.csv和一系列可视化图表。重点看三个指标metrics/mAP50、metrics/mAP50-95和train/box_loss。遥感检测的 mAP50 能到 0.7 以上就算不错mAP50-95 通常只有 0.4~0.5因为小目标的 IoU 很难做高。如果 box_loss 在前 20 个 epoch 下降很慢说明学习率可能还是偏大或者数据增强太强。如果 mAP 曲线在某个 epoch 突然断崖式下跌大概率是遇到了标注质量差的样本需要回头检查那批数据。results.csv可以直接用 pandas 读出来画图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/remote/exp1/results.csv) df.columns df.columns.str.strip() fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[0].set_xlabel(epoch) axes[0].set_ylabel(loss) axes[0].legend() axes[1].plot(df[epoch], df[metrics/mAP50], labelmAP50) axes[1].plot(df[epoch], df[metrics/mAP50-95], labelmAP50-95) axes[1].set_xlabel(epoch) axes[1].set_ylabel(mAP) axes[1].legend() plt.tight_layout() plt.savefig(training_curve.png, dpi150)这段代码把训练日志里的损失和精度曲线画在一起方便判断模型是否过拟合。如果 train/box_loss 持续下降但 val/box_loss 开始上升就是过拟合的典型信号需要增加数据增强或者减少模型参数量。4. 遥感检测避坑指南从标注质量到推理部署的五个血泪教训4.1 坑一标注框比目标大一圈模型学了个寂寞现象训练 loss 正常下降但推理时检测框明显偏大小目标尤其严重经常把背景一起框进去。原因遥感图像标注时标注人员习惯性地把目标周围的地物也框进去导致标注框比实际目标大 20%~30%。YOLO 的回归损失直接学这个框模型自然输出偏大的检测结果。解决用脚本统计所有标注框的宽高分布如果某个类别的平均宽高明显大于该类别在图像中的实际像素尺寸就需要重新审视标注。对于已经标注好的数据可以用labelImg或CVAT逐类抽查把明显偏大的框收紧。更彻底的做法是换用带旋转框的标注格式如 DOTA 格式但那就需要换检测头了。4.2 坑二类别 ID 不连续导致训练时索引越界现象训练启动时报IndexError: index 5 is out of bounds for dimension 0 with size 5。原因标注文件里的 class_id 是 0、1、2、3、5跳过了 4但data.yaml里只定义了 5 个类别0~4。YOLO 在构建分类头时按 names 的长度分配输出维度遇到 id5 直接越界。解决写脚本扫描所有标注文件收集实际出现的 class_id 集合然后重新映射为从 0 开始的连续整数。映射关系要同步更新到data.yaml的 names 里。这个坑在合并多个来源的遥感数据集时特别常见因为不同数据集的类别编号体系不一样。4.3 坑三图像格式是 16 位 TIFFYOLO 读进去全是噪声现象训练时 loss 一直是 nan或者可视化出来的训练图像一片灰白完全看不出目标。原因遥感原始影像很多是 16 位或 32 位浮点的 GeoTIFF像素值范围是 0~65535 甚至更大。YOLO 的数据加载器默认按 8 位图像读取不做归一化的话像素值直接溢出做归一化又因为位深不同导致对比度异常。解决在训练前统一转成 8 位 PNG 或 JPEG。转换时用 2%~98% 的百分位做线性拉伸而不是简单除以 255。下面这段代码批量处理import numpy as np import cv2 from pathlib import Path def convert_to_8bit(src_path, dst_path): img cv2.imread(str(src_path), cv2.IMREAD_UNCHANGED) if img is None: return False if img.dtype np.uint16: # 百分位拉伸保留主要动态范围 p2, p98 np.percentile(img, (2, 98)) img np.clip((img - p2) / (p98 - p2 1e-6) * 255, 0, 255).astype(np.uint8) elif img.dtype np.float32: img np.clip(img * 255, 0, 255).astype(np.uint8) # 如果是多波段只取前三个波段 if len(img.shape) 3 and img.shape[2] 3: img img[:, :, :3] cv2.imwrite(str(dst_path), img) return True src_dir Path(raw_images) dst_dir Path(dataset/images) dst_dir.mkdir(parentsTrue, exist_okTrue) for p in src_dir.glob(*.tif): convert_to_8bit(p, dst_dir / (p.stem .png))百分位拉伸比直接除以最大值更稳因为遥感图像里经常有极亮的云层或极暗的水体直接归一化会把有效目标的对比度压没。4.4 坑四验证集 mAP 很高但实际推理一塌糊涂现象results.csv里 mAP50 到了 0.85但拿几张新图推理检测结果惨不忍睹。原因验证集和训练集来自同一批图像划分分布完全一致。如果数据集本身场景单一比如全是港口区域模型只是记住了这个场景的背景特征换一个场景就失效。解决划分验证集时按场景或地理区域划分而不是随机划分。比如按图像来源的卫星或区域分组确保验证集里的场景在训练集中没出现过。如果数据集本身场景多样性不够就需要额外收集不同区域的图像做外部验证。4.5 坑五推理时置信度阈值设 0.25小目标全被过滤掉现象训练指标正常推理时大目标能检测到小目标一个都出不来。原因YOLO 默认的置信度阈值是 0.25但遥感小目标的分类置信度普遍偏低很多真实目标的置信度在 0.1~0.2 之间。阈值一卡全没了。解决推理时把conf降到 0.05~0.1同时把iou设为 0.3~0.4 来抑制重叠框。更好的做法是在验证集上画 precision-recall 曲线找到 F1 分数最大的阈值点而不是拍脑袋定一个值。yolo detect predict \ modelruns/remote/exp1/weights/best.pt \ sourcetest_images/ \ conf0.08 \ iou0.35 \ imgsz1024 \ saveTrue5. 把遥感检测模型推到实用线切片推理与类别平衡的进阶技巧训练出一个 mAP 好看的模型只是第一步真正落地时你会发现两个绕不开的问题大图推理时小目标被缩放没了以及某些类别样本太少导致漏检严重。这两个问题在 1825 张规模的数据集上尤其突出因为数据量本身不足以让模型学到所有类别的鲁棒特征。先说切片推理。遥感图像的实际尺寸往往是 4000×4000 甚至更大直接缩放到 1024 输入模型小目标在缩放后只剩不到 5 个像素检测头根本无力回天。我一般用 SAHISlicing Aided Hyper Inference的思路把大图切成有重叠的小块每块单独推理后再把结果合并回原图坐标。核心代码如下from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/remote/exp1/weights/best.pt, confidence_threshold0.08, devicecuda:0 ) result get_sliced_prediction( large_image.tif, detection_model, slice_height1024, slice_width1024, overlap_height_ratio0.2, overlap_width_ratio0.2 ) result.export_visuals(export_dirsliced_output/)overlap_height_ratio0.2表示相邻切片有 20% 的重叠区域防止目标正好落在切片边界上被截断。切片推理的代价是推理时间成倍增加但小目标的召回率通常能提升 15~25 个百分点对于遥感场景来说这个 trade-off 完全值得。再说类别平衡。1825 张图像里如果某个类别只有几十个目标模型对这个类别的学习会严重不足。我习惯用两种手段缓解一是对稀有类别做过采样在训练时通过copy_paste增强把稀有类别的目标粘贴到其他图像上二是调整损失函数里分类损失的权重让稀有类别的分类错误惩罚更大。YOLOv8 本身不直接支持类别权重但可以通过自定义 dataset 类在__getitem__里对稀有类别样本做重复采样。最后一个实用技巧是模型集成。用同一份数据训练 3 个不同初始化的 YOLOv8s推理时把三组检测框做加权框融合WBFmAP 通常能再涨 2~3 个点。代价是推理时间三倍适合对精度要求高、对延迟不敏感的场景。我自己踩过最深的坑是迷信 mAP 数字在验证集上刷到 0.9 就以为可以交付了结果实际部署时发现模型对某个类别的漏检率超过 40%。后来养成习惯每次训练完必须拿一批完全没参与训练的新图做人工抽检看每个类别的实际检出情况而不是只看汇总指标。遥感检测这件事数据质量比模型结构重要场景覆盖比数据数量重要实际抽检比验证集 mAP 重要。希望帮到你。本文还有配套的精品资源点击获取