ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

低空安防无人机检测:5400张YOLO数据集训练与调优实战

低空安防无人机检测:5400张YOLO数据集训练与调优实战

1. 低空安防场景下的无人机检测需求拆解

1.1 为什么低空安防突然成了香饽饽

这两年做视觉项目的朋友应该都有感觉,低空安防相关的需求肉眼可见地变多了。机场、监狱、大型活动场馆、重要基础设施周边,都在陆续上马无人机侦测与反制系统。背后的逻辑其实不复杂:消费级无人机价格已经跌到几百块,飞行门槛几乎为零,但对应的管控手段却长期停留在“人眼+对讲机”的阶段。这种供需落差,就是低空安防这个赛道被快速催熟的根本原因。

从技术实现路径来看,低空安防目前主要有三条路线:雷达探测、无线电频谱侦测、以及视觉检测。雷达和频谱设备成本高、部署复杂,而且对“悬停不动”或者“贴地飞行”的目标往往力不从心。视觉检测的优势在于成本低、部署灵活、可视化程度高,一台带云台的相机加上边缘计算盒子就能跑起来。但视觉检测的命门也很明显——它极度依赖高质量的标注数据。没有足够多、足够真实的无人机图像,模型在复杂天空背景下根本分不清飞鸟、风筝和四旋翼。

这就是为什么一个5400张规模的YOLO格式低空安防数据集,在圈子里会显得很有分量。它解决的不是“能不能检测”的问题,而是“能不能在真实场景下稳定检测”的问题。

1.2 5400张这个量级意味着什么

先给不太熟悉目标检测数据规模的朋友一个参照系。公开的通用目标检测数据集,比如COCO,动辄十几万张,但那是覆盖80个类别的通用场景。对于单一类别的专用检测任务,5000张左右的精标数据已经可以支撑一个相当可用的模型了。YOLO系列在单类别任务上,通常3000到5000张高质量标注就能达到不错的收敛效果,5400张属于“够用且有余量”的水平。

更重要的是,这个量级的数据集如果场景覆盖做得好,可以显著降低过拟合风险。很多团队自己采集的数据往往集中在某几个固定机位、固定时间段,模型换一个场景就崩。一个成熟的数据集会在不同光照、不同背景、不同飞行姿态上做均衡采样,这才是它真正的价值所在。

1.3 适合哪些人直接上手

这个数据集的目标用户其实很明确。第一类是正在做低空安防产品原型的算法工程师,需要快速验证检测方案可行性,自己采数据标注周期太长,直接拿现成数据集能省掉至少两周的前期工作。第二类是高校和科研机构做相关课题的研究人员,需要一个标准化的基准来对比不同改进算法的效果。第三类是做边缘计算设备部署的团队,需要在自己的硬件上跑通检测流程,数据集可以用来做量化和剪枝后的精度验证。

哪怕你只是对YOLO训练流程感兴趣,想找一个真实场景的单类别数据集练手,这个无人机检测数据集也比拿COCO做猫狗检测更有实战意义,因为它的背景复杂度、目标尺度变化、小目标占比都更接近工业级应用。

2. 数据集核心细节与YOLO格式解析

2.1 目录结构与标注格式说明

拿到一个数据集,第一件事不是急着训练,而是把目录结构和标注格式摸清楚。YOLO格式的数据集通常长这样:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml

images下面放的是jpg或png原图,labels下面放的是同名的txt标注文件。这里有个新手特别容易踩的坑:图片和标注文件必须严格同名,只是扩展名不同。比如images/train/001.jpg对应的标注必须是labels/train/001.txt。如果名字对不上,训练时程序会直接忽略这张图,而且很多框架不会报错,你只会发现训练集数量莫名其妙少了一截。

YOLO的标注格式是每行一个目标,格式为:

class_id x_center y_center width height

这五个值之间用空格分隔。class_id从0开始编号,单类别任务就是0。后面四个值全部是归一化到0到1之间的浮点数,也就是相对于图片宽高的比例值,不是绝对像素坐标。这一点和VOC的XML格式、COCO的JSON格式都不一样,转换的时候要特别注意。

2.2 归一化坐标的计算过程

