简介:这是一套面向国际算法算例大赛遥感图像物体检测任务的完整工程包,包含Python源代码、文档说明与数据集,适合计算机、人工智能、通信等专业的在校生作为毕业设计、课程设计或竞赛练习使用,也适用于希望入门目标检测实战的开发者。整个压缩包共405个文件,体积约7.55MB,其中350个Python脚本构成核心检测与训练逻辑,22个YAML文件负责模型与超参数配置,22个Markdown文档提供使用说明与项目结构解读,另附CSV格式的标注/结果数据、Jupyter Notebook演示以及示例图片,便于快速检索、二次开发或用于课程报告。所有源码均经过运行测试,答辩评审平均分达到96分,整体完成度与参考价值较高,也可直接用于项目初期立项演示。目前已有274人下载学习,若深入理解其代码逻辑,可在此基础上升级网络结构、扩展检测类别,从而适配自己的科研或工程场景。
1. 遥感图像物体目标检测赛题:入门门槛不高,跑通容易跑好难
遥感图像物体目标检测是国际算法算例大赛里出现频率最高、也最容易让参赛者产生“入门很简单”错觉的赛题。训练集里几百张高分遥感图,目标却可能小到只有十几个像素——一架停在机场的飞机、一艘漂在港口的货轮,在整张几千乘几千的图像里就是个亮点。用通用目标检测套路直接上去跑,十有八九在验证集上拿到一个不算低的 mAP,却在小目标类别上全面崩盘。这篇笔记不打算复述某个现成项目的源码,而是把这类赛题从零搭基线、调模型、修边界问题的完整路径讲清楚,覆盖技术选型、数据准备、训练调参和提交前验证,适合准备参赛或正在被遥感目标检测折磨的开发者。
2. 赛题与数据集拆解:先弄清评测指标和标注格式,再谈模型选型
2.1 遥感目标与自然图像目标差在哪
自然图像里的目标(人、车、猫狗)通常占据画面较大面积,且方向稳定——人不会横着走,车不会倒立。而遥感图像是俯视视角,目标方向完全任意。一艘船舶在港口里可以朝任意角度停靠,一架飞机在停机坪上更是四面八方都有。如果沿用自然图像里“水平锚框”的思路,一个水平框往往同时框住了两三个不同朝向的相邻目标,特征被互相污染,检测头在回归时也会左右为难。
另一个显著差异是目标尺度分布。高分遥感图像的原始分辨率往往到几千甚至上万像素,目标却集中在几十像素量级。飞机、车辆、储油罐这类小目标在整图里占比极低,直接整图缩放到固定尺寸训练,目标会缩到几个像素,特征完全丢失。这就是为什么遥感检测的通用做法是“切块训练”或设计强多尺度结构,而不是像自然图像那样整图缩放。不少人拿通用目标检测模型直接训遥感图,发现 loss 降不下去,根源就在尺度失配。
类别不均衡也是翻车重灾区。遥感场景中背景区域占比极高,一片裸地、一块云层、建筑阴影,都比目标更“像目标”。分类头很容易学出“宁可信其有”的倾向,导致误检框数量爆炸。所以在赛题中,置信度阈值、NMS 阈值的调参优先级,往往比网络结构本身的调整更高。这些差异最终决定了技术选型的方向:需要更强的多尺度能力、需要处理任意方向的目标、需要在大量负样本中维持正样本的纯度。
2.2 水平框还是旋转框:评测指标说了算
赛题评测指标是决定的唯一依据。多数自然图像检测用 IoU 0.5 或 0.5:0.95 下的 mAP,遥感赛题中常见两种设定:一种是仍然用水平框评测,那么水平框检测器就足够;另一种是用旋转框评测,水平框检测器无论预测多准,IoU 天然吃亏。旋转框的标注通常用四端点坐标或“中心点+宽+高+角度”表示,四端点格式每行是“类别名 x1 y1 x2 y2 x3 y3 x4 y4”,四个点按顺时针或逆时针排列。解析时最容易出问题的是坐标顺序不一致,有的数据从左上角开始排,有的从任意角开始排,这直接决定了旋转 IoU 能不能算对。
选型建议分三种情况。评测用水平框、目标密度低:直接用常规水平框检测器,把精力放在数据增强和调参上,性价比最高。评测用水平框、目标密集且方向复杂:建议仍然用水平框检测器,但数据增强里加随机旋转 90 度的倍数,让模型学会不同朝向下目标的特征。评测用旋转框:必须上旋转框检测器,这是绕不开的。判断方法很简单:看大赛提供的评测脚本里调用了什么函数。如果脚本里出现旋转 IoU 的计算逻辑、或者标注文件里每个目标有四个点的坐标,那十有八九是旋转框评测。
我曾在第一次参加这类赛题时吃过亏——标注给的是四端点,我却强行转成水平框训练,验证集分数掉了近 5 个点。这属于典型的“没看评测脚本就动手”,费了几天训练时间,最后推倒重来。所以拿到赛题的第一件事,不是急着跑通模型,而是把评测脚本读一遍,确认评测框类型、IoU 阈值和类别定义。这一步多花半小时,后面能省几天。
2.3 数据集目录结构和标注格式:动手前必须读懂的基础
拿到数据集后,第一步不是写模型,而是把目录结构和标注格式完整摸清楚。这类赛题的数据集常见组织方式如下:
dataset/ ├── train/ │ ├── images/ # 训练图像 │ └── labels/ # 训练标注 ├── val/ │ ├── images/ │ └── labels/ ├── test/ │ └── images/ ├── class_names.txt # 类别名称列表 └── train_val_split.txt # 划分文件标注格式常见有 VOC XML、COCO JSON 和 TXT 三种。遥感赛题里 TXT 格式最普遍,因为旋转框用四个点表达最方便。读 TXT 标注的代码通常长这样:
import numpy as np def read_dota_label(label_path): """ 读取四端点格式的旋转框标注 每行: class_name x1 y1 x2 y2 x3 y3 x4 y4 difficult """ boxes, labels = [], [] with open(label_path, "r", encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) < 9: continue class_name = parts[0] # 四端点共8个坐标值,按顺序组成4个点 coords = np.array(parts[1:9], dtype=np.float32) # 末尾的difficult标记决定是否参与训练 difficult = int(parts[9]) if len(parts) > 9 else 0 if difficult: continue boxes.append(coords.reshape(4, 2)) labels.append(class_name) return boxes, labels这段代码做了三件事:解析类别名、提取四端点坐标、过滤 difficult 样本。参数说明:parts[1:9]取的是从第一个坐标开始的 8 个数值,reshape(4, 2)把一维数组变成 4 个二维点坐标。difficult 标记是很多数据集的隐藏坑,如果评测时不计入这些样本,而训练却把它们的梯度算进去,模型的置信度会被带偏。
这里有个细节值得注意:坐标值直接用原图分辨率,不是归一化后的。之后无论切块还是缩放,都要同步变换坐标,否则标签和图像对不上。我习惯在解析后立刻打印一组样本的坐标和对应图像尺寸做交叉验证,确认坐标没有超出图像范围。这个习惯帮我避免过至少三次标注坐标系不一致的问题。目录下还可能附带类别名字典,务必以它为准,不要自己重排类别 ID——最终提交的预测文件里,类别编号错了直接零分。
3. 复现基线:从环境配置到跑通训练与验证的最小路径
3.1 环境依赖与版本匹配:conda、CUDA 与算子编译
遥感图像的检测框架依赖项比自然图像多一些,主要是旋转框相关的计算库。我建议新建独立环境,避免把服务器上其他项目的依赖搞乱。常见的做法是用 conda 隔离:
conda create -n remote_det python=3.8 -y conda activate remote_det pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install mmcv-full openmim mim install mmdet第一行创建 Python 3.8 环境,这个版本兼容性最稳,主流检测框架都对它有完整支持。PyTorch 的安装索引指定了 CUDA 11.8 版本,如果你本机驱动只支持 CUDA 11.3 及以下,这里要改成对应版本,否则推理时会报驱动不匹配。mmcv-full 是关键依赖,很多检测框架的旋转 IoU、自定义算子都依赖它编译,如果 pip 下载慢,可以用 mim 安装预编译包。安装完成后建议立刻跑一次算子自检:
python -c "import mmcv, mmdet; print(mmcv.__version__, mmdet.__version__)"这一步能确认核心依赖装齐没有。常见翻车现场是 mmcv 和 mmdet 版本不匹配,训练到第一个 epoch 直接报算子不存在。解决办法是核对框架官方文档中声明的版本对应关系,不要盲目追最新版。这个检查看起来像是在记流水账,但在比赛这种赶时间的场景里,它能拦住一大半的环境类报错。
3.2 把标注转成训练管线认识的格式:四端点转中心表示
拿到数据集后,通常需要把标注转换成训练框架支持的格式。以旋转框检测框架为例,它一般只认 DOTA 格式的 txt 或 COCO 格式的 json。如果你的数据集恰好是四端点 TXT,但框架要求“中心点+长宽+角度”格式,就需要一次转换。转换代码的常见做法如下:
import math def quad_to_theta(quad): """四端点转 cx cy w h angle,angle 单位是弧度""" # 取四个点的均值作为中心点 cx = quad[:, 0].mean() cy = quad[:, 1].mean() # 以第一个点与第二个点的连线计算长边方向角度 dx = quad[0, 0] - quad[1, 0] dy = quad[0, 1] - quad[1, 1] w = math.hypot(dx, dy) h = math.hypot(quad[0, 0] - quad[3, 0], quad[0, 1] - quad[3, 1]) angle = math.atan2(dy, dx) return cx, cy, w, h, angle逻辑说明:四端点旋转框转为带角度的中心表示,是模型输入前最常用的预处理步骤。这里用均值求中心点,用第一条边长度作宽、第四条边长度作高。角度 atan2 的取值范围在 [-pi, pi],不同框架对角度定义域有不同习惯,有的定义域在 [-pi/2, pi/2),有的在 [0, pi),转换后要统一到框架预期范围。
这段代码的边界问题值得留意:如果四端点的起点不是同一个角(比如有的从左上角开始排,有的从右上角开始排),计算出的角度会偏移 90 度或出现宽高互换。我通常会在转换后随机抽几张图,把旋转框画出来人工核对一次方向,确认角度定义一致再批量转换。这个“画框验证”的步骤每次都会做,比写一百行测试代码都管用。
3.3 训练、验证与生成预测文件的三个命令
配置文件和数据集结构就绪后,训练、验证、推理各有一个核心命令。这些命令在多数检测框架里是通用约定:
# 训练:指定配置和工作目录 python tools/train.py configs/remote_retinanet.py \ --work-dir work_dirs/remote_retinanet # 验证:加载训练好的权重,跑验证集指标 python tools/test.py configs/remote_retinanet.py \ work_dirs/remote_retinanet/latest.pth \ --out results.pkl # 推理:生成提交用的预测文件 python tools/inference.py configs/remote_retinanet.py \ work_dirs/remote_retinanet/latest.pth \ --save-path submission/训练命令里的 config 文件是核心,它同时定义数据路径、模型结构、优化器参数和训练策略。--work-dir 决定日志和 checkpoint 的存放位置,建议每个实验单独建目录,方便后续对比。验证命令的 --out 参数很有用,它把每张图每个框的预测结果和置信度序列化成 pkl 文件,后续画图分析、调阈值、做集成都要靠它。推理命令则负责把预测结果整理成大赛要求的提交格式,通常是一个 txt 或 csv,每行一个目标框。
这里有个容易被忽略的关联:三个命令不是割裂的。验证集上的指标只反映模型状态,提交文件则决定最终排名。我一般会在验证通过后,手动检查推理脚本输出的内容和验证集指标对得上——比如验证集 mAP 是多少,推理脚本输出的框数量是不是同一个量级。如果推理脚本里忘了过滤低置信度框,提交文件会膨胀得离谱,轻则评测变慢,重则直接被判无效。
3.4 文档说明里最容易跳过的三处信息
源码包里的文档说明不能跳过的地方有三个。第一是类别定义和编号。数据集的类别列表和论文里常用的类别往往不完全一致,某些数据会把“船”拆成“大船”“小船”,这时需要去读文档里对每个类别的定义,或者看标注里实际的框大小分布,避免类别语义理解错位。第二是评测脚本的输入输出约定。部分赛题要求预测文件的坐标格式是浮点数,有的要求四舍五入到整数;有的要求框按置信度从高到低排序,有的脚本默认顺序。这些细节全写在文档说明里,但很少人逐字读。第三是训练集和验证集的划分方式。有的数据集自带划分文件,有的要求参赛者自己划分。如果文档提到划分规则,要以它为准,不要自己重新随机划分,否则验证集指标无法横向对比别人。
文档说明里关于提交格式和评测逻辑的部分,重要性高于模型结构说明。我曾经跳过了一个关键说明——提交文件里的坐标是相对于切块后的图像坐标,不是原图坐标,结果所有预测框都画错了位置,提交分数接近零。后来养成习惯,每次拿到源码包先打开文档搜“评测”“提交”“坐标”,把这三处摘抄到笔记里,再开始读代码。
4. 遥感目标检测避坑指南:五个让我翻车的真实问题
4.1 小目标大面积漏检:注意力全被大目标带走
现象:验证集 mAP 看着还行,但按类别拆分后,飞机、车辆这类小目标的 AP 不到 10。原因在于模型的多尺度特征里,深度特征图对尺度小于 16×16 像素的目标几乎不敏感,而这类目标在遥感图里恰恰是数量最多的。解决:最有效的是图像切块训练。把原始大图切成 512×512 或 1024×1024 的 patch,目标在 patch 里的相对尺度变大,检测器能学到足够细节。切块时要让相邻 patch 有重叠,比如重叠 128 像素,避免目标正好被切在边界上。同时配合多尺度训练,随机在 0.5 到 1.5 倍尺度之间缩放。
提示 切块不是越碎越好。patch 越小,单张图需要的推理次数越多,而且切碎后目标上下文信息丢失,误检反而可能增加。512 到 1024 是一个常见平衡区间,我通常从 1024 开始试,验证集不足再往下降。
4.2 旋转框角度回归不稳:换了检测器 mAP 反而掉
现象:把水平框检测器换成旋转框检测器后,mAP 不升反降。这在第一次做旋转框时很常见。原因:角度回归的损失函数对角度边界非常敏感。旋转框的角度定义域存在周期性,0 度和 180 度其实是同一个框,但数值上差距巨大。如果不做周期处理,模型在边界附近产生巨大梯度,训练震荡,loss 曲线像锯齿一样上下跳。解决:优先选择带角度周期性的损失函数或编码方式,比如把角度拆成 sin 和 cos 两个值交给回归头,避开角度数值跳变;或者用高斯方式对角度做平滑编码。另一个实用技巧是训练初期给角度损失一个较低权重,等框的位置和尺寸回归稳定后,再逐步放开角度权重。这个课程式训练策略在很多框架的配置文件里都有对应参数,值得一试。
4.3 背景误检:裸地和云层被判成目标
现象:推理结果里出现大量框落在裸地、云层阴影上,置信度还不低。原因是遥感图的背景方差大,模型天然容易把纹理丰富的区域当作目标。解决:第一步提高置信度阈值,从默认的 0.05 提到 0.1 或 0.15,误检数量会明显下降。但阈值提高会牺牲召回,更本质的方案是增加难负样本挖掘。常见做法是在数据加载时,随机生成一些不与真实目标重叠的候选框,强制分类头学习“这些不是目标”。此外,把背景类显式加入分类器、改用带 Focal Loss 的检测器,也能缓解正负样本不平衡。
4.4 验证集与测试集分布不一致:本地分数高、提交翻车
现象:本地验证集 mAP 达到 78%,提交后官方分数只有 60%。这类赛题里训练集和测试集往往来自不同区域、不同传感器,存在明显分布偏移,验证集上过拟合得越狠,测试集上掉分越重。解决:从训练集里划分本地验证集时,尽量按区域或按时相划分,不要纯随机。更贴近大赛评测的方法是做交叉验证:训练三个模型,分别在三个划分上评估,取平均值估计真实水平。另一个实用技巧是减少训练 epochs。遥感赛题的过拟合拐点通常比自然图像来得早,如果验证集分数开始停滞或下降,果断 early stop。这个操作我用血泪经验验证过,多训十轮不如早停十轮。
4.5 显存不足训练中断:跑了一夜才发现没存权重
现象:loss 曲线正常下降,但日志在某个 epoch 后戛然而止,重启后发现没存 checkpoint。原因一般是显存峰值出现在每轮的最后几次迭代,加上日志保存和验证占用的资源叠加,恰好超过显存上限。解决:训练命令里设置梯度累积,将有效批量大小拆成多个小批量执行,显存峰值显著下降。同时开启自动保存间隔,设置每两个 epoch 存一次权重,避免白跑。还有一个常被忽略的细节:数据加载线程数不要拉满,把加载器的 num_workers 设成 4 到 8 足够,过多会导致内存交换,间接引发训练中断。
5. 从基线到前排:数据增强、推理增强和模型集成的组合拳
5.1 多尺度训练与测试时增强:最稳妥的免费午餐
多尺度训练是遥感赛题里性价比最高的增强手段。遥感图像中同一类目标的尺寸跨度极大,一个县城里既有占地上千像素的体育场,也有十几个像素的汽车。让模型在一个固定尺度下学习,很难同时照顾两端。多尺度训练的做法是在每个 epoch 里随机采样一个尺度因子,比如从 [0.5, 0.75, 1.0, 1.25, 1.5] 里随机选一个,把整批图像缩放到该尺度后送入网络。这样同一个目标在训练过程中以不同尺寸出现多次,模型的多尺度特征被充分利用。
测试时增强是另一层保险。推理时对同一张图做水平翻转、垂直翻转,再把预测结果反变换回原图坐标后合并,通常能带来 0.5 到 1.5 个点的稳定提升,而且几乎不需要额外训练。TTA 的合并逻辑要注意坐标变换,不能把翻转后的框直接叠加,需要先还原坐标再做 NMS。
import numpy as np def tta_merge(flip_boxes, origin_boxes, img_width, iou_thr=0.5): """ 翻转预测框与原始预测框合并 flip_boxes: 水平翻转图上的预测框 [N,5] x1 y1 x2 y2 score origin_boxes: 原图预测框 """ flip_boxes = flip_boxes.copy() # 水平翻转框坐标映射回原图 flip_boxes[:, [0, 2]] = img_width - flip_boxes[:, [2, 0]] all_boxes = np.concatenate([origin_boxes, flip_boxes], axis=0) # 按置信度排序后做NMS keep = [] idx = np.argsort(-all_boxes[:, 4]) while idx.size > 0: keep.append(idx[0]) iou = compute_iou(all_boxes[idx[0]], all_boxes[idx[1:]]) idx = idx[1:][iou < iou_thr] return all_boxes[keep]这里的关键参数是 img_width,翻转后的 x 坐标必须用它还原,如果误用了原始图像实际宽度或忘了映射,合并结果会有一半框位置错位。TTA 虽然叫“免费午餐”,但推理时间成倍增加,比赛中如果推理次数受限,只在最后几次提交时用 TTA 即可。
5.2 图像切块推理与重叠去重:大图推理的标准做法
训练时用切块,推理时同样要切块,否则训练和推理的输入分布不一致,性能会打折扣。大图直接送进网络有两个问题:一是超过模型输入尺寸会被强制缩放,小目标信息丢失;二是显存可能直接溢出。切块推理的标准做法是用一个滑窗在大图上滑动,每块分别推理,再把所有块的预测框映射回原图坐标,最后做一次全局 NMS 去重。
def slide_infer(image, model, patch_size=1024, overlap=128): """ 滑窗推理大图 返回原图坐标系下的预测框 """ h, w = image.shape[:2] all_boxes = [] stride = patch_size - overlap for y in range(0, h, stride): for x in range(0, w, stride): # 裁剪patch并限制边界 y1, y2 = y, min(y + patch_size, h) x1, x2 = x, min(x + patch_size, w) patch = image[y1:y2, x1:x2] # 模型推理 boxes = model(patch) # 坐标偏移回原图 boxes[:, [0, 2]] += x1 boxes[:, [1, 3]] += y1 all_boxes.append(boxes) all_boxes = np.concatenate(all_boxes, axis=0) return nms(all_boxes, iou_thr=0.5)overlap 是最关键的参数。重叠区域存在的意义是处理目标正好跨在切块边界上的情况,如果 overlap 为 0,边界目标会被切成两半,两个半块各自只能看到残缺目标,大概率漏检。overlap 一般设为 patch_size 的八分之一到四分之一。切块的边界坑是:同一个目标可能出现在相邻两个 patch 里被重复检测,所以最后必须做全局 NMS,而不是每个 patch 单独出结果。我在比赛里遇到过一个诡异现象——目标没漏检,但全部被重复框了两个,就是因为窗口重叠后没有做全局合并。
5.3 多模型集成与预测融合:从 NMS 到加权框融合
单模型调到头之后,最直接的提升手段是模型集成。不同初始化、不同 backbone 的模型,错误模式往往互补。集成的方式不止投票一种,常见的做法是把多个模型的预测框合并后做加权框融合,比单纯拼接再做 NMS 保留更多有效框。加权框融合的核心是聚类:先把所有模型的预测框按 IoU 分组,同一组的框视为对同一个目标的重复预测。
def wbf(boxes_list, iou_thr=0.55, weights=None): """ boxes_list: [model1_boxes, model2_boxes, ...] 每项[N,5] x1 y1 x2 y2 score weights 可为每个模型分配不同权重 """ if weights is None: weights = [1.0] * len(boxes_list) # 合并所有框并按score排序 all_boxes = [] for model_boxes, w in zip(boxes_list, weights): model_boxes[:, 4] *= w all_boxes.append(model_boxes) all_boxes = np.concatenate(all_boxes) all_boxes = all_boxes[np.argsort(-all_boxes[:, 4])] # 聚类融合 clusters = [] for box in all_boxes: assigned = False for c in clusters: if compute_iou(box, c["avg_box"]) > iou_thr: c["boxes"].append(box) c["avg_score"] = np.mean([b[4] for b in c["boxes"]]) assigned = True break if not assigned: clusters.append({"avg_box": box[:4], "boxes": [box], "avg_score": box[4]}) # 按平均置信度输出 results = [] for c in clusters: results.append(np.concatenate([c["avg_box"], [c["avg_score"]]])) return np.array(results)wbf 里权重参数的作用是纠正不同模型的置信度偏差。如果模型 A 置信度整体偏高、模型 B 偏低,不设权重时 B 的框很难参与聚类,融合效果大打折扣。我在实际使用时,通常给验证集 mAP 更高的模型分配更高权重,并按验证集分数比例归一化。集成的成本是显存和推理时间的成倍增加,比赛资源有限的情况下,选两个差异最大的模型集成通常性价比最高,三个以上的收益递减明显。
6. 提交前的最后一小时:可视化检查与分尺寸指标分析
6.1 批量可视化预测结果
训练到最后一版模型后,不要急着生成提交文件。先做一件事:随机抽验证集 50 张图,把真实框和预测框画在同一张图上保存下来,人工快速扫一遍。可视化能暴露指标看不出的问题——比如预测框整体偏移、旋转框角度反了、某些类别的框明显偏大或偏小。做可视化的代码量不大,核心是用 OpenCV 把框画上去:
import cv2 def draw_boxes(image, boxes, labels, colors): """把框画到图像上, boxes: [N,4] 或 [N,8]""" img = image.copy() for box, label in zip(boxes, labels): pts = box.reshape(-1, 2).astype(np.int32) cv2.polylines(img, [pts], True, colors[label], 2) return img绘制时注意区分旋转框和水平框。旋转框要用 polylines 按四端点连线画闭合四边形,水平框直接用 rectangle 画矩形。人工扫图时重点看三类错误:小目标是否漏检、大目标是否被重复框出多个框、背景区域是否有成片误检。这三类问题在指标上都能反映,但看图才能定位到底发生在哪些场景里。
6.2 按尺寸与类别拆分指标
可视化之后再做一个量化分析:把验证集预测结果按目标尺寸分成小、中、大三档,分别计算 AP。这个拆解能让最后半天的优化方向变得清晰,而不是凭感觉调参。计算方式是在评测脚本里加入按面积分组的逻辑:面积小于 32×32 算小目标,32×32 到 96×96 算中目标,大于 96×96 算大目标。如果发现小目标 AP 明显低于其他档位,优先考虑更激进的切块策略,比如把推理 patch 从 1024 降到 512,或者对整图做一次放大推理。如果某一类别的 AP 显著低于其他类别,针对该类别的数据增强比全局调参更有效,比如对含该类别目标的图像做复制粘贴增强。
这个“先可视化、再分组量化”的习惯,是我在多次比赛里总结出来的最后一步流程。每次走到这一步,都能在提交前发现至少一个值得修复的问题,有时候是阈值没调好,有时候是某个类别被其他类别压制。与其把最后时间花在盲目试参数上,不如先用这组检查把问题定位到具体类、具体尺度,再对症下药。
我个人的教训是:不要在验证集 mAP 涨了 0.2 个点时兴奋地直接提交。先把那 50 张可视化图看完,把分档指标看完,确认提升不是来自某种对测试集无效的过度拟合,再按下提交键。希望这篇笔记能帮你在遥感目标检测赛题上少走几步弯路。
本文还有配套的精品资源,点击获取