
简介飞机型号识别数据集04面向从事目标检测、军机识别与细粒度分类研究的算法工程师及高校学生采集自俄罗斯机场覆盖苏霍伊、米格、安东诺夫、伊尔、雅克、图波列夫等47种军民飞机可用于训练与验证飞机型号识别模型。资源包共2001个文件含1000张1024×768可见光RGB图像、1000个labelimg标注的xml标签及1个说明txt压缩包约250.43MB图像与标注一一对应可直接接入YOLO、Faster R-CNN等检测框架。目前已有218人学习下载。该批次与02、03、05等批次采集地点和飞机种类均不相同适合作为多场景泛化实验的独立子集xml标签便于转换为VOC或COCO格式配合清晰的文件命名可快速划分训练集与验证集为细粒度分类与目标检测任务提供扎实的数据基础。1. 飞机型号识别数据集从军机识别到目标检测一份数据集的真实落地路径手里拿到一份标注好的飞机型号识别数据集第一反应往往不是兴奋而是犯愁——类别怎么分、标注格式怎么转、小目标漏检怎么解、军机识别和民航分类能不能塞进同一个模型。这份数据集标题里同时出现了飞机分类数据集、飞机目标检测、军机识别、目标检测几个关键词说明它大概率是一套兼顾分类标签和检测框的多用途数据。对做遥感图像目标检测、红外可见光目标检测数据集、小目标检测的从业者来说这类数据的价值在于飞机目标尺度跨度大、背景复杂、型号间外观差异细微正好是检验目标检测模型鲁棒性的试金石。这篇文章不讲空泛概念只讲我拿到这类数据后怎么拆解、怎么训练、怎么排错适合已经跑通过 YOLO 系列、想认真做一版飞机型号识别的工程师。2. 飞机型号识别数据集的结构拆解与格式转换2.1 先搞清楚分类标签和检测框是不是同一套飞机型号识别数据集最常见的坑是分类目录和检测标注各说各话。分类任务按型号分文件夹检测任务用 XML 或 JSON 存框两者类别名可能一个叫 F-16 一个叫 fighter直接混用会导致训练时类别错位。我一般先做一次全量扫描把分类目录名和检测标注里的类别名拉出来对齐。import os, json, xml.etree.ElementTree as ET from collections import Counter # 扫描分类目录 cls_root dataset/classification cls_names sorted(os.listdir(cls_root)) print(分类类别:, cls_names) # 扫描检测标注假设是 VOC XML det_root dataset/detection/Annotations det_counter Counter() for xml_file in os.listdir(det_root): tree ET.parse(os.path.join(det_root, xml_file)) for obj in tree.findall(object): det_counter[obj.find(name).text] 1 print(检测类别分布:, det_counter)这段代码的作用是先摸清家底。cls_names给出分类任务的类别全集det_counter给出检测标注里每个类别出现的次数。如果两边类别名不一致就需要建一张映射表如果检测里某些类别样本极少比如少于 50 个框要么合并到相近型号要么在训练时用重采样补足。参数上cls_root和det_root按实际目录改XML 解析路径注意大小写Linux 下文件名敏感。2.2 把 VOC 转成 YOLO 格式坐标归一化和四个边界坑目标检测常用标注工具产出的多是 VOC 格式而 YOLO 系列要的是归一化后的class x_center y_center w h。转换脚本网上一搜一大把但飞机数据有几个特殊坑停机坪上的飞机框经常贴边、部分标注框宽高为负、图像尺寸不一致。import os from PIL import Image import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_dir, out_dir, class_map): tree ET.parse(xml_path) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) w, h Image.open(img_path).size lines [] for obj in root.findall(object): cls obj.find(name).text if cls not in class_map: continue bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 边界裁剪防止归一化后越界 x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) if x2 x1 or y2 y1: continue # 丢弃无效框 xc (x1 x2) / 2 / w yc (y1 y2) / 2 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{class_map[cls]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, img_name.replace(.jpg, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))逻辑说明先读图像真实宽高再把 VOC 的左上右下坐标转成中心点加宽高的归一化形式。四个边界坑分别是——框超出图像边界、宽高为零或负、类别名不在映射表、图像文件缺失。参数上class_map是类别名到索引的字典必须和后续训练时的data.yaml完全一致:.6f保留六位小数是 YOLO 官方推荐精度少一位在密集小目标上会有肉眼可见的偏移。2.3 划分训练集验证集时别按图片随机分飞机数据有个隐蔽问题同一架飞机可能在同一次拍摄里出现多张连拍图随机划分会让高度相似的图同时进训练和验证验证指标虚高。我一般按拍摄批次或图像哈希聚类后再分保证验证集里的飞机外观和训练集有足够差异。常见做法是用imagehash算感知哈希相似度高于阈值的归到同一组再按组划分。import imagehash from PIL import Image from collections import defaultdict groups defaultdict(list) for img_name in all_images: h imagehash.phash(Image.open(os.path.join(img_dir, img_name))) # 简化处理按哈希前 8 位分组 groups[str(h)[:8]].append(img_name) train, val [], [] for key, imgs in groups.items(): if len(imgs) % 5 0: val.extend(imgs) else: train.extend(imgs)这段不是标准答案而是一个可用的近似策略。参数上哈希位数越少分组越粗8 位在飞机数据上一般够用如果数据本身多样性很高可以直接随机分但要在报告里注明划分方式。3. 用 YOLO 训练飞机型号识别模型的参数与流程3.1 从预训练权重出发别从零训飞机型号识别属于细粒度分类加检测数据量通常几千到几万张从零训练收敛慢且容易过拟合。我一般用 COCO 预训练的 YOLO 权重做初始化把 backbone 学习率设小、head 学习率设大。以 YOLOv8 为例配置文件里关键参数如下# data.yaml path: ./dataset train: images/train val: images/val names: 0: fighter 1: bomber 2: transport 3: helicopter 4: awacsyolo detect train \ modelyolov8m.pt \ datadata.yaml \ epochs120 \ imgsz640 \ batch16 \ lr00.001 \ lrf0.01 \ warmup_epochs3 \ cos_lrTrue \ close_mosaic10 \ patience30参数说明modelyolov8m.pt是中等规模预训练权重飞机数据用 m 或 l 比较稳imgsz640是常见输入尺寸如果小目标多可以提到 1024但显存要跟上lr00.001初始学习率预训练微调时比从零训小一个量级close_mosaic10表示最后 10 个 epoch 关闭马赛克增强让模型在真实分布上收尾patience30是早停耐心值验证指标 30 轮不升就停。军机识别场景里如果类别间差异极小比如不同批次同型号可以把lr0再降到 0.0005同时把warmup_epochs加到 5。3.2 小目标检测的三种实用改法飞机在遥感图像里经常只占几十个像素属于典型小目标检测。不改任何结构直接训召回率往往卡在 0.5 上下。我试过三种成本较低的改法一是提高输入分辨率从 640 提到 1024 或 1280代价是显存和推理时间二是加 P2 小目标检测层在 YOLO 的 neck 部分增加一个更高分辨率的特征图输出三是用切片推理把大图切成重叠小图分别检测再合并。# 切片推理示意 def sliced_inference(model, img, slice_size640, overlap128): h, w img.shape[:2] results [] for y in range(0, h, slice_size - overlap): for x in range(0, w, slice_size - overlap): patch img[y:yslice_size, x:xslice_size] pred model(patch) # 把 patch 坐标映射回原图 for box in pred.boxes: box.xyxy [x, y, x, y] results.append(box) return results逻辑说明切片推理的核心是重叠切分加坐标回映射overlap一般取 slice_size 的 20% 左右太小会漏掉跨切片目标太大则重复框多。参数上slice_size要和训练分辨率一致否则尺度不匹配。三种改法里提高分辨率最省事但吃硬件加 P2 层需要改模型结构切片推理适合推理阶段补救训练阶段还是建议前两种。3.3 类别不平衡时用 focal loss 还是重采样飞机型号识别数据集里战斗机样本往往远多于预警机、加油机这类特种机。直接训会让模型偏向多数类。常见做法有两种一是损失函数换 focal loss降低易分样本权重二是重采样对少数类过采样或对多数类欠采样。YOLOv8 默认用 BCE loss改 focal loss 需要动源码。我的经验是如果少数类样本不少于 200 个框优先重采样简单可控如果少于 100 个focal loss 配合强增强更稳。# 过采样示意在 dataset 类里按类别权重重复采样 class_weights {0: 1.0, 1: 2.0, 2: 3.0, 3: 2.5, 4: 4.0} # 在 __getitem__ 里按权重决定是否返回该样本参数上class_weights一般设成类别频率的倒数再归一化别设得太极端否则少数类过拟合。重采样后要重新检查验证集分布确保验证集保持真实比例不然指标没法横向对比。4. 飞机型号识别落地中的避坑与排查4.1 验证集 mAP 很高但实际推理一塌糊涂现象训练日志里 mAP0.5 到 0.9拿真实场景图一测框得乱七八糟。原因通常是验证集和训练集同分布且划分时没有按拍摄批次隔离模型记住了背景而不是飞机特征。解决按 2.3 的哈希分组重新划分或者手动挑一批不同来源的图做独立测试集训练时只信这个测试集的指标。4.2 军机识别里不同型号被合并成同一类现象F-16 和 F-2 这种外观相近的型号模型总是混。原因有两个一是标注时类别名不统一二是模型特征分辨率不够细粒度差异被池化掉了。解决先统一类别名再考虑提高输入分辨率或换更大的 backbone。如果数据量够可以把分类头单独拿出来做细粒度微调检测框只负责定位型号由分类分支决定。4.3 训练到一半 loss 突然变 NaN现象前几十轮正常突然 loss 爆成 NaN。原因多是学习率过大或某批数据里有异常框宽高为负、坐标越界。解决先把lr0减半再在 dataloader 里加断言检查把非法框过滤掉。YOLO 训练时如果开了amp混合精度某些异常值也会触发 NaN可以临时关掉 amp 定位问题。4.4 推理时小目标全漏大目标正常现象大飞机框得准小飞机一个不出。原因通常是训练分辨率下小目标特征在深层特征图里已经消失。解决按 3.2 提分辨率或加 P2 层推理时用切片。注意提分辨率后要同步调batch否则显存溢出。4.5 换数据集后类别索引对不上现象用 A 数据集训的模型换 B 数据集推理类别全错。原因两个数据集的names顺序不同模型输出的索引含义变了。解决推理前先核对data.yaml的names顺序必要时在推理脚本里做索引映射。这个坑血泪经验最多建议把类别映射表存成 JSON 跟权重放一起。5. 飞机型号识别的进阶技巧用分类分支反哺检测最后一章讲一个我实际用过的技巧把飞机分类数据集的标签拿来做辅助任务让检测模型在训练时同时学型号分类推理时用分类分支的置信度修正检测框的类别。具体做法是在 YOLO 的 head 上加一个分类分支输入是检测框对应的 RoI 特征输出是型号类别。训练时检测 loss 和分类 loss 加权求和推理时把两个分支的类别概率相乘。# 辅助分类分支示意 import torch.nn as nn class AuxClassifier(nn.Module): def __init__(self, in_channels, num_classes): super().__init__() self.pool nn.AdaptiveAvgPool2d(1) self.fc nn.Linear(in_channels, num_classes) def forward(self, roi_features): x self.pool(roi_features).flatten(1) return self.fc(x) # 训练时 det_loss detector_loss(pred_boxes, gt_boxes) cls_loss nn.CrossEntropyLoss()(aux_logits, gt_labels) total_loss det_loss 0.5 * cls_loss参数上分类 loss 的权重我一般从 0.3 试到 0.7太高会干扰检测收敛太低没效果。这个技巧对军机识别特别有用因为型号差异主要体现在细节纹理上单独的分类分支能强迫 backbone 学到更细的特征。验证时除了看 mAP还要看分类分支的 top-1 准确率两个指标一起涨才说明辅助任务起了正作用。我自己的习惯是每换一个飞机数据集先跑一版不加任何 trick 的 baseline把 mAP、召回率、小目标召回单独记下来再逐个加改进每次只动一个变量。这样即使翻车也能快速定位是哪一步引入的问题。飞机型号识别这个方向数据质量比模型结构重要得多标注一致性、类别平衡、划分合理性这三件事做到位YOLO 中等模型就能出可用的结果。希望帮到你。本文还有配套的精品资源点击获取