简介基于YOLOv5算法构建的道路交通标识识别系统附带完整Python源码和标注数据集面向毕业设计、课程设计及期末大作业场景适合需要快速落地目标检测项目的深度学习学习者。资源包含266个文件压缩包约423.33MB核心由53个Python脚本、59个YAML配置、10个预训练权重及jpg/png/jpeg等图像样本组成另有容器化部署配置、shell脚本、训练日志与可视化结果文件覆盖数据准备、模型训练、评估和部署全流程。目前已有50人学习下载。项目代码带有详细注释便于新手理解YOLOv5的检测原理与工程实现系统功能完整、界面美观经过严格调试确保可直接运行。通过TensorBoard日志和训练曲线等产物还可分析训练过程中的损失与精度变化为论文写作和答辩提供数据支撑是一份易上手、高完成度的毕设参考项目。1. 为什么毕设和工程落地都选YOLOv5做交通标识识别一个反直觉的结论先说结论在道路交通标识识别这个方向上YOLOv5不是精度最高的模型YOLOv8、RT-DETR都比它新但它仍然是绝大多数毕设、课程设计和中小型工程项目的首选。原因不复杂资料多到溢出来、代码坑都被人踩平了、部署链路成熟得不像一个开源项目。对要交论文的学生和要快速验证算法的工程师来说稳定可复现比指标高两个点重要得多。这套系统本质上解决的是这样一个问题——给定一张道路图像或一段视频流模型要在其中定位出交通标识的位置并给出类别比如限速40、禁止左转、人行横道警告。听起来像标准的目标检测任务但交通标识有它自己的脾气目标尺寸小、户外光照变化剧烈、存在遮挡和退化这些都会让模型的表现和它在COCO数据集上的表现差一大截。所以真正值得花时间的不是把YOLOv5跑起来而是把数据、超参数和评估方式调得符合这个特定场景。适合读这篇的人也很明确正在做毕设需要快速出一套完整系统的学生或者刚接手类似项目、想把第一个版本的检测效果从“能跑”推到“能看”的工程师。下面所有章节都按这个目标来写不绕弯子。2. 道路交通标识识别任务的特殊性先搞懂要检测什么再谈模型2.1 交通标识的检测难点小目标、颜色退化与类别不均衡交通标识识别的第一个难点是目标太小。在一张1920×1080的 roadside 图像里一个限速标志可能只占20×20像素换算成YOLOv5的输入尺寸640×640这个目标大约只有7×7像素。YOLOv5默认的检测头有三个尺度80×80、40×40、20×2020×20那个网格上每个格子负责约32×32像素的区域小目标勉强落在40×40和80×80两个尺度上。如果不做任何处理直接训练小目标的召回率会很难看。第二个难点是颜色信息的退化。交通标识的设计非常依赖颜色——红色表示禁令、蓝色表示指示、黄色表示警告——但实际拍摄时逆光、阴影、夜间、褪色都会让颜色失真。这意味着模型的骨干网络不能只靠颜色特征纹理和形状特征同样关键。这也是为什么要用深度学习而不是传统颜色分割方案的原因后者在光照变化下极其脆弱。第三个难点是类别不均衡。公开数据集里“限速”和“禁止停车”这类常见标志样本充足但“禁止拖拉机通行”这类罕见标志可能只有几十张。YOLOv5默认的BCEWithLogitsLoss和分类损失对不均衡类别没有特殊处理训练出来的模型会对高频类别有偏置。2.2 YOLOv5网络结构里哪些部分是为这类任务服务的YOLOv5的结构大致分三段CSPDarknet骨干网络负责提取特征PANet颈部负责多尺度特征融合Detect头负责输出检测结果。和交通标识识别直接相关的是PANet和Detect头里的anchor机制。骨干网络输出的三个特征层分别来自不同深度的下采样结果浅层特征保留空间细节但语义弱深层特征语义强但分辨率低。PANet通过自顶向下和自底向上的路径把这两类信息融合在一起让20×20网格上的深层特征也能拿到浅层的边缘和纹理信息。这个设计对小目标检测是关键因为小目标主要靠浅层特征提供定位线索。Detect头里每个anchor负责预测一组参数目标框的x、y、w、h目标置信度以及每个类别的置信度。YOLOv5默认给每个尺度分配3个anchor一共9个。训练时会计算预测框和真实框的IoU大于正样本阈值的归为正样本小于负样本阈值的归为负样本。anchor的尺寸如果和目标分布差距太大正样本数量就少模型学不动。交通标识的宽高比相对固定大部分集中在0.8到1.2之间但实际项目中用自适应anchor计算YOLOv5默认在训练前自动运行就能解决不建议手动写死。2.3 数据集的选型逻辑公开数据集与自建数据的组合策略道路交通标识识别常用的公开数据集有这么几个按实际情况选数据集特点适用情况TT100K腾讯街景标注含上万张图像类别多毕设首选中文场景但部分类别样本极少CCTSDB长沙理工标注聚焦中国交通标志类别相对集中配合TT100K做补充类别覆盖好GTSRB德国交通标志单目标分类不做检测只适合做分类头预训练LISA美国交通标志英文场景不太推荐中文毕设使用实际做毕设时我的建议是以TT100K为主用CCTSDB的标注做类别映射和样本补充。原因一是TT100K的标注质量高二是它的场景就是国内街景和毕设要展示的效果一致。但要注意TT100K的类别体系很杂有些类别只有几十个实例训练前要做类别筛选和合并。提示公开数据集的标注格式通常是COCO或VOC格式YOLOv5用的是TXT格式转换脚本是绕不开的第一步。转换时注意坐标是归一化的且类别ID要从0开始。3. 搭建可复现的训练环境从conda到YOLOv5源码的一键操作3.1 环境版本矩阵哪套Python和PyTorch组合最稳YOLOv5对环境的兼容性在开源项目里算好的但还是有一个稳定组合值得推荐。Python 3.8或3.9搭配PyTorch 1.12或1.13CUDA 11.6或11.7这是经过大量项目验证的组合。不建议一上来就装PyTorch 2.x不是因为不能用而是部分第三方库的兼容性还没完全跟上毕设阶段没必要冒这个险。用conda创建虚拟环境是最可控的方式命令如下conda create -n yolov5 python3.9 -y conda activate yolov5 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117这段命令创建了一个名为yolov5的Python 3.9环境然后安装CUDA 11.7版本的PyTorch。这里指定--index-url是为了确保装的是GPU版本而不是CPU版本CPU版本在训练时慢到怀疑人生。装完可以用下面的Python代码验证GPU是否可用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU mode)输出中torch.cuda.is_available()为True说明GPU环境正常。如果为False检查显卡驱动是否支持CUDA 11.7在终端执行nvidia-smi看驱动版本驱动太旧会识别不了新版的CUDA运行时。注意PyTorch版本和CUDA版本要匹配不是装得越新越好。如果你的显卡是30系或40系用上面的组合没问题如果是老显卡CUDA版本可以降到11.3对应torch 1.10左右。3.2 获取YOLOv5源码与权重选对分支比选新版本重要YOLOv5的源码托管在GitHub上要注意的是它的分支策略比较特殊。master分支是持续开发的代码更新频繁release分支是每月的稳定版本有对应的论文和权重文件。做毕设建议拉release分支而不是master因为release分支的代码和权重是配对的不会出现权重格式不兼容的问题。git clone -b v6.0 https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txtv6.0是一个使用率极高的版本它的权重文件和导出接口都相对成熟。requirements.txt里列了所有依赖包括numpy、opencv-python、matplotlib等一次性装完。下载预训练权重时有个讲究yolov5s.pt是速度优先的轻量模型yolov5m.pt是均衡选择yolov5l.pt和yolov5x.pt精度更高但训练和推理都更慢。交通标识是小目标检测我一般会建议从yolov5m.pt或yolov5l.pt开始迁移学习yolov5s的骨干网络容量有限对细节特征的表征能力偏弱。3.3 跑通第一个推理命令在没有训练之前先验证环境是真的好了在训练之前先跑一次推理这个习惯能帮你过滤掉大量环境问题。找一张含有交通标识的图片放到项目目录下执行python detect.py --weights yolov5s.pt --source data/images/bus.jpg --conf-thres 0.5这条命令用yolov5s权重对示例图片做推理置信度阈值设为0.5。如果环境正常输出会在runs/detect/exp目录下生成带检测框的图片。这时候bus.jpg里检测到的是COCO的80类目标人、车、公交车等跟交通标识没关系但至少证明了整个推理链路是通的。如果你手上已经有交通标识图片把--source换成自己的图片路径就行。这一步不要跳过因为后面数据集处理和训练的步骤都依赖同一套环境如果在这里就翻车后面每走一步都是在错误的地基上盖楼。4. 把公开数据集转成YOLOv5格式转换脚本与标注细节4.1 VOC与COCO标注格式到YOLO TXT格式的转换逻辑公开数据集最常给的标注格式是VOC的XML和COCO的JSON。YOLOv5训练时读取的是每个图片对应的TXT文件每行格式是class_id x_center y_center width height这四个坐标值都是相对图片宽高的归一化比例范围在0到1之间。VOC格式的XML里存的是左上角和右下角的绝对像素坐标转换时要先算出中心点坐标和宽高再除以图片宽高pythonimport xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, class_names, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bbox obj.find(bndbox) x_min int(float(bbox.find(xmin).text)) y_min int(float(bbox.find(ymin).text)) x_max int(float(bbox.find(xmax).text)) y_max int(float(bbox.find(ymax).text)) x_center (x_min x_max) / 2 / img_width y_center (y_min y_max) / 2 / img_height w (x_max - x_min) / img_width h (y_max - y_min) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines这段代码的核心逻辑是读取XML中的每个目标框把绝对像素坐标转成归一化的中心点坐标和宽高。class_names是类别名和ID的映射列表比如[speed_limit_40, no_left_turn, ...]。注意归一化坐标保留6位小数这已经足够精确多保留没有实际意义。COCO格式的JSON转换要稍复杂一点。COCO的标注是image_id到annotations的映射每个annotation里有bbox字段格式是[x, y, width, height]这是左上角坐标加宽高和VOC类似但少两个字段x_max、y_max不需要额外计算。转换时注意COCO的类别ID从1开始而YOLO要求从0开始要做减一操作。4.2 类别筛选与重映射TT100K的类别合并策略TT100K官方有221类交通标识直接全类训练是不现实的。大多数类别样本量太少模型完全学不到有效特征。我见过很多翻车案例都是因为图省事直接全类训练结果loss降不下去mAP0.5只有零点几。常见的做法是把TT100K的类别按交通标志的功能语义做合并。比如把不同数字的限速标志限速40、限速60、限速80合并为一个大类“限速标志”或者保留数字做成子类。这取决于你的毕设题目要求如果题目是“道路交通标识识别”做粗粒度分类警告、禁令、指示三大类最稳如果题目是“限速标志识别”才需要细分数字。类别重映射的实现是在转换脚本里加一个字典merge_map { i2: prohibitory, i4: prohibitory, i5: prohibitory, p3: warning, p5: warning, p6: warning, w2: mandatory, w3: mandatory, w4: mandatory } def map_class(original_name): return merge_map.get(original_name, None)合并之后要检查每个类别的样本数低于300个实例的类别要么删除要么做数据增强补偿。YOLOv5训练时会按类别计算损失稀有类别的梯度贡献会被高频类别淹没。4.3 数据集目录结构与train/val划分每一层目录都不能错YOLOv5读取数据集的目录结构是写死的除非你改yaml配置标准的组织结构如下datasets/ ├── traffic_sign/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/images/train和labels/train下的文件名必须严格对应图片是00001.jpg标签必须是00001.txt。YOLOv5不会帮你做文件匹配检查如果图片和标签文件名对不上训练时那张图片会被跳过而且你只会看到数据集加载时的warning而不是error很容易被忽略。划分比例一般用8:2或9:1注意要按整个视频序列来划分而不是按单帧随机划分。同一个视频连续帧高度相似如果训练集和验证集里出现同一段视频的帧会严重高估模型性能。有了目录结构之后还需要在yolov5项目下准备一个数据集配置yaml文件train: /path/to/datasets/traffic_sign/images/train val: /path/to/datasets/traffic_sign/images/val nc: 3 names: [prohibitory, warning, mandatory]nc是类别数names是类别名称列表顺序必须和TXT标注文件里的class_id对应。文件名和路径不要用相对路径YOLOv5在训练时的工作目录是yolov5项目根目录相对路径很容易踩坑。4.4 数据增强参数怎么调小目标场景下mosaic和copy-paste的取舍YOLOv5内置的数据增强在hyp.scratch.yaml里配置对小目标检测影响最大的几个参数是mosaic: 默认1.0开启把4张图拼成一张训练能有效增加小目标数量hsv_h、hsv_s、hsv_v: 颜色增强交通标识对颜色敏感不宜调太大fliplr: 水平翻转注意交通标识里有些类别翻转后语义变化不大但文字类标志翻转会失真scale: 缩放增强对小目标场景可以适当加大到0.7左右mosaic: 1.0 mixup: 0.2 hsv_h: 0.01 hsv_s: 0.5 hsv_v: 0.4 fliplr: 0.5 scale: 0.7这个配置里把hsv_h调得很小因为色相偏移过大时红色禁令标志可能偏成橙色甚至紫色而模型在真实场景中遇到的是褪色而非色相改变。scale调到0.7让模型看到更多不同尺度的目标缓解小目标泛化问题。mixup是v6.0新增的增强方式0.2的权重能让模型更鲁棒但也不宜过大否则训练收敛变慢。5. 训练关键参数与常见翻车现场为什么你的模型loss很低但检测不到小目标5.1 超参数调整优先级batch_size、img_size和anchor是三个坑训练参数里影响最大也最容易出错的三件事batch_size、输入分辨率和anchor设置。Batch size不是越大越好它受显存限制。以RTX 3060 12GB为例YOLOv5m在img_size640下batch_size设16基本到顶了。显存不够时优先减batch而不是减img_size因为输入分辨率对小目标检测的影响远大于batch size。输入分辨率--img默认是640但对交通标识这种小目标场景建议提高到768或896。代价是训练时间变长显存占用上升。如果你用的是yolov5m768分辨率下12GB显存大概只能跑batch 8。分辨率和batch要一起权衡。python train.py --data traffic_sign.yaml --weights yolov5m.pt --img 768 --batch 8 --epochs 100 --device 0这条命令用yolov5m预训练权重在768分辨率下训练100轮。--device 0指定用第一张GPU没有GPU就改成--device cpu但训练速度会慢几十倍。Anchor是YOLOv5自动计算的训练时会先跑一遍自适应anchor计算脚本。有些老教程会让你手动调anchor大多数时候没必要。但如果你发现某个类别漏检特别多可以在models/yolov5m.yaml里手动指定anchor尺寸或者关掉自动计算--noautoanchor用预设值。5.2 训练过程的监控指标loss曲线会骗人训练日志里最重要的不是loss降没降而是验证集上的mAP和PR曲线。loss下降只能说明模型在训练集上拟合得越来越好不代表在验证集上有效。YOLOv5的日志文件在runs/train/exp/results.csv里里面有每一轮的train_loss、val_loss、mAP0.5、mAP0.5:0.95等指标。用Excel或pandas画出来关注三个关键节点前10轮val_loss应该快速下降如果不降反升说明学习率太大或数据集有问题mAP0.5在30轮左右应该超过0.5如果还停在0.2以下优先检查标注文件mAP0.5:0.95远低于mAP0.5是正常的但差距过大说明定位精度不够这时要检查anchor和img_sizeimport pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/exp/results.csv) plt.plot(df[epoch], df[metrics/mAP_0.5], labelmAP0.5) plt.plot(df[epoch], df[metrics/mAP_0.5:0.95], labelmAP0.5:0.95) plt.legend() plt.show()这段代码读了训练日志的CSV文件画mAP曲线。要在训练结束后或训练过程中另开终端执行训练本身不需要停。5.3 避坑5个让交通标识识别训练翻车的真实案例第一个坑标注框超出图像边界。TT100K里有些标注的x_max或y_max比图片宽高还大YOLOv5在训练时直接报IndexError或者跳过该目标你会看到loss突然波动。解决方法是转换脚本里加裁剪逻辑把越界坐标强行clip到图片范围内。第二个坑类别ID从1开始。COCO格式转过来的标注常见问题模型把你标的所有目标都当成了第二类ID1。验证方法很简单随机抽一张训练图片用python detect.py跑一下看框的类别对不对。我见过不止一次有人训练完发现所有目标都被识别成同一类查了半天是转换脚本少减了个1。第三个坑训练和验证数据重叠。从同一个视频切片时没有按时间顺序分割随机shuffle后同一段视频的帧同时进了训练集和验证集。训练时mAP一路走高换到真实场景视频测试时性能崩塌。这不是玄学是数据泄漏。第四个坑小目标在mosaic增强里被裁掉。mosaic会把4张图缩放到中间区域再拼接如果原图里目标本来就小缩放后可能只剩几个像素label还在但特征已经完全丢了。如果数据集里小目标占比高建议把mosaic概率降到0.6左右或者配合--noautoanchor手动调小anchor。第五个坑模型权重和训练配置不匹配。有人用yolov5s.pt的权重去训练yolov5m.yaml的模型程序直接报错还好最怕的是权重能加载但参数量对不上表现是loss下降极慢。启动训练时注意日志里的Model Summary和Transferred确认骨干网络的参数量和你选用的模型一致。注意以上每个问题我都实际踩过前两个是数据侧的硬伤第三个是方法论错误第四个是增强策略和场景不匹配第五个是基本操作失误。排查顺序建议先检查数据再看增强最后看网络配置。6. 模型评估、导出与部署把检测结果可视化并验证泛化能力训练完成后第一步是用验证集做一次完整评估YOLOv5的val.py会生成Precision、Recall、mAP和PR曲线。这里的--save-txt参数会把所有预测结果输出为TXT文件方便做逐项分析。python val.py --weights runs/train/exp/weights/best.pt --data traffic_sign.yaml --img 768 --conf-thres 0.25 --iou-thres 0.45 --save-txt --save-conf上面的命令用best.pt权重做验证评估保存预测结果和置信度。--conf-thres 0.25是置信度阈值--iou-thres 0.45是NMS的IoU阈值这两个参数会影响评估结果和训练时的参数保持一致才有可比性。评估完之后建议做一次真实视频流的推理测试不要只用测试集图片。从网上找一段真实的道路行车记录仪视频放到本项目目录下运行python detect.py --weights runs/train/exp/weights/best.pt --source road_video.mp4 --conf-thres 0.4 --save-vid视频推理是检验模型泛化能力的试金石。如果图片测试mAP不错但视频里漏检严重大概率是训练数据里缺少动态模糊和运动畸变的样本这种场景下可以反过来把视频抽帧补进训练集。模型导出方面如果需要展示Web端效果或者集成到其他系统推荐导出成ONNX格式python export.py --weights runs/train/exp/weights/best.pt --img 768 --batch 1 --include onnxONNX导出的参数里有个容易踩的坑--img必须和训练时一致否则导出的模型输入尺寸不对。导出的ONNX文件不会包含原始的类别名部署时要在代码里维护一份类别映射表。我自己的习惯是训练结束之后的第一步永远不是急着部署上线而是拿手机随便拍几张路边的交通标识照片做盲测。模型在真实拍摄场景下如果漏检了先看置信度分数如果分数很低说明特征学习不到位如果分数不低但框偏了说明定位分支有问题。这个习惯帮我定位过很多看起来“玄学”的模型问题本质上是模型对训练分布过拟合了。写这篇内容时我把这个习惯再强调一遍希望帮到你。本文还有配套的精品资源点击获取