很多人第一次自己转YOLO格式时会在归一化上翻车,这里把计算过程完整走一遍。假设一张图片尺寸是1920x1080,你标注了一个无人机目标,它的左上角像素坐标是(800, 300),右下角是(1000, 500)。

先算绝对宽高:宽 = 1000 - 800 = 200,高 = 500 - 300 = 200。再算中心点绝对坐标:x_center = (800 + 1000) / 2 = 900,y_center = (300 + 500) / 2 = 400。

然后归一化:x_center_norm = 900 / 1920 ≈ 0.46875,y_center_norm = 400 / 1080 ≈ 0.37037,width_norm = 200 / 1920 ≈ 0.10417,height_norm = 200 / 1080 ≈ 0.18519。

最终写入txt的一行就是:0 0.46875 0.37037 0.10417 0.18519。

注意:归一化后的值必须严格在0到1之间。如果出现大于1或者负数,说明标注框超出了图片边界,这种数据在训练时会导致loss异常,最好在预处理阶段就过滤掉。

2.3 小目标占比与尺度分布

无人机检测最棘手的问题之一就是小目标。低空安防场景下,无人机往往在几十米到几百米外被拍到,在1080P画面里可能只占几十个像素。我实际统计过类似数据集的目标尺度分布,通常会出现明显的双峰:一部分是近距离的大目标,占画面比例可能超过10%;另一部分是远距离的小目标,占比不到1%。

这种尺度分布对模型提出了很高要求。YOLO的FPN结构本身就是为了应对多尺度检测设计的,但如果小目标样本太少,浅层特征学习不充分,远距离无人机就很容易漏检。5400张这个量级的好处是,即使小目标只占20%,也有超过1000个样本,足够让网络学到有效特征。

在训练前建议先跑一个脚本统计一下所有标注框的宽高分布,画个直方图。如果发现小于32x32像素的目标占比超过30%,就要考虑在训练时提高输入分辨率,或者使用专门针对小目标的检测头。

2.4 背景负样本的价值

一个容易被忽视的细节是负样本。纯无人机数据集如果全是正样本,模型很容易学到“天空=无人机”这种错误关联,导致在真实场景中把云朵、飞鸟、建筑边缘都误检成无人机。好的数据集会包含一定比例的纯背景图,也就是没有标注框的负样本。

负样本的txt文件是空的,但文件必须存在。训练时这些图会参与计算,帮助模型学习“什么不是无人机”。一般来说负样本占比控制在5%到15%之间比较合适。太少起不到抑制作用,太多又会稀释正样本的学习信号。

如果你拿到的数据集负样本不足,可以自己用一些天空、云层、城市远景的图片补充进去,标注文件留空即可。这是提升模型实际部署效果的一个低成本高回报操作。

3. 从零跑通YOLO训练全流程

3.1 环境搭建与依赖安装

训练环境这块,我个人的建议是能用Linux就用Linux,Windows下虽然也能跑,但路径分隔符、多进程数据加载这些地方容易出幺蛾子。Python版本选3.8到3.10之间比较稳,太新的版本有些库还没跟上。

以Ultralytics的YOLOv8为例,安装过程很直接:

conda create -n drone python=3.9 conda activate drone pip install ultralytics

如果你有NVIDIA显卡,还需要确认CUDA和cuDNN版本匹配。跑YOLOv8的话,CUDA 11.8配合PyTorch 2.0以上是比较成熟的组合。装完之后用一行代码验证:

import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))

输出True和显卡型号就说明环境没问题。如果显示False,八成是PyTorch装成了CPU版本,需要去官网找对应CUDA版本的安装命令重装。

提示:不要盲目追求最新版本的CUDA。很多视觉库对CUDA版本有硬性依赖,版本太新反而会导致编译失败。选一个社区验证过的稳定组合比追新更重要。

3.2 data.yaml配置文件的写法

data.yaml是整个训练流程的入口配置,写错了后面全白搭。针对这个无人机数据集,典型的配置如下:

path: /home/user/dataset train: images/train val: images/val test: images/test nc: 1 names: ['drone']

path是数据集根目录,train/val/test是相对于path的图片目录路径。注意这里写的是图片目录,不是标注目录,框架会自动去同级目录找labels。nc是类别数量,单类别就是1。names是类别名称列表,顺序必须和标注文件里的class_id对应。

