ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLOv9四分类路面状态数据集实战与避坑指南

YOLOv9四分类路面状态数据集实战与避坑指南

简介:面向智能交通、自动驾驶及道路养护场景的雨雪天气路面状况数据集,覆盖结冰路面、雪地、下雨湿滑与干燥路面四类典型路况,可用于训练和评估YOLOv9等目标检测模型,适合算法工程师、科研人员以及计算机视觉学习者使用。整体zip压缩包共包含1293个文件,其中646张jpg原始图片与646个对应txt标注文件一一匹配,另附1个yaml配置文件,包体约26.89MB,结构简洁,便于直接接入训练流程。目前已有860人学习下载,适合需要真实恶劣天气样本的模型迭代与验证。图片均采用原始实拍图像,未做过度增强,能较好保留雨雪天气下的真实纹理与光照变化;txt标注文件与yaml配置可直接配合YOLOv9使用,方便快速复现训练流程,也能帮助开发者检验模型在结冰、湿滑等复杂路况下的鲁棒性。

1. 雨雪天气路面状态识别:这份 yolov9 标注好的四分类路面数据集,下载下来就能训练

做辅助驾驶或者道路巡检的朋友应该都有体会:路面状态识别跟车辆检测完全是两码事。车是刚体,轮廓清晰,coco 预训练权重直接迁移就能用;但路面是纹理,结冰、积雪、湿滑、干燥在视觉上往往是渐变过渡的,边界模糊到人眼都要仔细分辨,更别说模型了。我之前在项目里试过拿通用目标检测数据集硬训,结果晴天干燥路面当成了湿滑,结冰路面直接漏检,后来换了专门的路面数据集才把指标拉起来。

这份数据集就是干这个用的。图片全部是原始拍摄图,没有做过压缩和增强处理,类别覆盖结冰路面、雪地、下雨湿滑、干燥路面四个状态,而且已经用 yolov9 的标注格式做好了标签,解压之后不用自己画框,直接按 YOLO 格式训练即可。适合正在做辅助驾驶路面感知、道路结冰预警、机场跑道巡检这一类场景的开发者。下载前先搞清楚里面的目录结构、标注格式和几处容易踩的坑,能帮你省掉至少半天调试时间。

2. 数据集结构与标注格式:Roboflow 导出的 YOLO 标签是怎么组织的

2.1 文件命名与来源判断

拿到压缩包之后,先别急着解压训练,花两分钟看一眼文件名的规律。压缩包里的图片长这样:

ice-road31_jpg.rf.647734b2a0c45eadc7006abc92df2e1c.jpg ice-road77_jpg.rf.112ffc45d7f26c53ada13b1992045dfe.jpg ice-road-6_jpg.rf.6c534e7f6702aa8dbce5d0d5fa2cc366.jpg

文件名里面那个rf.是 Roboflow 的标记,后面跟的一串十六进制字符串是图片的唯一哈希值。这说明数据集是从 Roboflow 平台导出的。Roboflow 导出 YOLO 格式时默认会生成train、valid、test三个目录,每个目录下各有一个images和labels子目录,外加一份data.yaml配置文件。

理论上原始图片的分辨率没有被压缩过,但 Roboflow 在处理时会按你导出时选择的参数做一次统一尺寸调整。如果导出时选的是Resize to 640x640,那图片大概率被拉伸过,不是严格意义的原始分辨率。我的习惯是解压后先identify一下图片尺寸,确认是否被统一缩放过,这关系到后续训练时imgsz参数的设置。

2.2 四个类别与标签文件内容

解压后打开任意一个labels目录下的 txt 文件,你会看到每行四个数字加一个类别编号的结构:

2 0.485937 0.631944 0.768750 0.302083

这行数据的含义是:类别编号 2,目标中心点的 x 坐标占图片宽度的 48.59%,y 坐标占图片高度的 63.19%,目标框宽度占图片宽度的 76.88%,高度占图片高度的 30.21%。所有坐标值都是归一化后的 0 到 1 之间的小数,训练的时候模型直接读这种格式,不需要额外转换。

