news 2026/8/27 6:42:55

YOLOv8遥感小目标检测实战:NWPU VHR-10与DOTA数据集改进与训练全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv8遥感小目标检测实战:NWPU VHR-10与DOTA数据集改进与训练全解析

简介:目标检测是计算机视觉的核心任务之一,而小目标检测因其特征稀缺、定位敏感和样本不均衡等问题,始终是工程落地中的难点。以YOLOv8为代表的单阶段检测器在通用场景表现优异,但直接应用于遥感影像时,面对大量仅有十几像素的车辆、船舶等目标,默认网络结构往往力不从心。本文从检测原理出发,解析了通过引入高分辨率P2检测层、坐标注意力机制以及NWD损失函数来强化小目标特征表达的技术路径,并基于NWPU VHR-10与DOTA两大公开遥感数据集,完整演示了数据标注格式转换、大图切分、模型训练与调参的全流程。该方法适用于学术研究、毕业设计以及遥感智能解译等工程场景,为在有限算力下提升小目标检测精度提供了可复现的参考方案。 拿这个源码包的时候,我第一反应是先把目录树扫了一遍。确认过眼神,这不是那种 README 写得天花乱坠、跑起来全是坑的玩具项目——里面把 YOLOv8 从数据准备、格式转换、改进网络到训练推理整条链路都打通了,重点就是针对 NWPU VHR-10 和 DOTA 这两个遥感数据集做小目标检测。说实话,遥感小目标一直是目标检测里比较磨人的方向,很多开源项目要么只支持 VOC/COCO 这种常规数据集,要么对超大尺寸遥感图像没有配套处理脚本,让人卡在第一步就想弃坑。这份源码刚好把这两块短板补齐了,适合正在做遥感目标检测研究、准备毕业论文,或者想在小目标场景里把 YOLOv8 调到更好效果的工程师和同学参考。

1. 项目背景与整体设计思路

1.1 为什么偏偏是 NWPU VHR-10 和 DOTA

NWPU 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 和 OBBOBB(4 个顶点 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 Attention(CA)注意力模块。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.10,PyTorch 直接安装当前稳定的 2.x 版本即可,网上有些教程提到“PyTorch 2.13”这类编号其实是误传,截止目前官方稳定版还没有这个编号。CUDA 建议使用 cu118 或 cu121 对应的安装源,具体根据你的显卡驱动版本选择。

conda create -n yolov8 python=3.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", encoding="utf-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 xmin ymax xmax y转成水平框时,会把背景也框进来,导致正样本质量下降。建议转换完用可视化脚本在图上把标注框画出来检查一遍,确认没问题再进训练流程。

3.3 DOTA大图切分与坐标映射

DOTA 图像尺寸动辄几千像素,直接整图输入训练既放不下显存,也会把目标缩得太小。常规做法是先把大图切成若干 1024×1024 的 patch,再对每个 patch 做格式转换。切图时的核心参数是 patch_size 和 gap(重叠率),重叠的目的是避免目标正好落在patch边界被截断。

def split_image_with_boxes(img, boxes, patch_size=1024, gap=200): 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 data=datasets/NWPU_VHR10.yaml \ model=projects/yolov8_smalltarget.yaml \ epochs=300 batch=4 imgsz=640 \ amp=True workers=2 device=0

Windows 系统下workers建议设 2 或者 0,设大了经常会遇到BrokenPipeError,这是老生常谈但每次都有人踩的问题。显存实在不够时,还可以在训练配置里增加梯度累积,相当于用时间换空间,效果上差距不大。

4. 训练实操与核心源码解读

4.1 从训练命令看懂参数设计

很多新手直接把训练命令当成“咒语”复制粘贴,跑通了也不知道每个参数在干什么。这里逐项拆解一下:

data指向数据集 yaml,model指向网络结构 yaml。epochs在遥感小目标场景下建议至少 300 轮起步,因为小目标本身信息量少,收敛速度比正常目标慢不少。ir0lrf分别控制初始学习率和最终学习率系数,默认的 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, reduction=32): 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_size=1, stride=1, padding=0) self.bn1 = nn.BatchNorm2d(mip) self.act = nn.SiLU() self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0) self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0) 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], dim=2) y = self.conv1(y) y = self.bn1(y) y = self.act(y) x_h, x_w = torch.split(y, [h, w], dim=2) 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 stride=4 - [-1, 1, C2f, [64, 3, True]]

然后 Neck 部分再接一条融合路径。需要注意的是,加了 P2 头之后,detect层的输出维度也要相应调整,否则前向传播会报 shape 不匹配的错误。这个细节在源码里应该有注释说明,但如果你自己从零改,很容易漏。

4.3 训练曲线怎么看

训练跑起来之后,很多人的状态是“眼睛盯着 loss 曲线,但不知道曲线到底在说什么”。我建议重点看三条曲线:train/box_losstrain/cls_lossmetrics/mAP50(B)

box_loss反映的是边界框回归的收敛情况,如果训练中 epoch 数增加但 loss 不降,大概率是标签坐标明显异常。cls_loss反映分类效果,如果掉得很慢,要怀疑类别不均衡,去查一下各类目标的样本数量。mAP50(B)是整体精度的直观指标,遥感小目标场景里它通常比 mAP50-95 高不少,因为小目标的 IoU 计算天然吃亏。

