news 2026/9/28 7:49:14

基于ResNet的恶劣天气图像分类:1000张小样本数据集实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于ResNet的恶劣天气图像分类:1000张小样本数据集实战指南

简介:面向恶劣天气识别与图像分类任务的已标注数据集,覆盖大雾、暴雨、沙尘暴、暴雪四类天气,样本约1000张,适合深度学习入门、CNN分类课程设计、算法复现以及多类别天气识别场景研究,也可用于道路能见度预警等应用探索。整个压缩包含1030个文件,以1028张JPG图片为主体,配有一个JSON标注文件用于查看四类标签,另附Python可视化脚本,可快速预览样本并检查数据质量;压缩包大小约132MB,文件命名规范,类别目录清晰。已有667人学习下载。资源已划分好训练集与测试集,目录按类别存放,直接可用于模型训练、评估与调参,也便于在Jupyter Notebook等环境中自行做数据增强与对比实验;对需要自建天气数据集的用户来说,可显著节省爬取、清洗和标注的时间,同时可作天气分类基准数据使用。

1. 大雾、暴雨、沙尘暴、暴雪图像分类:1,000张已经够做八成的天气过滤任务

做户外监控、自动驾驶数据预筛、或者电厂巡检告警时,你大概率遇到同一个尴尬:摄像头一拍到坏天气,后续的目标检测、语义分割模型就开始集体“失明”。与其让主模型硬扛,更务实的做法是在前面挂一个天气图像分类器,把大雾、暴雨、沙尘暴、暴雪的图片先分流出来单独处理。这个标题给的就是一份已标注的四种恶劣天气图像分类数据集,约1,000张。

反直觉的结论是:1,000张在四分类任务上够用,甚至能跑到85%以上的验证准确率。原因是大雾、暴雨、沙尘暴、暴雪在视觉上是颜色分布和纹理结构差异极大的四类场景,类别之间的特征距离很宽,不是“猫和狗”那种细粒度差异;再加上预训练模型迁移学习,等效样本量远大于1,000张。但前提有两件事必须做对:一是数据本身要仔细校验,二是训练时要防过拟合。这套方案适合两类人——刚入门图像分类、需要一份干净数据练手的学生,以及要在真实业务里快速搭一个天气前置过滤器的算法工程师。

2. 拿到手先别训模型:1,000张已标注数据的目录组织与标签校验

“已标注”三个字是起点,不是终点。标注文件里标签写的是“暴雨”,不代表图片内容真的是暴雨——有的是文件名带“rain”但内容是雨后的地面,有的是大雾里夹着雨丝被标成了暴雨,还有的是沙尘暴里的扬沙被标成了雾。这种噪声在1,000张的小数据集里占比哪怕5%,就会让模型学会抄捷径而不是学天气特征。所以动手训练前的第一步,永远是数据盘点。

2.1 拿到数据先做四项体检:数量、尺寸、格式、标签分布

用一段脚本把这四件事全查清楚。别用眼睛一个个点开看,效率太低。

import glob import os from collections import Counter from PIL import Image img_paths = glob.glob("dataset/**/*.jpg", recursive=True) + \ glob.glob("dataset/**/*.png", recursive=True) formats = Counter() sizes = [] labels = [] for path in img_paths: ext = os.path.splitext(path)[1].lower() formats[ext] += 1 with Image.open(path) as im: sizes.append(im.size) labels.append(os.path.basename(os.path.dirname(path))) print("图片总数:", len(img_paths)) print("格式分布:", formats) print("标签分布:", Counter(labels)) print("尺寸范围:", min(w for w, h in sizes), min(h for w, h in sizes), "-", max(w for w, h in sizes), max(h for w, h in sizes))

这段脚本能同时回答四个关键问题:数量是否接近1,000张、四种天气的类别是否均衡、尺寸是不是有极端超宽图或分辨率极低的小图、格式是否混杂。如果发现某类只有180张而另一类有320张,后面训练时就要处理类别不平衡;如果发现图片里混着webp或者bmp,统一转成jpg,避免DataLoader读取时踩格式兼容的坑。

格式统一这一步,我一般用下面的代码批量转掉,顺便把中文文件名和目录名全部改成英文。Windows环境下Python读取中文路径容易出编码问题,改成英文目录名后,训练脚本和部署脚本都不会被路径坑到。

