
简介YOLOv5算法在遥感图像目标识别中的应用项目资源包是面向计算机视觉方向学生与从业者的完整工程包适用于毕业设计、课程项目及遥感图像检测竞赛场景。包内含基于YOLOv5的卫星图像目标识别程序代码、配套说明文档与已验证模型权重覆盖数据集配置、训练推理与结果可视化全流程可直接运行也可扩展优化。整个zip包共有156个文件以Python脚本、YAML配置文件、PT模型权重和PNG/JPG图像样例为主并附有TensorBoard训练日志与Dockerfile环境定义便于复现实验条件、观察训练指标资源整体约242.81MB。目前已有42人学习下载资料经作者整理、导师审核答辩成绩95分代码稳定性较有保障。对初学者适合作为遥感目标检测的入门练习对有一定基础者则可在该框架上调整网络结构、增加数据集或迁移至其他遥感识别任务。1. 遥感图像目标识别为什么绕不开YOLOv5从“检不出小目标”说起用自然场景训练好的YOLOv5拿到遥感图像上常常直接“翻车”——飞机漏检、车辆框漂、舰船误检。这不是模型不行而是遥感图像和自然图像的分布差异大得惊人一张512像素的遥感图里一个目标可能只占16×16像素背景却是密密麻麻的屋顶、植被和阴影。很多团队做遥感目标识别时第一版往往就是拿YOLOv5直接跑公开数据集然后被小目标和密集排列虐得体无完肤。这篇文章沿着一条可复现的路线展开遥感图像与自然图像的差异在哪里、数据集怎么准备、YOLOv5训练命令怎么跑、哪些超参数和网络结构必须动、推理部署时躲不开的坑在哪儿。目标是让你拿着这套方案能把检测精度往上拉一截而不是停留在“能跑通”的层面。2. 遥感图像与自然图像的本质差异YOLOv5要改的不只是网络结构2.1 遥感图的三个“反直觉”特性小目标、密集排列、多角度遥感图像和自然图像最直观的区别在“看”的方式。自然图像里目标通常占画面的主体一个行人可能是224×224像素里的80%但在遥感图像里同样一个行人可能只有8×8像素。以常见的高分二号卫星影像为例0.8米分辨率下一辆轿车大约占据12×28像素即便用无人机低空采集目标尺寸也不会像自然图像那样“大得任性”。这是遥感目标识别最核心的难点小目标太多YOLOv5默认的输入尺寸和锚框尺寸是按COCO数据集设计的直接套用必然出问题。第二个反直觉特性是密集排列。自然图像里同一类目标一般不会成百上千地挤在一张图里但遥感图像里停车场、港口、密集居民区的车辆和房屋动辄数百个。这直接挑战YOLOv5的NMS后处理——目标框重叠严重时默认的NMS阈值会误删大量真实目标。第三个特性是目标角度任意。自然图像里的目标人、猫、车基本都是“正着”的但遥感图像里的飞机、舰船朝向任意旋转目标检测在遥感领域一直是独立方向YOLOv5的水平框在舰船并排停靠时会出现严重重叠。理解这三个差异你就明白为什么“拿预训练权重直接finetune”在遥感场景下效果不好。COCO预训练权重里包含的anchor尺寸和特征层级和遥感目标的尺度分布根本不匹配后面章节会逐一展开怎么改。2.2 数据集格式转换从DOTA/RSOD到YOLO格式的落地脚本遥感领域公开数据集DOTA、RSOD、DIOR多数是VOC或COCO格式的标注。YOLOv5训练需要的是每张图像对应一个同名txt文件每行表示一个目标格式为“类别id x_center y_center width height”坐标全部归一化到0-1之间。这个转换踩坑不少最常见的问题是坐标归一化时图像尺寸取错。python # convert_voc_to_yolo.py import xml.etree.ElementTree as ET import os from pathlib import Path def convert(xml_path, out_dir, class_map): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue # 过滤掉不在类别列表中的目标 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) # 计算YOLO格式的归一化中心坐标和宽高 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{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) # 输出同名txt到out_dir txt_path Path(out_dir) / (Path(xml_path).stem .txt) with open(txt_path, w) as f: f.write(\n.join(lines))逻辑说明VOC格式中坐标用的是像素绝对坐标需要先读取图像宽高把左上角/右下角坐标换算成中心点坐标再归一化。这段脚本里加了类别过滤原因是遥感公开数据的类别集合与你的目标类别不一定一致例如RSOD包含飞机、操场、立交桥、车辆四类而你可能只需要飞机和车辆。参数说明class_map需要是你自己定义的类别映射字典比如{airplane: 0, vehicle: 1}。转换完成后建议写一个检查脚本打印每张图的txt行数、是否有越界坐标小于0或大于1越界通常出现在DOTA数据集的多边形标注转VOC时。2.3 遥感图像标注半自动标注与项目资源包的文件组织遥感图像标注比自然图像费时得多一是目标数量大二是需要放大到像素级才能看清。常见做法是先用预训练YOLOv5跑一轮推理生成伪标注再用LabelImg或X-AnyLabeling人工修正。这个过程能省一半时间但要注意伪标注的漏检目标不会凭空出现所以对于密集区域建议把大图切成512x512的小图分别推理能显著提高初始标注召回率这一技巧在最后一章会展开。一个完整的“应用项目资源包”在文件组织上通常按这个结构管理coty/ # 项目根目录 ├── data/ │ ├── images/train/ │ ├── images/val/ │ ├── labels/train/ │ ├── labels/val/ │ └── dataset.yaml ├── scripts/ │ ├── convert_voc_to_yolo.py │ ├── split_dataset.py │ └── check_labels.py ├── weights/ │ └── yolov5s.pt └── runs/ ├── train/ └── detect/这种组织方式配合YOLOv5的目录约定训练和推理时不需要到处改路径。我见过不少项目把标注文件和图像混放或者训练集验证集有重叠这会让mAP虚高到“看着能上线一测就露馅”的程度。用split_dataset.py按7:2:1切分时务必先对图像文件名排序再随机切避免同一场景的连续帧同时出现在训练集和验证集里。3. 用YOLOv5训练遥感目标检测模型从环境配置到命令落地3.1 环境与项目结构yolov5仓库的最小可用配置环境配置是第一个坑但不是大坑。YOLOv5官方仓库对依赖要求比较友好PyTorch 1.8以上基本都能跑。我一般会建议用Python 3.8或3.10避免某些系统自带Python版本过高导致opencv-python编译报错。安装命令bash # 创建虚拟环境推荐conda或venv conda create -n remote_yolo python3.8 -y conda activate remote_yolo # 安装PyTorchCUDA 11.8对应命令按自己机器驱动版本选择 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装YOLOv5依赖 pip install -r requirements.txt # 验证环境 python -c import torch; print(torch.cuda.is_available())逻辑说明requirements.txt会一并安装opencv-python、matplotlib、pandas等组件其中opencv-python在部分Linux机器上需要额外安装libgl库才能运行报错信息是ImportError: libGL.so.1。这时用apt install libgl1即可解决。参数说明PyTorch的CUDA版本必须和驱动的CUDA Driver版本兼容但不需要完全一致驱动版本 运行版本即可。如果机器是纯CPU环境把torch那行改成pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu即可训练会慢很多但能跑通。3.2 数据集YAML配置与目录对拷YOLOv5靠一个YAML文件描述数据集路径和类别信息。这个文件写错后面所有训练都会在报错和玄学低精度之间反复横跳。yaml # data/dataset.yaml # 训练集和验证集的图像目录路径绝对路径最保险 train: /home/coty/data/images/train/ val: /home/coty/data/images/val/ test: # 没有测试集可以留空 # 类别数量 nc: 3 # 类别名称必须与标注txt中的id一一对应 names: [airplane, ship, vehicle]逻辑说明train和val路径指向的是图像目录而非标注目录YOLOv5会根据图像路径自动寻找同名的txt标注文件。常规约定是图像在images/train下标注在labels/train下两者目录名必须一致否则会报错找不到标注。参数说明nc是类别总数names列表里的顺序决定了推理时输出的类别名称不要和标注txt里的数字id对应错位。很多人在这一步翻车标注txt里类别id是0、1、2但names顺序写反了训练结束推理时飞机被标成车辆。3.3 训练命令与关键超参数batch、imgsz、epochs怎么定训练命令本身不复杂复杂的是参数选择。遥感图像检测有两个经验值非常关键输入尺寸imgsz不能一味求大训练轮数也不是越多越好。bash python train.py \ --data data/dataset.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache \ --name remote_v1逻辑说明--cache会把图像缓存进内存遥感数据集常见的几千张图能装满十几GB内存但能显著减少IO等待。--img指训练时输入网络的边长YOLOv5会等比缩放后按32的倍数补齐。参数说明imgsz是遥感检测最值得反复试的参数512和640是两个起点。目标太小640未必比512更好因为缩放本身可能把小目标信息抹掉目标普遍小于32像素时建议用448或512并配合后续的anchor重聚类。batch-size受显存限制一般单卡16G跑yolov5s用16-32合适梯度累积可以弥补小batch带来的不稳定。epochs在遥感场景下80-150轮足够超过150轮mAP往往不再上升甚至回落这时候不是过拟合的问题而是学习率策略下模型在小目标上的表征能力到顶了。训练日志里重点看val/0.5的mAP值IoU0.5而非mAP0.5:0.95因为遥感目标小高IoU阈值下的mAP天然偏低用0.5阈值判断模型收敛更有参考意义。3.4 迁移学习策略预训练权重选s还是m遥感数据量通常只有几千张标注图从头训练YOLOv5效果很差必须用COCO预训练权重做迁移学习。yolov5s、yolov5m的选择要在检测精度和训练成本之间取平衡。bash # 更充分的训练用m版本推理速度要求高用s版本 python train.py --weights yolov5m.pt --img 640 --batch 12 --epochs 120 --data data/dataset.yaml --name remote_v2我遇到的情况是s版本在遥感车辆检测上mAP0.5约在75-80%m版本能到83-85%但单张推理时间从8ms变到13msT4显卡对实时性要求高的场景会犹豫。这时候我会先跑一版s确认数据没毛病再切m做最终模型。不要在项目一开始就上x或l版本训练时间翻倍且遥感数据集没达到一定规模时大模型在小数据集上的增益非常有限。另外冻结backbone前三层的做法在遥感场景下不太适用遥感图像的低层特征分布与自然图像差异大全量微调是更常见的选择。4. 遥感场景下YOLOv5的六个必调参数与两个结构修改4.1 Anchor重聚类遥感目标尺寸分布与COCO差异太大YOLOv5默认anchor尺寸从COCO数据集聚类得到最小的一组锚框是10×13、16×30、33×23像素。但遥感图像中大量目标只有8×8、12×12像素直接套用等于让模型去猜一个它从未“见过”的尺度。YOLOv5提供了自动聚类anchor的功能但需要正确开启。bash # 训练时自动计算anchor并保存到anchors参数 python train.py --data data/dataset.yaml --weights yolov5s.pt --img 512 --noautoanchor FalseYOLOv5的train.py在每次训练前会调用check_anchors函数会计算数据集标注框的尺寸分布和默认anchor的匹配度当最佳召回率低于0.98时会自动聚类新anchor。但默认的k-means聚类对极小目标不敏感我一般会在跑第一轮训练前写个小脚本手动聚类anchor尺寸输出结果替换到模型yaml里。python # 聚类anchor的简化流程 # 用k-means基于所有标注框的宽高做聚类k9 # 将聚类出的9个(宽度, 高度)从小到大排序按YOLOv5的层分配规则 # 第1-3个分配给小目标检测层P3第4-6个给P4第7-9个给P5逻辑说明手动聚类anchor的核心逻辑是把标注框的绝对像素宽高收集起来用k-means算法聚成9类然后按尺寸从大到小匹配到特征金字塔的P3/P4/P5层。小目标占比高时前三组anchor应接近8-20像素范围而不是默认的10×13、16×30、33×23。参数说明聚类完成后不一定要用全部9个如果你的目标类别只有飞机尺寸集中在10-50像素可以把anchor收敛到5-6组减少冗余计算。替换anchor的位置在models/yolov5s.yaml里的anchor:字段改完再训练即可。4.2 超参数文件修改从hyp.scratch-low到自定义遥感方案YOLOv5训练前要加载一个超参数YAML文件data/hyps/hyp.scratch-low.yaml。这个文件在遥感小目标检测中至少有两个值必须动lr0初始学习率和augment参数里的mixup、copy_paste。yaml # data/hyps/hyp.remote.yaml基于hyp.scratch-low.yaml修改 lr0: 0.005 # 初始学习率从0.01降到0.005小目标对学习率更敏感 lrf: 0.12 # 最终学习率因子warmup结束后逐步下降 fl_gamma: 0.0 # focal loss gamma关闭focal loss遥感类别均衡时可不开 mixup: 0.0 # 遥感图像目标密集mixup容易产生虚假目标关闭 copy_paste: 0.0 # copy-paste增强在遥感场景可用但需谨慎默认关闭 degrees: 0.0 # 遥感目标任意角度旋转增强很关键建议0.2逻辑说明lr0从0.01降到0.005主要考虑是遥感数据集较小预训练模型本身已经有一个较好的局部最优位置过大的学习率会让模型在训练初期偏离最优解。mixup在遥感目标检测中会生成混合图像导致原本密集的目标边界更模糊我遇到过开mixup后mAP掉了2个百分点的情况。参数说明degrees旋转增强对遥感场景是“白捡的收益”因为舰船、飞机朝向本来就任意旋转增强可以帮助模型学习角度不变性。但注意degrees不要设成180度遥感图像有固定的“上北下南”语义旋转90度的倍数更合理。fl_gamma即focal loss的gamma参数COCO数据集上默认0.0表示关闭如果你的遥感数据存在严重的前景/背景不平衡背景占比99%可以考虑开启0.5-1.0但需要同时把类别权重处理好。4.3 网络结构修改给小目标增加P2检测层标准YOLOv5 s模型从P3/P4/P5三个尺度输出预测对应下采样8/16/32倍。遥感小目标8-16像素经过32倍下采样后只剩不到1个像素的有效信息P5层对小目标来说基本是“摆设”。常见做法是增加一个P2输出层让模型在4倍下采样的特征图上做小目标检测。这个修改需要改模型yaml和对应代码。yaml # models/yolov5s_p2.yaml精简版结构 # 在Backbone的P2位置stride4加一个检测头 backbone: # ... # P2层的特征来自第2层输出通道数较少需要额外加一个CSP模块融合 head: - [-1, 1, Conv, [64, 3, 1]] # 对P2特征做通道压缩 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 2], 1, Concat, [1]] # 与P2特征融合 - [-1, 3, C3, [64, False]] # C3模块 # 然后是P2尺度的检测输出逻辑说明P2层取自Backbone浅层特征图尺寸是输入图像的1/4包含更多空间细节但语义信息较弱。增加检测头的代价是计算量上升约30%显存占用增加同时小目标误检率可能上升因为浅层特征对背景纹理更敏感。参数说明改完模型yaml后必须同步修改anchors数量P3/P4/P5三个输出层变成P2/P3/P4/P5四个输出层后anchor数量应从9个增加到12个每层3个。同时train.py里的nc和模型输出通道数会自动适配但如果你用了自定义的detect模块就需要手动改。这个改造适合目标尺寸集中在16像素以下的场景如果目标普遍大于32像素P2层收益不大反而拖慢训练。4.4 YOLOv5后处理参数NMS阈值和置信度在遥感场景的不同调法YOLOv5后处理阶段两个参数影响直接conf_thres和iou_thres。自然图像推荐的conf_thres是0.25但在遥感密集场景下这个阈值会产生大量重叠框和误检。bash # 推理时推荐参数 python detect.py \ --weights runs/train/remote_v2/weights/best.pt \ --source data/images/val/ \ --conf-thres 0.35 \ --iou-thres 0.35 \ --imgsz 512 \ --save-txt逻辑说明iou-thres在密集目标场景下应适当调低从默认0.45降到0.3-0.35避免NMS把邻近的不同目标合并成一个框。但调太低会导致同一目标产生多个重叠框需要配合conf-thres一起调整。参数说明这两个参数本身有很强的场景属性建议在验证集上用网格搜索跑一遍看不同阈值组合下mAP和F1的变化。遥感小目标检测中我常用的组合是conf0.3、iou0.3同时开启--agnostic-nms跨类别NMS因为飞机和车辆在遥感图上不会同一位置重叠跨类别NMS适合。5. YOLOv5遥感目标识别避坑指南五条血泪经验5.1 现象训练损失下降正常mAP在30%以下徘徊原因anchor尺寸和数据集的尺度分布严重不匹配。尤其在只用了COCO预训练权重而没有重聚类anchor的情况下小目标匹配不到足够多的正样本损失虽然在下降但模型学到的是“背景中区分模糊目标”而不是“精确框出小目标”。解决按4.1节的方法做anchor重聚类或者把imgsz从640改成512/448让目标在输入图像中的相对尺寸变大。改完后mAP一般能提升10-20个百分点。5.2 现象模型推理大尺寸遥感图时显存溢出或者一张5000×5000的图像检测结果全是错位的框原因遥感原图通常大于5000×5000像素直接resize到640×640导致目标缩成1-2像素模型几乎崩溃直接原图推理显存又不够。这不是模型的错是大图处理策略不对。解决使用切片推理sliding window。把大图切成512×512的小图重叠率10-20%分别推理后再把预测框映射回原图坐标最后合并NMS。OpenCV的滑动窗口配合detect.py的--nosave模式即可实现。重叠率保证目标在切片边缘被切断时不会漏检。5.3 现象同一目标在图像中央能检测出来移到边缘就漏检原因训练时如果用了mosaic增强目标在图像边缘的概率较高模型对边缘目标的鲁棒性应该更好但推理时边缘目标被resize到640时可能被拉伸模糊。另外训练集里目标在边缘的样本过少模型没有学到边缘目标的尺度变化。解决训练时打开--augmentYOLOv5默认开启部分增强验证集里单独统计目标位置分布。如果边缘目标占比确实低可以在数据增强中加入RandomTranslate或者推理时把大图切成更多子图让目标尽量落在子图中央。5.4 现象舰船、飞机等带角度目标检测框严重漂移两个目标并排时被框成一个原因YOLOv5用水平矩形框无法表达目标旋转角度。舰船在港口并排停靠时水平框的IoU非常高NMS会误判定为同一目标导致一个框吃掉多个目标。解决短期方案是调低NMS的iou-thres到0.3配合conf-thres筛选能在一定程度上保留重叠框。如果项目对角度敏感比如需要精确计算舰船朝向需要转向旋转目标检测方案如YOLOv5 RoI Transformer或者在YOLOv5基础上加一个角度回归分支但这属于不小的工程改动适合作为项目二期。5.5 现象车辆类别始终训练不上去loss里其他类别的贡献掩盖了车辆原因遥感数据集里车辆目标通常只有几百张飞机却有几千张类别不平衡导致模型把参数空间都留给了大类别。mAP度量时小类别一拉垮整体指标就难看。解决在dataset.yaml里不直接支持类别权重但可以在损失函数层面处理。实践中最简单的做法是用class_weight参数或者直接把车辆的样本复制扩充不是硬加是保持原图的同时对包含车辆的图重复采样。另外可以用--weights参数加载上一次训练的结果对小类别继续fine-tune几轮本质上模拟了难例挖掘。6. 让训练好的模型真正落地切片推理、验证与部署路径6.1 大图切片推理最小可用的滑动窗口脚本遥感原图几乎不可能整张直接喂给模型切片推理是你第一件要写对的事。下面这个脚本用OpenCV把大图切成指定尺寸的子图推理后把框坐标偏移回原图坐标并对重叠区域做一个简单的NMS合并。python # 简化版滑动窗口推理 import cv2 import torch import numpy as np def slice_inference(model, img_path, slice_size512, stride460): img cv2.imread(img_path) h, w img.shape[:2] boxes [] confs [] for y in range(0, h, stride): for x in range(0, w, stride): # 边界处用padding而不是丢弃避免漏检 patch np.zeros((slice_size, slice_size, 3), dtypenp.uint8) patch[:min(slice_size, h-y), :min(slice_size, w-x)] img[y:yslice_size, x:xslice_size] result model(patch, sizeslice_size) # 将子图坐标映射回原图坐标注意x,y偏移 for det in result.xyxy[0].cpu().numpy(): x1, y1, x2, y2, conf, cls det boxes.append([x x1, y y1, x x2, y y2]) confs.append(conf) return boxes, confs逻辑说明stride小于slice_size意味着两次切片有重叠重叠区域的目标在两张子图中都会被检测到。最终合并时对同一目标使用置信度加权输出通常保留两次检测中置信度更高的那个。为了减少无效计算可以裁剪掉完全没有目标的子图但最简单的办法是先用yolov5s快速跑一遍整图的低分辨率版本过滤明显无目标的区域。参数说明slice_size一般取训练时的imgsz值保证模型输入分布一致。stride是重叠步长越小重叠越多漏检越少但推理时间越长遥感场景下我会用相对推荐的值slice_size512时stride取460重叠10%slice_size640时stride取576。6.2 用混淆矩阵和F1曲线判断模型值不值得上线训练结束后很多人只看mAP或者训练loss曲线但这不够。我会额外做两件事。第一用val数据跑一遍推理生成混淆矩阵。遥感检测的混淆矩阵会精确告诉你是“飞机容易被识别成车辆”还是“背景被识别成建筑物”。如果误检集中在“背景误报为小目标”即FPR高优先调conf阈值如果是类间混淆则需要补数据或者考虑类别权重。第二画出precision-recall曲线计算F1值在不同置信度下的峰值。遥感项目的验收标准如果写的是“无人机图像中识别车辆精确率不低于80%”那直接用F1曲线找到对应的最优conf阈值部署比拿着默认0.25的阈值上线靠谱得多。6.3 部署选项GPU到边缘设备部署路径无非三条Nvidia GPU上用TensorRT加速Jetson/NVIDIA边缘设备上用TensorRT或DeepStream纯嵌入式CPU上用OpenVINO或ONNX Runtime。遥感图像处理往往不是实时约束而是吞吐量约束所以量化的目标是把单张推理时间压到可控范围同时保住小目标检测精度。bash # 导出ONNX静态shape遥感推理时固定输入尺寸更稳定 python export.py --weights runs/train/remote_v2/weights/best.pt --include onnx --img-size 512 # 用onnxruntime跑推理float16或int8量化按需选择 python -m onnxruntime.quantization --model_type float16 best.onnx best_fp16.onnx推理端如果做RK3568等边缘设备部署YOLOv5的int8量化会遇到小目标检测精度骤降的问题常见做法是只量化Conv层而保持Detection头为fp16或者在量化时使用“量化感知训练”重训几轮。这里给出一个明确建议部署前先用6.2的验证集做完整评估量化前后的mAP差异用表格记录差异超过5个百分点就别强行量化退回fp16。结尾用我自己的习惯收住做遥感目标识别我养成了一个“多看一眼数据分布再跑模型”的习惯因为百分之八十的翻车都发生在数据准备阶段模型参数反而好调。如果你正为遥感图像小目标检测头疼按这篇文章的顺序把数据集整理、anchor重聚类、超参数调整、切片推理做扎实再回头看你之前的结果会发现很多精度问题根本不在模型。希望帮到你。本文还有配套的精品资源点击获取