ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

飞机结构检测数据集:7280张图助你快速上手YOLO目标检测

飞机结构检测数据集:7280张图助你快速上手YOLO目标检测 简介面向计算机视觉目标检测学习者这是一套飞机结构检测数据集覆盖机头、垂直稳定器、机翼、轮子4类目标共7281张图片及对应标注适用于训练YOLO、Faster R-CNN等主流检测模型。包内标注格式同时提供Pascal VOC的XML与YOLO的TXT文件无需额外转换即可直接接入常用训练框架。压缩包内共2000个文件以xml标注文件为主并附说明txt整体大小约351.37MB。全部标注合计31455个目标框其中机翼与轮子样本丰富便于模型学习典型结构特征类别分布与框数明细已一并整理方便快速评估数据均衡性。目前已有253人学习使用适合作为飞机目标识别、遥感图像分析等场景的算法验证与教学实践数据。1. 飞机结构检测数据集7280张图先解决“有没有标注”的问题做目标检测的人都知道模型本身不是瓶颈瓶颈永远是数据。飞机结构检测这个场景尤其典型机头、垂直稳定器、机翼、轮子这四类目标外形接近、遮挡多、远距离小目标多真要自己从零标注一万张图至少要耗掉一个人半个月。这份数据集直接给了7280张已标注图片且同时提供VOC和YOLO两种标签格式解压后不用做格式转换就能喂给yolov5或yolov8训练。适合三类人做目标检测课程设计和毕设的学生、要快速验证模型思路的算法工程师、以及想迁移到其他航空巡检业务的一线开发。先有标注再谈模型这份资源解决的就是“最耗时那一环”。2. 拆开压缩包VOC 与 YOLO 双格式的字段、坐标和校验2.1 压缩包里的目录结构先弄清谁是谁拿到压缩包后第一件事不是急着训练是把目录结构摸清楚。常见的目标检测数据集通常按下面这套组织方式存放这份飞机结构数据集也不例外飞机结构检测数据集/ ├── JPEGImages/ # 原始图片全部是JPG格式 ├── Annotations/ # VOC格式标注每张图对应一个同名XML ├── labels/ # YOLO格式标注每张图对应一个同名TXT └── ImageSets/Main/ # 数据集划分文件train.txt / val.txt这里的关键是“同名对应”JPEGImages 里的image_0001.jpg在 Annotations 和 labels 里必须能找到image_0001.xml和image_0001.txt。如果出现某张图只有图片没有标注或者标注文件名对不上训练时会直接报错或者静默跳过这两种情况都相当难排查。VOC 格式的好处是“人眼可读”直接用文本编辑器打开 XML就能看到目标类别和坐标框YOLO 格式的好处是“喂模型快”每行文本就是一个目标不额外带图片尺寸信息读取效率更高。这套双格式设计让我不用再跑一次 xml-txt 的转换脚本省了一个最容易被数据格式问题绊倒的环节。2.2 VOC 与 YOLO 坐标体系像素坐标和归一化坐标的区别VOC 的 XML 标注里坐标是像素绝对值也就是图片上框的真实位置。字段结构如下annotation folderJPEGImages/folder filenameimage_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object namewing/name bndbox xmin320/xmin ymin240/ymin xmax960/xmax ymax720/ymax /bndbox /object /annotation而 YOLO 的 TXT 标注里每一行是class_id cx cy width height全部是相对图片宽高的比例值取值范围在 0 到 1 之间。两组坐标的换算关系是# VOC像素坐标 - YOLO归一化坐标 # cx: 框中心点的x坐标除以图片宽度 # cy: 框中心点的y坐标除以图片高度 cx ((xmin xmax) / 2) / width cy ((ymin ymax) / 2) / height w (xmax - xmin) / width h (ymax - ymin) / height注意YOLO 格式里只存类别编号不存类别名字。比如wing在类别表里排第 2 位那 TXT 行首就写 2。这带来一个核心风险如果你用自己写的转换脚本生成 YOLO 标签类别排序必须和训练时 data.yaml 里的names完全一致错一位就是“把机头当轮子”的灾难。后面避坑章节我会专门讲这个我见过太多人栽在这上面。所以在正式训练前最好做一次双格式交叉校验随机抽 5-10 张图解析 XML用上面的公式算一遍归一化坐标再和 TXT 里的数值逐行比对误差控制在 0.001 以内才说明格式没问题。这个校验步骤不复杂但能提前暴露大部分数据损坏和格式错位问题。3. 训练前的数据体检类别分布、空标注与小目标清理3.1 类别统计脚本知道标注里到底有什么很多人的训练翻车root cause 不在模型而在标注数据本身。最常见的三类问题类别编号错位、空标注图片、小目标过密导致 loss 波动。这些问题光看不练是发现不了的我建议拿到这份数据集先跑一遍体检脚本用数据说话# 数据体检脚本 # 功能统计每类目标数量、空标注图片、标注框宽高分布 # 依赖pip install lxml opencv-python import os import glob import cv2 from lxml import etree annotations_dir Annotations # VOC的XML目录 JPEGImages_dir JPEGImages # 图片目录 category_counter {} # 类别计数器 empty_list [] # 空标注图片列表 box_sizes [] # 标注框宽高比列表 xml_files glob.glob(os.path.join(annotations_dir, *.xml)) for xml_path in xml_files: tree etree.parse(xml_path) root tree.getroot() filename root.findtext(filename) img cv2.imread(os.path.join(JPEGImages_dir, filename)) if img is None: print(f警告图片无法读取{filename}) continue height, width img.shape[:2] has_object False for obj in root.findall(object): name obj.findtext(name) category_counter[name] category_counter.get(name, 0) 1 has_object True bbox obj.find(bndbox) xmin int(bbox.findtext(xmin)) ymin int(bbox.findtext(ymin)) xmax int(bbox.findtext(xmax)) ymax int(bbox.findtext(ymax)) # 记录归一化后的宽高用于判断小目标占比 if xmax xmin and ymax ymin: box_w (xmax - xmin) / width box_h (ymax - ymin) / height box_sizes.append((box_w, box_h)) # 越界检查标注框超过图片边界 if xmin 0 or ymin 0 or xmax width or ymax height: print(f坐标越界{filename}{name}bbox({xmin},{ymin},{xmax},{ymax})) if not has_object: empty_list.append(filename) # 输出统计结果 print( 类别统计 ) for k, v in sorted(category_counter.items(), keylambda x: -x[1]): print(f{k}: {v}个目标) print(f\n 空标注图片{len(empty_list)}张 ) print(\n.join(empty_list[:10])) if box_sizes: avg_w sum(s[0] for s in box_sizes) / len(box_sizes) avg_h sum(s[1] for s in box_sizes) / len(box_sizes) print(f\n 标注框平均尺寸 : {avg_w*100:.2f}% × {avg_h*100:.2f}%)这个脚本覆盖了三个关键维度类别数量分布、空标注、坐标越界。其中“标注框平均尺寸”尤其值得关注如果大部分框的宽度占图片宽度不到 10%那么按 640 分辨率训练时这些目标可能只占十几个像素模型基本学不到有效特征。对这份飞机结构数据集来说常见的健康分布是机翼和机头占比最高轮子和垂直稳定器相对少一些。跑完脚本你会发现四类数量差距一般不会超过 3 倍如果超过这个倍数就要考虑做类别平衡处理了。3.2 空标注、坏图和类别不平衡的处理策略体检脚本跑完后针对问题图片的处置通常分三类。空标注图片属于“留着不如删了”。某张图里没有任何标注目标如果放进训练集模型会学习“这张图全是背景”增加误检率。对应做法是把空图从 JPEGImages 和 Annotations 目录里一起删除然后重建 labels。删除时务必同步删三个目录里的同名前缀文件漏删一个就是训练时数据流阻塞。坏图无法读取的图片也是直接剔除不要犹豫尝试修复。图片文件损坏往往是传输或压缩过程导致的没有修复价值。类别不平衡则需要从数据层面和采样层面两头入手。如果某类数量明显偏少常见做法是使用目标检测训练时开启 mosaic 数据增强或者在验证集里用分层采样保证每类都有足够的样本。这里要提醒不要轻易对标注数据做随机裁剪复制容易把机翼的上下文信息剪切掉反而让模型学到错的特征。最稳妥的方式是先用原始数据跑 baseline看各类的 AP 差距再决定是否针对弱势类别做增强。这套体检流程不长大约十分钟跑完但它决定了后面训练会不会莫名 loss 崩溃。数据质量不先过关后面改模型都是白费力气。4. 用 YOLO 跑起来数据集划分、data.yaml 与迁移学习参数4.1 划分 train/val/test 并生成 data.yaml数据体检没问题之后下一步就是划分训练集和验证集。划分动作虽然简单但有两个坑一是必须设置随机种子保证划分结果可复现二是要保证图片和标签一起移动拆散了就是模型训练不了。# 数据集划分脚本 # 功能按 8:1:1 划分 train/val/test并生成 data.yaml # 注意划分后图片和对应标签文件必须同步移动 import os import random import shutil from glob import glob random.seed(42) # 固定随机种子保证可复现 images_dir JPEGImages labels_dir labels out_base dataset_split # 目标目录结构如下 # dataset_split/images/train、images/val、images/test # dataset_split/labels/train、labels/val、labels/test split_ratio [0.8, 0.1, 0.1] image_paths sorted(glob(os.path.join(images_dir, *.jpg))) random.shuffle(image_paths) n_total len(image_paths) n_train int(n_total * split_ratio[0]) n_val int(n_total * split_ratio[1]) paths_map { train: image_paths[:n_train], val: image_paths[n_train:n_train n_val], test: image_paths[n_train n_val:], } for split_name, paths in paths_map.items(): os.makedirs(f{out_base}/images/{split_name}, exist_okTrue) os.makedirs(f{out_base}/labels/{split_name}, exist_okTrue) for img_path in paths: base_name os.path.basename(img_path).replace(.jpg, ) label_path os.path.join(labels_dir, f{base_name}.txt) # 图片和标签同步移动 shutil.move(img_path, f{out_base}/images/{split_name}/) shutil.move(label_path, f{out_base}/labels/{split_name}/) print(ftrain: {len(paths_map[train])} | val: {len(paths_map[val])} | test: {len(paths_map[test])})逻辑说明这段脚本先把图片列表洗牌按固定比例切成三段然后逐个把图片和同名 txt 标签同步搬进新生目录。random.seed(42)是必须保留的关键参数否则每次跑出来的划分都不一样前后对比实验就没意义了。随后在dataset_split同级目录创建 data.yaml# data.yaml # 注意路径是相对你执行训练命令的终端位置 path: dataset_split train: images/train val: images/val test: images/test nc: 4 names: [nose, vertical_stabilizer, wing, wheel]names里的顺序就是类别索引必须和 labels 目录里 TXT 文件行首的数字对应上。比如 TXT 里写2 0.5 0.5 0.2 0.1那它对应的就是names[2]也就是 wing。这里的顺和我前面说的一样完全决定模型学名还是学错务必先抽查确认自己这份压缩包里classes.txt的排序再抄进 yaml。4.2 迁移学习训练模型选型与关键超参数据集七千张出头四类目标这不算大数量级。从零随机初始化训练一个检测头九成会欠拟合正确姿势是加载预训练权重做迁移学习。以 YOLOv8 为例# 训练命令 yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ workers8 \ patience15 \ ampTrue \ device0参数说明modelyolov8s.pt加载的是 COCO 预训练权重不是随机网络。迁移过来的特征提取层已经能识别边缘、纹理、形状这些底层语义我们要学的只是“飞机部件的组合方式”训练量级省好几个量级。7280张数据配 s 级模型是稳妥组合n 级太轻特征容量不够学不好四类部件m 级偏重这个数据量下容易过拟合训练速度还慢。imgsz640yolo 默认输入分辨率。如果你的显卡显存允许后续再试imgsz1280小目标远处机轮会有明显收益但训练时间和显存占用都会增加约一倍。batch16这个值取决于显存。16G 左右的卡能跑batch16加 640 分辨率显存小降到batch8不要动其他参数。patience15连续 15 个 epoch 验证集 mAP 没提升就提前停止训练它负责兜底防止最后几十个 epoch 白跑。训练中途ampTrue开混合精度要注意这是“bn 崩溃”高发地带。具体症状是 loss 在前几个 epoch 突变成 nan或者验证集 mAP 一直卡零。遇到这种情况第一反应不是改学习率而是先关掉 AMP 重跑十个 epoch 看曲线是否恢复正常——数据集标注异常和 AMP 数值溢出都会造成这个症状后者改ampFalse能直接解决。4.3 训练日志与指标曲线怎么看训练结束后生成三个核心指标文件都在runs/detect/train目录下results.png、confusion_matrix.png、val_batch0_pred.jpg。results.png顶部有四条 loss 曲线box_loss、cls_loss、dfl_loss。这里有个常见误读很多人看到 cls_loss 降到 0.6 就觉得模型好了但实际上 yolo 的损失函数是加权组合box_loss 和 dfl_loss 才是定位精度的主要贡献项cls_loss 是分类置信度的辅助项。判断收敛的标准不是单看某一条曲线而是看 mAP50 是否连续 10 个 epoch 不增长、训练 loss 和验证 loss 是否同步下降。如果训练 loss 还在降、验证 loss 开始反弹那就是过拟合信号epochs 设再大也没意义直接回到上一步调 early stopping。confusion_matrix.png看法更有讲究YOLO 输出的混淆矩阵包含 background 行列且每行做了归一化所以矩阵总和对不上 1 是正常现象不是 bug。重点看对角线上的数值如果某一类明显低于其他类说明这类目标被背景误检吃掉需要回到数据层面补充该类样本。5. 常见问题与避坑坐标错位、loss 崩溃与混淆矩阵解读5.1 格式与坐标相关坑现象训练跑完验证集 mAP 很高但推理结果把机翼的框标到了垂直稳定器上类别全乱了。原因data.yaml 里names的顺序和 labels 目录 TXT 里行首数字代表的类别对不上。最常见的触发点是转换脚本或整理工具用字典遍历类别名Python 字典的插入顺序和资源作者的排序不一样导致“取到的类别名和文件里已写好的编号错位”。很多人没看压缩包里已有的classes.txtword index 从 0 开始数的习惯也没改就直接抄了一个顺序进 yaml。解决拿到数据后先打开任意三个 TXT 文件读行首数字再打开同名 XML 看name建立“编号-类名”映射表再抄进 data.yaml。这是最直接的校验法五分钟能避免整轮训练白跑。现象加载预训练权重训练前三个 epoch loss 还是正常下降第四个 epoch loss 突然变成 nan训练中断。原因大部分场景是标注框出现非法数值某个框的宽或高被转换成 0或者归一化坐标超过 1。浮点除零或边界溢出让梯度爆炸loss 直接不可恢复正常水平。解决训练前把第 3 章的体检脚本跑一遍重点检xmax xmin或ymax ymin的标注就地删除或修正。另外把ampTrue改成ampFalse能在不删数据的情况下排除混合精度导致的数值不稳定问题。这两个样例在实际训练中至少能覆盖八成“loss 变 nan”的翻车现场。5.2 训练与验证相关坑现象轮子目标在验证集上 AP 一直低于 0.3其他类都到了 0.9 上下无论怎么调 epoch 都上不去。原因轮子在图像里往往是远距小目标面积占比可能不到整图的 1%而默认锚框和 640 分辨率对这种尺寸的物体不友好。目标太小下采样后的特征图里它可能只剩一到两个像素特征提取器无米下锅。解决两条路线可以并行验证。第一条路线是把imgsz提到 1280小目标像素尺寸翻倍多数小目标场景 AP 会明显上升代价是显存吃不消时降 batch第二条路线是切图训练把 1920 宽的图像按 640 尺寸切块带重叠切完后相当于用放大镜学习小目标细节。这两招都属于 yolo 改进方向里常见的成熟手段不需要改模型结构先试数据侧的解法比魔改网络更划算。现象混淆矩阵对角线上的数值看着不低但 val 集 PR 曲线的面子区域很小查了发现是某两类之间互相误检严重。原因机头和机翼在交付场景里经常被机身和遮挡挡住部分轮廓两类长得很像模型学到的是“有个突出的形状就标”的粗糙语义而不是“连接部位在机身前端”的上下文特征。解决这类误检靠调参数救不回来数据层面处理更有效。看预测错误的图片里哪几个小场景最容易误检从 JPEGImages 里挑出相似场景图片补标进训练集优先让模型看到“难区分的同类样本”。实际测试中额外补 200 张难点图比增加 500 张普通图对 AP 的提升更明显。这也是一线习惯先看错在哪再决定补什么数据。现象训练完成后看confusion_matrix.png每一行加起来不到 100%有的行甚至只有 30%。原因YOLO 的混淆矩阵右下角还有 background 列且矩阵做了归一化把背景、漏检的占比也画进去了所以行列总和本来就不会等于 1。这不是训练翻车也不是数据集有问题只是坐标系里东西比想象中多一样。实际看指标优先看 mAP0.5 的绝对值以及对角线数值的横向对比不要纠结矩阵行和。解决确认背景类的 False Positive 占比是否过高。如果 background 列里数值异常说明模型在背景区域乱吐框此时需要看是不是验证集里大量“无目标区域”长得像机翼边角考虑增加负样本或者调低 conf 阈值。6. 验证数据集质量的硬指标mAP 曲线与人工抽检当你的模型训练完先别急着说“跑通了”。一份数据集好不好用要从三个层面验证EF 指标、混淆矩阵、抽检图片。第一个是 EF 指标。打开results.png看 mAP50 曲线。基于七千张数据做迁移学习mAP50 低于 0.8 说明模型没学好先别研究数据问题回看训练日志有没有 warningmAP50 在 0.85 到 0.92 之间算是这份飞机结构数据的正常水平高于 0.95 则要警惕过拟合结合验证 loss 曲线确认是不是真的泛化了有时候只是测试集划分太简单。第二个是混淆矩阵。重点关注的是 vertical_stabilizer 这一行因为它的目标最少。如果对角值低于 0.7说明模型对小类目标敏感度不够需要做类别增强。另外看一下机头和机翼的互相误检率这个场景中两者最像误检率超过 5% 就需要针对性补数据。第三个是人工抽检。不要只看指标把runs/detect/train/val_batch0_pred.jpg和val_batch0_gt.jpg并排打开数一数漏检和误检的框。要知道指标是平均数平均数掩盖了所有极端情况# 抽检可视化脚本核心逻辑 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(dataset_split/images/test, saveTrue, conf0.25) # 按置信度排序找出“模型自己都没把握”的预测 # conf取值0.25是最低置信度低于这个值的不画框直接漏检savaTrue生成的预测图建议专门挑出置信度在 0.25 到 0.5 之间的框看一遍。这批框属于模型“勉强认为对”的结果它们最能暴露数据标注中边界模糊、遮挡严重、目标不完整的问题。修这部分的标注质量比改十轮学习率收益都大。说一个我的个人习惯现在拿到任何一份目标检测数据集默认先花半小时跑体检脚本再花十分钟校对类别顺序最后才碰训练命令。这三步做下来后面所有的调参焦虑都能少掉一半。数据质量是目标检测项目里最没有玄学成分的部分付出多少收益都是可见的。这份飞机结构数据集能省掉你前面最重的标注工时但能不能把它的价值完全挖出来还得看你自己愿不愿意在训练前多花这四十分钟。希望帮到你。本文还有配套的精品资源点击获取
返回列表