从零复现 UAV-DETR:无人机目标检测论文复刻全流程踩坑记录

本文记录了我复现 UAV-DETR 这篇无人机目标检测论文的完整过程,分享了环境搭建、数据集处理、训练调参中遇到的各种问题和解决方案。最终在 VisDrone 数据集上取得了 mAP@50 = 52.90% 的成绩,超过了原论文报告的 48.8%。


前言

最近在做无人机相关的毕设/课题,需要复现一篇目标检测论文。选来选去,最终选了今年 1 月份挂在 arXiv 上的 UAV-DETR,原因很简单:

  1. 它基于 RT-DETR(端到端检测),不需要 NMS 后处理,部署方便
  2. 它专门针对无人机图像的小目标问题做了改进
  3. 代码基于 Ultralytics 框架,上手相对容易

但实际复现下来,发现坑远比想象的多。这篇文章就是来记录这些坑的。


一、论文在讲什么?

UAV-DETR 的核心思路是:在 RT-DETR 的基础上,加入频域信息来增强小目标的检测能力。

无人机图像的特点是目标小、密度高。传统方法只在空间域做卷积,高频信息(边缘、纹理)在多次下采样后会丢失。UAV-DETR 的做法是引入 FFT(快速傅里叶变换),在频域中对特征进行增强。

它主要改了三个地方:

第一个改动:MFFF 模块

这个模块的全名是 Multi-Frequency Feature Fusion。它的核心思想是:先把特征图变换到频域,做一波注意力加权,再变换回来。

有意思的是,它并不是对所有通道都做频域处理,而是只处理 25% 的通道,剩下 75% 直接通过。这种"分组处理"的设计在控制计算量的同时保持了效果。

第二个改动:频域感知下采样

普通的下采样(MaxPool、Stride Conv)会丢掉高频信息。这个模块在下采样的同时,通过一个并行的频域分支来保留这些细节。

第三个改动:语义对齐校准(SAC)

不同尺度的特征图拼接时,空间位置可能对不齐。SAC 通过可变形采样来校正这个偏移,让融合更精准。

理解了这三个改动,后面看代码就清晰多了。


二、环境搭建的坑

坑 1:Windows 编译问题

论文代码里有一个可变形注意力模块 MSDeformAttn,需要编译 CUDA 算子。在 Linux 下问题不大,但在 Windows 下会报各种编译错误。

我折腾了半天,最后发现 Ultralytics 框架里已经内置了一个纯 PyTorch 的实现(在 ultralytics/nn/modules/utils.py 里),虽然慢一点但完全能用。所以如果你也是 Windows 用户,不要去编译那个 CUDA 算子,直接用 Python 版本就行。

坑 2:Ultralytics 版本冲突

我一开始习惯性地 pip install ultralytics,结果发现最新版的 API 跟论文代码不兼容,各种报错。

后来才明白,论文代码里自带了一份修改过的 ultralytics,你得用项目目录下的那个版本,不能用 pip 安装的。正确的做法是 cd 到项目根目录下运行,这样 Python 会优先导入当前目录的 ultralytics。

坑 3:显存不够

我的 GPU 显存不大,一开始用 batch=8 直接 OOM。后来降到 batch=4 才跑起来。

如果你也遇到这个问题,除了降 batch size,还可以试:

  • 开启混合精度训练(amp=True)
  • 降低输入分辨率(imgsz=512)
  • 清理 GPU 缓存:训练前加 torch.cuda.empty_cache()

三、数据集处理的坑

我用的是 VisDrone-2019-DET 数据集,这是无人机检测领域最常用的数据集之一。

坑 4:标注格式转换

VisDrone 的标注格式是这样的:

bbox_left, bbox_top, bbox_width, bbox_height, score, category, truncation, occlusion

但 UAV-DETR 需要的是 YOLO 格式:

class_id center_x center_y width height

这里有个容易忽略的细节:VisDrone 的类别 ID 是从 1 开始的,而且 0 表示"忽略区域"。如果你在转换时没有过滤掉 category=0 的标注,模型会学到一些错误的信号,精度会明显下降。

坑 5:data.yaml 的类别顺序

这个坑很小但很致命。data.yaml 里的类别顺序必须和标注文件里的类别 ID 对应。如果顺序错了,模型会把"行人"识别成"汽车",精度直接归零。

建议转换完数据后,随机抽几张图用 OpenCV 画一下标注框和类别名,确认无误再开始训练。


四、训练调参:最花时间的部分

我的最终配置