import os from PIL import Image root = "dataset" mapping = {"大雾": "fog", "暴雨": "rain", "沙尘暴": "sandstorm", "暴雪": "snow"} for ch_name, en_name in mapping.items(): ch_dir = os.path.join(root, ch_name) en_dir = os.path.join(root, en_name) os.makedirs(en_dir, exist_ok=True) for fname in os.listdir(ch_dir): src = os.path.join(ch_dir, fname) if not os.path.isfile(src): continue im = Image.open(src).convert("RGB") en_path = os.path.join(en_dir, os.path.splitext(fname)[0] + ".jpg") im.save(en_path, quality=95) print("完成转换:", ch_name, "->", en_name)

2.2 用ImageFolder按类别组织目录:整理脚本与重命名规则

torchvision的datasets.ImageFolder要求数据按“根目录/类别名/图片文件”的层级存放,目录名就是标签名。好处是零配置文件,训练代码里三行就能加载,坏处是目录名是唯一的标签来源,一旦放错文件夹,标签就是错的。

我建议的顶层结构是这样的:

dataset/ ├── fog/ │ ├── fog_001.jpg │ ├── fog_002.jpg │ └── ... ├── rain/ │ ├── rain_001.jpg │ ├── rain_002.jpg │ └── ... ├── sandstorm/ └── snow/

文件名统一用类别_序号.jpg,不要保留原始文件名。原始文件名经常带着来源网站的水印痕迹、时间戳甚至拍摄者信息,训练时模型如果通过文件名“作弊”判断类别,部署后文件名一变模型就废了。重命名脚本很简单,遍历每个类别目录,按序号补零重命名即可。

2.3 不迷信“已标注”:用快速可视化抽检代替逐张人工复核

1,000张图逐张看大概要半小时到一小时,对工程师来说太奢侈。更快的方式是从每个类别随机抽9到16张,拼成一个九宫格,扫一眼就能发现大部分标注问题。

import matplotlib.pyplot as plt from torchvision import datasets, transforms dataset = datasets.ImageFolder( "dataset", transform=transforms.Resize((224, 224)), ) fig, axes = plt.subplots(4, 8, figsize=(16, 8)) for cls_idx in range(4): cls_images = [i for i, (_, label) in enumerate(dataset.samples) if label == cls_idx] for row in range(8): img, _ = dataset[cls_images[row]] axes[cls_idx, row].imshow(img) axes[cls_idx, row].axis("off") axes[cls_idx, 0].set_ylabel(dataset.classes[cls_idx], fontsize=12) plt.tight_layout() plt.savefig("check_sample_grid.png", dpi=120)

重点看两类问题:一是混入了完全不属于该类天气的图,比如晴天傍晚被标成“沙尘暴”;二是类别定义本身冲突,比如“雨+雾同时出现”到底归哪个类。这个抽检图我也会保留一份作为项目记录,后面训练完回来复盘时能对比标注版本差异。

3. 用预训练ResNet快速跑通基线:最小训练脚本与超参数设定

让1,000张数据发挥价值,最快路径不是从零训练一个卷积网络,而是拿在ImageNet上预训练好的模型做迁移学习。常见做法是把ResNet34的最后一层全连接从1000类换成4类,然后整个网络微调。速度比从零训练快一个数量级,准确率还能高十个百分点以上。

3.1 为什么选ResNet而不是从零训练的卷积网络或Vision Transformer

从零训练一个ResNet或者ViT在1,000张图上基本是灾难。机器学习的冷启动规律在小数据集上体现得最残酷:没有预训练权重,模型前几十个epoch都在学边缘和纹理,四分类任务还没学到“雾是低对比度灰白色”这个层面,就已经过拟合训练集了。ViT更挑数据,1,000张连它的位置编码都喂不饱,即便用预训练ViT微调,效果也不会明显优于ResNet34,反而推理和训练都更慢。

所以这里的选型逻辑是:ResNet34配合ImageNet预训练权重,是“小样本+四分类+需要快速出基线”的最稳组合。ResNet18更轻,适合测试环境;ResNet50精度略高但小数据集上容易过拟合,需要更重一点的数据增强来压制。我一般先跑ResNet34。

3.2 最小训练代码:加载、随机增强、SGD和早停

这份代码可以直接存成train.py跑起来。为了让你能复现,我把所有超参数集中在args里,关键的改动我都加了注释。

