首页 / 资讯中心 / 文章详情

飞机100分类数据集测试集全解析:从PyTorch加载到评测避坑指南

飞机100分类数据集测试集全解析:从PyTorch加载到评测避坑指南 ★ FEATURED ARTICLE
简介面向深度学习图像识别与图像分类任务整理出飞机100分类数据集的测试部分覆盖波音737-76J、波音737-700等100种机型无需清洗与手工标注即可直接用于模型评估和泛化能力验证。测试图像按类别存放于data-test子文件夹每类对应一个独立目录依托torchvision或keras等常用框架即可快速读取目录结构清晰便于按类别批量处理。资源包共2000个文件以JPG图像为主体另附classes.json类别映射字典和可视化脚本py压缩包体积约878MB解压后即可开展实验。已有591人学习适合具备一定深度学习基础、需要标准测试集来检验分类模型效果的研究者或竞赛参与者。在使用时可借助类别字典快速生成标签序列用可视化脚本抽样核对图片与类别是否匹配也可在ImageFolder等数据工具中直接使用整体简洁适合作为论文实验或课程设计的验证数据。1. 飞机100分类数据集测试集为什么说它才是决定模型能不能上线的关键做深度学习图像识别数据集相关项目时飞机100分类数据集测试集往往是最容易被低估的一块。训练集动辄几千张图片很多人把精力都花在调模型结构、调学习率上等到评估阶段才发现测试集给出来的结果跟训练时差的不是一星半点Top-1精度忽高忽低、个别类别几乎全错、甚至跑完一遍才发现数据加载本身就有问题。这个标题里的“测试集”三个字恰恰是整个100类飞机识别任务里最考验工程细节的部分。无论是做毕业设计、比赛刷榜还是实际项目中要交付一个可上线的识别模型测试集都直接决定了你最终拿出去的数字可不可信。这篇文章会沿着数据集的目录结构、加载评测、常见坑位和进阶用法一步步讲透适合刚跑通代码的新手也适合想拿测试集做精细化分析的熟手。2. 读懂飞机100分类测试集标签体系、目录结构与数据分工拿到一份飞机100分类数据集别急着写训练代码先花半小时把数据集本身的“脾气”摸清楚。测试集里隐藏的许多问题往往不是模型不行而是数据读法和预期不一致。以下内容适用于大多数以“按类别分目录”格式组织的细粒度图像分类数据集飞机100分类只是其中一种具体对象。2.1 标签体系100类飞机到底按什么粒度划分飞机分类和猫狗分类最大的区别在于“类”的定义。有的数据集按机型分比如波音737、空客A320、C-130运输机有的按任务角色分比如客机、战斗机、直升机、无人机还有的按生产厂商加型号后缀分出更细的类别。落在“100分类”这个规模上常见做法是把同一大类的不同子型号也当成独立类别比如波音737-700、波音737-800、波音737-900会成为三个类。这个粒度选择直接影响我们写评测代码时对标签编号的理解。拿到数据后我一般会先看一下类别目录名。如果目录名就是纯数字编号那还需要找到附带类别映射文件通常是一个labels.txt或classes.csv把编号和飞机型号语义对应起来。如果这类映射文件缺失测试集的可用性会大打折扣因为你即使跑出了95%的精度也没法向别人解释这95%到底识别对了哪些飞机。# 常见目录结构一按类别子目录存放 test/ 000/ 000_001.jpg 000_002.jpg 001/ 001_001.jpg ... 099/ 099_001.jpg # 常见目录结构二扁平目录 标注文件 test/ image_00001.jpg image_00002.jpg test_labels.csv第二种结构里test_labels.csv每一行至少包含image_name,label两列。有的版本还会加一列label_name用来直接显示飞机型号。拿到数据后第一步应该先把这两列读出来统计每个类别的样本数防止某个类别一张图都没有。import pandas as pd # 读取测试集标签文件检查类别分布 df pd.read_csv(test_labels.csv) counts df[label].value_counts().sort_index() print(counts) # 如果发现某个类别样本数为0说明该类别在测试集里缺失 # 评测时要在类别集合层面做对齐而不是直接按100类全算。这段代码在项目启动时必须先跑一次。它的作用是确认测试集里包含哪些类别、每类多少张图以及标签编号是否连续。对于100分类任务编号往往从0到99但也不排除从1到100的情况这会直接导致后续CrossEntropyLoss的类别维度不匹配属于最基础却最容易翻车的问题。2.2 一张图理清训练集、测试集、验证集的分工边界很多初学者会把验证集和测试集混为一谈。先说结论验证集是用在训练过程中的目的是帮你决定什么时候停、要不要调学习率、选哪一版权重测试集是模型训练完全结束后只用一次的最终裁判。如果你反复用测试集去试不同的超参数那测试集就已经被“污染”了最终指标会虚高上线后真实场景表现往往会差几个点。飞机100分类任务里比较稳妥的数据划分方式是这样的如果数据集本身不提供官方验证集就从训练集里切出一部分当验证集比例一般取10%到15%。训练集和测试集的标准做法是保证类别分布独立且基本一致但实际拿到的数据集源头不同分布可能会有偏差比如训练集里民航客机偏多测试集里战斗机偏多。这种分布偏移会在评估时直接暴露出来也是后面要重点排查的问题。在动手写评测流程前建议你先回答下面三个问题测试集的图片分辨率是否统一颜色通道是不是都是RGB文件名和标签是否严格对应这三个问题想不清楚后面所有精度数字都是悬的。2.3 数据质量初检不依赖模型也能发现的问题还没训练任何模型之前有几项检查可以帮你避开很多后期麻烦。最简单的就是把整个测试集过一遍统计图片尺寸、通道数和损坏文件。不要侥幸觉得“既然叫数据集数据一定是好的”很多开源数据在打包或转换时会出现莫名奇妙的文件缺失。from PIL import Image import os root_dir test broken, size_set [], set() for subdir in sorted(os.listdir(root_dir)): sub_path os.path.join(root_dir, subdir) if not os.path.isdir(sub_path): continue for fname in os.listdir(sub_path): fpath os.path.join(sub_path, fname) try: with Image.open(fpath) as img: size_set.add(img.size) img.verify() except Exception: broken.append(fpath) print(损坏图片数:, len(broken)) print(图片尺寸种类:, size_set)这段脚本的意义在于提前暴露两个问题一是图片是否能被Pillow正常解码二是测试集中是否存在多种分辨率。飞机会从不同距离、不同角度拍摄测试集里混入多种分辨率是正常的但你的预处理管线必须能兼容它们不能默认所有图都是224×224。如果尺寸种类特别多后面要统一走resize逻辑而不是直接裁中心。3. 把飞机100分类测试集跑通PyTorch加载脚本与评测基线当你能完整回答“测试集里有什么”这个问题后下一步就是把它真正送入模型。下面这套加载和评测流程是我在细粒度图像识别任务里反复使用的模板适用于PyTorch框架也能直接移植到其他项目里。一个能正常工作的测试加载流程应该包含三个环节目录遍历、预处理、批次化迭代。3.1 从目录到DataLoader一套能直接改的加载代码针对按类别子目录组织的飞机100分类测试集可以用下面的Dataset类完成加载。如果你拿到的是test_labels.csv扁平结构只需要把__init__里的目录遍历换成pandas读取逻辑不变。import os from PIL import Image from torch.utils.data import Dataset class AircraftTestDataset(Dataset): 按类别子目录组织测试集。 目录结构: test_root/class_id/image_file class_id 是整数编号要求与模型输出维度一一对应。 def __init__(self, root_dir, transformNone): self.samples [] self.targets [] self.transform transform valid_ext (.jpg, .jpeg, .png, .bmp) for name in sorted(os.listdir(root_dir)): class_dir os.path.join(root_dir, name) if not os.path.isdir(class_dir): continue # 类别目录名可能是 000 这种字符串需要转成 int label int(name) for fname in sorted(os.listdir(class_dir)): if fname.lower().endswith(valid_ext): self.samples.append(os.path.join(class_dir, fname)) self.targets.append(label) def __len__(self): return len(self.samples) def __getitem__(self, idx): img Image.open(self.samples[idx]).convert(RGB) if self.transform: img self.transform(img) return img, self.targets[idx]这段代码的关键在int(name)这一行它假设目录名就是类别编号。如果000被当成字符串被模型读取后面计算损失时一定会出类型错误。所以先打印self.targets[:10]确认标签是0, 1, 2而不是000, 001。__getitem__里强制convert(RGB)是为了避免灰度图片导致输入维度不一致这一步对飞机这类大量户外航拍图尤其重要因为有些老旧图库会混入单通道图片。接下来是预处理与DataLoader。测试集不要用训练集那样复杂的随机增强常见做法是用与训练时完全一致的缩放和归一化参数但关掉随机裁剪和随机翻转。以ImageNet预训练权重为例归一化均值与标准差是固定的必须和训练时保持一致。from torchvision import transforms from torch.utils.data import DataLoader test_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize( mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ), ]) test_dataset AircraftTestDataset(test, transformtest_transform) test_loader DataLoader( test_dataset, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue, ) print(测试集样本总数:, len(test_dataset))shuffleFalse在这里不是可有可无的细节。它保证每次评测时样本顺序一致复现混淆矩阵和逐条分析时不会因为顺序漂移产生困惑。batch_size64是一般显存容量下的合理取值如果单卡显存只有8G建议降到32。num_workers在Windows上要设为0或在if __name__ __main__保护下运行否则多进程加载会在某些环境下报错。3.2 评测指标怎么算Top-1、Top-5与混淆矩阵测试集只有一个官方指标还不够我通常会算三个东西Top-1准确率、Top-5准确率和混淆矩阵。对100类飞机分类来说Top-1代表主推结果是否完全正确Top-5则能反映模型是否把相似机型放在了候选列表里这个在“同系列不同子型号”的区分上特别有参考价值。import torch import numpy as np from sklearn.metrics import confusion_matrix device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) model.eval() all_preds, all_targets [], [] with torch.no_grad(): for images, labels in test_loader: images images.to(device) logits model(images) # top5: 每个样本取概率最大的5个类别索引 _, pred_top5 logits.topk(5, dim1) pred_top1 pred_top5[:, :1] all_preds.append(pred_top5.cpu().numpy()) all_targets.append(labels.numpy()) all_preds np.concatenate(all_preds, axis0) all_targets np.concatenate(all_targets, axis0) top1_acc (all_preds[:, 0] all_targets).mean() top5_acc np.array([ all_targets[i] in all_preds[i] for i in range(len(all_targets)) ]).mean() cm confusion_matrix(all_targets, all_preds[:, 0], labelslist(range(100))) np.save(aircraft_test_cm.npy, cm) print(fTop-1 Acc: {top1_acc:.4f}) print(fTop-5 Acc: {top5_acc:.4f})这段代码的核心在于logits.topk(5, dim1)它一次性取出了前5个预测。scikit-learn的confusion_matrix可以按类别输出谁和谁最容易混淆比如波音737-800被误判成波737-700这类混淆在细粒度识别里非常典型。把混淆矩阵存成npy文件之后后续做错误模式分析就不用重新跑一遍模型省时间也省算力。3.3 让测试集“干净”起来的三个检查步骤跑通上述脚本后先别急着看精度按下面三个步骤确认整个测试管线没有硬伤。第一步比较样本总数和标签文件里的行数不一致就说明有图片被加载逻辑漏掉了。第二步随机抽10张图片手动查看把模型预测结果打印出来看是否和图片内容大致合理这一步能发现加载顺序和标签错位。第三步重复运行一次测试对比两次Top-1结果完全一致才说明评测流程稳定。# 快速抽查从测试集里按索引抽出5张图打印标签和预测 import matplotlib.pyplot as plt sample_idx [0, 50, 100, 500, 999] for i in sample_idx: img, true_label test_dataset[i] # img 是归一化后的tensor展示时需要反归一化 img_show img * torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1) img_show torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1) plt.imshow(img_show.permute(1, 2, 0).numpy()) plt.title(ftrue: {true_label}, pred: {all_preds[i, 0]}) plt.show()如果抽查结果显示图和标签对不上那就别再看精度了先回到数据加载逻辑多半是文件名排序方式和标签文件顺序没对齐。这类问题最坑因为它不会让程序报错只会让指标悄悄失真。提示测试集的预处理参数必须和训练时完全一致包括resize大小和归一化均值标准差。差一个像素值都可能让精度掉1到2个百分点这属于最常见的非模型因素。4. 飞机100分类测试集最容易踩的5个坑现象、原因与解决办法围绕测试集本身做排查是我在多个图像识别项目里花时间最多、收益也最明显的工作。下面5个坑是按出现频率排的每一条都来自一线项目中的真实踩坑经历。4.1 坑一测试集里混进了训练样本精度虚高到不敢信有些数据集在网上流传时打包的人会把训练集和测试集混在一起或者做数据清洗时没有严格去重。这会导致模型在测试集上的表现异常好比如Top-1精度超过98%但一上真实场景数据立刻掉十几个点。排查方法很简单用图片哈希算法对测试集和训练集做交叉比对。常见做法是计算感知哈希pHash因为它对缩放、轻微压缩有容忍度。import os from PIL import Image import imagehash def phash_path(path, hash_size16): with Image.open(path).convert(L) as img: return imagehash.phash(img, hash_sizehash_size) train_hashes {} for root, _, files in os.walk(train): for fname in files: fpath os.path.join(root, fname) h phash_path(fpath) # 用字典记录相同哈希直接覆盖后续只需要碰撞判断 train_hashes[h] fpath duplicated [] for root, _, files in os.walk(test): for fname in files: fpath os.path.join(root, fname) h phash_path(fpath) for train_hash, train_path in train_hashes.items(): # 汉明距离小于5认为近似重复 if h - train_hash 5: duplicated.append((fpath, train_path)) break print(疑似重复样本数:, len(duplicated))这组代码在图片总数很大时会比较慢建议先用测试集内的重复检测过滤一遍再做训练集与测试集的交叉比对。hash_size16生成的哈希长度更大对细粒度差异敏感度更高但阈值需要适当放宽一般汉明距离在5以内都算可疑。如果确认重复率超过1%我建议把重复样本从训练集里删掉而不是从测试集里删否则失去的是公平评估的意义。4.2 坑二标签文件行数与图片数对不上Dataloader静默丢图用扁平目录加CSV组织的数据集最容易出现这种问题CSV里有1000行但test文件夹里实际只有997张图某张图片在传输时损坏被系统跳过或者文件名编码问题导致加载不到。最常见的结果是DataLoader没有报错但测试集参与评测的样本比预期少精度数字失真。排查时把CSV文件里的“图片文件名集合”和“目录下的实际文件名集合”做差集一条命令就能看出谁丢了。ls test/ | sed s/\.[^.]*$// actual_files.txt cut -d, -f1 test_labels.csv | sed s/\.[^.]*$// | sort label_files.txt comm -3 (sort actual_files.txt) (sort label_files.txt)如果输出为空文件集合完全一致。如果两边都有各自独有的行说明标签文件写入了不存在的图片或者目录里有未标注的图片。这两种情况都要修正前者删掉无效行后者要么补标注要么从加载列表里排除。对于飞机100分类这类细粒度任务哪怕只有三五个样本对不上也可能影响某个小类的准确率好几个点。4.3 坑三分类粒度不一致波音737和737-800到底算不算一类这是飞机100分类里最“玄学”也最容易让年轻人翻车的坑。制作数据集的人在不同子集里采用了不同粒度训练阶段把波音737-800标成“波音737”测试集里又标成“波音737-800”模型的输出层定义就会和测试标签对不上。解决的核心是建立一个类别映射表统一粒度后再计算指标。不要试图通过改模型输出维度来掩盖数据问题。# 将细粒度标签合并到粗粒度类别 merge_map { Boeing737-700: Boeing737, Boeing737-800: Boeing737, Boeing737-900: Boeing737, } def normalize_label(raw_label): return merge_map.get(raw_label, raw_label) df[label_main] df[label_name].apply(normalize_label) df[label_id] df[label_main].astype(category).cat.codes合并之后训练集和测试集需要重新统计样本数确认双方在同一个粒度体系下。如果只调整测试集而不调整训练集模型输出类别和测试标签还是对不上评测代码会直接报维度错误。我在项目里建议把这份merge_map保存成JSON作为数据集说明的一部分传递下去方便复盘。4.4 坑四把测试集当成验证集反复调参验证集曲线越调越没法看不少同学会在一个测试集上来回试不同学习率、不同主干网络对比哪个精度高就选哪个。这种做法在项目开发中非常普遍但本质上是把测试集的信息泄露到了模型选择过程中。最终你选出的模型在另一个真实测试集上往往比当前结果差2到4个百分点。正确做法是从训练集里切出独立的验证集比如随机抽15%当验证。每次修改模型结构、学习率、数据增强策略都只看验证集的结果只有最终模型完全敲定后才允许在测试集上跑一次。如果你手里只有一个测试集没有官方验证集可以考虑从测试集中抽一小部分当开发集但必须明确这部分样本不再参与最终指标汇报。注意不要在测试集上做任何基于结果反馈的决策包括“这个类识别不好我要加训练数据”这种想法。一旦测试结果影响你的改动测试集就不再是测试集而是变成了验证集。4.5 坑五预处理与训练时不一致精度凭空掉三四个点这个问题在多人协作项目里最容易出现。训练脚本里用的是RandomResizedCrop(224)加RandomHorizontalFlip但测试脚本只用了Resize((224, 224))。表面看都是224×224实际输入分布差很多。ImageNet预训练模型对中心裁剪、缩放方式很敏感飞机这类有细长机身的物体尤其明显。我一般在项目根目录放一个config.py把训练和测试共用的预处理参数集中定义。# config.py IMG_SIZE 224 NORMALIZE_MEAN [0.485, 0.456, 0.406] NORMALIZE_STD [0.229, 0.224, 0.225] # 测试时推荐等比缩放后中心裁剪 TEST_TRANSFORM [ transforms.Resize((int(IMG_SIZE * 1.14), int(IMG_SIZE * 1.14))), transforms.CenterCrop(IMG_SIZE), transforms.ToTensor(), transforms.Normalize(NORMALIZE_MEAN, NORMALIZE_STD), ]Resize到256再CenterCrop到224这种方式比直接Resize((224, 224))更能保留机身细节因为直接压缩会改变长宽比导致机型轮廓变形。如果你训练的预处理里用了RandomResizedCrop测试阶段直接CenterCrop会在整体精度上更接近。总而言之训练和测试的预处理参数必须来自同一个配置文件这是消掉非模型因素影响的最低成本手段。5. 把测试集用到极致困难样本排序与伪验证集技巧评测跑通、坑也填了测试集还可以在项目后半段发挥更大价值。许多人只把测试集当“最终打分机器”实际上它是一份免费的错误分析宝库尤其适合细粒度分类场景。5.1 用测试集构造“伪验证集”快速定位误分类模式在交最终报告前我会从测试集里随机抽20%样本组成一个临时的“伪验证集”专门用来快速验证加载脚本、可视化代码、混淆矩阵生成逻辑是否正常。注意这个伪验证集只用来调试流程不参与任何调参决策它更像一把尺子而不是一个对手能帮你确认评测管线本身没有手工流程带来的偏差。5.2 按置信度对错误样本排序找模型盲区保存测试集的预测概率按“真实类别预测错误的置信度”从高到低排序置信度越高但预测错了说明模型对这类样本存在“过度自信的盲区”。把这些样本单独拉出来看你会发现它们通常属于侧面光照极强、机翼遮挡严重或背景包含复杂地物的图片。针对这些样本做数据增强比盲目增加整类数据更高效。5.3 一个我保留下来的习惯训练前先跑通全流程的Dry Run我现在每次做新数据集都会先写一个只加载100张图片的dry-run脚本启动训练前先在CPU上跑一遍验证从数据集加载到输出评测报表的整条链路通不通。这个习惯帮我避免过好多次在训练了十几个小时后才发现测试集读取代码有问题的“事故”。训练时可以没有测试集但交付时不能。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站