简介:在雾天、雨雪、沙尘等多种恶劣天气下,自动驾驶与智能交通系统对行人、车辆的可靠检测面临较大挑战。这份数据集专门收集并标注了约一千张真实道路图像,涵盖小汽车、公交车、行人、卡车等七个类别,采用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;第二行是bus,id为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_img+val_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.pt,YOLOv5会自动读取上次的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.2,hsv_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张,确认无误后才开始训练和调参。这个习惯帮我避免了好多次训练完发现数据有问题的翻车局面。希望帮到你。
本文还有配套的精品资源,点击获取