ultralytics 训练完会自动生成results.csvresults.png,前者包含每个 epoch 的完整指标,后者是曲线图。如果你需要自己画更精细的损失曲线图,可以读results.csv用 matplotlib 可视化。个人建议训练早期先别频繁看曲线,前 50 个 epoch 的波动不代表最终结果,等训练结束后再整体分析,避免因为焦虑而浪费时间反复中断训练。

4.4 常见报错与排查技巧

训练过程中一定会遇到报错,这部分我整理了几个高频问题,基本都是实战中必然会碰到的:

报错现象产生原因解决办法
CUDA out of memory显存不足batch 降到 4,imgsz 降到 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 过高workers=20
loss 变成 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 的导出接口很成熟,一行命令就能转 ONNX:

yolo export model=best.pt format=onnx opset=12

如果目标平台是嵌入式设备,比如 NVIDIA Jetson 系列,再往后还要走 TensorRT 路线,把 ONNX 转成 engine 格式,必要时用 INT8 量化压模型体积。嵌入式部署的难点不在导出,而在精度和速度的平衡。实测下来,小目标检测模型对量化格外敏感,INT8 量化后小目标 AP 掉得比通用模型更明显,因为小目标的特征本身就非常微弱,低比特量化会把这些微弱信号进一步抹掉。如果量化后掉点超过可接受范围,可以考虑对高敏感层保留 FP16,或采用量化感知训练,在小目标数据集上微调量化参数。

模型导出还有一个常被忽略的细节:输入尺寸要固定为训练时的分辨率。如果你训练用的是 640×640,导出时也建议固定成 640,或者用动态 batch 和动态宽高,但 TensorRT 对动态 shape 的支持会复杂一些,工程上尽量在训练时就把最终部署的输入尺寸定下来。

最后再分享一点个人体会

这份源码真正让我觉得值钱的,不是那三个改进模块,而是它把数据预处理的功夫做扎实了。我见过太多人在模型结构上卷来卷去,最后发现性能上不去的原因只是训练集和验证集有泄漏、标注坐标算错、类别索引对不上这种基础问题。拿到这份源码,我建议你先别急着改代码,把 NWPU VHR-10 的转换脚本跑通,用可视化工具把标注框画出来看一眼,再开始训练。小目标检测没有银弹,P2、注意力、NWD 都是把模型潜力的方向往小目标那边掰,但真正决定上限的还是你给网络输入了多少清晰有效的目标信息。如果你打算把这个项目迁移到自己的遥感数据集上,优先确保图像分辨率够高、标注质量够好,再谈模型改进。踩过的坑都写在前面了,希望你能少走一半弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 6:41:55

ROS 2四足机器人单腿逆运动学实战:从关节坐标到运动控制

最近机器人赛道的话题热度很高&#xff0c;从四足机器人到人形机器人&#xff0c;相关企业的动态频繁出现在科技新闻里。比如宇树科技 IPO 的消息&#xff0c;就让原本偏小众的机器人开发方向被更多开发者注意到。很多人在聊估值、聊短期波动&#xff0c;但作为技术工程师&…

作者头像 李华
网站建设 2026/8/27 6:40:02

AI模型罗盘:从ReAct到Agent的工程化选型与评测方法

看到AI Models – Political Compass这个标题&#xff0c;我第一反应不是某个具体项目&#xff0c;而是一张坐标图。最近两年&#xff0c;AI 模型的讨论里确实流行用罗盘、象限图这类形式给模型做定位。热度高的讨论往往落在“这个模型在价值观上更接近哪一边”上&#xff0c;但…

作者头像 李华
网站建设 2026/8/27 6:39:37

基于DETR的智能冰箱物品识别:训练、部署与zip解压避坑全攻略

简介&#xff1a;目标检测是计算机视觉中的核心任务&#xff0c;要求同时识别物体类别与位置。传统方法依赖锚框和非极大值抑制&#xff0c;流程复杂。DETR&#xff08;Detection Transformer&#xff09;基于Transformer架构&#xff0c;将检测建模为集合预测问题&#xff0c;…

作者头像 李华
网站建设 2026/8/27 6:39:33

IEEE39节点模型深度解析:从文件结构到电力系统仿真落地

简介&#xff1a;节点模型是电力系统仿真的基础单元&#xff0c;指代电网中具有明确电气特性的母线连接点&#xff0c;其本质是一组满足基尔霍夫定律与潮流方程的参数化约束集合。理解节点模型需掌握拓扑结构、参数标幺化、动态元件耦合三大原理&#xff0c;技术价值在于支撑暂…

作者头像 李华
网站建设 2026/8/27 6:39:32

自制Arduino Uno兼容单板:从硬件设计到grbl固件烧录全攻略

前阵子整理工作台&#xff0c;翻出一块自己画了打样的Single-Board Arduino Uno Shield-Compatible开发套件&#xff0c;正好有朋友在问这类板子怎么用、以及怎么给它们烧grbl固件的事。这里就把我从硬件设计思路到grbl 1.1h.20190825.zip烧录、再到实际跑CNC任务的完整过程捋一…

作者头像 李华
网站建设 2026/8/27 6:38:58

村田IPD集成无源器件,为SX126X LoRa射频前端匹配提供新思路

做了这么多年LoRa物联网硬件&#xff0c;我越来越觉得&#xff0c;射频匹配这部分才是真正决定项目生死的地方。早些年用SX1278做433MHz模块&#xff0c;一颗颗电感电容在网分上反复调&#xff0c;匹配调好了&#xff0c;产品却可能在量产时翻车&#xff1b;后来换了SX1262&…

作者头像 李华