简介:面向恶劣天气下道路行人、车辆检测任务,这份YOLO格式数据集提供约1000张已标注图像,覆盖car、bus、person、truck等7个类别,适合计算机视觉学习者与目标检测算法研究者用于模型训练、验证及鲁棒性改进。资源共2000个文件,包括972张jpg原图与1027个txt标注文件,另附1个Python数据可视化脚本;压缩包整体133.76MB,数据已预先划分训练集与测试集,运行show脚本即可直观检查标注结果。目前已有157人学习下载,可直接接入YOLOv5等主流检测框架进行实战。数据集聚焦降雪、薄雾、沙尘、大雾等典型恶劣天气场景,可作为自动驾驶感知、恶劣环境目标检测等课题的基准数据;配合作者主页中的YOLOv5改进实战案例,有助于读者快速理解数据组织与标注格式,并在此基础上进行数据增强、模型评估与算法优化实验。
1. 恶劣天气目标检测:这套 YOLO 标注数据能解决什么
做目标检测最怕的不是模型选型,而是训练数据跟部署环境严重脱节。晴天采集的数据在雪天、雾天、沙尘天里精度掉得厉害,本质原因是视觉退化让图像丢了大量可供模型学习的纹理和边缘信息。这套约 1000 张、按 YOLO 格式标注的恶劣天气道路数据集,正是冲着这个痛点来的。它覆盖了下雪、起雾、沙尘暴、雨雾混合等场景,标注类别包括 car、bus、person、truck 等 7 类,已按训练集和测试集做了划分,还附带了 show 可视化脚本和 classes 文件。适合两类人:一是做毕业设计或课程项目、需要一份真实恶劣天气数据来跑通 YOLO 流程的初学者,二是准备做天气鲁棒性改进、需要受控对比实验的进阶开发者——因为数据自带划分,改进前后的 mAP 对比可以直接在固定测试集上做。
2. 恶劣天气图像为什么难检测:先把退化模型拆开
2.1 雪、雾、沙尘对成像的三类物理干扰
恶劣天气检测难,根子在成像链路。雾天和沙尘天属于大气散射主导的退化,远景目标被介质散射和吸收后,对比度衰减,颜色向背景色偏移;雪天则叠加了雪花的局部遮挡和反光,属于空间域噪声叠加大气散射的混合退化;雨雾混合场景更接近实际,雨线造成局部纹理撕裂,雾又压低全局对比度。这三种退化在手写特征时代几乎是不可解的问题——因为传统特征设计者要手工建模每一种退化。深度学习模型理论上可以端到端学习,但训练数据里必须有足够丰富的退化样本,模型才能学到把「低对比度下的轮廓」和「被雪花遮挡的局部特征」拼起来的能力。
这也是为什么很多人在晴天数据上训练、直接上恶劣天气测试效果差:模型把高频边缘当成可靠特征,但雪天里雪花本身就有高频边缘,模型会把这些噪声边缘当成目标,误检率很高。所以数据的场景多样性直接决定模型上线的下限。
2.2 基于 YOLO 格式的标注验证与类别分布摸底
拿到数据后的第一件事不是训练,而是验证标注是否符合 YOLO 格式,并对类别分布做一个摸底。我一般先看标注文件的内容:
# 查看单个标注文件的结构,YOLO 格式每行: class_id x_center y_center width height cat train/images/snow_storm-347.jpg.txt # 统计全部标注的类别分布 awk '{print $1}' train/*.txt | sort | uniq -c | sort -nr第一行命令输出形如2 0.53125 0.47361 0.08203 0.15447的内容:第一列是类别 ID,后四列是归一化后的中心坐标和宽高。这里有一个容易忽略的细节:YOLO 格式的坐标全部是相对图片宽高的归一化值,数值范围在 0 到 1 之间,如果出现大于 1 的值或负数,标注一定有问题。第二行命令统计类别分布,可以用来看样本均衡性——如果 person 只有几十张而 car 有三百多张,训练时就要考虑引入类别权重或抽样策略。数据集的 7 个类别(car、bus、person、truck 等)具体以 classes 文件为准,不同项目来源可能对同一类用不同 ID,训练前先对照 classes 文件确认 ID 映射关系,否则会出现类别错位。
2.3 训练集测试集划分的合理性检查
SURE 数据预处理阶段除了标注格式,还要检查划分质量。数据集中已对训练集和测试集做了划分,我的做法是先验一下划分比例:
import os train_dir = 'train/images' val_dir = 'test/images' n_train = len(os.listdir(train_dir)) n_val = len(os.listdir(val_dir)) print(f'Train: {n_train}, Val: {n_val}, Val ratio: {n_val / (n_train + n_val) * 100:.1f}%')目标检测的验证集比例通常在 10% 到 30% 之间。如果验证集太小,测试 mAP 的方差会很大,同一个模型反复训练得到的结果可能差 2 到 3 个点,不利于对比实验;如果验证集占比太大,训练数据不够,模型欠拟合。建议按尺度分层检查验证集的场景分布——确认雪天、雾天、沙尘天在测试集中都占有合理比例,而不是全部集中在某一个天气类型。如果发现某一类天气只在训练集出现、测试集没有对应样本,那么后面对比实验里该类天气的性能就完全不可信。
3. 用 YOLOv5 跑通训练链路:从环境配置到可视化验证
3.1 数据组织与目录结构设置
YOLOv5 对数据目录结构有固定要求。拿到这份数据后,按下列方式组织目录:
project/ ├── data.yaml # 手动创建 ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 └── labels/ ├── train/ # 训练标注 └── val/ # 验证标注数据集的原始目录如果与你自己的项目结构不同,需要做一次目录映射,用软链接或直接移动都行。同时在工程根目录下创建 data.yaml 文件,内容如下:
train: images/train val: images/val nc: 7 names: ['car', 'bus', 'person', 'truck', 'motorcycle', 'bicycle', 'other']其中nc是类别总数,names列表的索引顺序必须与标注文件中的 class_id 一一对应。names 顺序非常重要——如果 classes 文件里 car 的 ID 是 0,而 data.yaml 里第 0 个名字写成了 bus,训练过程不会报错,但推理输出的类别标签就会全部错位。这种错误极其隐蔽,跑完训练才发现。也是因为提示里有公布的“class 文件”,要先拿它对齐 names。
3.2 YOLOv5s 配置与训练命令参数拆解
网络训练骨架我通常选 YOLOv5s——在 1000 张数据规模下,v5s 比 v5m、v5l 更不容易过拟合,训练速度也快得多。执行训练:
python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --workers 4 \ --device 0每个参数背后都有讲究。--img 640是输入分辨率,过低(例如 320)会丢失小目标的特征,过高(如 1280)则显存占用大、训练时间长。--batch 16在显存允许范围内稍大一些可以让 BatchNorm 统计更稳定,但数据只有 1000 张,batch 太大会提前过拟合。--epochs 100是通常的起点,配合早停策略观察验证集 loss。--workers 4是数据加载线程数,Windows 下这个值建议设小一些,否则会报 DataLoader worker 错误。
训练过程中重点看两组指标:训练集 loss 持续下降但验证集 loss 不降反升,说明已经过拟合;验证集 mAP 曲线震荡剧烈,多半是学习率过大或 batch 太小导致梯度不稳定。
3.3 用 show 脚本可视化:确认标注与预测的双重校验
数据集中带了 show 可视化脚本,这部分对于确认标注质量和预测效果都有价值:
import cv2 img = cv2.imread('train/images/snow_storm-347.jpg') with open('train/labels/snow_storm-347.txt', 'r') as f: lines = f.readlines() height, width = img.shape[:2] for line in lines: cls_id, x_c, y_c, w, h = map(float, line.strip().split()) x1 = int((x_c - w / 2) * width) y1 = int((y_c - h / 2) * height) x2 = int((x_c + w / 2) * width) y2 = int((y_c + h / 2) * height) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite('visualize_snow_storm-347.jpg', img)这个脚本的核心工作是把 YOLO 的归一化坐标转回像素坐标。x_c - w / 2计算左边界,x_c + w / 2计算右边界,像素坐标必须乘上图像的宽和高。有一个常见 bug:用 OpenCV 读入的图片是 BGR 三通道,保存时不会报错,但用某些工具打开会颜色偏蓝。更隐蔽的问题是:如果某张图片是灰度图,读入后只有 h,w 两个维度,切片shape[:2]没问题,但如果后续直接拉伸会出错。可视化时逐张检查雪花密集区域的框大小是否合理——如果远处行人框明显偏小且不完整,说明标注时小目标被漏检了。这个步骤虽然不产出模型,但它能一次性发现坐标归一化错乱、类别 ID 错位、目标边界框过大或过小这三类数据错误,这种错误在纯数字统计时看不出来。
4. 恶劣天气场景的模型训练:数据增强策略对比
4.1 通用增强与天气相关增强的适用边界
在恶劣天气数据上做增强,要区分两种策略。通用增强包括 Mosaic、随机翻转、色彩抖动、平移缩放,这些能提升模型对位置、尺度、光照变化的鲁棒性,但不能解决天气退化问题——因为模型并未见过新的退化模式。天气相关增强则针对退化的物理特性做模拟,比如给干净图像叠加随机透明度的白色噪声来模拟雪花、使用高斯模糊模拟雾气造成的边缘退化、降低对比度模拟沙尘暴的低可见度。两者的关系不是互斥而是互补:通用增强提升整体鲁棒性,天气增强补充退化分布。在这份数据中,因为本身已经包含真实恶劣天气样本,通用增强是基础配置,天气相关增强建议只在验证集表现不佳的特定天气类型上叠加,避免过度增强导致模型对清晰图像反而不敏感。
4.2 增强参数配置在 YOLOv5 中的落地
YOLOv5 的增强参数集成在训练配置中,不必额外写脚本:
python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --hsv_h 0.015 \ --hsv_s 0.7 \ --hsv_v 0.4 \ --deg 30 \ --fliplr 0.5这里的--hsv_h 0.015是色调扰动幅度,--hsv_s 0.7和--hsv_v 0.4分别控制饱和度与亮度的随机变化范围。数值不宜再加大,否则颜色失真会让模型学到错误的颜色关联(比如把红色车和尾灯绑定得过死)。--deg 30是随机旋转角度上限,旋转超过 30 度会让目标边界框的拟合变得困难,因为横竖框无法准确覆盖斜向目标且旋转后周围补边的像素会干扰特征提取。--fliplr 0.5表示 50% 概率水平翻转,这个增强对车辆、行人这类左右对称的目标非常友好,但要注意,如果数据集里的公交车上印有文字(等下章说),翻转会让文字反向,模型可能学到文字朝向这一无关特征。
4.3 增强前后效果对比的实验设计
做对比实验时,最稳妥的做法是保持其他变量完全一致。具体操作分三步:先用默认增强跑一遍作为基线,记录验证集的 mAP@0.5 和 mAP@0.5:0.95;再用加上天气增强的配置跑第二遍;最后把训练日志里的结果对比输出:
# 第一组 cp data.yaml data_baseline.yaml python train.py --data data_baseline.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --name exp_baseline # 第二组(只改动色域扰动,不使用翻转之外的几何增强) python train.py --data data_baseline.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --hsv_v 0.3 --hsv_s 0.5 --name exp_weather对比时重点关注一个细节:不要只看最终 mAP,要看按天气类型拆分的类别精度。通用增强很可能提升 car 这种样本量大的类别,但 person 是稀疏小目标、又容易被雪遮挡,增强如果不够精细,person 的 AP 可能不升反降。这类拆分分析能指导后续的改进方向:如果 person 提不上去,下一步考虑在数据层面补小目标样本,而不是继续盲目堆增强参数。
5. 资源落地时的常见问题与排查清单
5.1 目录结构不匹配导致加载失败:三个检查点
现象:训练启动后报错AssertionError: train: No images in ...,或者图像数量为 0。 原因:YOLOv5 的 data.yaml 中路径与实际情况不一致,最常见的是相对路径基准不对。YOLOv5 的默认工作目录是工程根目录,不是 data.yaml 所在目录。 解决:首先确认 data.yaml 里的 train/val 路径是从工程根目录出发的相对路径,例如train: images/train。其次检查 labels 目录和 images 目录是否在同一个父目录下。第三,用find train -name "*.txt" | wc -l统计标注文件数量,与图像数量比对——标注缺失的图片会被静默跳过,而不是报错,这会导致实际参与训练的图片远小于预期。
另外还要检查 txt 标注文件名与图片名是否完全一致。YOLO 的约定是图片名去掉后缀后加.txt,大小写必须严格一致,尤其注意 Windows 下文件系统大小写不敏感、Linux 下敏感,同一个项目在 Windows 上能跑、传到 Linux 服务器上却报找不到标注,这就是最典型的翻车场景。解决方式是提交前执行一段统一的文件名核对脚本,将每张图片名与对应 txt 名做集合减法。
5.2 data.yaml 中类别 ID 与 classes 文件不一致
现象:模型训练正常,但推理时预测框对应的类别名称完全错乱,比如把 bus 显示为 truck。 原因:标注文件里的数值类别 ID 与 data.yaml 的 names 列表顺序不一致。有的数据集类别按拼音排序、有的按出现频率排序,如果 labels 里的 0 代表 car,而 data.yaml 中 names[0] 是 bus,模型输出的置信度映射就全局错位。 解决:用 classes 文件做一次核对,重点确认 ID 与类别名的唯一对应,而不是只看类别名集合。检查方式是对每个标注文件内容做唯一值统计:
import os unique_classes = set() for file in os.listdir('train/labels'): with open(os.path.join('train/labels', file), 'r') as f: for line in f: unique_classes.add(line.strip().split()[0]) print(sorted(unique_classes, key=lambda x: int(x)))输出的类别 ID 集合应当是从 0 到 6 的连续整数且无缺失。如果输出为 0, 1, 4(缺少 2、3),要么是数据采集不全,要么是标注文件在转换时 ID 映射出了问题。我见过一个案例——某个 7 类数据集转换脚本硬编码了 0-6 的旧类别映射,但原始标注中有两个类别是从 1 开始的,转换后类别整体偏了两位,模型训练了十几个 epoch 后才发现方向错了,只能从头再来。
5.3 可视化颜色偏蓝与图片解码异常
现象:show 脚本可视化的图片颜色发蓝,或者某些图片无法正常解码。 原因:前者是 OpenCV 默认以 BGR 排列读入图片,imwrite 输出时颜色通道错位导致;后者是图片本身损坏或数据下载转移过程中字节丢失,一些网络图片以 RGBA 格式存储,通道数不一致也会让数组处理和训练环节出现异常。 解决:可视化时先做通道转换:
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)对于无法解码的图片,在预处理阶段就统一过滤掉:
import cv2 valid = [] for img_file in os.listdir('train/images'): img = cv2.imread(img_file) if img is not None: valid.append(img_file) else: print(f'Corrupted: {img_file}')这条检查不是可选项——一张损坏的图片可能导致训练集 loss 出现一个尖峰,而这个尖峰在 100 个 epoch 的训练里不太容易被注意到,但它会干扰学习率的动态调整策略,让训练过程被拖慢。
5.4 在不同操作系统间转移数据时的路径与编码坑
现象:数据包在 Windows 解压后拷贝到 Linux 服务器,训练报错 FileNotFoundError 或察觉到中文路径乱码。 原因:Windows 和 Linux 路径分隔符不同(反斜杠 vs 斜杠),压缩包内若存在中文路径,在两个系统默认编码不一致的情况下(GBK vs UTF-8)会出现乱码。 解决:统一用英文目录结构管理数据,压缩包解压后先检查是否存在中文或空格路径。如果原始包里是中文命名,写一段批量重命名脚本,改成拼音或英文编号。另外,用 zip 格式传输时明确指定 UTF-8 编码参数,避免解压后文件名乱码。实践中我一般直接用 zsh 的unzip -O gbk或 Python 脚本处理跨系统解压乱码问题。
6. 训练小数据集的三个进阶技巧:写给想提升精度的你
6.1 从检测器到数据层面:先检验模型在哪类天气上最弱
大多数人在恶劣天气数据集上训练出模型后就直接交差了,但如果你想做改进,第一步应该是先把验证集的推理结果按天气类型分组统计。按照数据文件名里的前缀(snow_storm、mist、sand_storm、foggy)把 predict 结果分成几组,逐一统计每组的 mAP。如果 sand_storm 组明显低于 foggy 组,说明模型对沙尘暴中的低对比度目标没有泛化能力。有了这个定位,再谈改进方向,才不会用错力气。
6.2 针对性引入伪标签,别把所有数据塞给模型
另一条经验是针对弱项天气类型做伪标签扩充。做法是先用当前模型对没有标注的数据做推理,把置信度高于 0.9 的预测框当成伪标签直接加入训练集。这个过程要控制两点:伪标签比例不超过总数据的三分之一,否则噪声累积会拖垮整体精度;每轮只加入最高置信度的那部分,逐步降低阈值,别一步到位。我在实践中的阈值策略是:第一轮 0.95,第二轮 0.9,第三轮 0.85。置信度阈值低于 0.85 时,误检的边界框会开始明显增多——尤其是 snow 场景中雪花被识别为远距离行人的情况,伪标签噪声会反馈式放大误检。核心机制并不复杂:模型对容易样本的预测置信度高,从难到易逐步纳入,会让模型学会越来越难的目标而不至于被误导。
6.3 用单类注意力缓解小目标劣势
1000 张数据里 person 目标数量通常远低于 car,而恶劣天气下个人目标的边界信息又更弱。一个能较快见效的手段是把 YOLOv5 的类别损失改成按类别频率加权的版本,针对数据量少的类别用更高的 loss 权重。具体操作是在损失函数中增加类别权重向量:
cls_freq = [0.40, 0.12, 0.25, 0.10, 0.06, 0.04, 0.03] cls_weight = [1.0 / max(f, 0.05) for f in cls_freq]加权的方式有多种,简单的是在 loss 计算时乘上这个权重向量,复杂的是用 Focal Loss 的变体调整难易样本的平衡。这类推荐不要直接改 loss——当数据集只有 1000 张时,梯度噪声本身就大,再调整 loss 权重容易造成训练不稳定。最稳妥的方案是:训练一个不加权重的基础模型,找到模型最弱的类别,再针对该类别从数据层面补充样本,而不是一上来就改损失函数。
这个方法本质上是把小数据集单类别劣势问题拆成「数据层面加强 — 模型层面感知 — 推理层面验证」的闭环。我自己的教训是:最初做天气鲁棒性改进时,我把所有增强参数调到满,结果模型在测试集上反而比基线低了 2 个点。后续改为先按天气类别分组分析、再针对弱项类别做伪标签扩充,才真正把沙尘暴场景的 AP 从 61% 提到 74%。从那以后我每次在恶劣天气数据上训练,都强制做一遍分组统计和置信度阈值扫描,先把边界探明再动手调参,这门折腾少走了一半。希望帮到你。
本文还有配套的精品资源,点击获取