2026/10/19 11:03:31

交通标志物图像分类数据集:43类4.7万张图,ImageFolder直接开跑

交通标志物图像分类数据集:43类4.7万张图,ImageFolder直接开跑 简介这份交通标志物图像分类数据集面向计算机视觉入门者、深度学习课程实验者以及需要快速验证分类模型的研究人员解决从零采集与标注交通标志样本耗时费力的问题。数据按文件夹组织可直接用ImageFolder加载无需额外清洗或划分。压缩包共2000个文件以1999张jpg图像为主另附1个json字典文件用于映射43类交通标志的中文名称涵盖红绿灯、限速、左右转等常见类别整体约58.4MB。目录下分train、val、test三个子集训练集31,374张、验证集与测试集各7,835张划分比例合理也可直接用于YOLOv5分类任务。目前已有333人学习下载。读者拿到后能省去数据整理环节把精力集中在模型搭建、超参调优与结果对比上同时借助中文字典快速核对类别标签适合作为课程作业、小论文实验或分类算法入门的基准数据。1. 交通标志物图像分类数据集43 类、4.7 万张图拿到就能直接开跑如果你正在找一个能直接丢进ImageFolder就能跑的图像分类数据集又不想花半天写清洗脚本那这份交通标志物图像分类数据集值得先看一眼。它把训练集、验证集、测试集按文件夹分好43 个类别覆盖红绿灯、限速、左右转这些常见交通牌总量接近 4.7 万张图压缩包只有 60MB 左右。对做交通物流场景分类、想快速验证模型结构或者跑通训练流程的人来说这种「目录即标签」的组织方式省掉了最烦人的一步——你不需要再写解析标注文件的代码torchvision.datasets.ImageFolder指过去就能用。下面我按「它是什么、怎么跑、怎么改」的顺序把这份数据集拆开讲清楚。2. 数据集结构与类别设计为什么 ImageFolder 能直接读2.1 三个目录的职责划分这份数据集在data目录下分了三个子目录train、val、test。训练集 31,374 张验证集和测试集各 7,835 张。这个划分比例大约是 8:1:1属于图像分类里比较常见的做法——训练集负责学参数验证集用来调超参和早停测试集只在最后评估时动一次避免信息泄漏。每个子目录下面再按类别建文件夹同一类别的图片全部放在对应文件夹里。这种「一级目录是划分、二级目录是类别」的结构正好是ImageFolder默认期望的布局。它会在初始化时扫描二级目录名按字母序生成class_to_idx映射然后把每张图的路径和对应标签存进samples列表。你不需要额外提供标签文件目录名本身就是标签。注意ImageFolder的类别索引是按文件夹名字母序排的不是按数字顺序。如果你后续要输出中文类别名得自己维护一份映射不能想当然认为索引 0 就是第一个类别。2.2 43 类别的覆盖范围与字典文件43 个类别涵盖了红绿灯、限速标志、左右转指示、禁止通行等常见交通牌。数据集里附了一个 JSON 文件存的是这 43 种交通标志物的中文字典相当于把类别索引和中文名称对应起来。这个文件在展示预测结果或者做可视化时很有用——模型输出的是 0 到 42 的索引你得靠它翻译成人能看懂的中文。我一般会先把这个 JSON 读进来构建一个idx_to_name的字典评估阶段打印混淆矩阵或者抽样展示时直接查表。JSON 的结构通常是{0: 红绿灯, 1: 限速50, ...}这种形式具体字段名以实际文件为准读的时候先打印一下 keys 确认结构别硬编码。2.3 目录树长什么样把压缩包解压后目录结构大致是这样data/ ├── train/ │ ├── 红绿灯/ │ │ ├── 00001_00027.jpg │ │ ├── 00001_00028.jpg │ │ └── ... │ ├── 限速50/ │ └── ...共43个类别文件夹 ├── val/ │ ├── 红绿灯/ │ └── ... └── test/ ├── 红绿灯/ └── ...每个类别文件夹里的图片命名类似00001_00027.jpg、00014_00029.jpg前缀数字可能对应原始采集批次或标注编号后缀是序号。命名本身不影响训练ImageFolder只关心文件夹层级。但如果你要做数据溯源或者去重这个命名规则可以拿来参考——同一前缀的图片可能来自同一段采集视频划分数据集时要注意别让同一批次的图同时出现在训练和测试里否则评估结果会偏乐观。2.4 用 ImageFolder 加载的最小示例下面这段代码是加载这份数据集最直接的方式我加了类别映射和基本检查import json from torchvision import datasets, transforms from torch.utils.data import DataLoader # 基础预处理统一尺寸 转张量 归一化 train_tf transforms.Compose([ transforms.Resize((224, 224)), # 按模型输入调整这里以224为例 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # ImageFolder 直接指向 train 目录二级文件夹名自动成为类别 train_ds datasets.ImageFolder(rootdata/train, transformtrain_tf) val_ds datasets.ImageFolder(rootdata/val, transformtrain_tf) test_ds datasets.ImageFolder(rootdata/test, transformtrain_tf) # 打印类别数和类别名确认43类都读到了 print(类别数:, len(train_ds.classes)) print(前5个类别:, train_ds.classes[:5]) print(class_to_idx 示例:, list(train_ds.class_to_idx.items())[:3]) # 加载中文类别字典 with open(data/labels_zh.json, r, encodingutf-8) as f: zh_map json.load(f) train_loader DataLoader(train_ds, batch_size64, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers4)这段代码里几个关键点Resize((224, 224))是按你选的骨干网络输入尺寸来的用 ResNet、EfficientNet 这类常见结构就设 224Normalize用的是 ImageNet 的均值和方差如果你从预训练权重开始微调这一步必须保持一致否则预训练学到的分布对不上收敛会变慢。shuffleTrue只在训练集开验证和测试集保持顺序方便后面逐样本分析。num_workers按机器 CPU 核数调一般设成 4 或 8设太大反而可能因为进程切换拖慢速度。3. 训练环境搭建与首次跑通从解压到看到 loss 下降3.1 环境依赖与版本选择这份数据集本身不挑框架PyTorch、TensorFlow、PaddlePaddle 都能读只要支持按文件夹加载就行。我下面以 PyTorch 为例因为ImageFolder和DataLoader的组合最顺手。环境上建议 Python 3.8 以上PyTorch 1.10 以上torchvision 版本跟 PyTorch 对齐。如果你用 GPUCUDA 版本按显卡驱动选别盲目装最新版先nvidia-smi看一下驱动支持的 CUDA 上限。安装命令大概是这样pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install numpy pillow tqdm scikit-learn matplotlibscikit-learn和matplotlib不是训练必需但后面看混淆矩阵、画 loss 曲线要用。tqdm用来给训练循环加进度条4.7 万张图跑起来没进度条会很难受。3.2 数据加载的完整配置上一节给了最小加载示例这里补一个更贴近实际训练的配置包含数据增强和验证集处理from torchvision import transforms # 训练集增强随机裁剪 翻转 颜色抖动 train_tf transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 验证/测试集只做确定性的 resize 和归一化 eval_tf transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])训练集用RandomResizedCrop而不是固定Resize是因为交通标志在画面里的位置和大小有变化随机裁剪能让模型见到更多尺度组合泛化更好。scale(0.7, 1.0)表示裁剪区域占原图 70% 到 100%别设太小否则标志可能被裁掉。验证和测试集必须用确定性变换Resize(256)加CenterCrop(224)是标准做法保证每次评估输入一致结果可复现。3.3 一个能跑通的训练循环下面是一个精简但完整的训练脚本用 ResNet18 做迁移学习跑几个 epoch 就能看到 loss 下降import torch import torch.nn as nn from torchvision import models from torch.utils.data import DataLoader from tqdm import tqdm device torch.device(cuda if torch.cuda.is_available() else cpu) # 加载预训练 ResNet18替换最后一层为43类 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 43) model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max10) train_loader DataLoader(train_ds, batch_size64, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers4) for epoch in range(10): model.train() running_loss 0.0 for imgs, labels in tqdm(train_loader, descfEpoch {epoch1}): imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() * imgs.size(0) scheduler.step() # 验证阶段 model.eval() correct, total 0, 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) preds model(imgs).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fEpoch {epoch1} | loss {running_loss/total:.4f} | val_acc {correct/total:.4f})几个参数说明lr1e-3是 AdamW 微调预训练模型的常用起点如果你发现 loss 震荡厉害降到 5e-4 试试。weight_decay1e-4做正则防止过拟合。CosineAnnealingLR让学习率按余弦曲线衰减T_max10对应总 epoch 数跑满 10 轮后学习率降到接近 0。batch size 64 在 8GB 显存上跑 ResNet18 加 224 输入基本够用显存小就降到 32。3.4 跑之前先做一次数据体检正式训练前我习惯先跑一遍数据完整性检查避免训练到一半才发现某类图片损坏或者数量异常from collections import Counter from PIL import Image def check_dataset(ds, name): counter Counter() bad [] for path, label in ds.samples: counter[label] 1 try: with Image.open(path) as im: im.verify() # 只检查文件头不完整解码速度快 except Exception as e: bad.append((path, str(e))) print(f[{name}] 总样本 {len(ds.samples)}类别数 {len(counter)}) print(f[{name}] 各类数量 min{min(counter.values())} max{max(counter.values())}) if bad: print(f[{name}] 损坏文件 {len(bad)} 个示例: {bad[:3]}) check_dataset(train_ds, train) check_dataset(val_ds, val) check_dataset(test_ds, test)im.verify()只读文件头比完整加载快很多适合大批量筛查。如果发现某类数量明显偏少训练时可以考虑给该类加权采样或者用WeightedRandomSampler平衡。损坏文件直接删掉或者移到单独目录别留在训练集里否则DataLoader读到会直接抛异常中断训练。4. 避坑与常见问题跑这份数据集时容易翻车的几个点4.1 类别索引对不上中文名现象训练完输出预测结果发现索引 0 对应的中文名和预期不符。原因ImageFolder按文件夹名字母序生成class_to_idx中文文件夹名的排序规则和你的直觉可能不一致而且 JSON 字典里的键值顺序未必和class_to_idx一致。解决不要假设索引对应关系用train_ds.class_to_idx拿到实际映射再和 JSON 字典做一次对齐构建idx_to_zh时以class_to_idx为准。4.2 验证集和测试集用了训练增强现象验证准确率波动很大同一模型多次评估结果差好几个点。原因验证/测试集误用了训练集的RandomResizedCrop、RandomHorizontalFlip等随机变换每次评估输入都不同。解决验证和测试必须用确定性变换ResizeCenterCropNormalize不要加任何随机操作。检查方法是连续跑两次评估如果结果不一致基本就是这个问题。4.3 图片尺寸差异导致 batch 组装失败现象DataLoader报错提示无法 stack 不同尺寸的张量。原因原始图片尺寸不统一而 transform 里没有做 resize直接ToTensor()后每张图形状不同。解决在 transform 里加Resize或RandomResizedCrop保证输出统一尺寸。这份数据集虽然经测试可直接用但保险起见还是显式加 resize别依赖原图尺寸一致。4.4 训练集和测试集分布不一致现象训练集准确率很高测试集掉一大截。原因同一批次采集的图片被同时分到了训练和测试模型记住了采集特征而非类别特征。解决如果图片命名前缀能反映采集批次按批次划分而不是随机划分。这份数据集已经分好了 train/val/test直接用即可但如果你要重新划分记得按前缀分组。4.5 显存不够导致训练中断现象跑几个 batch 后报 CUDA out of memory。原因batch size 太大、输入尺寸太大、或者num_workers过多导致内存峰值叠加。解决先把 batch size 减半再把输入尺寸从 224 降到 192 或 160 试试。num_workers设成 2 到 4 之间通常够用设太大反而因为每个 worker 都要拷贝数据而增加内存压力。如果还不行用梯度累积模拟大 batch。5. 二次开发与改造把这份数据集接进你自己的流程5.1 换骨干网络与调整输入尺寸这份数据集 43 类、4.7 万张图属于中等规模用 ResNet18 或 ResNet34 做迁移学习通常就能到不错的准确率。如果你想换更轻量的模型比如 MobileNetV3 或 EfficientNet-B0只需要改模型定义那一行输入尺寸按模型要求调整。EfficientNet-B0 原生输入是 224B1 是 240B2 是 260改Resize和CenterCrop的参数即可。换模型后记得重新调学习率轻量模型一般可以用大一点的学习率比如 3e-3。5.2 导出为 ONNX 或 TorchScript 做部署训练完想部署到边缘设备或者服务端导出 ONNX 是常见做法import torch model.eval() dummy torch.randn(1, 3, 224, 224).to(device) torch.onnx.export( model, dummy, traffic_sign.onnx, input_names[input], output_names[logits], dynamic_axes{input: {0: batch}, logits: {0: batch}}, opset_version12 )dynamic_axes把 batch 维度设为动态这样导出后的模型可以接受任意 batch size 输入。opset_version12兼容性比较好如果目标推理引擎支持更高版本可以往上调。导出后建议用onnxruntime跑一遍和 PyTorch 输出对比确认数值误差在可接受范围内。5.3 用类别权重处理不均衡如果体检发现某些类别样本明显偏少训练时可以用加权损失import numpy as np counts np.bincount([label for _, label in train_ds.samples], minlength43) weights 1.0 / (counts 1e-6) weights weights / weights.sum() * 43 # 归一化保持量级 class_weights torch.tensor(weights, dtypetorch.float32).to(device) criterion nn.CrossEntropyLoss(weightclass_weights)1.0 / counts给少数类更高权重归一化是为了不让 loss 量级变化太大影响学习率。加权后训练集准确率可能下降但测试集上的少数类召回会改善评估时重点看 macro F1 而不是整体准确率。5.4 一个我踩过的坑有次我拿到一份类似结构的数据集看目录分好了就直接开跑训练集准确率冲到 99%测试集只有 70% 出头。排查了半天才发现同一张原图经过不同增强后分别进了训练集和测试集等于测试集里的图模型在训练时见过。从那以后我每次拿到新数据集都强制先跑一遍图片哈希去重确认 train/val/test 之间没有重叠再开始训练。这份数据集已经预先划分好但如果你要自己重新切分记得按采集批次或者图片哈希分组别用纯随机切分。希望帮到你。本文还有配套的精品资源点击获取