import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms, models import numpy as np from sklearn.metrics import classification_report, confusion_matrix BATCH_SIZE = 16 EPOCHS = 30 LR = 1e-3 WEIGHT_DECAY = 1e-4 NUM_CLASSES = 4 DEVICE = "cuda" if torch.cuda.is_available() else "cpu" train_transform = transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomResizedCrop(224, scale=(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.2, hue=0.05), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) val_transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) train_ds = datasets.ImageFolder("dataset/train", transform=train_transform) val_ds = datasets.ImageFolder("dataset/val", transform=val_transform) train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=4) val_loader = DataLoader(val_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=4) model = models.resnet34(weights=models.ResNet34_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, NUM_CLASSES) model = model.to(DEVICE) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=LR, momentum=0.9, weight_decay=WEIGHT_DECAY) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode="min", factor=0.5, patience=3 ) best_acc = 0.0 for epoch in range(EPOCHS): model.train() train_loss_sum, train_correct = 0.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() train_loss_sum += loss.item() * images.size(0) train_correct += (outputs.argmax(1) == labels).sum().item() model.eval() val_correct, val_loss_sum = 0, 0.0 all_preds, all_labels = [], [] with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(DEVICE), labels.to(DEVICE) outputs = model(images) loss = criterion(outputs, labels) val_loss_sum += loss.item() * images.size(0) all_preds.extend(outputs.argmax(1).cpu().numpy()) all_labels.extend(labels.cpu().numpy()) train_acc = train_correct / len(train_ds) val_acc = np.mean(np.array(all_preds) == np.array(all_labels)) val_loss = val_loss_sum / len(val_ds) scheduler.step(val_loss) if val_acc > best_acc: best_acc = val_acc torch.save(model.state_dict(), "weather_cls_best.pt") print(f"Epoch {epoch+1:02d} | train_acc {train_acc:.3f} | " f"val_acc {val_acc:.3f} | val_loss {val_loss:.3f}") print("Best val acc:", best_acc) print(confusion_matrix(all_labels, all_preds)) print(classification_report(all_labels, all_preds, target_names=train_ds.classes, digits=3))

几个必须说清楚的参数选择:

  • 批大小16,不要用64。1,000张级别的数据,大batch会加速过拟合,小batch带来的梯度噪声反而有正则化效果。
  • 优化器用SGD而不是Adam。迁移学习微调阶段,SGD+momentum的泛化能力通常比Adam好,特别在样本量小的场景下差距更明显。如果换用AdamW,学习率建议降到3e-4。
  • ReduceLROnPlateau设patience=3,连续三个epoch验证损失不降就把学习率减半。这比固定epoch衰减更贴合训练动态。
  • 必须保存best_acc对应的权重,而不是最后一轮的权重。小数据集的训练后期往往在震荡,最后一轮通常不是最优的。

3.3 评估指标:看混淆矩阵而不是只看准确率

四分类准确率90%听起来不错,但要看具体错在哪。沙尘暴被误判成雾、暴雨被误判成雪,这两种错误的业务代价完全不同。所以我在训练脚本最后打印了混淆矩阵和classification_report,重点看每一类的precision和recall。

如果某类的recall特别低,说明这类被系统性误判,需要回看训练集该类图片是否有标签噪声,或者该类样本数过少。此时优先检查数据,不要急着换模型。模型层面的调整建议放在第四章的数据增强和类别平衡之后再做。

4. 把1,000张样本用到极限:按天气特征定制的增强与交叉验证

在1,000张图上,数据增强不是“锦上添花”,而是“救命稻草”。但增强不是越多越好,而是要顺着四种天气的物理特征来设计。瞎用增强会引入和天气无关的伪规律,比如把雾天图裁剪成局部特写,模型学到的可能是一堵墙而不是雾。

4.1 增强策略按恶劣天气视觉特征来定:亮度、对比度、噪声和色偏

四种天气在视觉上有清晰特征:雾天整体低对比度、亮部发白;暴雨的画面亮度和对比度变化剧烈,暗部细节丢失;沙尘暴图像整体偏黄,饱和度偏低;暴雪的画面有大面积高亮区域,边缘纹理被雪覆盖。针对这些特征,我把增强分成四组。

