
简介这份资源面向计算机视觉方向的学习者与研究者提供不同天气条件下的道路路况图像分类数据集可用于图像分类、分割网络的训练与改进实验。数据已完成预处理可直接作为分类网络输入涵盖晴天、雾天、雨天等5个类别具体类别以包内json文件为准并已划分训练集与测试集各类别图片分别存放便于直接调用。资源包共2000个文件以jpg图像为主1998张另含1个Python脚本与1个json标注文件压缩包约463.02MB其中show脚本可用于数据集可视化方便快速查看样本分布与图像质量。目前已有53人学习下载。借助该数据集读者可省去繁琐的采集与标注环节直接投入模型训练与对比实验结合配套的分类与分割改进思路快速验证算法在不同天气场景下的鲁棒性适合作为课程设计、毕业设计或科研实验的基础数据。1. 拿到 17000 张带天气标签的路况图先别急着喂给 ResNet做道路场景感知的团队大多踩过同一个坑模型在晴天测试集上 mAP 漂亮得不像话一上路遇到团雾或暴雨就集体失灵。根因往往不在网络结构而在训练集里根本没有足够的恶劣天气样本。这份约 17000 张、已按 5 类天气晴天、雾天、雨天等具体类别以随包 json 为准标注好的道路路况图像分类数据集解决的正是这个分布偏移问题。它已经完成预处理训练集与测试集按同类图片分目录存放可以直接作为分类网络的输入省掉了自己爬图、清洗、标注的整条链路。适合做自动驾驶感知预研、图像分类模型对比、以及想快速验证 backbone 泛化能力的从业者。下面从目录结构、加载管线、训练配置到排错按能复现的顺序拆一遍。2. 目录结构与 json 标签映射先读懂数据集再写 DataLoader2.1 训练集/测试集分目录的物理布局这类已标注分类数据集常见做法是「一个类别一个文件夹」训练与测试各自独立成树。资源正文里那串747b40ee-dc2acc5c.jpg形式的文件名说明图片用了 UUID 命名避免了中文路径和重名问题但也意味着标签不能从文件名推断必须依赖目录名或 json。典型结构如下road_weather_dataset/ ├── train/ │ ├── sunny/ # 晴天 │ ├── foggy/ # 雾天 │ ├── rainy/ # 雨天 │ ├── snowy/ # 雪天以 json 为准 │ └── cloudy/ # 阴天以 json 为准 ├── test/ │ ├── sunny/ │ └── ... ├── classes.json # 类别索引映射 └── show.py # 可视化脚本先跑一遍统计确认每类样本量和是否存在空目录这一步能提前暴露类别不平衡import os, json from collections import Counter root road_weather_dataset with open(os.path.join(root, classes.json), r, encodingutf-8) as f: classes json.load(f) # 形如 {sunny:0, foggy:1, ...} print(类别映射:, classes) for split in [train, test]: split_dir os.path.join(root, split) counter Counter() for cls in os.listdir(split_dir): cls_dir os.path.join(split_dir, cls) if os.path.isdir(cls_dir): counter[cls] len(os.listdir(cls_dir)) print(split, dict(counter))逻辑说明classes.json是标签到索引的唯一权威来源训练时的num_classes必须等于它的长度而不是硬编码 5。参数上os.listdir只统计一层若你的包是多级嵌套需要改成os.walk。跑完如果发现某类只有几百张、另一类上万张后面 loss 就要考虑类别权重。2.2 用 ImageFolder 还是自定义 Dataset如果目录名恰好等于类别名torchvision.datasets.ImageFolder能直接吃下它会按字母序生成class_to_idx。但这里有个隐蔽的坑ImageFolder 的索引顺序是字母序而 json 里的索引可能是人工指定的顺序两者不一致会导致预测结果整体错位。稳妥做法是自定义 Dataset显式用 json 映射import os, json from PIL import Image from torch.utils.data import Dataset from torchvision import transforms class WeatherRoadDataset(Dataset): def __init__(self, root, split, classes_json, transformNone): self.transform transform with open(classes_json, r, encodingutf-8) as f: self.class_to_idx json.load(f) self.samples [] split_dir os.path.join(root, split) for cls, idx in self.class_to_idx.items(): cls_dir os.path.join(split_dir, cls) if not os.path.isdir(cls_dir): continue for name in os.listdir(cls_dir): if name.lower().endswith((.jpg, .png, .jpeg)): self.samples.append((os.path.join(cls_dir, name), idx)) def __len__(self): return len(self.samples) def __getitem__(self, i): path, label self.samples[i] img Image.open(path).convert(RGB) # 统一三通道防灰度图混入 if self.transform: img self.transform(img) return img, label逻辑说明convert(RGB)是关键路况图里可能混有单通道或带 alpha 的图不统一会在 batch 拼接时报维度错误。class_to_idx从 json 读取保证与标注一致。参数上transform由外部传入训练和验证要用不同的增强策略下一节展开。2.3 预处理与增强参数怎么设数据集号称「已预处理」但通常只做了尺寸归一增强还得自己加。道路天气分类的核心判别线索是对比度、色彩饱和度和纹理模糊度——雾天低对比、雨天多反光条纹、晴天高饱和。增强时不要用会破坏这些线索的强操作比如大幅度的 ColorJitter 会把雾天调成晴天。增强操作训练集测试集理由Resize256256统一短边保留长宽比RandomCrop224CenterCrop 224训练随机、测试居中RandomHorizontalFlipp0.5无左右翻转不改变天气语义ColorJitter轻微 (0.1)无过强会抹掉雾/雨特征NormalizeImageNet 均值方差同左用预训练权重必须对齐train_tf transforms.Compose([ transforms.Resize(256), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.1, contrast0.1), # 克制别动饱和度 transforms.ToTensor(), transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]), ]) val_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]), ])提示如果后续要换成自监督或 ViT 类 backboneNormalize 的均值方差要跟着换别照抄 ImageNet 那组。3. 训练一个天气分类 baseline从 DataLoader 到混淆矩阵3.1 DataLoader 与类别不平衡处理17000 张分 5 类平均每类 3400 张但实际分布往往不均雾天、雪天这类采集难度大的类别通常偏少。先算权重再决定要不要用WeightedRandomSamplerimport numpy as np from torch.utils.data import DataLoader, WeightedRandomSampler train_ds WeatherRoadDataset(root, train, road_weather_dataset/classes.json, train_tf) val_ds WeatherRoadDataset(root, test, road_weather_dataset/classes.json, val_tf) # 统计每类数量反比生成采样权重 labels [s[1] for s in train_ds.samples] counts np.bincount(labels, minlengthlen(train_ds.class_to_idx)) class_w 1.0 / np.clip(counts, 1, None) sample_w [class_w[l] for l in labels] sampler WeightedRandomSampler(sample_w, num_sampleslen(sample_w), replacementTrue) train_loader DataLoader(train_ds, batch_size64, samplersampler, num_workers8, pin_memoryTrue) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers8, pin_memoryTrue)逻辑说明WeightedRandomSampler让稀有类被抽到的概率上升等价于过采样比直接复制图片省内存。num_workers在 Linux 上设 8 左右Windows 上建议设 0 或 2 避免多进程卡死。pin_memoryTrue配合 GPU 训练能减少 H2D 拷贝开销。如果显存吃紧把 batch_size 降到 32同时把学习率按比例下调。3.2 迁移学习训练循环与关键超参从零训 17000 张容易过拟合常见做法是加载 ImageNet 预训练的 ResNet50 或 EfficientNet-B0替换最后一层。下面这段是可直接跑的骨架import torch, torch.nn as nn from torchvision import models device cuda if torch.cuda.is_available() else cpu num_classes len(train_ds.class_to_idx) model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V2) model.fc nn.Linear(model.fc.in_features, num_classes) # 替换分类头 model model.to(device) criterion nn.CrossEntropyLoss(label_smoothing0.1) # 缓解过拟合 optimizer torch.optim.AdamW(model.parameters(), lr3e-4, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30) for epoch in range(30): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() scheduler.step() # 每个 epoch 后在测试集上评估见 3.3逻辑说明label_smoothing0.1对天气这种边界模糊的类别阴天 vs 雾天特别有用能防止模型对模棱两可的样本过度自信。AdamW的weight_decay与 Adam 解耦比原版 Adam 更稳。学习率 3e-4 是微调预训练模型的常用起点若 loss 前几个 epoch 不降先查 Normalize 是否和预训练权重对齐。CosineAnnealingLR的T_max设成总 epoch 数。3.3 用混淆矩阵定位「雾天被认成阴天」准确率会掩盖类别间的混淆天气分类尤其要看混淆矩阵因为雾天和阴天、雨天和阴天的视觉边界本就模糊from sklearn.metrics import confusion_matrix, classification_report import numpy as np model.eval() preds, gts [], [] with torch.no_grad(): for imgs, labels in val_loader: out model(imgs.to(device)) preds.extend(out.argmax(1).cpu().numpy()) gts.extend(labels.numpy()) cm confusion_matrix(gts, preds) print(classification_report(gts, preds, target_nameslist(train_ds.class_to_idx.keys()))) print(cm)逻辑说明classification_report给出每类的 precision/recall/F1confusion_matrix的行是真实类、列是预测类。如果发现雾天大量落到阴天列说明两类特征重叠可以考虑加一个二阶段细分模型或在增强里对雾天样本做更强的对比度扰动。参数上target_names的顺序必须和class_to_idx一致否则报告会张冠李戴。4. 可视化脚本与坏样本排查show 脚本之外还要看什么4.1 跑通 show 脚本并抽样核对标签资源自带的show脚本通常做的是随机抽样展示先跑它确认图片能正常解码、标签目录对得上python show.py --data_root road_weather_dataset --split train --num 16如果脚本参数不明确直接读源码找argparse部分。跑完重点看两件事一是图片是否清晰、有无损坏PIL 解码报错会在这里暴露二是同一目录下的图片是否真的属于同一类。已标注数据集最常见的质量问题就是标签噪声——雨天文件夹里混进几张阴天图17000 张里哪怕 2% 的噪声也够把 F1 拉低几个点。4.2 用 embedding 聚类揪出标签噪声人工翻 17000 张不现实常见做法是用训练好的模型提特征再降维聚类把离群的样本挑出来复核import torch, numpy as np from sklearn.manifold import TSNE import matplotlib.pyplot as plt feat_model torch.nn.Sequential(*list(model.children())[:-1]).to(device).eval() feats, labs [], [] with torch.no_grad(): for imgs, labels in val_loader: f feat_model(imgs.to(device)).flatten(1) feats.append(f.cpu().numpy()) labs.extend(labels.numpy()) feats np.concatenate(feats) emb TSNE(n_components2, perplexity30, initpca).fit_transform(feats) plt.scatter(emb[:,0], emb[:,1], clabs, cmaptab10, s5) plt.colorbar(); plt.savefig(tsne.png, dpi150)逻辑说明list(model.children())[:-1]去掉最后的全连接层拿到池化后的特征向量。TSNE 把高维特征压到二维同类样本应该聚成一团。如果某个颜色类别的样本散落在别的簇里那些点大概率是标签错了。perplexity一般取 5 到 50样本多时调大。这一步在验证集上做因为验证集没参与梯度更新特征更能反映真实分布。4.3 常见报错与排查路径现象可能原因排查动作训练 loss 不降Normalize 与预训练权重不匹配核对均值方差验证准确率远低于训练过拟合或测试集分布不同看混淆矩阵、加增强DataLoader 卡死num_workers 过多或 Windows 多进程降到 0 或 2某类 recall 极低类别不平衡或标签噪声加权采样 4.2 聚类复核CUDA OOMbatch_size 过大减半并同步降 lr注意测试集不要参与任何形式的训练决策包括用测试集调早停。要调参就再从训练集切一个验证集出来否则报出来的指标没有参考价值。5. 进阶把天气分类头接到检测/分割管线上5.1 天气标签作为条件输入单独训一个天气分类器只是第一步真正有价值的是把它作为前置条件喂给下游任务。比如做目标检测时不同天气下检测器的置信度阈值应该不同——雾天整体置信度偏低硬套晴天的阈值会漏检。常见做法是把天气分类的 softmax 输出拼成条件向量接进检测头的 FiLM 层或直接做后处理路由# 伪代码天气概率作为后处理路由依据 weather_probs torch.softmax(weather_model(img), dim1) # [B, 5] foggy_idx train_ds.class_to_idx[foggy] is_foggy weather_probs[:, foggy_idx] 0.6 # 雾天降低检测置信度阈值减少漏检 conf_thres torch.where(is_foggy, 0.25, 0.5)逻辑说明torch.where按样本动态给阈值比全局固定阈值更贴合场景。阈值 0.25/0.5 是经验起点要在自己的验证集上扫一遍找最优。这一步的前提是天气分类器本身够准所以第 3、4 章的 baseline 和噪声清洗不能省。5.2 用天气分类做数据筛选与主动学习另一个高性价比用法是拿分类器给未标注的实车数据打伪标签挑出置信度低的样本送人工标注这就是主动学习的闭环。具体做法对无标注图片跑推理取 max softmax 概率低于 0.7 的样本优先标注通常能比随机抽样快 2 到 3 倍地补上稀有天气样本。17000 张的初始集足够训出一个可用的筛选器后续每轮迭代把新标注数据并回训练集重训分类头即可。这套流程在自动驾驶数据闭环里是标准操作比盲目堆数据量有效得多。本文还有配套的精品资源点击获取