关于类别编号,这份数据集在 Roboflow 上的原始类别顺序我没法从文件名里看出来,但按照常见的导出习惯,比较可能是0: ice、1: snow、2: wet、3: dry。这个顺序直接决定了data.yaml里的类别映射表,训练前必须打开确认,否则会出现训练时显示class 0,推理出来却对不上实际路面的问题。

2.3 data.yaml 的配置核对

train: ../train/images val: ../valid/images test: ../test/images nc: 4 names: ['ice', 'snow', 'wet', 'dry']

这份 yaml 是训练时的核心配置文件。train、val、test三个路径默认是相对路径,如果你的项目结构跟 Roboflow 导出的不一致,需要手动改成绝对路径或调整相对层级。nc是类别数,这里是 4,names是类别名称列表,顺序必须与标签文件里的编号严格对应。

我一般会额外检查一下test目录是否存在且不为空。Roboflow 默认勾选数据划分,常见比例是 70% 训练、20% 验证、10% 测试。如果你的数据集里 test 目录是空的,训练前需要手动从 train 里抽一部分出来补充,否则评估阶段没有独立测试集,指标跑得再高也心虚。

注意:names的顺序一旦定下来,后面所有训练和推理都要沿用同一套顺序。中途改顺序等于重新训练,这个坑我踩过一次,后面第 5 章详细讲。

3. 训练前处理:数据划分、标签校验与类别映射三个必经步骤

3.1 校验图片与标签是否一一对应

从 Roboflow 导出的数据集偶尔会出现图片多一张、标签少一个的情况,尤其是手工整理过的数据集。我的做法是写个小脚本,把 images 目录和 labels 目录的基名取出来做一次差集比对:

import os from pathlib import Path def check_annotation_pairs(img_dir, lbl_dir): img_dir = Path(img_dir) lbl_dir = Path(lbl_dir) img_stems = {p.stem for p in img_dir.iterdir() if p.suffix.lower() in ('.jpg', '.jpeg', '.png')} lbl_stems = {p.stem for p in lbl_dir.iterdir() if p.suffix == '.txt'} missing_lbl = img_stems - lbl_stems missing_img = lbl_stems - img_stems if missing_lbl: print(f'有 {len(missing_lbl)} 张图片缺少标签: {list(missing_lbl)[:5]}') if missing_img: print(f'有 {len(missing_img)} 个标签缺少图片: {list(missing_img)[:5]}') if not missing_lbl and not missing_img: print(f'图片和标签完全对应,共 {len(img_stems)} 对') return missing_lbl, missing_img check_annotation_pairs('ice-road-1/train/images', 'ice-road-1/train/labels')

这套脚本的核心逻辑是用文件基名做集合差集。Roboflow 导出时图片和标签文件是同名的,图片是.jpg、.txt扩展名区分,.stem会把扩展名去掉,剩下的哈希字符串一一对应。跑完如果提示缺标签,先看是缺了哪几张,确认不是空标签文件漏了写出。缺标签的图片在训练时会被当作背景图处理,但这张图本身明明是带目标的,模型就会学到「有结冰也要输出 no object」,属于欠采样噪声,建议直接删掉这类图片。

如果缺得比较多,我一般会重新看一遍原始数据集来源,确认是导出时丢的,还是原始数据本身就缺。不要抱着「反正缺一两个无所谓」的心态,训练集里的负样本噪声会直接影响 precision。

3.2 统计类别分布,提前预判样本不平衡

路面数据的真实场景里,干燥路面永远是最多的,结冰路面和雪地的样本会比较少。类别不平衡在目标检测里比分类任务影响更明显——模型倾向于把不确定的目标预测成高频类别。先统计一下每个类别的目标框数量:

# 统计训练集所有标签文件中每个类别的出现次数 for f in ice-road-1/train/labels/*.txt; do awk '{print $1}' "$f" done | sort | uniq -c | sort -nr

把train/labels换成你自己的实际路径。awk '{print $1}'是提取每行第一个字段,也就是类别编号,sort | uniq -c按类别计数。跑完之后看四个数字的差距,我见过比较悬殊的情况是 dry 有几千个框,ice 只有一两百个,这种分布直接训练的话,ice 类别的 recall 会非常难看。