train_transform = transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomResizedCrop(224, scale=(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.ColorJitter( brightness=0.4, contrast=0.4, saturation=0.3, hue=0.05, ), transforms.GaussianBlur(kernel_size=3, sigma=(0.1, 1.5)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ])
  • RandomResizedCrop(scale=(0.7, 1.0))模拟摄像头抓拍时画面里天气区域的占比变化。不要用(0.08, 1.0)那种ImageNet默认值,裁得太碎会把远处一座山当成天气特征。
  • ColorJitter的brightness和contrast各给到0.4,重点模拟暴雨天气下光线骤变和雾天能见度变化的场景。
  • saturation给0.3、hue只给0.05。hue调太大,会制造出物理上不存在的颜色,比如把沙尘暴的黄色调成蓝绿色,模型学到的是“偏色的图就是沙尘暴”,这是要避开的坑。
  • 加了GaussianBlur模拟镜头上的水雾和沙尘颗粒。不需要每次生效,PyTorch的GaussianBlur以50%概率随机生效。

另外一个细节:不要做垂直翻转。户外监控场景里天空永远在画面上方、地面在下方,垂直翻转会把天空和地面的物理位置颠倒,模型会学习“上暗下亮的是暴雨”这种错误规律。水平翻转不受影响,因为从左来风还是从右来风不改变天气类别。

如果1,000张里某一类特别少,我还会用torchvision.transforms.RandomErasing做随机遮挡,强迫模型不要依赖单一区域的特征。比如沙尘暴类只有180张,模型很容易靠某张图右下角的黄沙色调取巧,RandomErasing可以逼它看整个画面。

4.2 类别不平衡:加权采样与焦点损失二选一

四类样本数量大概率不会完全相等。假如雾天300张、暴雨280张、沙尘暴200张、暴雪220张,直接训练loss会被大头类别主导。最简单的解决办法是用WeightedRandomSampler,让每个类别在每轮epoch中的采样次数相等。

from torch.utils.data import WeightedRandomSampler labels = np.array([s[1] for s in train_ds.samples]) class_counts = np.bincount(labels) sample_weights = 1.0 / class_counts[labels] weights = torch.from_numpy(sample_weights.astype(np.float32)) sampler = WeightedRandomSampler(weights, num_samples=len(weights), replacement=True) train_loader = DataLoader(train_ds, batch_size=16, sampler=sampler)

1.0 / class_counts[labels]的含义是:样本越多的类别,每个样本被抽到的权重越小,反向拉平各类别在训练中的出现次数。num_samples=len(weights)表示每轮仍然抽1000次,但每次抽取时按权重采样,类别少的那一类会被重复抽到。注意这种采样会让模型看到重复样本,配合上一节的增强一起用才有效果。

如果你的项目里类别数量悬殊极大,比如暴雨400张、沙尘暴100张,加权采样还不够,可以换Focal Loss。它的原理是让模型更关注难分类的样本,但需要额外调节gamma参数,默认gamma=2比较合适。在小数据集上我通常先试加权采样,效果不明显再上Focal Loss,不做无谓的复杂度叠加。

4.3 小样本评估:5折交叉验证算可信度区间

1,000张数据如果只划分一次train/val,结果会强烈依赖随机划分。运气好点验证集准确率92%,运气差点只有85%,你很难判断这是数据问题还是模型问题。常见做法是直接做5折交叉验证,用五轮结果的平均值和方差来说明模型稳定性。

from sklearn.model_selection import StratifiedKFold train_ds = datasets.ImageFolder("dataset", transform=train_transform) kf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) samples = np.array(train_ds.samples) fold_results = [] for fold, (train_idx, val_idx) in enumerate(kf.split(samples, samples[:, 1])): fold_train = torch.utils.data.Subset(train_ds, train_idx) fold_val = torch.utils.data.Subset(train_ds, val_idx) # 在每一折中重新训练模型,并记录验证准确率 # fold_results.append(val_acc)

StratifiedKFold在划分时保留各类别的样本比例,避免某一折里完全没分到沙尘暴图片导致评估失效。每一折都从头训练一次,五折跑完耗时大约十五分钟,但你能得到一个准确的估计:如果所有折的准确率都在85%-88%之间波动,说明模型稳定;如果第3折突然掉到70%,说明那一折的训练集里存在标签噪声或某类样本特别少,值得翻回去查数据。

最终的模型可以用全部数据重新训练一份,验证集只是一个评估手段,不是模型的一部分。交叉验证会暴露单个划分掩盖的过拟合问题,这是小数据集项目最值得花的十五分钟。

5. 避坑指南:恶劣天气分类最容易翻车的5个细节与排查路径

这部分是我实际做类似数据集项目时遇到的真实坑,每一条都按“现象→原因→解决”写。别人踩过的坑你不用再踩一遍。

5.1 误差不均衡:混淆矩阵里“暴雨”列混进了大量“雾”图

现象:整体准确率82%,但看混淆矩阵,实际是雾天的图片里有12%被预测成暴雨,而暴雨被预测成雾的只有3%。