model.train(
    data='data/visdrone/data.yaml',
    epochs=400,
    batch=4,
    imgsz=640,
    optimizer='AdamW',
    lr0=0.0001,
    patience=40,
    mosaic=1.0,
    mixup=0.2,
    amp=False
)

坑 6:warmup_epochs 不是你想的那个

配置文件里有个参数 warmup_epochs: 2000,我一开始以为是预热 2000 个 epoch,心想这也太夸张了。

后来查了源码才发现,这个值其实是 warmup 的迭代步数(steps),不是 epoch 数。如果 batch_size=4,训练集有 6471 张图,那每个 epoch 大约 1618 步,2000 步也就 1.2 个 epoch 的样子。

坑 7:学习率不衰减

配置里 lrf: 1.0,意思是最终学习率 = 初始学习率 × 1.0,也就是不衰减。

这跟我以前的习惯(cosine annealing)很不一样。我试过改成 cosine 衰减,结果精度反而下降了。后来想想也合理,AdamW 本身就有自适应学习率的能力,配合固定学习率可能更适合 DETR 这类模型。

坑 8:Early Stopping 时机

我设置了 patience=40,也就是连续 40 个 epoch 验证集精度不提升就停止。最终在第 342 个 epoch 触发了 early stopping。

从训练曲线来看,mAP 在 200 epoch 之后提升非常缓慢(从 51.3% 到 52.9%),但确实还在涨。如果你计算资源充足,建议把 patience 调大一些,可能会有更好的结果。

坑 9:训练日志的正确读法

Ultralytics 输出的 results.csv 里有很多列,我一开始没注意看,把 val/cls_loss 当成了训练 loss,白白分析了半天。

正确的关系是:

  • train/giou_loss + train/cls_loss + train/l1_loss = 训练总 loss
  • metrics/mAP50(B) = 验证集 mAP@50(这才是你要关注的精度指标)
  • val/* 开头的是验证集上的 loss

五、我的改进尝试

在跑通原论文代码后,我尝试了一些改进,主要是为了探索有没有进一步提升的空间。

改进 1:加 Coordinate Attention

坐标注意力(CA)把通道注意力分解成水平和垂直两个方向,可以更好地保留位置信息。我觉得这对无人机小目标可能有帮助。

实际测试下来,有一点提升但不大,大约 0.3-0.5 个点。

改进 2:用 GFNet 替换 FFM

GFNet(Global Filter Network)用可学习的全局滤波器在频域处理特征,比原版的 FFM 更简洁。好处是参数量更少,缺点是效果略有下降。

改进 3:MFFF + Coordinate Attention

把 CA 加到 MFFF 前面,让频域处理前先做一波坐标注意力。这个组合的效果还不错,但需要更多实验来验证。

总的来说,原论文的架构已经设计得很好了,简单的模块替换很难带来大的提升。如果要显著超越原论文,可能需要更深入的架构创新。


六、最终结果

精度对比

模型BackboneAPAP₅₀
UAV-DETR-R50 (论文)ResNet5031.551.1
UAV-DETR-R18 (论文)ResNet1829.848.8
UAV-DETR-EV2 (论文)EfficientFormerV228.246.7
我的复现 (R18)ResNet1832.8752.90

我在 ResNet18 backbone 上取得了 mAP@50 = 52.90%,超过了原论文报告的 48.8%,甚至比 R50 的 51.1% 还高。

为什么能超越?

说实话我也没完全搞清楚,可能的原因有:

  1. 训练更充分:我跑了 342 个 epoch,原论文可能没有训练这么久
  2. 数据增强有效:Mosaic + MixUp 的组合可能比原论文用的增强更强
  3. 超参数差异:不同的学习率、weight decay 等都会影响最终结果

不过精度对比需要谨慎看待,不同的训练环境、随机种子、数据划分都可能导致差异。


七、总结

这次复现最大的收获不是精度数字,而是对 DETR 类检测器有了更深入的理解。以前用 YOLO 只知道调参,现在对 Transformer 在检测中的作用、频域特征增强的思路有了更清晰的认识。

给想复现论文的同学几点建议:

  1. 先读论文,再看代码。不要上来就跑代码,先搞清楚论文在讲什么
  2. 一次只改一个东西。调参时不要同时改多个参数,否则你不知道哪个改动起了作用
  3. 记录每次实验。用 Excel 或 Notion 记录每次的配置和结果,方便回溯
  4. 善用 Git。每次改动都 commit,出了问题可以快速回退

复现论文是个体力活,但也是最好的学习方式。共勉。


代码已开源:GitHub,如果对你有帮助,欢迎 star。

更多推荐