如果发现某一类样本明显偏少,建议优先考虑两类操作:一是检查是不是原始标签漏标了一部分,通过查看对应类别的图片和标签叠加图来人工抽样确认;二是如果标签本身没问题,那么在训练时把这个类别的 loss 权重调高,或者对这类图片做离线增强。不要一上来就做在线 mosaic 增强,路面纹理这种大尺寸目标,mosaic 拼图反而会破坏连续性。

3.3 检查标签坐标是否越界和类别编号越界

YOLO 格式里有个隐形约束:归一化坐标理论上必须在 0 到 1 之间,但 Roboflow 导出偶尔会出现坐标值为 1.05 这种越界情况,训练时会导致计算 anchor box 跟 ground truth 的 IoU 出问题。另一个常见问题是类别编号超过nc - 1,比如数据里混入了第 5 类标签,但data.yaml只定义了 4 类,训练直接报错。

# 检查是否存在坐标越界的标签行 awk '{if ($2<0 || $2>1 || $3<0 || $3>1 || $4<0 || $4>1 || $5<0 || $5>1) print FILENAME, $0}' \ ice-road-1/train/labels/*.txt

正常输出为空。如果有输出,说明该文件里有越界标签,要么手动修正,要么删掉这一行。越界框对训练的影响不是报错那么简单——它会让模型在对应位置学习到错误的边界框形状,因为很多实现会在 loss 计算时对越界框做裁剪,结果 ground truth 发生了变化,模型学到的跟标注者对不上。

检查完这三项之后,数据集本身基本就干净了。如果是新手,可能到这里就想直接开训,但我的经验是再花五分钟看一眼标签在图片上的叠加效果,确认框确实框在结冰区域而不是旁边。

4. 用 yolov9 训练路面状态模型:命令、参数与训练产物说明

4.1 环境准备与目录规划

先确认环境里已经装好了 ultralytics 包,以及对应的依赖。YOLOv9 的基础训练代码随 ultralytics 一起分发,不需要单独拉 yolov9 的官方仓库。安装方式这里不展开,装完之后yolo命令在命令行里可用即可。

建议目录结构保持 Roboflow 导出的样子,别乱改:

ice-road-1/ ├── train/ │ ├── images/ │ └── labels/ ├── valid/ │ ├── images/ │ └── labels/ ├── test/ │ ├── images/ │ └── labels/ └── data.yaml

把整个ice-road-1目录放在项目根目录下,不要直接放在系统盘或中文路径里。训练框架对中文路径的支持虽然不是完全没有,但 Windows 下遇到编码问题会非常难排查。我之前在 Windows 上部署,路径带了个「路面」两个字,训练到一半突然报FileNotFoundError,查了半天是编码问题,最后把所有目录全改成拼音才解决。

4.2 训练命令与参数说明

准备好之后,用下面的命令启动训练:

yolo detect train \ data=ice-road-1/data.yaml \ model=yolov9c.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=run \ name=ice_road_exp1 \ patience=15

这段命令的主干是yolo detect train,后面跟的是key=value形式的参数。逐项解释一下关键参数:

  • data指向之前核对过的data.yaml文件路径
  • model=yolov9c.pt是加载预训练权重,COCO 上训过的权重对路面纹理的初始特征提取有帮助,比冷启动效果好很多
  • epochs=100是最大训练轮数,配合patience=15使用,15 轮没有验证集提升就提前停止,避免过拟合
  • imgsz=640是训练时缩放的输入尺寸。如果你的原始图片是 1920x1080,直接缩到 640 会丢失大量路面纹理细节。我的习惯是先用 640 跑一轮,看训练集 loss 收敛情况,如果发现结冰类别的 recall 上不去,再提高到 1024 或 1280
  • batch=16是显存允许范围内的值,具体多少受显卡显存限制,用 8G 显存跑 640 分辨率的 yolov9c,batch 设 8 比较稳
  • device=0是使用单张 GPU,CPU 训练的话速度会慢得让人怀疑人生,不建议尝试
  • project=run name=ice_road_exp1是训练日志和权重文件的保存路径