原因:标注阶段把“起雾时下了小雨”的场景归入了暴雨类,模型看到雾和雨同时出现的图片,在两类之间左右摇摆。小数据集中10%左右的不一致标注就足以造成局部预测系统性偏移。

解决:翻出第二章保存的抽检九宫格图,重点看雨雾两类,把标注为暴雨但视觉上雾占主导的图重新归类;如果标注标准实在难以统一,干脆把这两类合并成“雾雨”类,四分类改三分类。模型结构和代码都不用改,直接改目录名即可。

5.2 训练准确率99%,验证集准确率只有72%,典型的过拟合

现象:第10轮开始训练集准确率冲上99%,验证集准确率卡在72%的水平再不动。

原因:模型容量太大或者增强强度不足。ResNet34在1,000张图上完全有能力“背下来”全部训练样本,如果没有足够强的随机扰动,模型直接在训练集上做记忆,验证集自然拉胯。

解决:先增强RandomResizedCrop的scale下限到0.7,打开GaussianBlur,再给loss加label smoothing,nn.CrossEntropyLoss(label_smoothing=0.1)。PyTorch从1.10开始原生支持label_smoothing参数,不用额外实现。如果整轮加强后验证集仍然在75%以下,回查训练集是否混入了大量错标图,因为增强只能压制过拟合,救不了标签噪声。

5.3 DataLoader读取图片报错:中文文件名和EXIF方向

现象:ImageFolder在加载时偶发PIL.UnidentifiedImageError或者图片被旋转90度显示,训练中途崩溃。

原因:原始数据集里文件名含中文,或者拍摄设备在EXIF信息里写了旋转方向。Windows下中文路径被Python读取时编码不一致,容易出现打不开文件的问题;而手机拍摄的图片若不校正EXIF旋转,模型看到的图像是横着的,这属于与天气无关的伪特征。

解决:在第二章的统一转码环节就把文件名改成英文,并且用ImageOps.exif_transpose处理方向。下面的代码可以在读取图片时自动校正EXIF:

from PIL import ImageOps with Image.open(path) as im: im = ImageOps.exif_transpose(im) im = im.convert("RGB")

在datasets.ImageFolder里也可以写一个自定义loader传入,替换默认的PIL读取逻辑。

5.4 沙尘暴和雾天边界模糊:类别定义本身就是噪声

现象:沙尘暴类的分类报告里精确率很高、召回率很低,大量沙尘暴图片被预测成雾天。人工回看发现,确实有一部分沙尘暴图片颜色偏灰白,与浓雾照片几乎无法区分;而黄色偏色明显的图模型分得很好。

原因:恶劣天气是连续光谱,不是离散标签。轻度沙尘暴和中度雾的视觉效果重叠度很高,图像分类模型对这个边界的处理能力有限,这是任务本身的性质决定的,不是模型调参能彻底解决的。

解决:第一选择是人为放宽类别定义,强调沙尘暴必须是肉眼可见的“黄色调+风沙纹理”,把偏灰白的样本归为雾;第二选择是训练一个辅助任务,统计图像的R/B通道比值,沙尘暴图像R通道明显高于B通道,而雾天R/G/B三个通道接近。这个比值可以作为后处理规则,当模型概率输出在两类中间时,用颜色比值破平局。

5.5 部署图片和训练图片风格不一致:白平衡和HDR带来特征偏移

现象:用网络下载的公开图片训练,模型在验证集上表现不错,一部署到实际摄像头,准确率掉了15%以上。

原因:训练集里的图片多数是摄影师拍的,经过了白平衡校正、HDR调色,甚至后期滤镜;摄像头直出的原始图像色调偏灰、对比度低、动态范围宽。模型学到的“沙尘暴=黄色高饱和”可能在摄像头画面中是“灰黄色低饱和”,特征分布发生了偏移。

解决:收集目标场景的真实抓拍图,哪怕是几百张,也要加入训练集做finetune。如果暂时拿不到现场图,先做一个预处理层,对所有输入图执行灰度世界白平衡:

import numpy as np def gray_world_balance(img): img = np.array(img).astype(np.float32) mean_r = img[..., 0].mean() mean_g = img[..., 1].mean() mean_b = img[..., 2].mean() avg = (mean_r + mean_g + mean_b) / 3.0 img[..., 0] *= avg / mean_r img[..., 1] *= avg / mean_g img[..., 2] *= avg / mean_b return np.clip(img, 0, 255).astype(np.uint8)

这个预处理能抹掉大部分由于不同设备白平衡算法造成的色偏差异,让模型更关注天气的结构特征而不是绝对色调。

