ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv5道路交通标志识别实战:数据集、标注转换与训练调参全攻略

YOLOv5道路交通标志识别实战:数据集、标注转换与训练调参全攻略 简介基于YOLOv5的道路交通标志识别程序是一套包含完整源码与配套数据集的毕业设计项目面向需要完成课程设计、期末大作业或毕业设计的高校学生可用于交通标志检测、识别与模型训练等场景。资源包共266个文件压缩后约423MB其中以Python脚本py、数据集图片jpg/jpeg、模型配置文件yaml及预训练权重pt为核心另有训练日志、结果统计csv、环境配置Dockerfile等辅助文件便于快速搭建运行环境并进行推理测试。目前已有439人学习下载属于导师认可的高分项目源码附带详细注释从数据准备、模型训练到交通标志识别均有清晰实现逻辑适合新手参考与二次开发。使用者下载解压后按说明部署即可复用完整识别流程也可基于现有数据集与权重调参优化为课程答辩或项目展示提供扎实支撑。1. yolov5道路交通标志识别源码加数据集拿过来就能跑的毕业设计组合十个选交通标志识别的毕业设计有八个不是倒在算法理解上而是倒在跑通之前的环境、数据格式和训练命令上。基于yolov5的道路交通标志识别程序源码数据集恰好把最磨人的两件事——yolov5源码组织和标志标注数据集——预先准备好了你拿到手要做的是把环境配好、把格式理顺、把训练跑起来。这份资源解决的是“环境怎么配、数据怎么喂、训练怎么写”这类落地问题适合需要交期末大作业或毕设的学生也适合想快速验证yolov5在标志检测上效果的从业者。下面我按“先跑通、再调参、后验证”的顺序拆全程都是可以直接抄的步骤。2. 环境配置与数据组织Python、CUDA、数据集yaml一次配到位交通标志识别项目第一步永远是环境。yolov5对版本敏感Python、PyTorch、CUDA三者版本不匹配轻则警告重则直接崩溃。先clone官方yolov5仓库然后用conda建一个独立环境不要图省事往base环境里塞依赖。2.1 环境搭配CUDA、PyTorch与Python三方版本别硬来我一般会先确认显卡驱动支持哪一版CUDA再决定torch版本。下面这套组合跑yolov5训练和推理都稳定git clone https://github.com/ultralytics/yolov5.git cd yolov5 conda create -n traffic python3.8 -y conda activate traffic pip install -r requirements.txt逻辑说明yolov5仓库自带requirements.txt里面锁定了torch、torchvision、numpy、opencv-python等核心依赖的版本范围。用conda创建独立环境的核心目的是隔离——交通标志数据集和别的项目混在一起时最常翻车的就是numpy版本被另一个包改掉然后训练时loss开始出诡异数值。参数说明python3.8是兼容性最稳的版本3.9和3.10也能跑但遇到老版本onnx或某些opencv轮子时会踩编译坑。requirements.txt默认安装的是CPU版或GPU版取决于你机器上是否预装了CUDA版torch如果显卡驱动没问题建议先手动装GPU版再装requirements避免pip自动给你装成CPU版导致训练速度慢几十倍。2.2 数据目录结构images与labels一一对应是底线yolov5的数据组织方式很直接图片和标注文件分两个顶层目录下面按train和val再分一层。这种文件系统级结构和COCO的annotations.json完全不同。coco2017数据集结构里那种“一个json管所有标注”的方式在yolov5里不适用它认的是同名txt文件。datasets/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ └── img_002.jpg │ └── val/ │ └── img_003.jpg └── labels/ ├── train/ │ ├── img_001.txt │ └── img_002.txt └── val/ └── img_003.txt每条标注记录一行格式是类别id 中心点x坐标 中心点y坐标 框宽 框高。这五个值之间用空格分开。这里最关键的边界条件是图片文件名含前缀部分必须和txt文件名完全一致。比如img_001.jpg对应的标注文件必须是img_001.txt后缀不同都找不到对应标签。yolov5在训练时如果发现某张图没有同名txt会直接跳过并打Warning很多mAP异常就是文件对不上造成的。2.3 数据集yamltrain、val路径与类别名最容易写错数据组织好后需要在yolov5目录下建一个yaml文件描述数据集信息。这个文件里的路径写错了训练时会出现AssertionError: Train set not found之类的报错。# traffic.yaml train: /home/user/datasets/images/train val: /home/user/datasets/images/val nc: 3 names: 0: warning 1: prohibitory 2: mandatory逻辑说明train和val指向的是images目录而不是labels目录yolov5会自动去同级labels目录找对应txt。如果你把这两行错写成labels路径训练时一样会报找不到图片。参数说明nc是类别总数和names列表长度必须一致。names的顺序极其重要——这个顺序决定了类别id的编号比如warning在字典里排第0位那转换标注时warning类别id必须写成0写成1就会造成训练时类别错乱这是血泪经验。提示路径能用绝对路径就别用相对路径尤其当你习惯在别的目录下执行train.py时相对路径很容易指向错误位置。3. 标注格式转换VOC XML转YOLO txt的脚本与关键参数很多道路交通标志公开数据集的下载形态是VOC格式每张图配一个XML文件。yolov5不认XML认txt所以第一步是把标注格式全部转过来。这一步做不对后面训练、评估全是白费。3.1 YOLO的txt标注格式四个浮点数背后是归一化坐标VOC的XML里记录的是左上角xmin, ymin和右下角xmax, ymax的绝对像素值。YOLO格式要的是归一化之后的中心点坐标和宽高。换算公式是cx (xmin xmax) / 2 / image_width cy (ymin ymax) / 2 / image_height w (xmax - xmin) / image_width h (ymax - ymin) / image_height所有的值都除以图片宽高结果是0到1之间的小数。这样不管原始图片是1080p还是720p标注都不受影响。这组值如果大于1或小于0yolov5训练时大概率不报错但推理出来框的位置全是歪的。3.2 VOC转YOLO脚本XML读取与坐标换算一条龙下面这个脚本是我常用的转换模板直接用xml.etree.ElementTree解析XML不需要额外安装库import xml.etree.ElementTree as ET import os class_mapping {warning: 0, prohibitory: 1, mandatory: 2} def xml_to_yolo(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_mapping: continue box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_mapping[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) if __name__ __main__: xml_dir datasets/annotations out_dir datasets/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): xml_path os.path.join(xml_dir, xml_file) out_path os.path.join(out_dir, xml_file.replace(.xml, .txt)) xml_to_yolo(xml_path, out_path)逻辑说明脚本按顺序做三件事——读取图片宽高、遍历每个object、计算归一化坐标写入txt。class_mapping字典是脚本的大脑它决定每个标志类别对应数字几。参数说明class_mapping里的键必须和XML文件里name标签的字符串完全一致区分大小写比如XML里写的是Warning而你字典里是warning这条标注就会被直接跳过。输出格式{:.6f}保留6位小数训练精度完全够用不要用整数去存0.5这种值。3.3 训练集与验证集划分文件名错位是mAP异常的元凶标注转完后需要把图片和对应的txt按比例拆成train和val两个集合。最容易犯的错是图片复制过去了txt没跟着复制导致验证集图片全部“无标签”。import os import random random.seed(42) img_src datasets/images/all lbl_src datasets/labels/all ratio 0.8 imgs [f for f in os.listdir(img_src) if f.lower().endswith((.jpg, .png))] random.shuffle(imgs) split_idx int(len(imgs) * ratio) for i, img in enumerate(imgs): txt os.path.splitext(img)[0] .txt if i split_idx: os.rename(os.path.join(img_src, img), os.path.join(datasets/images/train, img)) os.rename(os.path.join(lbl_src, txt), os.path.join(datasets/labels/train, txt)) else: os.rename(os.path.join(img_src, img), os.path.join(datasets/images/val, img)) os.rename(os.path.join(lbl_src, txt), os.path.join(datasets/labels/val, txt))逻辑说明用rename而不是copy避免同一份数据占两倍磁盘空间。random.seed(42)固定随机数种子保证每次运行划分结果完全一样方便复现训练结果。参数说明ratio0.8表示80%进训练集、20%进验证集。如果你的数据集特别小比如只有几百张图建议把比例调到0.9验证集只留10%否则训练数据不足会导致模型欠拟合。另外注意同一条路上连续帧的图片不要因为shuffle而一边一半——如果数据集是视频抽帧来的建议按时间顺序截断划分而不是纯随机不然验证集和测试集过度相似评估结果虚高。4. 训练命令与超参数让模型在道路交通标志上收敛的关键设置环境和数据都就绪后训练就是一个调参过程。yolov5训练自己的数据集核心是把train.py的参数吃透。4.1 train.py命令拆解六个关键参数一次讲清python train.py \ --data traffic.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img 640逻辑说明yolov5的训练入口就一个train.py脚本--data指向你建的traffic.yaml--weights是预训练权重。选yolov5s.pt而不是从头训练是因为预训练权重在COCO上已经学好了通用特征交通标志这类小目标能直接受益于底层卷积特征收敛快很多。参数说明--data是数据集配置文件的路径强烈建议写绝对路径因为在别的目录下执行train.py时相对路径很可能解析错误。--img 640表示训练输入分辨率yolov5会自适应调整。交通标志普遍是中远距离拍摄的小目标分辨率太低比如416会明显掉精度但如果显存有压力降级到480也能接受。--epochs从100起步如果到80轮左右mAP已经不再涨可以提前停掉。4.2 超参数怎么调模型尺寸、batch-size与学习率的取舍模型尺寸直接决定速度和精度的平衡点这也是搜yolov5超参数时最常看到的内容。yolov5提供了n/s/m/l/x五个档位交通标志识别我一般选s或m。模型参数量推理速度适用场景yolov5s约7.2M快数据量小、显存有限、要赶进度yolov5m约21.2M中等数据量大、追求更高准确率batch-size设置逻辑很简单显存够就大显存不够就小。16是一个很稳的起点如果训练时报CUDA out of memory降到8再试不要直接开--cache把整个数据集塞进显存——那种操作很容易让显存直接打满。学习率方面yolov5默认lr00.01这个值在交通标志这种小目标数据集上经常偏大表现为loss震荡不下降。我一般会在自定义数据集上直接加--lr0 0.001换取更稳定的收敛曲线。优化器默认是SGD数据量大的时候SGD泛化能力更强。但如果你的标志数据集只有几百张用SGD会训练得很慢换成Adam收敛更快。4.3 训练日志里的信号results.csv告诉你的三个信息训练开始后输出目录runs/train/exp下会实时更新文件其中最关键的是results.csv。你可以用Excel或pandas打开里面每一列对应一个指标。epochs, train/box_loss, train/cls_loss, train/obj_loss, metrics/precision, metrics/recall, metrics/mAP50, metrics/mAP50-95看训练是否正常主要盯三个信号box_loss应该持续下降mAP50应该在20轮之后开始明显爬升precision和recall同时走高说明模型没走偏。如果mAP50已经连续30轮不涨直接CtrlC停掉训练用最后一次保存的权重别浪费时间去等满100轮。注意训练结束后的best.pt和last.pt含义完全不同。best.pt是验证集上mAP最高那一轮的权重last.pt是最后一轮的权重。部署和答辩演示永远用best.pt不要用last.pt。5. 避坑手册交通标志训练里最常见的五类翻车现场以下五条踩坑记录都来自实际跑yolov5交通标志项目的经验覆盖训练、数据、推理三个阶段。每一条都是现象、原因、解决三个环节对起来的遇到问题直接对号入座。5.1 训练阶段的翻车loss为nan与显存OOM踩坑1训练从一开始loss就显示nan权重文件完全不可用。现象第一个epoch跑完后终端输出box_loss: nan之后每一轮都是nan。原因学习率太大模型参数直接发散或者标注文件中出现了非法值比如某个txt里有一行是1 nan nan nan nan。yolov5不会主动校验数据内容非法值会一路传播到loss。解决先清洗标注数据执行grep -r nan\|Inf datasets/labels/检查所有txt然后训练时显式加--lr0 0.001把学习率降一个量级再试。踩坑2训练中途报CUDA out of memory进程直接崩溃。现象前几个epoch正常跑到第10轮左右突然OOM退出。原因batch-size和img组合起来超过了显存上限或者开启了--cache把整个数据集加载进显存叠加之下显存爆炸。解决把batch-size从16降到8如果还炸就降img到480。顺便检查系统里有没有其他进程占用显存比如另一个没关的jupyter内核nvidia-smi一眼就能看出来。5.2 数据与标注的翻车mAP为0和类别id错位踩坑3训练loss曲线很漂亮但是验证集mAP一直是0。现象train-loss稳步下降可metrics/mAP50始终是0precision和recall全是0。原因验证集图片没有对应txt标签。划分数据集时图片移动了标签文件没跟着移动。yolov5会对没有标注文件的图片跳过训练和验证所以验证集里全是没标签的图指标自然全是0。解决检查datasets/labels/val目录看里面有没有和验证集图片同名的txt。如果没有执行划分脚本时把图片和txt配对移动如果有但名字对不上检查图片扩展名是不是jpg和png混用导致txt文件名错位。踩坑4所有交通标志都被预测成同一个类别。现象检测框位置基本正确但不管警告标志还是禁令标志全部输出为同一个类别。原因VOC转YOLO时class id写错了。很多公开数据集标注的类别字段直接就是warning、prohibitory这类字符串但你转txt时忘了做映射把所有类别都写成了同一个数字或者没有按yaml里names的顺序去排。解决回头检查生成的txt第一列数字看看分布是否覆盖了0到nc-1的全部范围。确保class_mapping里每个类别对应唯一的数字且这个数字和traffic.yaml里names的索引完全一致。5.3 推理阶段的翻车conf-thres把目标全滤掉了踩坑5用best.pt跑detect.py输出目录空空的一张图一个框都没画。现象推理命令执行成功输出文件生成了但图片上没有任何检测框也没有txt结果。原因conf-thres默认值是0.25也就是说系统只保留置信度高于25%的框。交通标志这类小目标在推理时置信度普遍偏低0.25这个门槛会把大多数目标直接滤掉。解决显式降低置信度阈值python detect.py --weights runs/train/exp/weights/best.pt --source data/test --conf-thres 0.1降到0.1之后漏检情况会明显改善。如果0.1还是有很多目标没框出来那要怀疑是训练数据本身不足而不是阈值问题回去看训练集里该类标志的样本数量。6. 推理验证与模型导出用detect.py做冒烟测试再提前导出ONNX和TorchScript训练收敛后第一步不是急着写报告而是先用detect.py跑一遍冒烟测试确认权重加载正常、检测结果可用。这步能帮你避开“答辩前才发现模型根本跑不出框”这种尴尬。python detect.py \ --weights runs/train/exp/weights/best.pt \ --source data/test \ --conf-thres 0.4 \ --iou-thres 0.45 \ --save-txt \ --save-conf逐项说明--source支持图片目录、单张图片、视频文件和摄像头ID抽帧图片最常用--conf-thres刚才讲过0.4在测试集上算一个均衡值如果漏检就降到0.1--iou-thres控制NMS的去重力度交通标志重叠少保持0.45即可--save-txt把每张图的检测结果按yolov5格式输出成txt答辩时统计各类别的检测数量很方便--save-conf在txt里附带置信度数值可以用来画PR曲线。detect.py跑通后下一步建议顺手导出ONNX和TorchScript。这个动作不做你的模型就被锁死在PythonPyTorch环境里。导出后后续接TensorRT或OpenVINO部署或者用一个简单的C推理程序都有退路。python export.py --weights runs/train/exp/weights/best.pt --include onnx torchscript导出完在runs/train/exp/weights目录下会出现best.onnx和best.torchscript两个文件。TorchScript可以直接用torch.jit.load加载不需要训练依赖ONNX则可以跑在各种边缘设备上。有一回我赶进度训练完直接拿best.pt去跑摄像头实况结果在光线比较暗的地方什么都检测不到第一反应是模型废了。后来才发现不是模型的问题是默认阈值0.25把低置信度目标全滤掉了。从那以后我每次推理都会先拿一张训练集图片做冒烟测试——确认权重普通加载、框和类别对得上再去调阈值、换场景。这个习惯帮我省掉了无数次无效debug希望也能帮到你。本文还有配套的精品资源点击获取
返回列表