尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

交通标志图像分类数据集:PyTorch ImageFolder加载与训练避坑指南

交通标志图像分类数据集:PyTorch ImageFolder加载与训练避坑指南 简介一份面向图像分类与目标检测实践者的交通标志物图像分类数据集覆盖红绿灯、限速、左右转等43个常见交通标志类别。数据按标准ImageFolder格式组织目录下直接分为train、val、test三个子集无需额外预处理即可用于图像分类模型训练也可作为YOLOv5等目标检测框架的分类数据集。压缩包共2000个文件以JPG图片为主体另含1个JSON中文类别字典文件方便标签映射与结果展示资源包大小58.4MB结构清晰便于按需取用。数据划分明确训练集31374张、验证集7835张、测试集7835张适合直接开展分类实验与精度评估。目前已有333人学习下载适合需要规范数据集进行交通标志识别测试、模型调参或课程设计的人群。1. 图像分类数据集最怕拆不干净交通标志43类训练集/验证集/测试集开箱即用图像分类项目最怕的不是模型不够新而是数据集先翻车。常见的做法是拿ImageNet1K或者去网上一张张爬标注爬完还得花两三个小时做切分和标签清洗。这个交通标志图像分类数据集把最费事的环节省掉了训练集、验证集、测试集三个文件夹已经按4:1:1切好43个类别包含红绿灯、限速、左右转等常见标志总共60MB。用PyTorch的ImageFolder可以直接打开不用写自定义Dataset。想快速验证最新的图像分类模型或者给YOLOv5/YOLOv8做分类分支的基线评测这个数据集都够用。下面按目录结构、加载方式、训练流程和踩坑记录逐层拆开。2. 数据集拆成train/val/test三份目录布局、43类标志和中文字典如何对齐一个能用ImageFolder直接打开的数据集核心前提就是目录嵌套方式跟PyTorch的约定完全吻合。这个数据集的data目录下只有三个一级文件夹train、val、test每个文件夹里按类别再建子目录子目录里放对应类别的图像。我先按实际目录结构画出来。2.1 data目录的实际布局与文件命名规则以Linux下查看为例项目解压后你会看到这样的结构data/ ├── train/ │ ├── 00001/ │ │ ├── 00001_00027.jpg │ │ ├── 00001_00028.jpg │ │ ├── 00001_00019.jpg │ │ └── ... │ ├── 00002/ │ │ ├── 00002_00014.jpg │ │ └── ... │ ├── ... │ └── 00043/ ├── val/ │ ├── 00001/ │ │ ├── 00001_00029.jpg │ │ └── ... │ ├── ... │ └── 00043/ ├── test/ │ ├── 00001/ │ │ └── ... │ ├── ... │ └── 00043/ └── label.json这里的理解要点有两个。第一train、val、test三个平级目录就是三个独立的数据集切分train用来算梯度更新权重val用来每个epoch结束做一次指标评估、顺带决定要不要保存当前权重test是全部训练完成后用来做最终评测的平时训练过程不要碰test。第二类别子目录以四位数字命名00001到00043这个数字就是标签索引。图片文件名的格式类似00001_00027.jpg前一段是类别编号后一段是样本序号类别编号、子目录名、JSON字典里的key是同一套编号体系。对用PyTorch的人而言这种布局意味着连自定义Dataset都不用写。ImageFolder默认会扫描根目录下的每个子目录把子目录名按字典序排序后映射成类别索引00001对应索引000002对应索引1依此类推。正因为子目录名是补零的四位数字字典序和数值序完全一致43个类的索引是稳定的不会出现“第3个类其实是00011”这种错位。我拿到压缩包第一件事不是解压后直接训练而是先做三检查。第一检查train、val、test目录下的子目录数量是不是43个少了说明解压不完整第二检查train里的图片总数是不是31,374val和test是不是各7,835第三检查每个子目录里有没有混进非图片文件。这三项确认没问题再往下走训练流程能省下后面一大半排查时间。2.2 43类标志的类别边界与实际分布这个数据集做的是交通标志的粗粒度分类43个类别覆盖了日常道路上出现频率最高的几类标志牌。红绿灯是一类限速标志按速度值分成了多个类比如限速30、50、80、100这样的常见档位还有左右转、直行、禁止驶入、停车让行等禁令和指示标志。从分类任务的角度看这类标志的视觉差异比较明显类间距离够大模型训练难度比细粒度分类比如区分不同款式的车标要低适合用来验证模型结构、训练流程和调参策略跑出来的指标也更能反映模型本身的能力。有一个很容易被忽略的现实情况是类别样本分布并不绝对均匀。高频类别比如红绿灯、限速50的图片数量可能明显多于某些低平类别这在真实采集的交通标志数据里太常见了。如果你后面做类别均衡性分析会看到个别类别的train样本不到200张。这种不均衡直接影响训练时的loss分配模型的输出会对样本多的类别过拟合。后面第5章的避坑部分我会专门讲怎么处理。另外43个类别是固定编码的如果后续要换一批标志比如加上“注意儿童”或者某地方特有的标志类别索引会变必须重新走一遍标签映射不能直接拿旧模型的输出维度硬接。我自己第一次加类别时就吃过亏后面第6章会细说。2.3 label.json中文字典的映射方式数据集里带了一个label.json内容是把上面43个类别编号映射成中文名称结构大致是这样的{ 00001: 红绿灯, 00002: 限速30, 00003: 限速50, 00004: 限速80, 00005: 向左转, 00006: 向右转, ... }这个文件最大的用途是训练结束后的结果解释。模型输出的索引是数字业务方要看的是“这图里是限速50”而不是“类别7”。推理阶段我会把JSON读进来做一次索引到中文名的映射。注意读取的时候要跟ImageFolder的类别顺序对齐ImageFolder的class_to_idx是把子目录名按字典序排序后生成的而JSON文件里的key是原始的四位编号两者只有在“字典序排序结果等于数值序”时才一致。这个数据集的子目录名是全数字补零所以没问题但如果你后续把自己的新类别文件夹加进去就得重新生成JSON不能手动在旧JSON里追加。还有一点如果做中文字体相关的可视化比如画混淆矩阵时要把类别显示成中文这个JSON就是标签映射的唯一来源。画图之前先检查matplotlib有没有配好中文字体否则图表里全是方块白白浪费时间。我会在系统里提前安装中文字体并把rcParams的font.family改成对应的字体名称这个细节虽然小但真能卡住你半小时。3. 用ImageFolder加载数据集三路DataLoader和交通标志场景的预处理参数这一章直接上能跑的代码。数据既然按ImageFolder的格式排好了就没必要再造轮子写Dataset类。下面这段代码我放在项目入口文件里每次新数据集都是这套骨架。3.1 ImageFolder三路加载与数量校验加载三个数据集就是三次ImageFolder调用分别指向data/train、data/val、data/test。我一般会在加载之前先确认一下三个目录各自的图片数量跟摘要里的31,374、7,835、7,835对一下如果数量对不上说明解压过程中有文件缺失后面训练再多都是白费。import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms data_root data train_dataset datasets.ImageFolder( rootf{data_root}/train, transformtransform_train, ) val_dataset datasets.ImageFolder( rootf{data_root}/val, transformtransform_val, ) test_dataset datasets.ImageFolder( rootf{data_root}/test, transformtransform_val, ) print(类别数量:, len(train_dataset.classes)) print(类别索引:, train_dataset.class_to_idx) print(train样本数:, len(train_dataset)) print(val样本数:, len(val_dataset)) print(test样本数:, len(test_dataset)) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue)这里的逻辑分几层。train_dataset.classes返回的是按字典序排好的43个子目录名class_to_idx是每个子目录名对应的索引。第一次拿到数据集时先打印这两个字段确认一下我自己每次都打印确认没有新加目录或者目录顺序被工具改变过才继续。train_loader里shuffleTrue是因为训练集每个batch需要打乱避免模型学到样本顺序val和test的shuffleFalse是为了评估时结果可复现、方便按顺序对齐预测值和真实标签。num_workers设为4在大多数台式机上够用如果是在Windows上跑num_workers建议设为0因为Windows的多进程DataLoader在Jupyter里经常因为spawn机制卡死这是PyTorch在Windows上的老问题不值得花时间折腾。一个需要注意的细节是pin_memoryTrue只在GPU训练时有意义它会在内存里预分配锁页内存减少GPU拷贝的时间。如果你的环境是纯CPU推理这个参数设不设无所谓。batch_size64对应8GB显存如果你的卡只有4GB改成32同时把num_workers降到2。3.2 交通标志的增强参数尺寸、标准化和方向敏感性交通标志在真实场景里是户外拍摄的光照变化、雨后反光、角度倾斜都很常见。训练集的transform我一般这样配transform_train transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomRotation(degrees15), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) transform_val transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])参数选择的原因说一下。Resize((224, 224))是把所有输入统一成224×224这是ResNet、MobileNet这些常用分类网络的默认输入尺寸如果你后面换用YOLOv5的分类分支YOLOv5自带AspectRatio自适应缩放可以用更大分辨率比如320或者640来提高精度到时候重新生成一套transform就行。RandomRotation(degrees15)模拟车辆行驶中标志牌轻微倾斜15度以内是合理范围超过30度会切到背景反而干扰训练。这里有一个关于方向性标志的坑向左转和向右转这两个类别的判别核心是箭头方向如果旋转角度太大加上ColorJitter把颜色饱和度调低箭头方向特征容易被削弱这在后面避坑章节还会再提。ColorJitter的三个值取0.3是模拟不同天气和光照下的色偏。数值再大容易把红色限速牌的红色通道压掉如果你训练的模型在阴天或者黄昏场景下表现差优先查这个参数而不是查模型。val和test只用Resize和标准化不做增强这样评估结果能稳定复现不会因为随机增强导致同一次实验结果每次都不一样。Normalize用的mean和std是ImageNet统计值对大多数场景都够用。如果想要更贴合交通标志数据的统计值可以自己跑一遍全数据集的mean和std但收益不大ImageNet统计值在这个数据集上通常表现已经正常。这里也解释一下为什么没有加RandomHorizontalFlip。左转标志和右转标志翻转之后类别就变了水平翻转等于给模型喂错标签。这类方向性标志在交通标志数据集里是核心类别而不是角落类别所以这个增强在一般CIFAR、ImageNet上能用在这里建议直接不用。4. 训练分类模型YOLOv5分类模式跑通自定义PyTorch脚本控制细节数据加载没问题接下来就是训练。两条路线可以用一是直接拿YOLOv5/YOLOv8的分类训练命令跑适合快速出基线模型二是自己写PyTorch训练脚本适合需要灵活控制训练过程的情况。两条路我都跑过先说YOLOv5路线。4.1 YOLOv5分类训练命令与数据路径配置YOLOv5对分类数据的组织方式和PyTorch的ImageFolder一样只要目录结构合规可以直接用classify/train.py跑分类任务。我实际跑过的命令是这样的python classify/train.py --model yolov5s-cls.pt --data data \ --img 224 --epochs 50 --batch-size 64 \ --name traffic_sign_cls参数逐个说明。classify/train.py对应YOLOv5仓库的分类训练入口模型用yolov5s-cls.pt这个分类专用的小模型它比标准yolov5s少了对象检测头分类输出层只有1000个ImageNet类第一次运行时它会自动下载权重然后把最后一层替换成43类的输出。数据路径直接指到data根目录就行YOLOv5会自动识别train/val子目录。--img 224表示把输入图缩放到224×224这样训练速度快显存占用小。如果想换yolov8做对比的话也是同样的套路用yolo classify train命令主要区别是配置方式从命令行参数改成yaml文件模型名写法也变成yolov8n-cls.yaml。--epochs 50对60MB、三万张训练图的数据集来说够用了一般训练到第30个epoch左右val准确率就会进入平台期。如果训练集数量少epochs可以加到80并通过early stopping在val_acc连续20个epoch不涨时自动停掉。--batch-size 64在8G显存的显卡上可以跑如果显存不够改成32同时把--optimizer选成SGD的话学习率默认值能直接用。训练结束后结果会存到runs/train-cls/traffic_sign_cls目录里面有权重文件best.pt和last.pt还有训练过程的指标曲线图。best.pt是val集上得分最高的权重last.pt是最后一个epoch的状态。我每次训练完第一件事是把best.pt做一次测试集推理python classify/predict.py --weights runs/train-cls/traffic_sign_cls/weights/best.pt \ --source data/test/00002 --img 224这个命令会预测data/test/00002目录下所有图片也就是某个类别的测试样本。输出会显示每张图的预测类别和置信度。如果特定类别预测效果差优先怀疑是不是类别样本太少或者训练轮数不够而不是模型结构有问题。这里有个细节要注意predict.py默认读取的source是文件或目录目录下会递归扫描所有图片如果你只想测单张图直接写图片路径就行。4.2 自定义PyTorch训练脚本的关键节点如果不想用YOLOv5全家桶想自己控制优化器、学习率策略或者把训练过程接进自己的MLOps流程那就要写一个标准PyTorch训练脚本。我把核心训练循环的关键块拆开说。import torch.nn as nn model torchvision.models.resnet18(weightstorchvision.models.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 43) model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.SGD(model.parameters(), lr0.01, momentum0.9, weight_decay5e-4) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)这一段做的事情是加载ResNet18的ImageNet预训练权重把最后一层全连接替换成输出43类的线性层。因为预训练模型在ImageNet上学到的是通用特征提取能力保留这些底层的卷积参数可以大幅缩短收敛时间。优化器选SGD而不是Adam是因为SGDmomentum在这个规模的数据集上收敛更稳、泛化更好Adam在部分类别上容易过早收敛到局部最优。如果你更熟悉AdamW也可以用但学习率要调低到0.001左右weight_decay保持5e-4。学习率0.01是ResNet微调的常用起点如果换用更大的batch size比如128以上学习率可以适当提高到0.02。StepLR每10个epoch把学习率缩小到原来的十分之一这样训练后期loss下降会比较平稳。训练循环的关键是每个epoch结束后立刻在val集上评估保存val准确率最高的权重而不是保存最后一个epoch的权重。best_acc 0.0 for epoch in range(epochs): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() scheduler.step() model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() val_acc correct / total print(fepoch {epoch1} | loss {running_loss/len(train_loader):.4f} | val_acc {val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_resnet18.pth)这段代码里的几个细节值得注意。model.train()和model.eval()是为了控制BatchNorm层的行为训练时用当前batch的统计量验证时用训练阶段累计的滑动平均值。忘切模式会导致验证集指标异常偏高或偏低常见表现是val_acc振荡幅度特别大。torch.no_grad()在验证时禁用梯度计算省显存也加快推理速度不加它验证时显存占用跟训练一样大batch size稍大就爆显存。保存权重用state_dict()而不是整个model这样文件小、也更容易在脚本间迁移。如果你想保存训练结束时的完整状态包括优化器和调度器的进度可以加torch.save({model: model.state_dict(), optimizer: optimizer.state_dict(), epoch: epoch}, checkpoint.pth)但这通常用于断点续训不是最终部署。4.3 训练集和验证集比例下的超参数组合这套数据集的train、val、test比例大约是4:1:1也就是31,374除以7,835接近4倍。对三万张级别的数据集这种比例是合理的不需要再手动切分。超参数方面先固定batch size和epochs再调学习率和权重衰减通常就能到不错的水平。关于batch size64是下限32会引入更多梯度噪声但对于这个数据量32也可以接受只是收敛速度慢一些。batch size太大的问题不是内存而是收敛位置变差。epochs方面30~50就够再多了过拟合风险明显增加尤其是最后的几个类别样本本来就少。weight_decay取5e-4是ImageNet微调场景下比较常用的正则强度如果你用更深的网络比如ResNet50或者EfficientNet可以把weight_decay降到3e-4。一个比较省事的做法是先跑一轮只含前10个epoch的训练看val_acc是否在3~5个epoch内明显上升如果没上升优先检查数据加载和标签对齐而不是调学习率。这个习惯帮我避开了很多“训练了很久才发现数据路径错了”的低级事故。5. 避坑排查训练集、验证集、测试集使用中五个高频翻车现场这个数据集本身结构干净但实际操作中翻车的基本都集中在没处理好ImageFolder约定和类别映射。以下按“现象—原因—解决”写五条血泪记录。5.1 坑一训练loss下降但val_acc始终卡住不动现象是训练集loss一路往下走train_acc接近100%但每个epoch结束后的val_acc死活不涨甚至反向下降。原因是典型的过拟合而且大概率出在数据增强太弱或者没有开启。尤其在这个数据集里如果整个train_loader只用了Resize和ToTensor模型很快就能把训练样本背下来。解决做法是检查transform_train里有没有RandomRotation和ColorJitter。如果已经有了再看是不是增强强度太大比如rotation超过30度会把标志切出画面导致模型学到的特征变成周边环境而不是标志本身。我的习惯是把ColorJitter的brightness和contrast都放在0.2到0.4之间rotation保持在15度以内。另外一个很容易被忽略的点是RandomHorizontalFlip。这个数据集里同时有向左转和向右转两个类别开了水平翻转等于给模型喂错标签val_acc永远上不去。如果你手里的代码是从CIFAR或者ImageNet模板改过来的第一件事就是把flip删掉。5.2 坑二ImageFolder的类别顺序和JSON中文字典错位现象是预测结果打印出来的中文名和图片内容完全对不上比如一张限速50的图被显示成红绿灯。原因是ImageFolder的class_to_idx按子目录名字典序排序生成索引而加载JSON做映射时可能用手工顺序或者数值顺序直接拼了列表两者错位一格整个推理就全错。解决方法是别手动猜索引直接从train_dataset.classes和train_dataset.class_to_idx出发反向建立索引到类别名的映射表。我一般会这样做先用train_dataset.classes拿到排序后的子目录名列表再读入JSON把子目录名替换成中文名最后生成的映射表跟模型输出索引一一对应。建议在训练前就把这个映射表存成pkl或者json推理时直接加载省得每次重新对。这段代码可以这样写import json with open(data/label.json, r, encodingutf-8) as f: label_dict json.load(f) idx_to_name_list [] for class_name in train_dataset.classes: chinese_name label_dict.get(class_name, class_name) idx_to_name_list.append(chinese_name)这段逻辑不依赖手写顺序train_dataset.classes是什么顺序idx_to_name_list就是什么顺序模型输出的索引直接作为list下标就能拿到中文名。5.3 坑三测试集反复用来调参指标虚高部署就崩现象是测试集上的准确率比验证集高出好几个点结果一部署到新数据就崩了。原因是评估过程中把test集的数据参与到了验证调参里比如在验证迭代期间反复用test来检查过拟合、或者干脆用test做early stopping的判定依据。这样一来模型在test上见过梯度信息指标自然虚高。正确做法是val只用于训练过程中的模型选择和early stoppingtest在整个训练过程中一次都不能碰。最终训练结束后用保存的best.pt在test上做一次性的最终评估记录准确率、召回率、混淆矩阵然后就不再重复跑test了。如果数据集特殊、test集数量不大评估一次就足够不要反复用同一份test挑结果。这个规则不是我发明的但遵守它能避免很多自我感动式的指标。5.4 坑四训练和推理的预处理不一致导致掉点现象是训练时val_acc在95%以上但拿到YOLOv5或者onnxruntime推理时准确率明显下降甚至出现某种类别彻底不识别。原因是训练时候的transform和部署时候的transform不一样最常见的是尺寸不一致训练时用了224×224推理时用的图片原始尺寸或者另一套缩放方式。解决方法是把两张清单并排检查训练侧transform_val里Resize的尺寸和推理侧保持一致Normalize的mean和std必须一致。在YOLOv5的classify/predict.py里--img参数要和训练时的--img参数一致否则尺度不同特征分布就改变了。另外一个隐蔽的坑是使用PIL读图和cv2读图的通道顺序不一样PIL是RGB、cv2是BGR。如果训练时用的PIL、推理时用cv2读图颜色通道对调整个模型输出方向全错。如果你想验证是不是通道问题把一张图分别用两种方式读出来对比某个像素的RGB三个值颜色通道对调一目了然。5.5 坑五样本分布不均导致个别类别召回率偏低现象是整体准确率有93%以上但看每个类别的分类报告总有那么几个类别的召回率只有60%出头。原因在于数据集里不同类别的样本数天然不均衡一些低频类别在train里的数量远小于红绿灯这种高频类别。处理办法分两层。第一层是训练策略上在CrossEntropyLoss里传class_weight参数按每个类别的样本数反比计算权重让少样本类别在loss里占更大比重。第二层是评估策略上不要只盯overall accuracy要看混淆矩阵里那几个低召回类到底被误判成了谁。如果A类常被误判成B类排查一下这两个类的图像是否存在相似的视觉特征或者检查一下是不是RandomRotation把A类的标志转到B类的角度了。如果排查后还是不行可以给那两三个类别单独补数据把这几个类的train图片复制几份做数据增强之后放进原目录重新生成数据集再训练。class_counts [len(train_dataset.loader.class_to_idx)] # 简化起见实际要按类别统计 class_weight [1.0 / n for n in class_counts] class_weight torch.tensor(class_weight, dtypetorch.float32, devicedevice) criterion nn.CrossEntropyLoss(weightclass_weight)这段代码里class_weight需要按顺序传入顺序必须和train_dataset.classes的字典序一致。算完权重后可以print出来如果某两个类别的权重差了10倍以上说明数据分布确实偏这时候除了调权重还要考虑给少样本类别做针对性增强。6. 验证与进阶用测试集出混淆矩阵再把43类扩展到自己的业务类别训练结束不能只留一条命令行输出我习惯把测试集完整跑一遍生成混淆矩阵和分类报告验证模型对每个类别真实的识别能力。这段脚本长这样import numpy as np from sklearn.metrics import confusion_matrix, classification_report model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) cm confusion_matrix(all_labels, all_preds) print(classification_report(all_labels, all_preds, target_namesidx_to_name_list))classification_report里每一行的precision和recall比单独一个总准确率信息量大得多。我刚拿到这个数据集的时候第一轮训练完看到红绿灯、限速30这类常见类是99%的recall但“解除限速”这一类的recall只有78%而且看混淆矩阵发现它大量被误判成“限速80”。原因就是训练时数据增强里的RandomRotation把解除限速斜杠的方向信息模糊了。这类标志本身的判别关键就在斜杠方向旋转增强一多核心特征被削弱。发现这个问题后我把rotation从15度降到5度同时针对这类样本单独补了一版颜色扰动增强第二版recall从78%提升到了91%。再往后真正用好这个数据集的关键一步是把它扩展成你自己的业务数据集。原始43类对很多场景不够业务方会加“注意儿童”或者“施工标志”这种新类别。做法是先在data/train和data/val里分别新建以新类别编号命名的文件夹比如00044然后把对应图片放进去同时更新label.json。注意不要只改train不改valval和test也要保持同样的类别目录否则训练时模型输出维度是44类验证集一加载就报索引越界。改完之后重新跑一遍第3章的加载代码确认classes数量变成44再确认class_to_idx里新类对应的是索引43。从那次加类别踩坑之后我每次拿到新数据集、跑完第一轮训练之前都强制走一遍固定流程打印class_to_idx核对label.json的中文名检查三个目录的图片总数最后看一眼transform里有没有不该开的翻转。这套流程花不了两分钟但能拦住绝大多数低级错误。希望这些拆解能帮你在自己的交通标志分类或者类似的图像分类任务上少走几个弯路。本文还有配套的精品资源点击获取
返回列表