如果训练中途显存溢出,优先调小batch而不是调小imgsz。训练时 imgsz 影响的是模型对目标尺度的适应范围,调小了会导致推理时对小于某个尺度的目标表现变差。

4.3 训练产物说明

训练完成后,run/ice_road_exp1/weights/目录下会生成两个权重文件:

文件用途说明
best.pt验证集指标最优的权重用于后续推理和部署,aps 不需要再用
last.pt最后一轮的权重当你想继续训练时用它接着跑

另外还有一个results.csv,里面是每一轮的 loss、precision、recall、mAP 数据。训练完成后第一件事是打开这个 CSV 文件,看最后几行的趋势。重点关注train/box_loss和val/box_loss,如果训练损失持续下降但验证损失在第 50 轮开始回升,说明模型已经过拟合,此时应该把epochs调小,或者加大数据增强的强度。

4.4 训练时显存不够的替代方案

如果你手里的显卡显存只有 4G 甚至更小,直接跑 yolov9c 大概率会在第一个 epoch 中途挂掉。两个办法:一是换小模型yolov9s.pt,参数量更小,4G 显存勉强能跑 640 分辨率 batch=8;二是把imgsz降到 416,loss 特征图变小,显存占用明显下降。

但要注意,yolov9s 对结冰这种弱纹理特征的识别能力比 yolov9c 弱不少。如果你主要识别的是结冰路面,尽量保住 yolov9c 的分辨率,实在不行用 yolov9s 搭配更高分辨率跑。

注意:不要用model=yolov9.yaml这种从零训练的配置。从预训练权重起步收敛速度快,而且能明显减少标注噪声的影响。除非你是想验证模型结构,否则默认加载 c 系列预训练权重。

5. 路面数据集避坑指南:五个训练阶段容易翻车的细节

5.1 训练时报错「class number out of range」

现象:训练启动没几分钟就报错,提示标签文件里出现了超出nc范围的类别编号。

原因:Roboflow 导出后,有些标签文件的行首类别编号对不上data.yaml里的names顺序。比如names列表只有 4 个类别,但某个 txt 文件里出现了数值4(表示第 5 类)。

解决:用第 3 章的 awk 命令先全局扫描一遍,把类别编号大于等于nc的行找出来。如果只是个别文件,直接删除该行;如果很多文件都有,说明数据集本身带有多余类别,需要重新映射类别编号。可以写个简单脚本把类别编号逐个减 1,比如原本是1 2 3 4,缩成0 1 2 3。

5.2 names 顺序跟实际路面类别对不上

现象:训练跑完 100 轮,推理时发现模型把干燥路面预测成冰面,但验证集 mAP 却显示 0.85 以上。看起来指标很好,实际用起来完全不对。

原因:Roboflow 导出的names列表顺序跟你自己数据标注时的类别顺序不一致。比如标签文件里类别0是 dry,但data.yaml里names: ['ice', 'snow', 'wet', 'dry']把0对应成了ice,模型学到的其实是「类别 0 = 干燥」,推理时按 ice 输出,张冠李戴。

解决:训练前花一分钟做交叉验证——打开一张已知是结冰路面的图片的标签文件,确认行首类别编号,再打开data.yaml确认对应名称。我在每次训练前都会跑一个快速检查脚本,随机挑三张带标签的图片,把类别编号和图片内容人工核对一遍。这个习惯从那以后没变过。

5.3 结冰路面小目标漏检严重

现象:晴天干燥路面的高置信度预测很多,结冰路面却频繁漏检,recall 掉到 0.3 以下。

原因:结冰路面在视觉上接近于湿滑和干燥,纹理特征不明显。如果数据集里结冰样本占比本来就低,模型在 loss 优化时会把精力集中在数量多的类别上。另外,如果图片里结冰只出现在局部区域,目标框较小,模型对小目标的检测能力本来就更弱。

解决:先做类别统计确认样本比例。如果 ice 类别占比低于 10%,建议把训练集里 ice 类别的图片复制一份到 augumented 目录,配合离线增强后用额外的数据混合训练。另一种做法是在yolo detect train时把hsv_h、hsv_s增强参数调低,因为路面结冰的颜色偏灰白,增强过头了颜色信息丢失反而更难学。