6. 从四分类到多标签与检测迁移:让这套数据发挥剩余价值

在真实业务里,“一张图只有一种天气”这个假设经常不成立。暴雨中裹着雾、沙尘暴来之前先起风扬尘,这些场景都带有“兼类”属性。把四分类改造成多标签只需要改动两个地方:损失函数从CrossEntropyLoss换成BCEWithLogitsLoss,最后一层全连接保持4个神经元,每个神经元独立输出一个0到1之间的概率。

criterion = nn.BCEWithLogitsLoss() # 每个样本的标签是长度为4的01向量,例如 [1, 1, 0, 0] 表示既有雾又有雨 labels_multihot = torch.tensor([[1.0 if "fog" in tags else 0.0 for tags in sample_tags]])

多标签模型在推理时,你可以遍历验证集找出最优概率阈值。比如当概率大于0.5算命中,关闭模型已无法识别;如果大于0.65才提醒人工抽检,误报率会降低但漏报率上升,按业务容忍度调即可。

另外一个小技巧:这1,000张已标注数据也可以作为更下游任务的数据基础。图像分类只是最浅层的用法,再做目标检测时,你可以用cvat或labelimg在原有图片上框出雨丝、雾区、沙尘区域,把分类升级为检测。cvat适合团队协作,labelme适合单人快速打点,labelimg则是目标检测框标注的老牌选择,三者导出的格式不一样,后续要接YOLO系列的话,labelimg的YOLO格式输出最省事。我个人的习惯是分类模型先上线留作备用,同时用同批数据扩充检测样本,这样一套恶劣天气数据集能支撑两条算法线。

每个项目我都留一份增强后的可视化样本和混淆矩阵截图,回看归档才能知道哪些天气类别在什么阶段被系统性地分错。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 7:48:37

用Arduino Nano自制迷你特斯拉线圈:从原理到调参的完整指南

玩Arduino有一段时间之后,总会忍不住想搞点真正有“视觉冲击力”的东西。普通LED闪烁、舵机摆动已经满足不了那颗折腾的心,于是我把目光盯上了特斯拉线圈——那种能隔空点亮荧光灯管、能拉出蓝紫色电弧的装置,听起来像是高压电实验室才有的东…

作者头像 李华
网站建设 2026/9/28 7:48:30

实测服务器两个域名一个ip做两个网站吗对比评测避坑指南

实测服务器两个域名一个ip做两个网站吗对比评测避坑指南 找建站公司最怕什么?不是技术不行,而是被“高价低配”坑得明明白白。很多老板为了省钱,听信“一台服务器能跑俩站”的话术,结果上线后域名解析混乱、SSL证书报错,甚至因为IP被标记导致百度收录全丢。别急着下单,这篇 对比评测…

作者头像 李华
网站建设 2026/9/28 7:48:29

AI工具避坑指南:三步筛选法识别劣质套壳产品

AI工具这个圈子,这两年热闹得像早市,每天都能冒出几十个号称“颠覆效率”的新产品。但我在一线用了两年多,自己踩过坑、也帮团队填过坑,说句实话:真正值得长期用的工具,两只手数得过来,剩下大部…

作者头像 李华
网站建设 2026/9/28 7:48:10

wordpress的头像改图避坑指南:3类常见错误与成本拆解

wordpress的头像改图避坑指南:3类常见错误与成本拆解 自己不会代码想做网站,最怕的不是买不到模板,而是改个头像就把整站搞崩。我见过太多新手在后台把 gravatar 换成本地大图,结果页面加载慢得让人想砸键盘。做 WordPress…

作者头像 李华
网站建设 2026/9/28 7:47:54

双随机相位编码结合压缩感知的图像加密Matlab实现解析

做图像加密项目时,我最早接触双随机相位编码(DRPE)是在一次光学全息仿真的任务里。当时的需求很明确:要设计一种既能抵抗非法窃取,又不会让传输数据量爆炸的图像加密方案。单纯用双随机相位编码,密文是一幅…

作者头像 李华
网站建设 2026/9/28 7:47:39

从零自制迷你特斯拉线圈:Arduino驱动与谐振调试全指南

第一次刷到特斯拉线圈的视频,应该没人能忍住不点进去:电弧滋滋作响,甚至能跟着音乐节奏跳舞。我当时心想这东西要是摆自己桌上该多好,结果打开购物网站一看,成品价格贵得离谱,DIY套件也不便宜。干脆自己绕一…

作者头像 李华