ARTICLE DETAIL

资讯详情

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

航空卫星图像数据集YOLO训练实战:小样本遥感目标检测全流程

航空卫星图像数据集YOLO训练实战:小样本遥感目标检测全流程 简介本资源为面向YOLO系列目标检测算法的航空卫星图像数据集适用于遥感场景下的地物分类与目标检测任务可支撑森林、公路、作物、河流、住宅、工业、果园、牧场及贫瘠土地等多类地物的识别模型训练与验证。数据集已按训练与验证需求划分完毕并附带data.yaml配置文件兼容yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流版本方便直接投入实验。压缩包共2000个文件其中1230个xml文件对应VOC格式标注770个txt文件对应YOLO格式标注两种标签体系分别存放便于按框架灵活选用包体约60.51MB含1366张带标签图像。YOLO格式采用归一化中心点与宽高比例便于直接读取训练。目前已有42人学习下载适合遥感检测方向的初学者与研究者快速搭建基线、验证算法效果并开展对比实验。1. 航空卫星图像数据集与 YOLO 落地1366 张图能训练出什么拿到一个标题写着「1366 张图像带标签、覆盖森林公路作物河流住宅工业果园牧场贫瘠土地」的航空卫星数据集多数人第一反应是直接丢进 YOLO 训练脚本里跑一遍。但真实情况是1366 张遥感图像在目标检测任务里属于小样本量级如果按 8:1:1 切分验证集只有 136 张左右单类目标可能不到 200 个实例。这个量级下YOLO 能不能收敛、mAP 能到多少、哪些类别会拖后腿取决于你怎么处理数据、选哪个版本、怎么配置增强。这篇内容面向已经了解 YOLO 基础、手头有遥感标注数据、想跑通一条完整训练流水线的从业者从数据检查、格式转换、模型选型、训练参数到部署验证把每一步的参数含义和踩坑点讲清楚。适合做土地利用分类、遥感目标检测、边缘端部署的工程师参考不适合完全没接触过目标检测的新手直接照搬。2. 遥感数据集进 YOLO 之前格式、分布与标签质量核查2.1 先搞清楚标注格式和坐标体系航空卫星图像数据集的标签常见有三种格式Pascal VOC 的 XML、COCO 的 JSON、以及 YOLO 的 TXT。标题里写「带标签」但没有说明具体格式所以第一步不是写训练脚本而是打开标注文件看一眼。如果是 VOC 格式每个图像对应一个 XML里面记录了 bbox 的 xmin、ymin、xmax、ymax如果是 YOLO TXT每行是class_id x_center y_center width height且全部归一化到 0~1。遥感图像有个特殊点图像尺寸往往很大比如 1024×1024 或 2048×2048而 YOLO 默认输入是 640×640。直接缩放会导致小目标比如河流的窄段、公路的细线在特征图上只剩几个像素检测效果断崖式下降。所以核查标签时要同时统计目标框的绝对像素尺寸分布。import os import xml.etree.ElementTree as ET from collections import Counter # 统计 VOC 格式标签的类别分布和 bbox 尺寸 def parse_voc_annotations(anno_dir): class_counter Counter() size_list [] for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text class_counter[name] 1 bbox obj.find(bndbox) w float(bbox.find(xmax).text) - float(bbox.find(xmin).text) h float(bbox.find(ymax).text) - float(bbox.find(ymin).text) size_list.append((w, h)) return class_counter, size_list # 输出每个类别的实例数以及 bbox 宽高的分布 class_counter, size_list parse_voc_annotations(./annotations) print(类别分布:, class_counter) print(bbox 平均宽高:, sum(w for w,h in size_list)/len(size_list), sum(h for w,h in size_list)/len(size_list))这段代码做两件事统计每个类别的实例数量以及 bbox 的平均宽高。如果某个类别实例数低于 100比如「工业」或「果园」可能只有几十个训练时该类别的 AP 会非常不稳定。bbox 平均宽高如果小于 32 像素说明小目标占比高需要考虑增大输入分辨率或使用 P2 特征层。参数说明anno_dir指向 XML 文件所在目录class_counter输出各类别实例数size_list用于判断小目标比例。如果发现某个类别实例数过少常见做法是合并相似类别比如把「牧场」和「贫瘠的土地」合并为「草地/裸地」或者对该类别做过采样。2.2 类别不平衡与长尾分布的处理策略1366 张图覆盖 9 个类别大概率存在长尾分布森林、住宅、公路可能占多数工业、果园、牧场可能很少。YOLO 的损失函数默认对所有类别等权重长尾会导致模型偏向头部类别。常见做法有三种一是调整损失函数中各类别的权重二是对尾部类别做数据增强旋转、裁剪、色彩抖动三是在数据加载时对尾部类别样本过采样。我一般会先跑一遍上面的统计脚本如果头部和尾部实例数差距超过 10 倍就在训练配置里加cls_pw类别权重或者用 focal loss 替代默认的 BCE loss。YOLOv8 的配置里可以通过cls参数调整分类损失的增益但更直接的方式是在数据集 YAML 里给每个类别设置权重不过官方接口不直接支持需要改损失函数源码。提示遥感图像的类别定义往往有歧义比如「贫瘠的土地」和「牧场」在视觉上可能高度相似标注一致性差。训练前最好抽样 50 张图人工复核把混淆严重的类别合并否则 mAP 会被拉低。2.3 从 VOC 到 YOLO 格式转换脚本与四个边界坑如果原始标签是 VOC XML需要转成 YOLO TXT。转换逻辑不复杂但遥感图像有几个边界情况容易翻车。import xml.etree.ElementTree as ET import os # VOC 转 YOLO处理边界溢出和类别映射 def voc_to_yolo(xml_path, output_path, class_map, 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 class_map: continue # 跳过未定义类别 cls_id class_map[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) # 边界裁剪防止坐标超出图像范围 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 跳过无效框 if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(output_path, w) as f: f.write(\n.join(lines)) class_map {森林: 0, 公路: 1, 作物: 2, 河流: 3, 住宅: 4, 工业: 5, 果园: 6, 牧场: 7, 贫瘠的土地: 8}四个边界坑第一xmax 或 ymax 可能等于图像宽度或高度归一化后是 1.0YOLO 能接受但某些版本会报错建议裁剪到 img_w - 1第二有些标注框的 xmin 大于 xmax属于标注错误必须跳过第三类别名如果有空格或特殊字符映射时要统一第四图像尺寸要从对应的图像文件读取不能硬编码因为遥感数据集可能混合了不同分辨率。参数说明class_map是类别名到 id 的映射必须和数据集 YAML 里的 names 顺序一致img_w和img_h从图像文件读取不要假设所有图像尺寸相同。转换完成后随机抽 10 张图用可视化脚本画框验证确认没有偏移或漏框。3. YOLO 版本选型与训练参数从 v8 到 v11 的遥感适配3.1 为什么遥感任务优先选 YOLOv8 或 v11 而不是 v5YOLOv5 在 2020 年发布时是工业界默认选择但遥感图像有几个特性让 v8/v11 更有优势一是 v8 的 C2f 模块和 v11 的 C3k2 模块在浅层特征提取上更强对小目标更友好二是 v8 开始支持 anchor-free 检测头减少了遥感图像中密集小目标的 anchor 匹配问题三是 v8/v11 的官方仓库集成了更完整的数据增强和混合精度训练配置成本低。如果手头有 V100 或同级别显卡YOLOv8m 或 v11m 是性价比最高的选择参数量在 25M 左右640 输入下推理速度约 5ms/图。如果要做边缘部署比如 RK3588 或 Jetson 系列选 YOLOv8n 或 v11n参数量 3M 左右但小目标检测会明显下降需要把输入分辨率提到 1024。注意YOLOv5 的很多教程还在流传但它的 SPPF 和 PANet 结构对遥感图像的密集小目标已经不够用。除非你有历史项目必须兼容 v5 的权重格式否则新项目直接上 v8 或 v11。3.2 训练配置学习率、batch size 与输入分辨率的三角权衡遥感数据集的训练配置和自然图像数据集有区别。自然图像常用 640×640 输入、batch size 16、初始学习率 0.01。但遥感图像的目标尺寸分布更极端如果小目标多输入分辨率要提到 1024 甚至 1280此时 batch size 要降到 4 或 8学习率也要相应降到 0.001~0.005。# data.yaml path: ./dataset train: images/train val: images/val test: images/test names: 0: 森林 1: 公路 2: 作物 3: 河流 4: 住宅 5: 工业 6: 果园 7: 牧场 8: 贫瘠的土地# 训练命令YOLOv8m1024 输入batch 8100 epoch yolo detect train \ datadata.yaml \ modelyolov8m.pt \ imgsz1024 \ batch8 \ epochs100 \ lr00.005 \ lrf0.01 \ warmup_epochs3 \ cos_lrTrue \ mosaic1.0 \ mixup0.1 \ copy_paste0.1 \ device0 \ ampTrue \ patience20参数说明imgsz1024是遥感小目标的关键640 下很多河流和公路的细段会消失batch8是 1024 分辨率下 16G 显存的极限如果显存不够降到 4lr00.005比默认 0.01 低因为小数据集上大学习率容易震荡mosaic1.0开启马赛克增强但遥感图像拼接后可能出现不自然的边界如果验证集 mAP 波动大可以降到 0.5copy_paste0.1对实例分割有用纯检测任务可以关掉patience20表示 20 个 epoch 没有提升就早停小数据集上防止过拟合。3.3 数据增强的取舍哪些增强对遥感图像有效遥感图像和自然图像的数据增强策略不同。水平翻转、垂直翻转、90 度旋转对遥感图像完全合理因为卫星视角没有固定的上下方向。但色彩抖动要谨慎森林和作物的颜色是重要特征过度抖动会让模型混淆。Mosaic 增强在遥感图像上容易把不同地物拼接到一起产生不真实的边界建议从 1.0 开始如果验证集 mAP 下降就降到 0.5。Mixup 增强在遥感图像上效果一般因为不同地物的混合会产生现实中不存在的纹理。Copy-paste 增强对稀有类别有帮助比如把工业区的建筑复制到其他区域但要注意粘贴时的光照一致性。我一般会先跑一组基线只开水平翻转和垂直翻转mAP 作为基准然后逐步加入 mosaic、mixup、copy-paste每次只加一个观察验证集 mAP 变化。如果某个增强让 mAP 下降超过 2 个点就关掉。4. 训练过程排查BN 崩溃、混淆矩阵与 mAP 不升的常见原因4.1 BN 层崩溃现象、原因与解决训练遥感数据集时BN 层崩溃是高频问题。现象是 loss 突然变成 NaN或者验证集 mAP 从某个 epoch 开始断崖式下跌。原因通常是 batch size 太小BN 层在计算均值和方差时统计量不稳定。遥感图像输入分辨率高batch size 被迫降到 4 或 8BN 层的滑动平均跟不上。解决办法有三种一是改用 GroupNorm 或 LayerNorm 替代 BNYOLOv8 的配置里可以通过修改模型 YAML 把 BN 换成 GN二是冻结 BN 层的 running mean 和 variance在训练脚本里设置bn_momentum0.01或更低三是用梯度累积模拟大 batch比如batch4但累积 4 次梯度等效 batch size 16。# 在 YOLOv8 训练脚本中冻结 BN 层统计量 import torch import torch.nn as nn def freeze_bn_stats(model): for module in model.modules(): if isinstance(module, nn.BatchNorm2d): module.momentum 0.01 # 降低滑动平均动量 module.eval() # 冻结 running mean/var return model # 在训练循环中调用 model freeze_bn_stats(model)参数说明momentum0.01让 BN 层更依赖当前 batch 的统计量适合小 batch 场景module.eval()冻结 running mean 和 variance但会影响推理时的归一化建议只在训练前期用后期解冻。4.2 混淆矩阵总合不唯一类别定义与标注一致性YOLO 训练完成后会输出混淆矩阵如果发现矩阵的行和列总和对不上或者某些类别的预测数远大于标注数通常是类别定义有歧义。比如「牧场」和「贫瘠的土地」在视觉上都是草地或裸地模型很难区分导致大量误分类。另一个原因是标注不一致同一张图里类似的地物被标成了不同类别。解决办法先跑一遍验证集的可视化把误分类最多的类别对找出来比如「牧场→贫瘠的土地」有 50 个误判那就考虑合并这两个类别。如果不想合并就在数据加载时对这两个类别做难例挖掘把误判样本单独拿出来重新训练。4.3 mAP 不升的五个排查方向训练 50 个 epoch 后 mAP 还在 0.2 以下或者验证集 loss 不降按以下顺序排查第一检查标签格式是否正确用可视化脚本画框确认没有偏移第二检查类别映射是否一致data.yaml 里的 names 顺序和转换脚本里的 class_map 必须完全对应第三检查输入分辨率是否足够小目标多的话 640 不够第四检查学习率是否过大小数据集上 0.01 容易震荡降到 0.001 试试第五检查数据增强是否过度mosaic 和 mixup 同时开可能让模型学不到真实分布。提示遥感数据集的验证集 mAP 波动大是正常的因为验证集只有 100 多张图单个类别的 AP 可能因为几张图的检测结果变化 10 个点。建议用 k 折交叉验证或者至少跑 3 次不同随机种子的训练取平均 mAP。5. 从训练到部署ONNX 导出、RK3588 适配与误检率控制5.1 导出 ONNX 并验证推理一致性训练完成后部署前必须导出 ONNX 并验证推理结果和 PyTorch 一致。YOLOv8 的导出命令很简单但遥感图像的高分辨率输入在导出时要注意动态轴设置。# 导出 ONNX固定输入 1024x1024 yolo export modelbest.pt formatonnx imgsz1024 opset12 simplifyTrue # 验证 ONNX 推理结果 python -c import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name dummy np.random.randn(1, 3, 1024, 1024).astype(np.float32) output sess.run(None, {input_name: dummy}) print(输出形状:, [o.shape for o in output]) 参数说明opset12兼容大多数推理框架simplifyTrue会优化计算图但某些自定义算子可能被简化掉导出后要用真实图像对比 PyTorch 和 ONNX 的输出差异如果 mAP 下降超过 1 个点关掉 simplify。5.2 RK3588 边缘部署的量化与误检率控制RK3588 的 NPU 支持 INT8 量化但遥感图像的 INT8 量化容易导致小目标漏检。常见做法是先用 FP16 跑基线如果速度不够再上 INT8。量化时要用校准集校准集要从训练集里随机抽 200 张覆盖所有类别。误检率高是边缘部署的常见问题原因通常是量化误差导致背景被误判为目标。解决办法一是在后处理里提高置信度阈值从 0.25 提到 0.4二是用 NMS 的 IoU 阈值调低从 0.45 降到 0.3减少重叠框三是在训练时加入背景样本把纯背景图像也放进训练集让模型学会抑制背景。5.3 验证部署效果用混淆矩阵和 PR 曲线定位问题部署后不要只看 mAP要导出混淆矩阵和 PR 曲线按类别分析。如果某个类别的召回率低但精确率高说明模型保守可以降低置信度阈值如果精确率低但召回率高说明误检多可以提高阈值或增加背景样本。# 用验证集跑推理生成混淆矩阵 from ultralytics import YOLO model YOLO(best.pt) metrics model.val(datadata.yaml, imgsz1024, conf0.3, iou0.4) print(metrics.confusion_matrix) print(metrics.box.map) # mAP50-95 print(metrics.box.map50) # mAP50参数说明conf0.3是推理置信度阈值部署时根据误检率调整iou0.4是 NMS 的 IoU 阈值密集小目标场景可以降到 0.3。混淆矩阵会显示每个类别的误分类情况比如「牧场」被误判为「贫瘠的土地」的数量据此决定是否合并类别或补充训练数据。6. 小样本遥感检测的进阶技巧从 1366 张图里榨出更多信息1366 张图在遥感检测里不算多但通过几个技巧可以把数据利用率提到更高。第一个技巧是切图训练把 1024×1024 的原图切成 512×512 的切片重叠 128 像素这样训练样本数翻 4 倍小目标的像素占比也更大。切图后每个切片单独训练推理时再把切片结果拼回原图。切图的代价是边缘目标可能被切断需要在拼接时做 NMS 合并。第二个技巧是用预训练权重。YOLOv8 和 v11 都有在 COCO 上预训练的权重虽然 COCO 是自然图像但浅层的边缘和纹理特征对遥感图像也有用。加载预训练权重后冻结前 10 层训练 20 个 epoch再解冻全部层微调 80 个 epoch比从头训练收敛快一倍。第三个技巧是半监督学习。如果手头还有未标注的遥感图像可以用训练好的模型生成伪标签置信度高于 0.7 的保留低于 0.3 的丢弃中间的交由人工复核。伪标签数据加入训练集后mAP 通常能提升 3~5 个点但要注意伪标签的噪声会累积每轮迭代后要重新评估。# 切图脚本1024 切 512重叠 128 from PIL import Image import os def slice_image(img_path, output_dir, slice_size512, overlap128): img Image.open(img_path) w, h img.size stride slice_size - overlap count 0 for y in range(0, h - slice_size 1, stride): for x in range(0, w - slice_size 1, stride): crop img.crop((x, y, x slice_size, y slice_size)) crop.save(os.path.join(output_dir, f{os.path.basename(img_path)}_{count}.jpg)) count 1 return count # 对训练集所有图像切图 for img_file in os.listdir(./images/train): slice_image(f./images/train/{img_file}, ./images/train_sliced)参数说明slice_size512是切片大小根据 GPU 显存调整overlap128是重叠像素防止目标被切断切图后标签也要同步转换把原图坐标映射到切片坐标超出切片范围的框要裁剪或丢弃。最后一个习惯每次训练完把最佳权重、data.yaml、训练命令和 mAP 结果存到一个独立目录命名带上日期和关键参数比如20250115_v8m_1024_b8_lr005。遥感数据集的训练周期长参数组合多没有版本管理很容易忘记哪个权重对应哪组配置。我吃过这个亏后来每次训练都写一个train_log.md记录改动和结果复现和对比省了很多时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表