ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

900张标注路标数据集:YOLO与VOC格式详解及训练避坑指南

900张标注路标数据集:YOLO与VOC格式详解及训练避坑指南 简介YOLO目标检测路标数据集提供877张PNG路标图像及完整对应标注可用于交通标志识别、智能驾驶感知等场景的模型训练也可作为深度学习入门者的练习数据。面向计算机、电子信息工程、数学等专业学生满足课程设计、期末大作业与毕业设计对数据集和可运行代码的需求。资源共2635个文件包括877张PNG原图、877个XML标注文件VOC格式以及881个TXT标注文件YOLO格式压缩包约218.43MB图像与标注一一对应导入工程即可直接使用省去手工标注和格式转换环节。目前已有217人学习适合作为目标检测入门与课程实践的数据基础。资料内含参数化编程思路关键参数可灵活修改代码注释明细便于二次开发与算法对比作者具备十年算法仿真经验可帮助读者在YOLO路标检测实验中快速定位问题、少走弯路。1. 900张标注好的路标图片为什么我还要劝你先别急着训练做目标检测的人大概都经历过这种尴尬模型结构调得再花哨没有干净的数据集训练出来的权重就像在黑匣子里碰运气。路标检测算是目标检测里的入门级场景但自己从零标注一套数据集一轮下来少说两三天标完还要回头检查错框、漏框、类别不统一时间全耗在重复劳动上。这套带标注的路标数据集直接给了你900张已经处理好的图像同时提供YOLO和VOC两种格式的标注文件省掉的不只是标注工时更重要的是省掉了格式转换的坑。但我要先说一句泼冷水的话900张图像对于路标检测这种类别多、形态差异大的场景来说不是拿来就能训出高精度权重的量。它更适合用来跑通流程、验证网络结构、做预训练或者当迁移学习的起点。如果你期望的是直接拿这900张图训出一个能上车的模型那大概率会翻车。这篇文章我会从格式差异、目录怎么摆、训练命令怎么写、标注质量怎么查、常见坑怎么避这几个层面把它讲透。2. YOLO和VOC两种标注格式的底层差异不只是换了个后缀2.1 两种格式各自长什么样打开这个数据集的标注文件你会看到两种东西一类是.xml结尾的一类是.txt结尾的。.xml就是VOC格式每个图像对应一个xml文件里面用标签包着object每个object里面有name、bndbox坐标等关键信息。YOLO格式则是每张图像一个txt文件每行代表一个目标格式是“类别id x_center y_center width height”注意这里所有坐标都是相对于图像宽度和高度的归一化值不是像素坐标。VOC的bndbox里给的是xmin、ymin、xmax、ymax是像素绝对值。YOLO给的是归一化中心点和宽高。这就是两种格式最核心的差异一个记录绝对坐标一个记录相对坐标。为什么YOLO要归一化因为网络输入尺寸是固定的比如640x640如果坐标不归一化图像resize之后所有框就全错位了。归一化之后无论原图多大框都能跟着缩放同步映射。2.2 为什么数据集要同时给你两种格式很多人不理解说“给我一种格式不就行了我自己会转”。问题在于转换是个重复且容易出错的活。训练YOLO系列模型时代码里直接读的是txt但如果你要做数据清洗、可视化验证、或者用一些基于VOC的工具脚本xml反而更好用。两个格式都在你就可以跳过转换的环节直接用或者相互对照验证标注一致性。我一般会先做一步交叉验证随机抽几十张图把xml和txt分别解析出来画框对比一遍确认转换脚本当年没写错。这个数据集如果两种格式能对上说明发布者做转换时至少做了校验质量就有初步保障。当然不能排除个别文件有漏标或者错标后面我会讲怎么批量排查。2.3 双格式数据集的目录规划拿到rar解压后常见做法是整理成这样的目录结构dataset/ ├── images/ │ ├── train/ # 训练图片 │ ├── val/ # 验证图片 │ └── test/ # 测试图片 ├── labels/ │ ├── train/ # YOLO格式标注 │ ├── val/ │ └── test/ ├── annotations/ # VOC格式标注(xml) │ ├── train/ │ ├── val/ │ └── test/ └── classes.txt # 类别列表这段目录结构说明几个要点第一images和labels的train/val/test必须一一对应文件名一致否则训练时会报找不到图片或找不到标注的错误。第二建议把YOLO格式的txt统一放在labels目录VOC的xml单独放annotations目录不要混放否则后续用脚本清洗数据时分不清谁是谁。第三classes.txt的顺序决定了txt里类别id的含义YOLO训练时类别id是从0开始数的这个文件必须和标注里的id一一对应错一位就是全部错位。2.4 从VOC转YOLO的转换公式理解了下半辈子不慌虽然这个数据集两种格式都给了但你要处理其他数据集时转换是躲不掉的。VOC的xmin、ymin、xmax、ymax转成YOLO的cx、cy、w、h公式如下# VOC像素坐标转YOLO归一化坐标 import os # 假设一张宽度为img_w、高度为img_h的图像 # VOC框为 xmin, ymin, xmax, ymax img_w, img_h 1920, 1080 xmin, ymin, xmax, ymax 100, 80, 600, 400 # 计算中心点和宽高(像素) cx (xmin xmax) / 2.0 cy (ymin ymax) / 2.0 w xmax - xmin h ymax - ymin # 归一化 cx_norm cx / img_w cy_norm cy / img_h w_norm w / img_w h_norm h / img_h这段代码的逻辑就是把绝对坐标转成相对比例。注意这里有几个坑一是xmin、xmax必须保证xmax大于xmin否则框的宽度是负的训练时loss会变成NaN二是宽高归一化要除以原图的宽和高不是除以同一个数尤其图像不是正方形时两个分母不一样三是有些VOC标注里框的坐标越界了比如xmax写成了1925而图像宽度只有1920转出来归一化w会大于1这种情况下建议直接把坐标裁剪到图像范围内。3. 把900张数据接进YOLO训练流程配置与最小命令3.1 数据配置文件怎么写才不会被报错YOLO系列训练前都要写一个data.yaml这个文件是模型读数据的唯一入口写错一个路径就是白等一小时。常见的yaml长这样# data.yaml train: dataset/images/train val: dataset/images/val nc: 4 names: [speed_limit_40, stop, yield, crosswalk]train和val指向的是图片目录不是标注目录。YOLO代码会根据图片路径自动去找同名的txt标注默认规则是图片在images目录下标注在labels目录下目录结构要保持平行。如果你把标注文件放在别的位置就得手工改代码或者创建软链接。nc是类别数90不代表90个类别900张图里具体有多少类以标注里的name集合为准先统计一遍再填。这里我要特意说一句训练集的路径千万别写绝对路径。比如写/home/user/dataset换一台机器训练就要改一次血泪经验。直接写相对路径然后把data.yaml和dataset目录放在同一个父目录下这样不管是本地还是服务器拷过去就能跑。3.2 用YOLOv5跑一个最小训练命令确认数据配置没问题之后用YOLOv5做首次训练是最稳妥的因为它的生态最成熟、报错信息最友好。命令如下python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache各家参数说明一遍--weights指定预训练权重通常用coco上训好的yolov5s.pt作为起点这属于迁移学习比从零训练收敛快得多哪怕你的路标类别和coco的类别完全不同前几层的特征提取能力是可以复用的。--batch在单卡上设16比较稳妥如果显存不够就降到8但batch太小会导致BN层统计量不稳定训练早期loss波动会很大。--cache表示把图像提前缓存到内存里900张图很容易全塞进去能明显加快每个epoch的迭代速度代价是吃内存如果机器内存小于16G建议去掉这个参数。3.3 用YOLOv8训练时要注意的差异如果你用的是YOLOv8命令格式有些变化但数据yaml是通用的。YOLOv8的训练入口变成了这样yolo detect train \ datadata.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs100界面上的参数名变了--img变成了imgsz--weights变成了model含义一样。需要注意的是YOLOv8会默认开启AMP混合精度训练如果你的显卡是老架构比如GTX 10系混合精度可能会在个别卷积层上出现数值误差表现为loss卡住不降或直接变为NaN这时候要手动加上ampFalse关掉它。这个坑在老显卡上出现的概率并不低如果你训练时遇到loss异常优先怀疑这一点。3.4 训练过程中的监控手段训练启动后不要干等着。至少要看三个东西第一个是loss曲线正常情况是前20个epoch快速下降后面缓慢震荡下降如果是断崖式下跌然后反弹大概率是学习率设置不合理。第二个是验证集的mAP这个指标在50个epoch之后才开始有参考意义前面波动大是正常的。第三个是类别级别的PR曲线如果某个类别的召回率明显低多半是样本太少。看日志比看loss曲线更直接。YOLOv5的训练日志里每隔一段时间会输出一行当前epoch的box_loss、cls_loss、mAP等指标。我的习惯是训练到一半时随便挑一个epoch的验证集检测结果可视化出来看一眼确认模型确实学到了东西而不是在瞎猜。4. 训练前必须做的数据体检标注质量验证与类别分布检查4.1 统计类别分布先搞清楚900张图里有多少目标拿到数据的第一件事不是训练而是统计。写个简单脚本把两种格式的标注都扫一遍看看每个类别出现了多少次、每张图平均有几个目标、有没有类别数量悬殊的问题。这一步的目标是回答三个问题类别是哪些、有没有空标注的图、有没有某些类别完全没出现在val集里。脚本长这样# 统计YOLO格式标注的类别分布 import os from collections import Counter label_dir dataset/labels/train class_counter Counter() for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), r) as fp: for line in fp: cls_id int(line.strip().split()[0]) class_counter[cls_id] 1 print(类别id分布:, dict(class_counter))运行完你会发现一些细节比如某个类别的目标数量只有几十个而另一个类别有上千个这种不平衡会在训练时导致小类别被大类别“带偏”模型倾向于把所有框都预测成高频类别。处理方式后面会讲但先知道问题的存在是最重要的。4.2 把标注画回图上看直观检查漏标和错标统计只能看数量画框才能看质量。用OpenCV把txt里的坐标解析出来画到原图上过程很简单但收益极大。900张图分成9行10列拼成一张大图快速扫一遍就能看出主要问题。import cv2 img_path dataset/images/train/00001.jpg label_path dataset/labels/train/00001.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as fp: for line in fp: parts line.strip().split() cls_id, cx, cy, bw, bh int(parts[0]), *map(float, parts[1:]) # 反归一化得到像素坐标 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls_id), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(check.jpg, img)这段代码的核心是反归一化把YOLO格式的中心点坐标和宽高乘回图像的宽和高。画完之后重点看三类问题框明显比目标大一圈或小一圈、一个完整目标被切成了两个框、目标完全没被框到。如果发现某些图片的标注存在系统性问题比如所有小目标都漏标了那这块数据用之前就要慎重因为模型学不到小目标的特征。4.3 检查YOLO与VOC标注是否一致双格式数据集的好处就是可以互相对照。写个脚本把同名的xml和txt都解析出来看每张图的目标数量是否一致、坐标是否吻合。如果发现某些图的数量对不上需要用规则判断哪份更可信VOC的xml是人工标注出来的通常更可靠YOLO的txt如果是转换生成的可能存在丢框的情况。# 对比同一样本的YOLO和VOC标注数量 import os def count_yolo(label_path): return sum(1 for _ in open(label_path)) # 这里需要解析xml的object数量简化示意如下 def count_voc(xml_path): # 用xml.etree.ElementTree解析object节点数量 import xml.etree.ElementTree as ET tree ET.parse(xml_path) return len(tree.findall(object))补充说明不要指望两份格式100%一致如果误差率在1%以内属于可接受范围如果超过5%说明转换过程有问题需要用可视化方法逐类排查这个数据集的可用性就要打折扣。数据体检这一步新手最容易跳过去但老手的经验是跳过体检直接训练后面花在排查上的时间比训练时间还长。5. 路标数据集训练中的避坑指南现象、原因、解决5.1 训练时loss变成NaN模型直接崩掉现象训练到十几个epochloss突然变成nanlog里开始出现一堆warning之后loss再也回不来。原因最常见的是学习率过大导致梯度爆炸其次是混合精度计算导致数值溢出还有可能是标注文件里有坐标为负值或宽高为0的异常框。解决先把学习率从默认0.01降到0.001试试重启训练。如果还是nan在数据加载环节加一个过滤逻辑把包含异常坐标的标注文件剔出来# 过滤异常标注 def filter_invalid_labels(label_dir): for f in os.listdir(label_dir): if not f.endswith(.txt): continue lines [] with open(os.path.join(label_dir, f), r) as fp: for line in fp: parts line.strip().split() cx, cy, w, h map(float, parts[1:]) if cx 0 or cy 0 or w 0 or h 0: continue # 跳过异常框 if cx 1 or cy 1: # 归一化坐标不应大于1 continue lines.append(line) # 写回过滤后的标注按照我的经验90%的NaN问题都能通过降学习率或关闭AMP解决数据集本身有问题的概率相对低但也不排除。5.2 训练完mAP很高但实际用起来完全检测不到路标现象验证集mAP到了0.9以上拿一张没见过的路标照片去推理一个框都不出。原因这是典型的过程泄漏数据集的图像可能太“干净”了背景单一、光线均匀、路标都是正对镜头的大尺寸目标。模型学到的是“在特定背景下的路标”而不是“路标本身”的特征。解决手动把测试图的亮度降低、加一点模糊、旋转几度再试。如果模型立即失手说明泛化能力不行。对策是引入数据增强Mosaic、MixUp、HSV扰动都打开尤其把hsv_h、hsv_s、hsv_v三个参数调大一些模拟不同光照环境。另外900张图里如果能留出30张左右做现场实拍验证而不是只依赖测试集能更早发现这个问题。5.3 小目标路标完全检测不到现象远处的小路标在图中只占几十个像素模型完全无视近处大目标识别正常。原因YOLO的下采样倍数最低是8倍输入640x640时特征图最小单元对应原图8x8像素区域小于这个尺寸的目标几乎不可能被检测出来。900张图里小目标占比低模型又缺乏专门针对小目标的特征学习。解决训练时把输入尺寸从640提高到1280。这会显著增加显存消耗但小目标的检测效果提升非常明显。另外把数据集中小目标占比高的图片复制几份做增强相当于提高小目标的采样权重。如果1280显存不够可以尝试马赛克增强来拼贴小目标但代价是可能产生不自然的上下文实操时需要权衡。5.4 类别id错位模型把stop预测成yield现象训练过程不报错但预测结果的类别和实际目标对不上比如把所有stop都识别成yield。原因data.yaml里的names顺序和标注txt里的类别id编号不一致。比如txt里写了类别0是stop但yaml里names列表第一个是yield模型学到的0号类别对应关系就错了。解决打开dataset里的classes.txt逐行确认类别顺序再和data.yaml的names列表做diff对比。两边必须完全一致。常见的做法是直接让data.yaml里的names读取classes.txt的内容而不是手写从源头消除错位。5.5 训练集和验证集图像重叠mAP虚高现象训练完在验证集上mAP高达0.95看起来很完美但换一批新图效果就大幅下降。原因数据集本身可能没做严格的数据集划分比如从同一个视频抽帧得到的场景如果train和val抽取的帧有重叠模型相当于提前见过验证图。解决自己重新划分数据集。用哈希检查文件名和图像内容保证train、val、test三份之间没有重复图像。具体做法是把所有图像文件名做一次hash对比三个子集的hash集合如果有交集就把重复图从val中移到train或者删掉。这是数据体检里最容易被忽略的一步但对结果的可信度影响很大。6. 让这900张图发挥最大价值的进阶做法增强、迁移与人工校验闭环6.1 小数据集靠数据增强做补强但要控制概率900张图本身不够训练时把增强拉满能缓解一部分问题。YOLOv5的hyp.scratch.yaml里有几个关键参数hsv_h、hsv_s、hsv_v控制颜色扰动fliplr控制水平翻转scale控制缩放。我一般会把hsv的数值调大一倍因为路标在真实场景中受光照影响极大颜色偏移能帮模型学到颜色不变性。但翻转要谨慎路标里的文字翻转后就是错的如果类别里包含文字类路标建议关掉fliplr。6.2 用预训练权重做迁移学习比随机初始化省一半时间加载coco预训练权重起步就算99个类别跟路标完全不重叠底层的边缘、纹理、形状特征仍然有用。900张图从头训练要150个epoch才能收敛用了预训练权重一般60到80个epoch就稳定了。要不要冻结前几层的backbone如果数据量小建议冻结前10层减少被小数据带偏的风险如果数据量超过5000张就别冻结让全网络自适应调整。6.3 建立人工抽检习惯把数据集的“最后一公里”走完训练结束后挑20张验证集里没见过的图做推理把预测结果画出来和真实标注做对照。这个习惯看起来笨但比任何mAP数字都可靠。我做过多次实验mAP和实际观感经常不一致mAP高但推理效果差的例子并不少见。每轮训练结束都做一次抽检积累几次之后你对这套数据集的边界就会非常清楚哪些场景能打哪些场景必翻车。6.4 关于这份数据集的性价比判断900张标注好的路标图像加上双格式标注文件价值在于帮你跳过数据准备这个最耗时且最劝退的阶段。但如果你是奔着“开箱即用、直接部署”来的我的建议是调整预期把它当训练集种子通过数据增强、额外补充实拍图、混合其他公开数据集的方式逐步扩大规模。我自己的习惯是先用它完整跑通一遍训练和推理流程确认环境和代码链路没问题同时积累一套数据增强策略——这些经验反而是这份数据最大的价值。希望这篇笔记能帮你把这套数据用出效果少走几趟弯路。本文还有配套的精品资源点击获取
返回列表