有个细节很多人会忽略:path最好用绝对路径。用相对路径的话,训练时的工作目录一变就会找不到数据。另外Windows下路径反斜杠要改成正斜杠,或者用双反斜杠转义,否则yaml解析会出错。

3.3 训练参数的选择与计算

YOLOv8的训练命令看起来简单,但参数背后的门道不少:

yolo detect train data=data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16

model选yolov8s还是yolov8n,取决于你的部署硬件。如果是边缘设备,nano版本推理快但精度低;如果服务器端跑,s版本甚至m版本精度更好。我的经验是先用s版本跑一个baseline,看精度是否达标,再决定要不要往小模型蒸馏。

imgsz=640是输入分辨率。前面提到小目标问题,如果你的数据集小目标占比高,可以提到1280。但要注意,分辨率翻倍,显存占用大约翻四倍,batch size要相应调小。640下batch=16大概需要8G显存,1280下可能只能开到4。

epochs=100对于5400张单类别数据通常够了。判断是否收敛不要只看loss曲线,要看验证集的mAP50是否还在提升。如果连续20个epoch mAP不涨,就可以停了,再训下去只会过拟合。

学习率方面,YOLOv8默认用的是余弦退火加warmup,初始lr=0.01。如果发现训练初期loss震荡厉害,可以把lr0调到0.001。如果收敛太慢,可以适当加大到0.02,但不建议超过这个值。

3.4 训练过程监控与日志解读

训练启动后,控制台会输出每个epoch的指标。重点盯这几个:

指标含义健康范围
box_loss边界框回归损失持续下降并趋于平稳
cls_loss分类损失持续下降并趋于平稳
dfl_loss分布焦点损失持续下降并趋于平稳
mAP50IoU=0.5时的平均精度单类别通常能到0.9以上
mAP50-95多IoU阈值平均精度反映定位精度,0.6以上算好

如果box_loss下降但mAP不涨,可能是标注框质量有问题,比如框得太松或者太紧。如果cls_loss一直很高,检查一下是不是混入了其他类别的标注。如果训练loss正常但验证loss上升,那就是过拟合了,需要加数据增强或者提前停止。

训练完成后,runs/detect/train目录下会保存权重文件、训练曲线、混淆矩阵等。best.pt是验证集表现最好的权重,last.pt是最后一个epoch的权重。部署时用best.pt,继续训练时用last.pt。

4. 数据增强与模型调优实战

4.1 针对无人机场景的增强策略

YOLO默认开启的增强包括随机翻转、缩放、色彩抖动等。但对于无人机检测,有些增强需要特别调整。水平翻转可以保留,因为无人机左右对称,翻转后仍然是合理样本。但垂直翻转要慎用,倒飞的无人机在现实中极少出现,强行增强反而引入噪声。

Mosaic增强是YOLO系列的招牌,它把四张图拼成一张,能显著提升小目标检测能力。对于无人机数据集,我建议保持开启,但mosaic的概率可以调到0.5左右,太高会导致训练后期loss不稳定。

色彩抖动方面,低空安防场景的光照变化很大,从正午强光到黄昏逆光都有。适当加大HSV的扰动范围,能提升模型对不同光照的鲁棒性。具体可以在配置里把hsv_h调到0.02,hsv_s调到0.8,hsv_v调到0.5。

还有一个针对性的增强是运动模糊。无人机在飞行时,如果相机快门速度不够,画面会有拖影。在训练时随机加入轻微的运动模糊,能让模型在真实视频流中更稳定。这个需要自己写自定义增强,Ultralytics支持通过回调函数注入。

4.2 小目标检测的专项优化

如果验证时发现远距离无人机漏检严重,可以从几个方向优化。最直接的是提高输入分辨率,从640提到960或1280。代价是推理速度下降,需要权衡。

第二个方向是调整Anchor。虽然YOLOv8是Anchor-free的,但它的特征层分配策略仍然影响小目标。可以尝试修改检测头的stride,让更浅的特征层参与小目标预测。不过这需要改源码,对新手不太友好。

第三个方向是切片推理。把大图切成小块分别检测,再把结果拼回去。这个方法对小目标效果立竿见影,但推理耗时成倍增加。适合对实时性要求不高的场景,比如事后取证分析。

