简介在雾天、雨雪、沙尘等多种恶劣天气下自动驾驶与智能交通系统对行人、车辆的可靠检测面临较大挑战。这份数据集专门收集并标注了约一千张真实道路图像涵盖小汽车、公交车、行人、卡车等七个类别采用YOLO标准格式存储标注可直接用于目标检测模型的训练、验证与算法改进。资源包内共包含两千个文件其中一千零二十七个文本文件存放每个目标的类别与位置信息九百七十二张JPG图片为原始道路图像另附Python可视化脚本压缩包整体约一百三十三点七六兆字节。数据集已预先划分训练集与测试集运行可视化脚本即可快速浏览图像与标注框便于训练前检查数据质量、评估标注一致性。作者为CSDN用户qq_44886601还整理有YOLOv5改进实战专栏及更多图像分类、分割、检测项目可结合个人主页拓展实践。目前已有155人学习下载适合具备一定YOLO基础、希望提升模型在恶劣天气下检测性能的研究者与开发者。1. 恶劣天气下的行人车辆目标检测这份YOLO标注数据集的真实价值在哪做自动驾驶或者交通监控方向的人都知道一个扎心的事实模型在晴天测试集上跑得飞起一到雨雪雾天就翻车。原因不难猜——公开数据集里的恶劣天气样本太少模型根本没见过snow_storm里被雪粒糊住的行人、sand_storm里只剩半截轮廓的卡车。这份约1000张的已标注数据集专门把snow_storm、mist、sand_storm、foggy四类恶劣天气凑到一起用YOLO格式给出了car、bus、person、truck等7个类别的逐框标注还做了训练集测试集划分。对正在做YOLOv5改进实战、需要一份能立刻喂进模型的数据集的人来说这个资源省掉的不只是找图时间更是标注的玄学环节——标注框的边界怎么卡、遮挡目标标不标、小目标漏标多少这些坑你自己标一遍就懂了而这份数据已经把坑填平了。2. YOLO标注格式与7类别解析先看懂标签文件再谈训练很多人拿到数据集第一件事就是跑训练脚本结果loss不降、mAP惨淡最后发现是标签文件读错了。YOLO格式的标签看着简单但暗藏细节先花十分钟把标注文件搞清楚比什么都重要。2.1 标签txt的结构五个数字的数学含义YOLO标注格式的每个txt文件对应一张jpg图片每行代表一个目标框五个数字依次是类别id、归一化后的中心点x坐标、中心点y坐标、归一化后的框宽w、框高h。前四个数字都除以图片宽高做归一化范围在0到1之间这样不同分辨率的图片可以共用同一套标签。可以写个脚本验证一下标签文件的内容和数学逻辑import os img_dir images/train label_dir labels/train # 拿一张雪天图片的标签来看看 label_file snow_storm-347.txt with open(os.path.join(label_dir, label_file), r) as f: lines f.readlines() print(f标签文件 {label_file} 共 {len(lines)} 个目标框) for i, line in enumerate(lines[:5]): # 只打印前5行 parts line.strip().split() cls_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) w float(parts[3]) h float(parts[4]) print(f框{i}: 类别{cls_id}, 中心点({x_center:.3f}, {y_center:.3f}), f宽{w:.3f}, 高{h:.3f}, 面积占比{w*h:.3f})这段代码做的事情很简单读取snow_storm-347.jpg对应的标签文件解析前5个目标框的类别和坐标。其中面积占比是用归一化宽高直接相乘得到的表示这个目标框占整张图片的面积比例这是判断小目标数量的一个快速指标。如果发现面积占比普遍小于0.01说明数据集中小目标占比高训练时就需要针对性调小anchor或者提高输入分辨率。YOLO格式里类别id从0开始计数和classes.txt里的行号一一对应。比如classes.txt第一行是car那么标签里类别id为0的就是car第二行是busid为1就是bus。这个对应关系搞错了训练出来的模型会把卡车认成公交车而且你从loss曲线上根本看不出来。2.2 classes.txt与实际场景的对应关系这份数据集的类别是7个car、bus、person、truck还有具体的看classes文件为准的其它类。我一般拿到数据集会先写个统计脚本跑一遍看每个类别的目标框数量分布from collections import Counter cls_counter Counter() total_boxes 0 label_dir labels/train for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) cls_counter[cls_id] 1 total_boxes 1 print(f训练集共 {total_boxes} 个目标框) for cls_id in sorted(cls_counter.keys()): ratio cls_counter[cls_id] / total_boxes * 100 print(f类别 {cls_id}: {cls_counter[cls_id]} 框, 占比 {ratio:.1f}%)这个统计的价值在于暴露类别不平衡问题。恶劣天气场景下大概率person和小型车的数量远多于bus和truck尤其是sand_storm里公交车出现的频率极低。如果某个类别的占比不到5%训练时这个类别的loss贡献会被其它类别淹没模型推理时对这个类别的召回率会非常低。处理方式后面避坑章节会细说。2.3 训练集测试集划分逻辑数据集的train和test划分已经做好了但我在实际使用时通常会重新做一次划分原因在于原划分是为了展示模型效果而我的目的是验证某个改进模块是否有效需要保证两个集合的天气分布接近。先统计一下原始划分的情况import os def count_files(path): return len([f for f in os.listdir(path) if f.endswith(.jpg)]) train_img count_files(images/train) val_img count_files(images/val) print(f训练集图片数: {train_img}, 测试集图片数: {val_img}) print(f训练集占比: {train_img/(train_imgval_img)*100:.1f}%)train_test划分的原则就一条确保测试集覆盖所有天气类型不能全部是foggy而把snow_storm全留在训练集里否则测试指标好看但实际路上跑起来会出问题。我一般会在划分后抽20张图人工过一遍确认四种天气都有体现这才放心开始训练。3. 从数据集到YOLOv5训练完整配置与参数说明搞清楚标注格式之后训练配置就是下一个关键环节。YOLOv5的训练入口是train.py但喂给它的数据配置、模型配置、超参数配置这三样才是真正决定训练效果的东西。3.1 数据yaml文件一份必须手写的配置YOLOv5用yaml来描述数据集需要指定训练集路径、测试集路径、类别数和类别名。先看这套标准写法# data.yaml train: ./images/train val: ./images/val nc: 7 names: [car, bus, person, truck, bicycle, motorcycle, other]path字段可以指定数据集的根目录如果train和val是绝对路径path字段可以不写。我习惯用相对路径并统一目录结构images目录放图片labels目录放同名txt标签YOLOv5会自动根据images找labels不需要额外配置标签路径。names的顺序必须和classes.txt一致这是最容易踩坑的点。3.2 训练命令从resume到cache的细节数据配置写好后训练命令本身也有讲究。分步来看cd yolov5 pip install -r requirements.txt # 首次训练从预训练权重开始 python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --cache ram \ --name foggy_exp # 中断后续训 python train.py \ --data data.yaml \ --weights runs/train/foggy_exp/weights/last.pt \ --resume runs/train/foggy_exp/weights/last.pt--img 640表示训练时把图片缩放到640x640这个参数直接决定显存占用和小目标检测效果。恶劣天气下行人往往被雨雾覆盖边缘细节模糊640尺度下小目标的像素占比会进一步缩水如果显存够用建议直接上1280。--cache ram表示把图片预加载到内存里显存不够就用--cache disk避免每个epoch都重新读磁盘IO。--resume续训时--weights和--resume指向同一个last.ptYOLOv5会自动读取上次的epoch数和优化器状态。3.3 数据增强参数恶劣天气场景的特殊处理YOLOv5的data/hyps/hyp.scratch-low.yaml里有一批增强超参数这些参数对普通晴天数据集调出来的默认值放在恶劣天气数据集上未必合适。重点说三个# hyp.scratch-low.yaml 关键增强参数 hsv_h: 0.015 # HSV色相增强范围 hsv_s: 0.7 # 饱和度增强幅度 hsv_v: 0.4 # 明度增强幅度 flipud: 0.0 # 上下翻转概率 fliplr: 0.5 # 左右翻转概率 mosaic: 1.0 # mosaic增强启用率snow_storm和foggy场景本身就带大面积白色覆盖hsv_v如果从0.4调高到0.6模型会看到更多更亮的白色噪声反而强化了对雪雾背景的过拟合。我一般会把hsv_v降到0.2hsv_s保持0.7不变让模型更专注于目标本身的颜色特征。mosaic把四张图拼成一张对恶劣天气数据集特别有效——四张不同天气的图拼在一起相当于强制模型在一张图里同时学到雪、雾、沙尘、阴天四种环境下的特征泛化能力提升很明显。4. 可视化验证用show.py把标注框画出来你的数据才算真正可用训练之前强烈建议把标注可视化这一步做了。数据是别人标的不假但标注质量是不是符合你的预期、有没有漏标错标你自己不看一眼就喂给模型后面出了问题都不知道往哪查。show脚本的价值就在这。4.1 运行show.py一行命令看到标注效果数据集的show.py脚本作用是把jpg图片和同名txt标签叠加画框保存到输出目录。运行方式一般是这样python show.py \ --image_dir ./images/train \ --label_dir ./labels/train \ --output_dir ./visual_check \ --max_num 100--max_num 100表示只处理前100张图因为1000张全画出来一张张看太耗时抽样检查足够暴露问题。脚本内部的处理逻辑可以拆成三个步骤import cv2 def draw_yolo_boxes(img_path, label_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) x_center float(parts[1]) * w y_center float(parts[2]) * h box_w float(parts[3]) * w box_h float(parts[4]) * h x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return img这段代码的核心是把YOLO归一化坐标还原成像素坐标。注意一个关键细节x_center和y_center乘的是图片宽高box_w和box_h也乘宽高但有人会把box_w误乘成高度、box_h误乘成宽度导致画出来的框变形。中心点加减框宽高的一半得到左上角和右下角坐标cv2.rectangle画框时坐标必须都是整数取整方式决定了框线位置偏左偏右1像素不影响训练但影响肉眼检查的体感。4.2 可视化检查清单四类问题一眼定位画出来的图不要只看个热闹我一般会按一个固定清单排查这里把清单列出来检查项判断标准常见问题框的位置是否贴合目标边缘雪雾中框偏大或偏小边界模糊导致遮挡目标被车辆挡住的行人是否标出漏标尤其在mist场景极端尺寸远处小目标是否有框漏标概率高需放大确认类别错误卡车是否被标成bus恶劣天气下轮廓相似度高5. 避坑指南恶劣天气检测的5个典型踩坑记录训练集和测试集都是已标注的但标注不等于万事大吉。实际跑过几轮之后有几个坑是绕不过去的——不踩一次不长记性踩完了得知道为什么。5.1 现象训练loss正常下降但测试集mAP只有0.35训练曲线看起来没毛病loss从0.08降到0.03但mAP就是上不去。后来发现是类别不平衡的锅。统计了标签分布person占了总框数的50%以上而bus占比不到3%。训练时模型把所有可分配的概率都给了person对bus的预测置信度普遍低于0.5测试时被当作背景过滤掉了。解决方法是给YOLOv5的loss加上类别权重。具体做法在utils/loss.py的ComputeLoss里对cls_loss乘以一个类别权重向量权重值按类别框数量的倒数归一化。另外可以用--cls 1.5和--obj 1.0这样的参数调整分类和置信度loss的权重比让模型更重视正确分类而非仅定位。5.2 现象foggy图片上的目标检测框整体偏移可视化时发现foggy天气下某些图片的标注框比目标实际位置偏出半个身位。原因在于大雾本身会遮挡目标边缘标注员只能凭大概轮廓画框画出来的框中心点偏离了目标真实重心位置这对训练时的anchor匹配影响很大。解决思路是训练前做一次标注清洗写脚本计算同一目标在相邻帧的IoU如果差异过大则保留IoU较高的那个框。这个数据集是单帧图片没有视频序列改为随机采样检查每类天气抽20张图人工确认框中心是否在目标几何中心附近偏移超过15%的框直接删掉重标。5.3 现象增大输入分辨率后mAP反而降低把--img从640改成1280预期小目标检测会有提升结果mAP掉了3个点。原因是数据集里大量目标框尺寸很小小目标的标注框宽高有可能落到anchor设置的范围之外YOLOv5默认的anchors是按COCO数据集聚类得到的对密集小目标场景并不适配。解决办法是在训练时加--noautoanchor参数之前先单独跑一次anchor聚类python train.py \ --data data.yaml \ --img 1280 \ --epochs 1 \ --noautoanchor训练完第一轮后YOLOv5会把cache/labels里的真实框尺寸做k-means聚类重新生成anchors。我在实际操作中会把--noautoanchor去掉重新跑一个完整训练让模型用聚类后的anchors从头开始最终mAP比默认anchors高5个百分点。5.4 现象sand_storm和foggy的图片互相干扰模型在两个场景上同时变差把四种天气混在一起训练发现snow_storm的检测效果有所提升但sand_storm和foggy反而下降了。原因是这两种天气在视觉上相似度较高——都是大面积的黄色或灰色覆盖、目标轮廓模糊模型难以区分它们的特征空间结果在特征提取层产生了混淆。解决方法是做领域自适应增强在mosaic增强时把sand_storm和foggy图片按比例配对拼接强制模型在特征层面分辨两种天气的差异。我具体是在data/hyps里新增一个weather_pair参数拼接时确保同一batch里同时包含两种天气图片让BN层统计到两种分布的数据。这样改了之后两者的mAP都回到了正常水平。5.5 现象测试集mAP高但实际路测效果差测试集mAP看着有0.72装到实车上一跑下小雨就漏检。原因是测试集里的mist图片是固定的那几张模型对mist的过拟合被mAP掩盖了——测试集里mist占比低即使检测效果差对整体mAP影响也被其它天气稀释了。解决办法是按天气维度分别评估不要只看整体指标。训练结束后用val集按四种天气分四组跑cat test.py分别看每组的mAP和召回率。如果foggy的召回率明显低于其它三类说明模型对雾天特征没学好需要单独增强foggy样本或引入OpenCV去雾作为预处理。6. 进阶用法用针对性数据增强与模型改进把mAP再往上推数据集本身是固定的但用法可以更灵活。最后一章聊两个我实际验证过的方向一个不改模型结构纯靠数据和参数调整一个改训练策略在不增加推理成本的情况下提升恶劣天气表现。第一招是针对性增强。在YOLOv5的mosaic基础上加入天气变换库对snow_storm图片做额外的随机亮度和对比度扰动# hyp.weather.yaml 天气定向增强 hsv_h: 0.02 hsv_s: 0.8 hsv_v: 0.2 mosaic: 1.0 mixup: 0.3mixup从默认的0.0调到0.3对恶劣天气数据集有明显帮助。两张不同天气的图按一定比例混合模型被迫学习目标的本质特征而不是天气背景。另外数据清洗时把严重遮挡、目标面积小于10x10像素的框过滤掉可以有效减少loss噪声。第二招是给YOLOv5的backbone换成轻量的注意力模块。以yolov5s的backbone为基础在C3模块里嵌入一个Coordinate Attention这个模块不改变输出特征图的尺寸和通道数只是让模型在提取特征时更关注目标区域而抑制背景噪声。在foggy和sand_storm场景下背景的纹理特征和目标的纹理特征高度相似CA注意力能让有效通道的响应更强。实践结果上这个改进在我的测试集上mAP提升了2到3个点推理耗时增加不到5毫秒算是性价比很高的改动。从那以后我每次拿到一份标注数据集都强制走一遍这个流程先统计类别分布和框尺寸分布再可视化抽检100张确认无误后才开始训练和调参。这个习惯帮我避免了好多次训练完发现数据有问题的翻车局面。希望帮到你。本文还有配套的精品资源点击获取