简介:一套面向无人机视觉应用的目标检测数据集,包含5000张真实场景高清图像,覆盖多种飞行环境与目标形态。数据使用LabelImg标注,标签质量较高,并同时提供VOC(xml)、COCO(json)与YOLO(txt)三种格式,分别存放于不同文件夹,可无缝接入YOLO系列模型训练与评测流程。资源共2000个文件,以xml标签文件为主(1986个),另含Python划分脚本、环境配置与训练教程(html/txt等),压缩包大小约301.59MB。随包附带训练集、验证集、测试集划分脚本,支持一键生成ImageSets下的txt索引,方便按需划分数据;同时提供Linux与Windows双平台的YOLO环境搭建和训练案例教程,可引导用户快速修改配置训练自己的数据集。目前已有433人学习下载,适合刚入门目标检测或需要规范标注数据的开发者,能显著减少数据准备与环境配置的时间成本。
1. 5000张无人机目标检测数据集:省掉最磨人的标注环节
做目标检测训练的人都知道,模型跑不起来可以调代码,但数据不行就是真不行。尤其是无人机视角的数据,自己飞、自己拍、自己用labelimg一张张框,5000张图工作量有多恐怖,干过的人心里都有数。这份资源解决的就是这个痛点——5000张真实场景的无人机目标检测图片,VOC(xml)、COCO(json)、YOLO(txt)三种格式标签全部配齐,外加三个数据集划分脚本和一套完整的YOLO训练教程,从环境搭建到改参数训练自己的数据集,一条龙给你铺好。适合谁?想做无人机视角目标检测、遥感图像检测、小目标检测方向的学生和从业者,以及被标注和格式转换折腾到崩溃的YOLO新手。当然,老手也能用它的划分脚本省点事。
2. 数据集与三种标签格式:先搞懂VOC、COCO、YOLO标签到底差在哪
2.1 5000张真实场景图与labelimg标注:数据质量看这三个点
拿到一份目标检测数据集,先别急着开训,第一步是评估数据能不能用。这份无人机数据集的图片来自真实场景,不是渲染图或者合成图,这点对模型落地非常关键。无人机视角最大的特点是目标尺度小、背景复杂、光照变化大,如果数据里全是合成图,训练出来的模型一到真实场景就原形毕露。真实场景图虽然标注成本高,但泛化能力是合成图比不了的。
标注质量是第二个重点。资源里明确写了用labelimg标注软件完成,标注框质量高。我从实操角度解释一下这个"质量高"意味着什么:标注框贴边(没有大面积留白)、类别没有标串、遮挡目标只框可见部分。这些细节直接决定训练出来的模型边界框回归准不准。如果你之前用过那些自动标注工具生成的数据集,就会发现很多框都是歪的或者明显偏大,模型学出来框永远是虚的。
第三个点是场景丰富度。5000张图如果全是同一片天空同一块草地,那训练集再大也白搭。这份数据集的场景覆盖了不同高度、不同光照、不同背景,这保证了模型的鲁棒性。我的经验是,拿到数据集先随机抽50张图看一眼,确认场景多样性和标注质量,再决定要不要花时间训练。这个习惯能帮你省掉后面很多排查时间。
2.2 三种标签格式的结构差异:XML、JSON、TXT各管什么
目标检测领域有三种主流标注格式,几乎所有的训练框架都绕不开它们。这份数据集把三种格式都给你了,存放在不同文件夹下,可以直接用于YOLO系列模型训练。先看三种格式的本质区别:
| 格式 | 所属标准 | 文件后缀 | 坐标表示方式 | 典型框架 |
|---|---|---|---|---|
| VOC | PASCAL VOC | xml | 左上角x、y + 右下角x、y(像素坐标) | Faster R-CNN、SSD |
| COCO | MS COCO | json | 左上角x、y + 宽、高(像素坐标) | Detectron2、MMDetection |
| YOLO | Darknet/Ultralytics | txt | 中心点x、y + 宽、高(归一化到0~1) | YOLOv5、YOLOv8 |
这三种格式的区别不只是文件后缀不同,坐标系定义逻辑完全不一样。VOC格式是绝对值,记录的是标注框左上角和右下角的真实像素位置;COCO格式也是绝对值,但记录的是左上角坐标加宽高;YOLO格式则是相对值,所有数值都除以了图片宽高,归一化到0到1之间。这个归一化的好处是,无论图片resize成什么尺寸,标签都不用改,这也是YOLO系列训练起来省心的原因之一。
2.3 三种格式都留着的原因:不同训练框架的喂数据方式不同
很多初学者会问,我只用YOLO训练,为什么还要留VOC和COCO格式?原因很简单:你可能今天用YOLOv8,明天想试试MMDetection里的Faster R-CNN,或者跑一下Detectron2的Mask R-CNN做实例分割,这些框架对标签格式的要求完全不同。VOC格式转YOLO只需要用脚本解析xml再归一化,但YOLO转VOC就麻烦得多,因为归一化坐标要还原成像素坐标,还得生成xml的完整结构。所以拿到一份三种格式齐全的数据集,等于给自己留了后悔药。
我经常跟朋友说的一句话是:格式转换是目标检测里最没技术含量但最耗时间的工作。你花两小时写转换脚本,调试到凌晨,结果发现某个标签文件格式少了个字段,整个训练直接报错。这份数据集的作者已经把三种格式按文件夹分好,用哪个取哪个,省掉的这部分时间足够你把训练跑完一轮了。
3. 用划分脚本按需切分数据集:训练集、验证集、测试集怎么分才合理
3.1 三个划分脚本的分工:先搞清楚你要用哪个
压缩包里给了三个Python脚本,功能各不相同,我用之前先把它们区别列出来:
| 脚本名称 | 功能 | 适用场景 |
|---|---|---|
| 训练集、验证集、测试集划分脚本(图片标签划分写入新文件夹).py | 按比例把图片和标签同时分配到三个新文件夹 | 数据量充足,需要独立测试集做最终评估 |
| 训练集、验证集划分脚本(图片标签划分写入新文件夹).py | 按比例把图片和标签同时分配到两个新文件夹 | 数据量一般,只分训练集和验证集 |
| split_train_val生成ImageSets下txt文件划分脚本.py | 只生成包含文件名的txt索引文件,不移动文件 | VOC格式训练前的标准动作,配合VOCdevkit目录结构使用 |
三个脚本的核心理念都是"图片和标签必须同时移动",这是最容易踩坑的地方。很多人自己写划分脚本时只移动了图片,结果训练时报错找不到对应标签文件,又要重新折腾。这三个脚本直接把图片和标签按同比例、同随机种子分配到目标文件夹,从根上避免这个问题。
3.2 训练集、验证集划分脚本的参数与逻辑解析
先看两文件夹版本的脚本,核心逻辑不长,但几个细节值得注意:
import os import random import shutil # 配置参数 image_dir = 'JPEGImages' # 原图文件夹 label_dir = 'labels' # 原标签文件夹(txt格式) train_image_dir = 'images/train' # 训练图片输出目录 train_label_dir = 'labels/train' # 训练标签输出目录 val_image_dir = 'images/val' # 验证图片输出目录 val_label_dir = 'labels/val' # 验证标签输出目录 val_ratio = 0.2 # 验证集比例,可自行修改 # 创建输出目录 os.makedirs(train_image_dir, exist_ok=True) os.makedirs(train_label_dir, exist_ok=True) os.makedirs(val_image_dir, exist_ok=True) os.makedirs(val_label_dir, exist_ok=True) # 获取所有图片文件名(不含后缀) all_images = [f for f in os.listdir(image_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))] # 固定随机种子,保证每次运行结果一致 random.seed(42) random.shuffle(all_images) # 按比例切分 val_count = int(len(all_images) * val_ratio) val_images = all_images[:val_count] train_images = all_images[val_count:] # 图片和标签同步移动 for img_name in train_images: base_name = os.path.splitext(img_name)[0] shutil.copy(os.path.join(image_dir, img_name), os.path.join(train_image_dir, img_name)) shutil.copy(os.path.join(label_dir, base_name + '.txt'), os.path.join(train_label_dir, base_name + '.txt')) for img_name in val_images: base_name = os.path.splitext(img_name)[0] shutil.copy(os.path.join(image_dir, img_name), os.path.join(val_image_dir, img_name)) shutil.copy(os.path.join(label_dir, base_name + '.txt'), os.path.join(val_label_dir, base_name + '.txt')) print(f'划分完成: 训练集 {len(train_images)} 张, 验证集 {len(val_images)} 张')这段脚本的核心设计有三个要点。第一,random.seed(42)固定随机种子,让每次划分结果一致,这个习惯非常好——不然你每次运行脚本数据分布都不一样,复现实验结果时会很崩溃。第二,图片和标签通过base_name关联,保证一张图一定带着自己的标签一起走,不会出现图在训练集、标签在验证集这种低级错误。第三,用的是shutil.copy而不是os.rename,原文件保留,相当于复制操作,万一划分不合理可以重新来,这就是后悔药。
3.3 split_train_val生成ImageSets下txt文件:VOC训练前的必经一步
如果你打算用VOC格式跑训练,第三只脚本才是你真正需要的。VOC系列数据集的目录结构要求ImageSets/Main下必须有train.txt、val.txt、trainval.txt这些索引文件,里面一行一个图片文件名(不带后缀),训练框架通过这些txt去JPEGImages里找图、去Annotations里找xml标注。
import os import random # 配置参数 xml_dir = 'Annotations' # xml标注文件夹 save_dir = 'ImageSets/Main' # 索引文件输出目录 train_ratio = 0.8 # 训练集比例 val_ratio = 0.1 # 验证集比例,剩余为测试集 os.makedirs(save_dir, exist_ok=True) # 获取所有xml文件名(不含后缀) all_names = [os.path.splitext(f)[0] for f in os.listdir(xml_dir) if f.endswith('.xml')] random.seed(42) random.shuffle(all_names) total = len(all_names) train_count = int(total * train_ratio) val_count = int(total * val_ratio) train_names = all_names[:train_count] val_names = all_names[train_count:train_count + val_count] test_names = all_names[train_count + val_count:] # 生成txt索引文件 with open(os.path.join(save_dir, 'train.txt'), 'w') as f: f.write('\n'.join(train_names)) with open(os.path.join(save_dir, 'val.txt'), 'w') as f: f.write('\n'.join(val_names)) with open(os.path.join(save_dir, 'test.txt'), 'w') as f: f.write('\n'.join(test_names)) with open(os.path.join(save_dir, 'trainval.txt'), 'w') as f: f.write('\n'.join(train_names + val_names)) print(f'train: {len(train_names)}, val: {len(val_names)}, test: {len(test_names)}')这里有一个容易被忽略的点:trainval.txt是训练集加验证集的并集。很多VOC框架的配置里,训练的起始点读的是trainval.txt而不是train.txt,如果你只生成了train.txt,运行训练脚本时会报找不到trainval.txt。另外注意,这个脚本只生成索引文件,不会动你的图片和标注文件夹,所以它对原数据的侵入性为零,跑错了删掉txt重新生成就行。
3.4 划分比例怎么定:不同数据量下的常见做法
划分比例没有绝对标准,但有个经验值可以参考:数据量在几千张这个级别,训练集、验证集、测试集按8:1:1分配比较稳妥;如果总量只有几百张,建议只分训练集和验证集,比例9:1,测试集直接复用验证集做粗略评估;数据量过万时,可以调成训练集占90%以上,因为大数据量下验证集不需要太大就能反映分布。
我在实际项目里的做法是:先用脚本按8:1:1划分,训练完一轮后检查验证集的loss曲线。如果验证集loss和训练集loss差距很大,说明划分可能有问题,这时检查是不是某类目标只出现在验证集里。数据分布的随机性有时候会和你开玩笑,一个小类别在全量数据里占比只有2%,随机划分后可能验证集里一个都没有,训练集里一堆——所以固定随机种子并且重复实验很重要,多跑几次划分,挑一个类别分布最均匀的结果。
4. YOLO环境搭建与训练教程:Linux和Windows两条路都给你铺平
4.1 Linux版本环境搭建:Ubuntu + 显卡驱动 + CUDA的配置顺序
资源里的教程用HTML格式编写,从Linux之Ubuntu环境安装讲起,一路覆盖到YOLO环境搭建和训练。这套顺序是合理且完整的,我自己装环境的顺序也是如此。Linux下装深度学习环境,最常见的坑是顺序错了导致显卡驱动和CUDA冲突。
先看Ubuntu环境准备的基本流程:
# 更新系统源 sudo apt update && sudo apt upgrade -y # 安装基础开发工具 sudo apt install -y build-essential cmake git # 安装NVIDIA显卡驱动(建议通过ubuntu-drivers自动安装) ubuntu-drivers devices sudo apt install -y nvidia-driver-550 # 重启后验证驱动 nvidia-smi # 安装Anaconda wget https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Linux-x86_64.sh bash Anaconda3-2024.02-1-Linux-x86_64.sh # 创建虚拟环境并安装PyTorch conda create -n yolo python=3.10 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118驱动安装这块有个经验:不要自己去NVIDIA官网下载驱动然后手动装,很容易把系统搞崩溃或者出现循环登录问题。用ubuntu-drivers devices查看推荐版本,然后apt install自动安装,这是最稳的路径。nvidia-smi能正常输出就说明驱动OK。
CUDA这块需要解释一下:nvidia-smi显示的CUDA版本是驱动支持的版本,不代表你现在就能用。实际训练用到的是PyTorch内置的CUDA,所以只要PyTorch装对了,你不需要单独装完整的CUDA工具包。很多人看到nvidia-smi显示CUDA 12.4,就跑去装CUDA 12.4,然后又装了对应的cuDNN,折腾半天——其实根本不用。PyTorch的pip安装包里自带CUDA运行时,版本匹配就行。教程里如果让你装CUDA Toolkit,记住那个是给编译C++扩展用的,纯训练用不上。
4.2 Windows版本环境搭建:Anaconda + PyTorch踩坑记录
Windows环境相对好装,因为你不需要单独处理显卡驱动和CUDA的兼容性问题——用Anaconda创建环境后,装对PyTorch版本就行。Windows版本教程里完整覆盖了这条路径,配图跟着做就行。但有几个细节我说一下:
# Windows下用conda创建虚拟环境 conda create -n yolo python=3.10 -y conda activate yolo # 安装PyTorch(CUDA 11.8版本) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装ultralytics(YOLOv8官方库) pip install ultralytics # 验证环境是否可用 python -c "import torch; print(torch.cuda.is_available())"如果输出False,先说结论:90%的情况是PyTorch装成了CPU版本。检查方法是在Python里跑torch.cuda.is_available(),如果返回False,再跑torch.__version__看看版本号。如果版本号里带+cpu后缀,删掉重装+cu118版本就行。Windows上这个问题出现频率极高,教程里如果没特别强调,你大概率也会栽一次。
4.3 训练教程怎么改:从官方案例到自己的数据集
资源里附带的训练教程核心价值在于"根据案例修改训练自己的数据集",这句话说透了YOLO训练的本质。官方提供的yolov8n.pt预训练权重虽然能跑通流程,但直接用别人的权重检测无人机目标效果很差,因为COCO数据集的80个类别里没有无人机目标。你需要做的就是三件事:准备数据、改配置文件、跑训练命令。
数据准备部分第三步已经做完了,图片和标签按训练集、验证集分好,YOLO格式的txt标签也在。接下来最关键的是写data.yaml:
# data.yaml train: D:/drone_dataset/images/train # 训练集图片路径 val: D:/drone_dataset/images/val # 验证集图片路径 nc: 4 # 类别数量,改成你的实际类别数 names: ['drone', 'helicopter', 'plane', 'bird'] # 类别名称这里有个最常见的翻车点:路径分隔符。Windows下用反斜杠\在yaml里会被当成转义符,导致路径解析失败。我一般建议统一改成正斜杠/,Windows下也能正常识别。另一个坑是相对路径和绝对路径混用,推荐统一用绝对路径,避免训练脚本的工作目录变化导致文件找不到。
然后跑训练命令:
# Linux/Windows通用 yolo train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16epochs参数决定了训练轮数,无人机小目标检测场景100轮起步比较稳,前期可以先用50轮跑通流程,确认没问题再全量训练。imgsz这个参数影响很大,你的数据集图片如果分辨率较高,比如1920x1080的航拍图,建议用imgsz=640输入,这个是速度和精度的平衡点。如果想进一步提升小目标检测效果,可以试imgsz=1280,代价是训练时间几乎翻倍。batch参数受显卡显存限制,显存不够时优先把它调小。
5. 避坑指南:格式、路径、显存这些坑我都替你踩过
5.1 图片和标签文件名对不上,训练报错
现象:训练刚开始没几分钟就报错,提示File not found或者assertion failed,查看具体信息是某个图片找不到对应的txt标签文件。
原因:数据集是从不同渠道收集的,一些图片文件没有对应的标注文件,或者标注文件后缀不对(比如png图片配了jpg的标签名)。YOLO系列的加载逻辑是默认每张图必须有一个同名的txt文件,缺一个就整体中断。
解决:训练前写一个校验脚本,遍历所有图片文件,检查同名txt是否存在。我一般会直接在命令行里快速检查:
# 检查图片目录和标签目录的文件数量差异 ls images/train | wc -l ls labels/train | wc -l # 找出来缺少标签的图片 for img in images/train/*.jpg; do base=$(basename "$img" .jpg) if [ ! -f "labels/train/$base.txt" ]; then echo "Missing: $img" fi done如果有缺失,两种处理方式:图片质量好的,手动补一个空标签;图片本身有问题,直接删除。不要嫌麻烦,这个校验步骤能帮你省掉训练中途崩溃的时间。
5.2 txt标签里出现异常数据,loss直接变成nan
现象:训练开始时loss正常,几十个iteration后loss突然变成nan,然后一直保持nan,模型完全学不进去。
原因:标签文件里出现了异常值。最常见的两种情况:一是某个txt文件内容是空的,全空文件会让损失函数计算出错;二是坐标值中有0或者大于1的数(比如宽高算出来是0),导致边界框回归的损失变成无穷大。
解决:写个Python脚本扫一遍所有标签文件,过滤异常数据。检查逻辑是每行必须有5个值,且后四个值都在0到1之间且大于某个极小值(比如0.0001):
import os def validate_labels(label_dir): for f in os.listdir(label_dir): if not f.endswith('.txt'): continue path = os.path.join(label_dir, f) with open(path, 'r') as fp: lines = fp.readlines() for line in lines: parts = line.strip().split() if len(parts) != 5: print(f'Invalid line in {f}: {line}') else: # 检查坐标是否在(0,1)范围内 for val in parts[1:]: if float(val) <= 0 or float(val) > 1: print(f'Out-of-range value in {f}: {line}') break validate_labels('labels/train')这条检查花两分钟,能避免你浪费好几个小时在无效训练上。
5.3 路径带中文或是空格,数据加载阶段神秘报错
现象:数据和代码都按要求放好了,训练脚本也能正常启动,但每次跑到数据加载阶段就报各种奇怪的错误——有时候是FileNotFoundError,有时候是Image open failed,同一个文件夹在Python里能os.listdir出来,但图像就是加载不进去。
原因:路径里有中文或者空格字符。OpenCV和PyTorch的某些底层图像加载函数对非ASCII路径支持不好,尤其Windows环境下问题更严重。
解决:最省事的办法是一开始就避免——所有目录都用英文命名,不要有空格、不要有中文。如果已经踩了坑,把数据文件夹整个移到英文路径下,然后修改data.yaml里的路径即可,不需要重新改标签。这个问题的隐蔽性很高,因为它不是稳定复现的,有时候加载100张图只报5张的错。
5.4 显存不足,CUDA out of memory
现象:训练刚开始就报CUDA out of memory,或者训练到一半(通常是数据进入GPU的瞬间)直接崩掉。
原因:batch_size设得太大或者imgsz设得太大。很多人在教程里看到batch=16就照着填,但自己的显卡是6GB显存,跑640x640的图16张一次性进显存肯定爆。
解决:
# 降低batch_size重新训练 yolo train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=4如果batch调到2还爆显存,就要检查是不是有其他进程占用了显存。用nvidia-smi查看GPU占用情况,把不用的Python进程杀掉。还有一招是降低imgsz到480或者416,对小目标检测会稍微损失一点精度,但至少能跑起来。
5.5 验证集mAP很高,但实际检测效果很差
现象:训练结束,验证集上mAP达到0.85甚至更高,但拿自己的图片一测试,完全检测不出来,或者检测结果乱七八糟。
原因:这就是典型的过拟合到训练数据分布上。你的验证集划分没问题,但训练数据本身和真实使用场景存在分布差异——比如训练数据全是晴天光线好的照片,实际使用环境是多云或者逆光。
解决:分两步自查。第一步检查训练集和验证集是否来自同一分布,做法是随机抽训练集和验证集各50张图做对比,看光照、角度、目标大小是否接近。第二步是使用数据增强策略,YOLO内置了mosaic、hsv变换、random flip等增强手段,确认训练时这些增强没有被关闭。如果问题依旧,最有效的办法是补充更多场景的数据,把泛化能力这个物理问题交给数据量来解决。
6. 验证训练结果:用精确率、召回率和置信度门限判断模型能不能用
6.1 从训练日志到实时检测:看懂每项指标再决定要不要调
训练结束后会生成runs/detect/train目录,里面包含results.png、confusion_matrix.png、PR_curve.png等文件。我的习惯是先看三个数字:验证集上的精确率(Precision)、召回率(Recall)和mAP50。然后写个快速检测脚本,找几张训练集和验证集之外的真实照片跑一遍:
yolo detect predict model=runs/detect/train/weights/best.pt source=/path/to/test_images imgsz=640如果跑出来的效果跟预期差距大,最应该调的是置信度门限(conf threshold)。这个参数直接决定模型输出多少个预测框——设置太高会漏检,设置太低会误检。
# 用不同置信度跑同一张图,观察输出变化 yolo predict model=best.pt source=test.jpg conf=0.1 yolo predict model=best.pt source=test.jpg conf=0.56.2 用混淆矩阵定位模型到底错在哪
confusion_matrix.png能直观告诉你模型把哪两个类别搞混了。无人机检测场景里最常见的混淆是小型无人机和飞鸟——这两个目标在图片上特征很像,都是深色小斑点,如果没有足够的训练样本去区分细微差异,模型就会在这两个类别上来回横跳。如果混淆矩阵显示这个错误占比很高,说明你需要在数据层面补充更多"无人机和鸟同框"的图片,让模型学会用飞行姿态和纹理差异来区分,而不是单纯加大训练轮数。
那是无数个睡眠被精简标注和训练日志蚕食掉的夜晚换来的经验,从那以后我每次训练自己的数据集都会强制走一遍这套检查流程:先看标签有没有脏数据、再确认划分分布合理、训练完绝不只看mAP还要用真实场景图验证、最后检查混淆矩阵——每一步都至少节省我一周的排查时间。这份资源把数据集、标签格式、划分脚本和训练教程放在了同一个压缩包里,你不需要再去网上零散地找各种工具和说明,省下的时间可以全部花在模型调优和实际项目落地上面,希望帮到你。
本文还有配套的精品资源,点击获取