第四个方向是Copy-Paste增强。把小目标无人机抠出来,随机粘贴到背景图的各个位置,人为增加小目标样本数量。这个操作能快速提升小目标召回率,但要注意粘贴的位置要合理,不能贴到地面以下或者建筑内部。

4.3 模型剪枝与量化部署

训练出一个精度满意的模型只是第一步,真正部署到边缘设备还要过剪枝和量化两关。剪枝是去掉网络中冗余的通道,减小模型体积。YOLOv8s大概22M参数,剪枝后能压到10M以内,精度损失通常在1到2个点。

量化是把FP32权重转成INT8,推理速度能提升2到3倍,模型体积缩小到四分之一。但量化对精度的影响比剪枝大,尤其是小目标检测,量化后容易出现漏检。建议用训练后量化先试,如果精度掉太多,就用量化感知训练,在训练阶段就模拟量化误差。

部署框架方面,TensorRT在NVIDIA设备上性能最好,ONNX Runtime跨平台兼容性好,OpenVINO在Intel平台上优化到位。选哪个取决于你的硬件。不管选哪个,导出模型后都要用一批真实图片做精度对齐,确保转换过程没有引入额外误差。

注意:量化后的模型一定要在目标硬件上实测推理速度,不要只看理论算力。有些设备标称算力很高,但实际内存带宽是瓶颈,跑起来并没有想象中快。

5. 常见问题排查与避坑经验

5.1 训练不收敛的排查思路

训练不收敛是新手最常遇到的问题,表现是loss居高不下或者剧烈震荡。排查顺序建议这样走:先看数据,随机抽几张图把标注框画出来,肉眼确认框的位置和类别都对。我见过太多案例是标注文件里坐标没归一化,值都是几百的像素值,这种数据喂进去loss必然爆炸。

数据没问题就看学习率。lr太大loss会震荡,lr太小loss下降极慢。可以跑一个lr finder,让框架自动推荐一个合适的学习率。YOLOv8自带这个功能,在训练配置里把lr0设成0.01然后观察前几个epoch的loss变化就能判断。

如果数据和lr都没问题,检查batch size。batch太小梯度噪声大,loss曲线会很毛糙。单卡8G显存的话,640分辨率下batch开到8到16比较合适。实在开不大就用梯度累积,accumulate参数设成2或4,等效于增大batch。

5.2 验证集精度虚高的原因

有时候验证集mAP很高,但实际部署效果很差,这种“虚高”通常有几个原因。最常见的是训练集和验证集分布不一致。如果验证集图片和训练集来自同一段视频的相邻帧,那它们高度相似,验证集精度自然高,但换一个场景就崩。正确的做法是验证集要覆盖不同的场景、光照、背景。

第二个原因是数据泄露。比如你把同一张图的不同增强版本分别放进了训练集和验证集,模型在验证集上相当于见过原图。检查方法是看训练集和验证集的图片文件名有没有重叠,或者用感知哈希做相似度比对。

第三个原因是过拟合。验证集精度高但测试集低,说明模型记住了训练集的噪声。解决办法是加数据增强、加正则化、或者减少模型参数量。

5.3 推理速度不达标的优化

部署时推理速度不达标,先定位瓶颈在哪。用profiler工具看预处理、推理、后处理各占多少时间。很多时候瓶颈不在模型本身,而在图像解码和resize上。把预处理放到GPU上做,或者用硬件解码器,能省下不少时间。

模型层面的优化,前面说的剪枝和量化是主要手段。另外可以调整输入分辨率,从640降到416,速度能提升一倍多,精度损失大概3到5个点。如果场景里无人机不会太小,这个取舍是划算的。

后处理方面,NMS是耗时大户。可以改用更快的NMS变体,或者调大confidence阈值减少候选框数量。如果同一画面里无人机数量不多,把max_det从300降到50,也能省一点时间。

5.4 常见问题速查表

