
简介这份天气分类数据集面向计算机视觉入门与进阶学习者以及需要图像分类实战素材的开发者可用于CNN模型训练、迁移学习对比与数据增强实验。数据集共9000个样本涵盖cloudy、dew、fogsmog、frost、glaze、hail、lightning、rain、rainbow、rime、sandstorm、shine、snow、sunrise共14个类别训练集7180张、测试集808张目录按data/train_data与data/test_data划分便于直接接入主流框架。压缩包676.81MB内含约2000个文件以7987张jpg图像为主体另附1个py脚本与1个png图片脚本可用于快速搭建训练流程png可作类别分布或效果参考。已有1130人学习下载。利用CNN模型训练最好结果达到96.3%读者可据此复现基线、调整网络结构与超参并借助清晰目录完成数据加载、类别均衡分析与模型评估适合作为课程作业、竞赛练手或论文实验的起点。1. 天气分类数据集共9000个样本从拿到手到跑通第一个baseline你手里如果正好有一份「天气分类数据集共9000个样本」大概率是四种情况之一想做个能识别晴、阴、雨、雪、雾的图片分类器想拿它当预训练或蒸馏的辅助任务想验证某个新backbone在细粒度场景下的表现或者只是单纯想找个规模适中的多分类数据集练手。9000张图、通常4到6个类别这个体量很微妙——小到单卡几小时能跑完大到足以暴露过拟合、类别不均衡、标注噪声这些真实问题。它不像ImageNet那样让你望而生畏也不像MNIST那样干净到没有讨论价值。这篇文章就按一线做项目的顺序把这份天气分类数据集从目录结构、清洗、划分、训练到排错讲透让你拿到手当天就能跑出一个能看的baseline并且知道后面每一步该往哪调。2. 天气分类数据集共9000个样本先看清目录、类别和分布再动手拿到任何数据集第一件事不是写模型而是把数据本身摸清楚。天气分类数据集共9000个样本常见的组织方式是按类别分文件夹比如dataset/weather/下面挂着sunny、cloudy、rainy、snowy、foggy这样的子目录每个子目录里是对应类别的图片。也有少数版本是一张图配一行CSV标签。两种都要先确认因为后面写Dataset类的方式完全不同。2.1 用一段脚本统计类别数、样本数和图片尺寸不要凭感觉猜类别均衡不均衡。写个脚本把每个类别的文件数、图片宽高、通道模式、损坏文件全列出来。这一步花五分钟能省掉后面几小时的玄学调参。import os from PIL import Image from collections import defaultdict root dataset/weather stats defaultdict(lambda: {count: 0, sizes: set(), modes: set(), bad: []}) for cls in sorted(os.listdir(root)): cls_dir os.path.join(root, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): fpath os.path.join(cls_dir, fname) try: with Image.open(fpath) as im: stats[cls][count] 1 stats[cls][sizes].add(im.size) stats[cls][modes].add(im.mode) except Exception as e: stats[cls][bad].append((fname, str(e))) for cls, s in stats.items(): print(f{cls}: {s[count]} 张, 尺寸种类{len(s[sizes])}, 模式{s[modes]}, 损坏{len(s[bad])})这段代码的逻辑很直白遍历每个类别目录用PIL打开每张图累计数量、记录尺寸集合和色彩模式打不开的记进bad。跑完你会得到类似「sunny: 1800张尺寸种类37模式{RGB}损坏2」的输出。尺寸种类多说明图片没统一分辨率后面要么在预处理里resize要么在DataLoader里做augmentation时统一。损坏文件必须删掉或修复否则训练到一半报UnidentifiedImageError那种翻车最浪费时间。参数上没什么可调的唯一要注意的是如果数据集很大os.listdir换成os.scandir会快一些但9000张这个量级无所谓。2.2 类别不均衡的判断和三种处理策略9000个样本如果平均分到6类每类1500张那很理想。但实际拿到的天气数据集经常是晴天和阴天占大头雾天、雪天可能只有几百张。判断标准很简单最多类样本数除以最少类样本数超过3就算明显不均衡超过10就得认真处理。处理策略按代价从低到高排第一用WeightedRandomSampler给少样本类别更高采样权重。这是改动最小、最常用的做法不复制文件只在DataLoader层面调整。import numpy as np from torch.utils.data import WeightedRandomSampler # counts 是每个类别的样本数列表顺序与 dataset.classes 一致 counts np.array([1800, 1600, 1500, 900, 700, 500]) class_weights 1.0 / counts sample_weights [class_weights[label] for _, label in dataset.samples] sampler WeightedRandomSampler(sample_weights, num_sampleslen(sample_weights), replacementTrue)class_weights取倒数让少样本类权重高sample_weights把每个样本映射到它所属类的权重replacementTrue表示有放回采样这样每个epoch少样本类会被反复抽到。num_samples设成总样本数保证一个epoch的步数和原来一致。第二在损失函数里加weight参数比如CrossEntropyLoss(weighttorch.tensor(class_weights))。这和采样器二选一即可同时用容易矫枉过正。第三对少样本类做离线增强把图片翻转、裁剪、调色后存成新文件。这个最直接但会改变数据分布做之前先备份原始目录。提示先跑一次不均衡的baseline记下每类的recall再决定要不要处理。有时候模型对少样本类的recall没那么差过度处理反而拉低整体精度。3. 天气分类数据集共9000个样本划分训练验证测试集的正确姿势划分看起来简单但天气数据有个坑同一场景连拍的图片可能被分到不同集合导致验证集精度虚高。比如同一片乌云连拍十张八张进训练、两张进验证模型等于见过验证集。所以划分前要先做近似重复检测或者至少按「拍摄批次」分组划分。3.1 分层划分保证每类比例一致用sklearn的train_test_split加stratify参数按标签分层保证训练、验证、测试里每类比例和原始一致。from sklearn.model_selection import train_test_split paths [p for p, _ in dataset.samples] labels [l for _, l in dataset.samples] # 先分训练验证 和 测试测试占15% train_val_paths, test_paths, train_val_labels, test_labels train_test_split( paths, labels, test_size0.15, stratifylabels, random_state42 ) # 再从训练验证里分验证集占剩余的15%左右 train_paths, val_paths, train_labels, val_labels train_test_split( train_val_paths, train_val_labels, test_size0.176, stratifytrain_val_labels, random_state42 )test_size0.15表示测试集占15%第二次的0.176是让验证集最终占总量的15%左右0.85 × 0.176 ≈ 0.15。stratify保证分层random_state固定后每次划分结果一致方便复现。划分完把三个集合的文件列表存成txt或csv后面训练脚本直接读不要每次重新随机分。3.2 用感知哈希找出跨集合的近似重复图如果怀疑有连拍或重复图用imagehash库算pHash距离小于某个阈值的视为重复强制分到同一集合。import imagehash from PIL import Image def phash_of(path): with Image.open(path) as im: return imagehash.phash(im.convert(RGB)) # 对测试集每张图找训练集里hash距离5的 train_hashes {p: phash_of(p) for p in train_paths} dup_pairs [] for tp in test_paths: th phash_of(tp) for trp, trh in train_hashes.items(): if th - trh 5: dup_pairs.append((tp, trp)) break print(f测试集中有 {len(dup_pairs)} 张与训练集近似重复)phash对缩放、轻微调色不敏感距离阈值5是经验值越小越严格。发现重复后要么把测试集里这些图移到训练集要么直接删掉。这一步不做测试精度可能虚高5到10个点上线就露馅。4. 天气分类数据集共9000个样本训练一个能打的baseline数据摸清了、划分好了接下来搭训练流程。9000张图的体量用ResNet18或EfficientNet-B0从零训练容易过拟合常见做法是加载ImageNet预训练权重只微调最后几层或者全部微调但用较小学习率。4.1 用torchvision搭Dataset和增强管道import torch from torch.utils.data import Dataset, DataLoader from torchvision import transforms from PIL import Image class WeatherDataset(Dataset): def __init__(self, paths, labels, transformNone): self.paths paths self.labels labels self.transform transform def __len__(self): return len(self.paths) def __getitem__(self, idx): with Image.open(self.paths[idx]) as im: im im.convert(RGB) if self.transform: im self.transform(im) return im, self.labels[idx] train_tf transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) val_tf transforms.Compose([ transforms.Resize((256, 256)), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])RandomResizedCrop的scale(0.7, 1.0)控制裁剪面积比例天气图里天空占主体裁太狠可能把关键云层裁掉0.7是保守值。ColorJitter模拟不同光照和相机白平衡对天气分类有帮助但brightness别超过0.3否则晴天可能被调成阴天。Normalize用的ImageNet统计量因为后面要用预训练权重必须对齐。4.2 微调ResNet18的关键参数和训练循环import torch.nn as nn import torch.optim as optim from torchvision import models device torch.device(cuda if torch.cuda.is_available() else cpu) model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_classes len(set(train_labels)) model.fc nn.Linear(model.fc.in_features, num_classes) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.AdamW(model.parameters(), lr3e-4, weight_decay1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max20) for epoch in range(20): 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后在验证集上算accuracy和每类recalllr3e-4是微调预训练模型的常用起点比从零训练的1e-3小一个量级。weight_decay1e-4抑制过拟合。CosineAnnealingLR的T_max设成总epoch数让学习率从3e-4余弦降到接近0。如果显存够把resnet18换成efficientnet_b0通常能涨1到2个点但训练时间翻倍。batch size在8GB显存下ResNet18可以开到64EfficientNet-B0开到32。训练时重点看验证集的每类recall不要只看总体accuracy。天气分类里雾天和阴天容易混如果雾天recall明显低说明模型没学到雾的纹理特征可以考虑加更多雾天增强或换更大的输入分辨率。5. 天气分类数据集共9000个样本避坑与排查清单这一章是我自己踩过和见别人踩过的坑按「现象→原因→解决」写你训练时对照着查。现象训练loss正常下降验证accuracy卡在随机水平附近。原因通常是标签和图片没对齐比如CSV里路径顺序和实际文件顺序不一致或者Dataset返回的label索引错了。解决随机抽20张图把图片和标签打印出来人眼看一遍确认dataset.classes的顺序和label数字对应正确。现象验证accuracy比测试accuracy高一大截。原因大概率是划分时同一场景的图泄漏到了训练和验证。解决用3.2节的pHash做重复检测或者按拍摄时间、地点分组划分。另一个可能是验证集太小9000张图验证集至少留1000张以上。现象某些类别recall极低其他类别接近1.0。原因是不均衡加上增强不足。解决先加WeightedRandomSampler再对少样本类单独做离线增强。如果还不行检查少样本类的图片是不是本身标注就有问题比如把黄昏标成了雾天。现象训练到第3、4个epoch验证loss开始上升。典型过拟合。解决降低学习率到1e-4增大weight_decay到1e-3加Dropout在fc前插nn.Dropout(0.5)或者用早停。9000张图微调ResNet18通常5到8个epoch就到最佳20个epoch是上限。现象DataLoader报RuntimeError: DataLoader worker (pid xxx) is killed by signal。原因多是内存不足num_workers设太大每个worker复制一份数据。解决把num_workers从8降到4或2加pin_memoryTrue但别加persistent_workers除非确认内存够。另外检查是不是有超大图比如4000×3000没在预处理里resize一张图占几百MB内存。注意所有排查都建立在「先跑通小规模」的基础上。正式训练前用100张图跑2个epoch确认整个链路不报错再上全量。这个习惯能帮你省下大量等待时间。6. 天气分类数据集共9000个样本把精度再往上推的验证技巧baseline跑通后想再涨点别急着换大模型。先做两件低成本的事测试时增强TTA和混淆矩阵分析。TTA的做法是对验证/测试图做多次不同变换原图、水平翻转、不同裁剪分别推理后把softmax概率平均。代码上就是在验证循环里对同一批图跑多次前向def predict_tta(model, imgs): model.eval() probs torch.softmax(model(imgs), dim1) probs torch.softmax(model(torch.flip(imgs, dims[3])), dim1) return probs / 2水平翻转对天气分类通常安全因为云层左右翻转不改变类别。TTA一般能涨0.5到1.5个点代价是推理时间翻倍。如果延迟不敏感值得加。混淆矩阵比accuracy有用得多。用sklearn.metrics.confusion_matrix画出验证集的混淆矩阵你会清楚看到哪两类在互相误判。我做过的一个天气数据集里阴天和雾天的混淆率高达30%因为两者都是低对比度灰白天空。解决办法是加一个专门区分「有无雾滴纹理」的辅助分支或者把输入分辨率从224提到320让纹理更清晰。另一个技巧是类别权重和阈值的后处理。如果业务上更在意雾天的召回可以在推理时对雾天类别的logit加一个偏置牺牲一点整体accuracy换雾天recall。这个偏置在验证集上网格搜索步长0.1找recall和precision的平衡点。最后说个习惯每次实验都固定随机种子把配置文件、git commit、验证集指标存进一个csv。天气分类数据集共9000个样本这个量级你可能会跑几十组对比实验没有记录的话两周后就忘了哪组参数对应哪个结果。我一般用seed42torch.backends.cudnn.deterministicTrue虽然会慢一点但复现时不会因为随机性翻车。希望帮到你。本文还有配套的精品资源点击获取