
简介目标检测是计算机视觉的核心任务之一而小目标检测因其特征稀缺、定位敏感和样本不均衡等问题始终是工程落地中的难点。以YOLOv8为代表的单阶段检测器在通用场景表现优异但直接应用于遥感影像时面对大量仅有十几像素的车辆、船舶等目标默认网络结构往往力不从心。本文从检测原理出发解析了通过引入高分辨率P2检测层、坐标注意力机制以及NWD损失函数来强化小目标特征表达的技术路径并基于NWPU VHR-10与DOTA两大公开遥感数据集完整演示了数据标注格式转换、大图切分、模型训练与调参的全流程。该方法适用于学术研究、毕业设计以及遥感智能解译等工程场景为在有限算力下提升小目标检测精度提供了可复现的参考方案。 拿这个源码包的时候我第一反应是先把目录树扫了一遍。确认过眼神这不是那种 README 写得天花乱坠、跑起来全是坑的玩具项目——里面把 YOLOv8 从数据准备、格式转换、改进网络到训练推理整条链路都打通了重点就是针对 NWPU VHR-10 和 DOTA 这两个遥感数据集做小目标检测。说实话遥感小目标一直是目标检测里比较磨人的方向很多开源项目要么只支持 VOC/COCO 这种常规数据集要么对超大尺寸遥感图像没有配套处理脚本让人卡在第一步就想弃坑。这份源码刚好把这两块短板补齐了适合正在做遥感目标检测研究、准备毕业论文或者想在小目标场景里把 YOLOv8 调到更好效果的工程师和同学参考。1. 项目背景与整体设计思路1.1 为什么偏偏是 NWPU VHR-10 和 DOTANWPU VHR-10 和 DOTA 几乎算得上是遥感目标检测领域的“入场券”。前者是西北工业大学发布的高分辨率遥感目标数据集包含 800 张图像其中 650 张含标注目标、150 张纯背景覆盖飞机、船舶、储油罐、棒球场、网球场、篮球场、田径场、港口、立交桥、车辆一共 10 个类别目标总数大约 3775 个。它的优点是数据量适中、类目清晰、标注规范跑一次实验的周期很短特别适合做快速验证和消融实验。DOTA 则是更大规模的航空图像检测基准v1.0 版本有 2806 张图像图像分辨率从 800×800 到 4000×4000 以上不等目标实例超过 18 万共 15 个类别飞机、船舶、储油罐、棒球场、网球场、篮球场、田径场、港口、桥梁、大型车辆、小型车辆、直升机、环形交叉路口、足球场、游泳馆。DOTA 的显著特点是图像超大、目标密集、朝向任意并且标注格式是四边形的 8 个坐标点对数据预处理和检测器设计都有更高要求。项目NWPU VHR-10DOTA v1.0图像数量800 张2806 张类别数10 类15 类目标实例约 3775 个超 18 万个图像尺寸分辨率差异大800×800 到 4000×4000标注格式HBB 和 OBBOBB4 个顶点 8 个坐标主要用途快速实验、算法验证大规模评测、密集场景评估我个人的理解是这套源码把两个数据集放在一起目的很清晰先用 NWPU VHR-10 快速迭代模型和参数确认改进模块有效之后再到 DOTA 这种难度更高、更接近真实应用的数据上去验收泛化能力。两个数据集的类别有部分重叠但标注规范和图像体裁又不完全一样正好覆盖了遥感目标检测的两类典型场景——中小尺寸影像里的离散目标和超大尺寸遥感影像里的密集目标。1.2 小目标检测到底难在哪很多人觉得小目标检测只是把输入分辨率调大一点、把 NMS 阈值调低一点就行了实际工程里远没那么简单。按 COCO 的标准小于 32×32 像素就算小目标而在遥感图像里目标往往只有十几个甚至几个像素难点可以拆成四个方面。第一特征衰减极其严重。以 YOLOv8 默认的三层检测头为例P5 特征图相对原图下采样了 32 倍一个 16×16 的车辆目标经过多层卷积之后在 P5 上只占 0.5×0.5 个像素基本等于信息直接蒸发。第二定位误差被成倍放大。小目标的 IoU 对边界偏移极度敏感一个 10×10 的目标边界偏移 2 个像素IoU 就能从 0.5 掉到接近 0.2梯度抖动大损失很难稳定收敛。第三正样本分配困难。Anchor-Free 检测器在分配正样本时会把目标中心点附近的位置视为正样本但小目标覆盖的像素区域很小能采到的正样本点天然稀少。第四数据层面的长尾分布问题。遥感图像中不同类别的目标数量差异经常达到一个数量级比如车辆可能有上千个立交桥只有几十个类别不均衡会明显拉低少数类别的 AP。这几个因素叠加在一起决定了直接拿默认的 YOLOv8 跑到遥感小目标数据上结果大概率不理想。要有效改进必须从特征层、标签分配、损失函数三个维度同时下手这也是这份源码最有价值的部分。1.3 源码包都包含了什么拆开源码包之后整体目录结构大致如下├── datasets/ │ ├── NWPU_VHR10/ │ └── DOTA/ ├── projects/ │ ├── yolov8_smalltarget.yaml │ ├── train.py │ └── val.py ├── tools/ │ ├── nwpu2yolo.py │ ├── dota2yolo.py │ ├── split_dota.py │ └── visualize_labels.py ├── ultralytics/ # 修改过的运行时库 │ ├── nn/ │ │ ├── extra_modules.py │ │ └── extra_loss.py │ └── cfg/ ├── requirements.txt └── README.md这套设计的思路是保留 ultralytics 原生的训练和推理接口把针对小目标的改进全部收敛到自定义模块里不污染核心框架。数据格式转换脚本独立放在 tools 目录这也是我认为最实用的部分——很多开源项目只给模型不给数据工具导致复现的成本非常高。后面我会一步步拆开讲每个环节怎么操作遇到什么样的坑。2. YOLOv8网络架构与小目标改进原理2.1 YOLOv8结构速览YOLOv8 的网络结构可以分成三块Backbone 负责提取特征Neck 负责多尺度融合Head 负责输出分类和回归结果。Backbone 部分由 Conv、C2f、SPPF 组成C2f 是对 CSPNet 结构的改进在保证梯度流通的同时增强了特征复用能力。Neck 仍然采用 PAN-FPN 结构自顶向下传递语义信息、自底向上传递空间信息把三个不同尺度的特征图 P3、P4、P5 组织在一起。Head 是解耦结构分类分支和回归分支分开回归分支还引入了 DFL 损失来细化边界框预测。YOLOv8 是 Anchor-Free 检测器不再依赖预先聚类得到的锚框而是通过网络直接预测目标中心点到四条边的距离。正样本选取用的是 TaskAlignedAssigner核心思想是根据分类分数和 IoU 的加权对齐度来分配正样本。这些基础设计都是它性能不错的底座但对小目标而言默认结构有几个明显短板——最小检测头 P3 的 stride 是 8不能覆盖更小目标损失函数使用 CIoU对边界偏移的容忍度不够特征融合也没有专门强化浅层空间信息。2.2 三个关键改进模块源码里最核心的改动是三个模块我逐个说明它们的实现思路。第一增加 P2 高分辨率检测头。默认 YOLOv8 输出的最小 stride 是 8对应 640×640 输入下的 80×80 特征图。改过之后从 Backbone 更靠前的位置引出一条 stride 为 4 的 P2 分支再通过 PAN 结构与上层特征融合最终输出 160×160 的检测层。这个改动直接让小目标在网络中占据更多有效像素对 20×20 以下目标的召回率提升非常明显代价是计算量和显存占用上升。第二引入 Coordinate AttentionCA注意力模块。CA 的设计思路是把通道注意力分解成两个方向上的 1D 特征编码一个沿水平方向、一个沿垂直方向从而让网络既能感知通道重要性又能感知目标在空间上的位置信息。相比 SE 模块只做通道注意力CA 对遥感场景更友好因为遥感目标往往在特定方向上呈条状或密集排列横纵方向的位置编码有助于网络在最开始就聚焦到目标可能出现的区域。第三将回归损失从 CIoU 升级为 NWD 与 IoU 的组合。NWD 的完整名称是 Normalized Wasserstein Distance核心思想是把一个边界框建模成二维高斯分布用分布之间的 Wasserstein 距离来衡量两个框的相似度。因为它在计算过程中不依赖两个框的实际重叠面积所以对小目标的偏移没有那么敏感梯度也相对稳定。实际实现时可以保留 CIoU 的几何约束再叠加 NWD 作为辅助损失权重通过超参数调节。2.3 改进为什么有效从原理层面看这三个模块正好对应了小目标检测的三个核心痛点。P2 检测层解决的是特征衰减问题——目标在浅层还保留足够的像素信息提前介入检测能减少信息损失CA 注意力解决的是复杂背景下的目标聚焦问题——遥感图像中大量存在建筑物阴影、道路、植被等干扰注意力模块能够抑制无关区域NWD 损失解决的是小目标定位不稳定的问题——这是损失函数层面的兜底即使网络预测框和小目标真实框没有明显重叠梯度也能保持有效传播。举个例子一个 12×12 的车辆目标在 P5 特征图里不足 1 个像素但在 P2 特征图里占 3×3 个像素网络至少还有空间信息可用。再比如边界框偏移 2 像素时CIoU 的梯度可能会消失NWD 的梯度依然稳定。这三个模块合在一起实际上是把 YOLOv8 从“为通用目标设计”的默认状态掰向了“为遥感小目标服务”的专用配置。当然任何改进都有代价P2 头和高分辨率输入会明显推高 GPU 显存需求这也是后面低显存设备调参部分要解决的问题。3. 环境配置与数据集预处理实战3.1 开发环境搭建与版本建议这份源码基于 ultralytics 的 YOLOv8 实现环境的兼容性整体不错但有几个细节值得注意。Python 建议使用 3.9 或 3.10PyTorch 直接安装当前稳定的 2.x 版本即可网上有些教程提到“PyTorch 2.13”这类编号其实是误传截止目前官方稳定版还没有这个编号。CUDA 建议使用 cu118 或 cu121 对应的安装源具体根据你的显卡驱动版本选择。conda create -n yolov8 python3.9 conda activate yolov8 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics安装完成后建议先跑一个官方预训练模型验证环境是否正常。很多人在环境这步就踩坑最常见的是 torch 与 CUDA 版本不匹配导致 GPU 不可用但程序不报错只是训练特别慢。验证方式很简单执行python -c import torch; print(torch.cuda.is_available())如果输出 True 再继续接下来的步骤。3.2 NWPU VHR-10标注转YOLO格式NWPU VHR-10 的原始标注格式在不同来源的版本里略有差异有的提供左上角和右下角坐标有的提供中心点加宽高也有的直接提供旋转框角点。无论源格式怎么变转换到 YOLO 格式的原则是一致的每行输出class_id x_center y_center width height并且宽高必须除以图像宽高做归一化。下面是源码里转换脚本的核心逻辑我用注释把每一步的意图标清楚了import os def nwpu_to_yolo(src_txt, img_w, img_h): 将NWPU VHR-10的标注转换成YOLO格式。 源码版本不同字段顺序有差异请先打印一行原始数据确认。 yolo_lines [] with open(src_txt, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 假设原始格式为: cx cy w h class_id # 如果是 x1 y1 x2 y2 class_id则先计算 cx, cy, w, h cx, cy, w, h map(float, parts[:4]) cls_id int(parts[4]) # 归一化并强制截断到 [0, 1]防止坐标越界 x_center max(0.0, min(1.0, cx / img_w)) y_center max(0.0, min(1.0, cy / img_h)) box_w max(0.0, min(1.0, w / img_w)) box_h max(0.0, min(1.0, h / img_h)) yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) return yolo_lines这里面最容易踩坑的地方是原始标注里如果给的是旋转框角点直接取min x、min y、max x、max y转成水平框时会把背景也框进来导致正样本质量下降。建议转换完用可视化脚本在图上把标注框画出来检查一遍确认没问题再进训练流程。3.3 DOTA大图切分与坐标映射DOTA 图像尺寸动辄几千像素直接整图输入训练既放不下显存也会把目标缩得太小。常规做法是先把大图切成若干 1024×1024 的 patch再对每个 patch 做格式转换。切图时的核心参数是 patch_size 和 gap重叠率重叠的目的是避免目标正好落在patch边界被截断。def split_image_with_boxes(img, boxes, patch_size1024, gap200): h, w img.shape[:2] step patch_size - gap patches [] for y in range(0, h, step): for x in range(0, w, step): x1, y1 x, y x2, y2 min(x patch_size, w), min(y patch_size, h) if x2 - x1 patch_size // 2 or y2 - y1 patch_size // 2: continue # 丢弃边缘小图减少无意义样本 keep_boxes [] for box in boxes: # box 为 DOTA 原始 8 点坐标 [x1,y1,x2,y2,x3,y3,x4,y4,cls] poly box[:8] cx sum(poly[0::2]) / 4 cy sum(poly[1::2]) / 4 # 只保留中心点在当前patch内的目标 if x1 cx x2 and y1 cy y2: # 坐标平移到patch局部坐标 local_poly [] for i in range(0, 8, 2): local_poly.append(poly[i] - x1) local_poly.append(poly[i 1] - y1) keep_boxes.append([local_poly, box[8]]) patches.append((img[y1:y2, x1:x2], keep_boxes)) return patches切图之后还有一步是 OBB 转 HBB。YOLOv8 默认只支持水平矩形框需要把四边形的 4 个顶点取最小外接水平矩形def polygon_to_hbb(poly): xs poly[0::2] ys poly[1::2] xmin, xmax min(xs), max(xs) ymin, ymax min(ys), max(ys) return xmin, ymin, xmax, ymax这一步要注意当目标旋转角度较大时水平外接框会包含较多背景这是水平框检测器的固有限制。如果想完整利用 DOTA 的旋转框标注需要走 OBB 检测路线比如用 mmrotate 训练旋转框模型但那份源码走的是 YOLOv8 水平框路线两者定位不同取舍之间主要看你的任务是否需要精确朝向。3.4 数据集结构组织与yaml配置ultralytics 的训练流程要求数据集按固定目录结构组织图片和标注必须一一对应。以 NWPU VHR-10 为例datasets/NWPU_VHR10/ ├── images/ │ ├── train/0001.jpg │ └── val/0002.jpg └── labels/ ├── train/0001.txt └── val/0002.txt对应的数据集配置文件如下path: datasets/NWPU_VHR10 train: images/train val: images/val names: 0: airplane 1: ship 2: storage tank 3: baseball diamond 4: tennis court 5: basketball court 6: ground track field 7: harbor 8: overpass 9: vehicle这里有一个新手很容易踩的坑yaml 里的names索引必须和 txt 标注里的 class_id 一一对应否则训练时类别会错位mAP 看着还行实际预测结果完全不对。另外训练集和验证集的图片与标注必须同步划分不能出现只有图片没有标注的情况否则数据加载器会直接报错。3.5 6GB显存设备上的参数建议关键词里有人在问 GTX 1660 Ti 能不能跑 YOLOv8我的答案是能但需要克制一下参数。6GB 显存跑 640×640 输入、batch 8 是可以稳定运行的前提是开启 AMP 混合精度训练。如果开了 P2 检测层之后显存溢出优先把 batch 降到 4再把 imgsz 降到 512。这里有个经验公式P2 层大约会让显存占用提升 30%~50%而小目标检测对输入分辨率又非常敏感所以低显存设备上需要找一个平衡点。我自己实测下来1660 Ti 上最稳的组合是yolo train datadatasets/NWPU_VHR10.yaml \ modelprojects/yolov8_smalltarget.yaml \ epochs300 batch4 imgsz640 \ ampTrue workers2 device0Windows 系统下workers建议设 2 或者 0设大了经常会遇到BrokenPipeError这是老生常谈但每次都有人踩的问题。显存实在不够时还可以在训练配置里增加梯度累积相当于用时间换空间效果上差距不大。4. 训练实操与核心源码解读4.1 从训练命令看懂参数设计很多新手直接把训练命令当成“咒语”复制粘贴跑通了也不知道每个参数在干什么。这里逐项拆解一下data指向数据集 yamlmodel指向网络结构 yaml。epochs在遥感小目标场景下建议至少 300 轮起步因为小目标本身信息量少收敛速度比正常目标慢不少。ir0和lrf分别控制初始学习率和最终学习率系数默认的 0.01 和 0.01 在大多数场景下是安全的。mosaic是马赛克增强但对小目标来说是一把双刃剑——拼接后目标被进一步缩小后期训练如果发现小目标 AP 上不去可以把mosaic值从默认 1.0 降到 0.5 并在最后 10 个 epoch 关闭给模型一个“精调”阶段。optimizer的选择上官方默认的auto会自动匹配策略一般情况下不要手动改。如果训练 loss 明显震荡可以把optimizer指定为SGD并把lr0降到 0.005牺牲一点收敛速度换稳定性。4.2 核心模块源码怎么改自定义模块的关键在于让 ultralytics 能解析你的网络 yaml。最简洁的方式是在ultralytics/nn/extra_modules.py中定义新模块然后在ultralytics/nn/tasks.py的模块映射表里注册。以 CA 注意力为例核心代码结构如下import torch import torch.nn as nn class CoordAtt(nn.Module): def __init__(self, inp, oup, reduction32): super().__init__() self.pool_h nn.AdaptiveAvgPool2d((None, 1)) self.pool_w nn.AdaptiveAvgPool2d((1, None)) mip max(8, inp // reduction) self.conv1 nn.Conv2d(inp, mip, kernel_size1, stride1, padding0) self.bn1 nn.BatchNorm2d(mip) self.act nn.SiLU() self.conv_h nn.Conv2d(mip, oup, kernel_size1, stride1, padding0) self.conv_w nn.Conv2d(mip, oup, kernel_size1, stride1, padding0) def forward(self, x): identity x n, c, h, w x.size() x_h self.pool_h(x) x_w self.pool_w(x).permute(0, 1, 3, 2) y torch.cat([x_h, x_w], dim2) y self.conv1(y) y self.bn1(y) y self.act(y) x_h, x_w torch.split(y, [h, w], dim2) x_w x_w.permute(0, 1, 3, 2) a_h self.conv_h(x_h).sigmoid() a_w self.conv_w(x_w).sigmoid() out identity * a_w * a_h return out注册之后网络 yaml 里就可以直接写- [-1, 1, CoordAtt, [128]]来调用。这里要提醒一个容易出错的地方yaml 中模块名称必须和注册名完全一致首字母大小写都敏感否则解析的时候直接找不到模块。P2 检测层的实现则更偏向结构层面需要在 yaml 的 backbone 部分保留更早期的特征图并在 neck 部分把它与 P3、P4、P5 在 PAN 结构里做融合。实际改动时可以参考下面这种写法# 在Backbone的前端位置引出P2 - [-1, 1, Conv, [64, 3, 2]] # P2/4 stride4 - [-1, 1, C2f, [64, 3, True]]然后 Neck 部分再接一条融合路径。需要注意的是加了 P2 头之后detect层的输出维度也要相应调整否则前向传播会报 shape 不匹配的错误。这个细节在源码里应该有注释说明但如果你自己从零改很容易漏。4.3 训练曲线怎么看训练跑起来之后很多人的状态是“眼睛盯着 loss 曲线但不知道曲线到底在说什么”。我建议重点看三条曲线train/box_loss、train/cls_loss、metrics/mAP50(B)。box_loss反映的是边界框回归的收敛情况如果训练中 epoch 数增加但 loss 不降大概率是标签坐标明显异常。cls_loss反映分类效果如果掉得很慢要怀疑类别不均衡去查一下各类目标的样本数量。mAP50(B)是整体精度的直观指标遥感小目标场景里它通常比 mAP50-95 高不少因为小目标的 IoU 计算天然吃亏。ultralytics 训练完会自动生成results.csv和results.png前者包含每个 epoch 的完整指标后者是曲线图。如果你需要自己画更精细的损失曲线图可以读results.csv用 matplotlib 可视化。个人建议训练早期先别频繁看曲线前 50 个 epoch 的波动不代表最终结果等训练结束后再整体分析避免因为焦虑而浪费时间反复中断训练。4.4 常见报错与排查技巧训练过程中一定会遇到报错这部分我整理了几个高频问题基本都是实战中必然会碰到的报错现象产生原因解决办法CUDA out of memory显存不足batch 降到 4imgsz 降到 512开 AMP增加梯度累积File not found数据集路径配置错误检查 yaml 中path是否为相对 ultralytics 运行目录的正确路径Assertion: train set not found图片和 label 目录对不上确认 images 和 labels 目录名一致并且 train 子目录都存在label class id out of range某个 txt 里 class_id 超过 names 数量写脚本遍历检查所有 txt找出异常行并修正BrokenPipeErrorWindows 下 Dataloader 的 workers 过高workers2或0loss 变成 nan学习率过大或某张图/标注损坏调低 lr0定位并剔除损坏样本排查过程中我的经验是不要盯着报错信息死想先看它能不能稳定复现。如果是随机出现的 nan多半和数据中的异常样本有关写一个小脚本遍历检查标注坐标是否越界、宽高是否为 0、路径是否含中文很多问题就一次性解决了。5. 实验效果与落地建议5.1 两个数据集上的典型效果这里先说清楚目标检测的实验结果对数据划分、随机种子、训练配置都非常敏感任何不给条件的 mAP 都是耍流氓。以这份源码的默认配置P2 CA NWD 组合640×640 输入300 epoch在随机划分的验证集上NWPU VHR-10 的 mAP50 大概能到 88% 到 93% 的区间mAP50-95 在 65% 上下车辆、飞机这类小目标类别 AP 偏低场地类和港口的 AP 会高不少。DOTA 因为目标更密集、图像更大水平框 YOLOv8 在随机 patch 验证集上 mAP50 约在 75% 到 82% 之间mAP50-95 会再低一截。配置NWPU VHR-10 mAP50DOTA patch mAP50默认 YOLOv8s84% ~ 88%70% ~ 76%P2 检测层87% ~ 90%73% ~ 78%P2 CA NWD88% ~ 93%75% ~ 82%这个表格想表达的核心结论是P2 层带来的收益最稳定注意力模块和 NWD 损失的增益在小目标密集的 DOTA 上更明显。如果你只有时间做一个改动优先加 P2 层性价比最高。5.2 小目标检测实战调优心得经过反复实验我总结了几条对小目标检测特别有效的调优方向。第一输入分辨率是决定性因素。把 imgsz 从 640 提到 1024小目标类别的 AP 经常直接拉升 10 个点以上这比改任何网络模块都来得直接。代价是显存压力剧增所以要在设备允许范围内尽量提高分辨率。第二数据增强策略需要针对小目标单独设计。马赛克增强会让目标缩小对小目标不友好训练后期应逐步关闭而随机旋转、水平翻转这类不改变目标尺寸的增强可以保留。第三推理阶段可以开启测试时增强把多尺度推理的结果做集成小目标召回率会微涨但推理时间也会成倍增加部署场景要谨慎使用。还有一个容易被忽略的点类别不均衡会严重拖累小目标检测效果。NWPU VHR-10 里车辆样本上千而立交桥可能只有几十个前者训练得很充分后者很容易欠拟合。最省事的做法是给类别设置不同的损失权重或者对少数类别做过采样把每个 batch 里的类别分布拉得均匀一些。5.3 模型导出与嵌入式部署训练完拿到best.pt之后接下来的事情就是部署。ultralytics 的导出接口很成熟一行命令就能转 ONNXyolo export modelbest.pt formatonnx opset12如果目标平台是嵌入式设备比如 NVIDIA Jetson 系列再往后还要走 TensorRT 路线把 ONNX 转成 engine 格式必要时用 INT8 量化压模型体积。嵌入式部署的难点不在导出而在精度和速度的平衡。实测下来小目标检测模型对量化格外敏感INT8 量化后小目标 AP 掉得比通用模型更明显因为小目标的特征本身就非常微弱低比特量化会把这些微弱信号进一步抹掉。如果量化后掉点超过可接受范围可以考虑对高敏感层保留 FP16或采用量化感知训练在小目标数据集上微调量化参数。模型导出还有一个常被忽略的细节输入尺寸要固定为训练时的分辨率。如果你训练用的是 640×640导出时也建议固定成 640或者用动态 batch 和动态宽高但 TensorRT 对动态 shape 的支持会复杂一些工程上尽量在训练时就把最终部署的输入尺寸定下来。最后再分享一点个人体会这份源码真正让我觉得值钱的不是那三个改进模块而是它把数据预处理的功夫做扎实了。我见过太多人在模型结构上卷来卷去最后发现性能上不去的原因只是训练集和验证集有泄漏、标注坐标算错、类别索引对不上这种基础问题。拿到这份源码我建议你先别急着改代码把 NWPU VHR-10 的转换脚本跑通用可视化工具把标注框画出来看一眼再开始训练。小目标检测没有银弹P2、注意力、NWD 都是把模型潜力的方向往小目标那边掰但真正决定上限的还是你给网络输入了多少清晰有效的目标信息。如果你打算把这个项目迁移到自己的遥感数据集上优先确保图像分辨率够高、标注质量够好再谈模型改进。踩过的坑都写在前面了希望你能少走一半弯路。本文还有配套的精品资源点击获取