问题现象可能原因排查方法解决方向
loss为nan学习率过大或数据有脏标注检查标注坐标是否越界降低lr,清洗数据
mAP始终为0类别配置错误核对data.yaml的nc和names修正类别数
训练集loss降但验证loss升过拟合对比训练验证曲线加增强,加正则
小目标漏检严重输入分辨率不足统计目标像素尺寸提高imgsz或切片推理
推理速度慢模型过大或后处理耗时profiler定位瓶颈剪枝量化,优化NMS
误检飞鸟为无人机负样本不足检查负样本比例补充天空背景负样本
显存溢出batch或分辨率过大监控显存占用调小batch或imgsz
验证集精度虚高数据泄露或分布一致检查图片相似度重新划分数据集

5.5 几个我踩过的坑

第一个坑是标注文件里的类别ID。有些转换脚本会把class_id写成1而不是0,单类别任务里这会导致所有目标被当成背景忽略。训练时loss正常下降,但mAP一直是0,排查半天才发现是ID错了。

第二个坑是图片格式。数据集里混了png和jpg,有些png带透明通道,直接读进来是四通道,喂给模型会报错。统一转成三通道jpg能避免这个问题。

第三个坑是路径里的中文和空格。Windows下数据集放在“我的文档”里,路径带中文,训练时各种找不到文件。养成用纯英文无空格路径的习惯,能省很多事。

第四个坑是验证集里混入了训练集的图。我是用文件哈希比对才发现的,两张图内容一样但文件名不同,一张在train一张在val。这种泄露会让验证精度虚高好几个点,实际部署就露馅。

6. 数据集扩展与持续迭代思路

6.1 主动学习降低标注成本

5400张是个不错的起点,但真实场景千变万化,模型上线后总会遇到新的难例。这时候不要盲目地全量标注新数据,用主动学习能大幅降低标注成本。具体做法是让当前模型去推理未标注的图片,挑出置信度在0.3到0.7之间的“不确定样本”,这些样本对模型提升最大。只标注这些难例,可能几百张就能带来明显提升。

主动学习的迭代节奏可以这样安排:第一轮用5400张训练baseline,部署后收集一周的误检漏检样本,人工筛选出500张难例标注,加入训练集重新训练。如此循环几轮,模型在真实场景的精度会稳步上升。

6.2 合成数据补充长尾场景

有些场景真实数据很难采集,比如夜间红外、极端天气、特殊飞行姿态。这时候合成数据能派上用场。用3D建模软件渲染无人机在不同背景下的图像,自动生成标注,成本极低。虽然合成数据和真实数据有域差异,但作为补充能提升模型在长尾场景的鲁棒性。

合成数据的使用比例要控制,一般不超过真实数据的30%。训练时可以把合成数据和真实数据混在一起,让模型自己学习域不变特征。更高级的做法是域自适应,用对抗训练让模型在合成域和真实域上学到一致的特征表示。

6.3 多类别扩展的可能性

当前数据集是单类别“无人机”,但实际安防场景可能需要区分更多类别,比如固定翼、多旋翼、飞鸟、风筝、气球。扩展类别时要注意类别定义的清晰性,避免模棱两可的标注。比如“多旋翼”和“固定翼”是明确的,“大型无人机”和“小型无人机”的边界就模糊,不同标注员标准不一致会引入噪声。

扩展类别后,类别不平衡问题会凸显。无人机样本多,飞鸟样本少,模型会偏向多数类。解决办法包括重采样、类别加权损失、focal loss等。我一般先用类别加权,简单有效,不行再上focal loss。

6.4 持续迭代的数据闭环

一个成熟的低空安防系统,数据闭环是必须的。部署端持续采集视频流,模型推理后把低置信度样本和人工复核确认的误检样本回传,定期标注后加入训练集。这个闭环跑起来后,模型每个月都能迭代一版,精度和场景覆盖度持续提升。

闭环的关键是自动化程度。如果全靠人工筛选回传样本,成本高且不可持续。可以设置规则自动筛选,比如置信度低于阈值的、与历史样本相似度低的、特定时间段或特定机位的样本优先回传。把人工介入集中在标注环节,其他环节尽量自动化。

我个人在实际操作中的体会是,数据集的价值不在于一次性有多大,而在于能不能持续迭代。5400张的起点已经足够跑通整个流程,真正拉开差距的是后续的数据运营能力。谁能更快地把真实场景的难例转化为训练数据,谁的模型就能在实际部署中表现更稳。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进