5.4 训练时 loss 正常但验证集 mAP 为 0

现象:训练 loss 稳步下降,但每轮验证集 mAP 始终是 0 或者接近 0,没有任何提升。

原因:最常见的是验证集标签路径错误。Roboflow 导出的data.yaml里验证集路径是相对路径,如果你的项目结构跟导出时不一致,训练代码找不到验证集图片,验证过程就变成空跑。

解决:用绝对路径替换data.yaml里的相对路径,重新启动训练。还有一个可能是验证集和训练集完全重叠——如果你手动划分数据时重复复制了同一批图片到两个目录,模型在验证时看到的都是训练过的样本,mAP 虚高但实际没有任何参考价值。

5.5 推理时输出类别坐标正确但置信度极低

现象:训练完成后推理,框的位置和大小都差不多,但置信度只有 0.2~0.3,阈值设低了就输出一堆低质量预测。

原因:训练时imgsz=640,推理时如果用了imgsz=1280,模型在特征图尺度上看到的感受野跟训练时不一致,导致预测置信度整体被压低了。

解决:推理时把imgsz设置成跟训练一致。如果你确实需要更高精度的推理,那训练时也应该把imgsz调上去,而不是只在推理时换尺寸。另外,训练时如果开了mosaic=1.0而推理时关掉,也可能导致分布偏移,一般建议每组实验固定增强参数。

6. 训练后的验证手段:mAP 不如混淆矩阵直观,真实路测才是最后一道关

6.1 分析混淆矩阵定位类别混淆

训练完成后,run/ice_road_exp1/目录下会生成confusion_matrix.png和confusion_matrix_normalized.png。大部分人只看 mAP 就结束了,但路面状态识别最重要的其实是混淆矩阵——因为结冰和湿滑、结冰和干燥、雪地和干燥在视觉上的混淆程度差别很大。

打开归一化后的混淆矩阵图,重点看对角线以外的数值。如果 ice 那一列里有大量样本被预测成 wet,说明模型对这两种路面状态的边界没学好。这时候要回去看训练集里对应的图片,确认标注本身是否混淆了,比如把水渍未干的路面标成了湿滑,实际应该是结冰状态。

6.2 用验证集跑推理看到框效果

命令行推理可以直接验证你的权重在真实图片上的表现:

yolo detect predict \ model=run/ice_road_exp1/weights/best.pt \ source=ice-road-1/test/images \ imgsz=640 \ conf=0.25 \ save=True \ project=run \ name=pred_test

跑完之后,run/pred_test/里会生成标注好的图片。我一般会重点看三类图片:包含结冰路面的图片、同时包含多个类别的复杂场景、光线较暗的雨天图片。结冰路面在弱光下跟干燥路面的灰度值非常接近,如果这一类图片的预测框对不上实际结冰区域,说明特征提取还不够。

6.3 导出 ONNX 后做一次实际路测

验证的最后一步是把模型导出成 ONNX 格式,脱离 PyTorch 环境做一次真实场景测试:

yolo export \ model=run/ice_road_exp1/weights/best.pt \ format=onnx \ imgsz=640 \ opset=12

导出后的best.onnx可以直接接入你的推理框架,用摄像头或者视频流做实时检测。我习惯的做法是录一段 5 分钟的真实道路视频,覆盖干燥、湿滑、结冰、雪地四种路况,然后用 ONNX 模型跑一遍,统计每一帧的检测结果。

这段过程中最容易暴露的问题是模型在运动模糊下的表现——静止图片上框得很准,但视频帧一旦出现运动模糊,结冰路面的置信度会明显下降。遇到这种情况,可以尝试把推理时的imgsz从 640 提到 960,但代价是帧率下降,需要根据你的设备性能做取舍。

从那以后,我每次训练完路面状态模型都强制自己走一遍「混淆矩阵 → 验证集推理 → ONNX 路测」三件套,而不是只看 mAP 数字。这套数据集的标签质量整体可以,配合正确的训练参数,mAP 应该能跑到 0.8 以上,但真正落地是否可用,还是得看你在真实路况下的表现。希望这个流程能帮你在路面识别项目上少走些弯路。

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

返回列表