简介:本资源是一个基于YOLOv5实现的交通标志牌识别高分项目,面向人工智能、自动化、电子信息等专业的在校学生、教师及工程技术人员,适用于毕业设计、课程设计、竞赛原型开发与深度学习入门实践。项目完整复现了TT100K数据集上的检测流程,涵盖数据预处理、模型训练、验证评估与推理部署全流程,代码经实测可直接运行,配套文档详述环境配置、参数调优与结果分析方法。压缩包共149个文件,包含56个Python主程序与工具脚本、49个YAML/YML配置文件(定义模型结构、训练超参与数据路径)、6个Shell部署脚本、6个Markdown说明文档,以及Dockerfile(含CPU/ARM64多平台支持)、.gitignore等工程化文件,整体仅1.12MB,轻量易部署。已有79人下载学习,提供从零复现到二次开发的完整支撑,特别适合需要快速构建CV落地案例的学习者与项目开发者。 说实话,这类“基于yolov5的交通标志牌识别项目”我见得太多,但从标题就能看出,它不是一个随便跑跑demo的玩具项目,而是一个能真正落地、拿去交作业甚至写进简历的完整工程。yolov5作为目标检测里上手最快、生态最成熟的一套代码库,配合TT100K这个专门面向中国交通场景的数据集,组合起来的项目价值非常高——无论是毕业设计、课程设计,还是入门实际工程,这条路线都值得认真走一遍。
我这次就结合自己做过的实践,把这个项目从环境搭建、数据转换、模型训练到推理部署的完整链路,全部拆开来讲清楚,顺带把那些文档里不会写、但实操时一定会踩的坑也一并交代了。
1. 项目整体设计与思路拆解
1.1 为什么选yolov5做交通标志识别
选yolov5不是因为它最先进,而是因为它最“够用”。交通标志检测本质上属于中小尺寸目标检测任务,标志牌在画面中往往只占几十到几百像素,对模型的小目标感知能力有一定要求。yolov5提供了s/m/l/x四种规格,s模型只有7M左右参数,在CPU上都能跑出可用的速度,同时精度又不至于拉胯,这对学生项目和嵌入式部署都非常友好。
更关键的是yolov5的生态成熟度。从数据标注格式到训练脚本,从模型量化到TensorRT、ONNX导出,全套工具链都齐了。这意味着你可以把80%的精力花在数据和调参上,而不是去啃一个自研框架的源码。对大多数做毕设或入门工程的人来说,这个选择是性价比最高的。
yolov5本身是一个单阶段目标检测算法,它把目标检测问题拆成了“在特征图上做密集回归”这件事。简单理解就是,输入一张图,网络同时预测出大量候选框的位置和类别,然后通过NMS(非极大值抑制)去掉重复的框,留下最终结果。相比两阶段的Faster R-CNN,yolov5速度更快,结构更简洁,这也是它在工业界被广泛使用的原因。
1.2 TT100K数据集的选型理由
TT100K是腾讯发布的交通标志数据集,全称是Tencent Traffic-Sign 100K,包含10万张街景图像,其中标注了3万多个交通标志实例,覆盖5个大类、45个小类,包括禁令、警告、指示、指路等类型。这个数据集最大的特点是“中国本土化”——里面的标志都是国内道路上真实出现的样式,比如“限速40”“禁止通行”“注意行人”这些,跟国外数据集里的标志风格差异很大。
如果你用国外的GTSRB或者LISA数据集训练模型,拿到国内路况上测试,效果往往会打折扣,因为标志的配色、形状、文字排版都有差异。TT100K直接就是国内的真实街景数据,贴合度很高,这也是毕设和竞赛里它被大量使用的原因。
不过要提醒的是,TT100K虽然名义上有10万张图,但有标注信息的实际只有约1万张,而且这1万张也不是全部都能直接用。光照变化、遮挡、小目标、类别不均衡这些问题在TT100K里都很明显,做数据处理时需要花不少功夫去清洗和筛选。这部分我会在第三章详细讲。
1.3 项目模块划分与资料包结构
一个完整的yolov5交通标志识别项目,通常包含五个核心模块:环境与依赖、数据准备、模型训练、推理验证、文档资料。我在整理自己的项目时,目录结构大概是这样的:
yolov5/:核心检测代码库(官方源码,可保持原样)data/TT100K/:数据集存放目录,包括images和labelstools/:自写的脚本,主要是标注格式转换、数据集划分、类别统计runs/:训练日志、权重文件、检测结果图docs/:项目文档、实验报告、答辩PPT素材
这样的组织方式最大的好处是职责清晰。官方代码库保持原样,后续拉新版本不冲突;自己写的工具脚本单独放,方便复用;训练产出统一进runs目录,出问题排查也方便。很多同学喜欢把所有东西堆在一个文件夹里,结果训练到一半想找某个脚本都费劲,这种坏习惯最好一开始就改掉。
2. 环境搭建:yolov5安装与依赖配套
2.1 yolov5源码获取与环境要求
yolov5的安装流程本身不复杂,但版本兼容问题很容易让人头大。我自己的建议是:不要用最新版,直接用官方v6.0或v7.0版本的稳定分支,因为网上大多数教程、预训练权重和踩坑经验都是基于这些版本积累的,能帮你节省大量排错时间。
下载源码的方式,可以直接从GitHub上clone,也可以下载zip包本地解压。clone的好处是后续可以随时git pull拉更新,但对毕设项目来说意义不大。
环境方面,Python版本建议3.8到3.11之间。PyTorch的安装要和你的CUDA版本对应,这一点特别关键。一般来说,如果你的显卡是NVIDIA的,建议先装CUDA 11.8以上,然后安装对应版本的PyTorch。GPU环境不行的话,CPU版本也能跑,只是训练速度会慢很多,推理勉强能用。
这里要顺带说一句,很多人会忽略requirements.txt里各依赖库的版本关系。yolov5对OpenCV、numpy、matplotlib这些库的版本敏感度其实不高,但torch和torchvision的版本一定要严格匹配,否则会出现_C导入报错之类的问题。
2.2 安装步骤与依赖验证
安装的时候我习惯分两步走。第一步先创建独立的Python虚拟环境,避免污染系统环境,这一步在conda下就是一行命令的事:
conda create -n yolov5 python=3.9 -y conda activate yolov5第二步进入yolov5目录,安装项目依赖:
cd yolov5 pip install -r requirements.txt如果CPU版本的PyTorch已经安装好了,这一步只需要装剩下的依赖。如果是GPU环境,建议先手动安装好对应版本的PyTorch,再执行pip install -r requirements.txt,避免依赖自动装上CPU版的torch。
装完之后,验证安装是否成功最快的方式是跑一次官方自带的检测demo:
python detect.py --weights yolov5s.pt --source data/images/bus.jpg第一次运行会自动下载yolov5s.pt预训练权重,如果能看到输出图像里检测出的公交车和人,说明整个环境就打通了。我习惯跑通这个demo之后再开始下一步,因为后续的训练、推理都依赖这个基础环境的正确性,早验证比晚验证省事得多。
2.3 硬件配置建议与训练规划
关于硬件配置,我的建议非常朴素:有NVIDIA显卡就用GPU,显存8G以上可以比较舒服地训练yolov5s模型,12G以上能尝试yolov5m或更大的模型。没有显卡的话,用CPU训练也不是不行,但要做好心理准备——同样的数据集,GPU跑两个小时,CPU可能要跑两天。
如果条件比较受限,有一个妥协方案:直接用官方预训练权重yolov5s.pt做迁移学习,只冻结backbone层、训练检测头,这样显存和训练时间都能大幅下降。另一个方案是用GitHub Actions或者云GPU平台跑训练,不过对新手来说门槛略高,这里不展开。
3. TT100K数据准备:从原始标注到yolo格式
3.1 TT100K数据格式解析
TT100K的原始标注是JSON格式,这是整个项目里最容易卡住人的一个环节。它的目录结构通常是这样的:
train/:训练图片test/:测试图片train.json:训练标注文件val.json:验证标注文件annotations.json:所有标注文件
其中train.json的结构大致是:
{ "imgs": { "0.jpg": { "height": 2048, "width": 2048, "objects": [ { "bbox": [x1, y1, x2, y2], "category": "i100", "id": 1 } ] } } }注意这里的bbox是[左上角x, 左上角y, 右下角x, 右下角y]的格式,跟yolov5要求的[中心点x, 中心点y, 框宽, 框高]不一样,而且数值是像素坐标,没有归一化。如果直接把原始标注拿来训练,模型会直接学崩。所以数据准备的第一步,就是写一个脚本把JSON转成yolov5能识别的TXT格式。
3.2 标注转换脚本的编写要点
转换脚本的核心逻辑其实不复杂,就是把每一张图片的标注信息从JSON里读出来,计算归一化坐标,然后写入到与图片同名的TXT文件中。yolov5对TXT格式的约定是:每行代表一个目标,格式为类别id 中心点x 中心点y 框宽 框高,所有坐标值都是归一化到0到1之间的浮点数。
我写的转换脚本大概长这样:
import json import os def convert_tt100k_to_yolo(ann_file, image_dir, save_dir, class_names): with open(ann_file, 'r') as f: data = json.load(f) for img_name, img_ann in data['imgs'].items(): height = img_ann['height'] width = img_ann['width'] image_id = img_name.split('.')[0] txt_path = os.path.join(save_dir, image_id + '.txt') lines = [] for obj in img_ann['objects']: category = obj['category'] if category not in class_names: continue x1, y1, x2, y2 = obj['bbox'] cx = ((x1 + x2) / 2) / width cy = ((y1 + y2) / 2) / height w = (x2 - x1) / width h = (y2 - y1) / height class_id = class_names.index(category) lines.append(f"{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") if lines: with open(txt_path, 'w') as f: f.write('\n'.join(lines)) if __name__ == '__main__': class_names = ['i100', 'i110', 'p100', 'p130', 'w100'] # 自定义类别列表 convert_tt100k_to_yolo('train.json', 'train', 'labels/train', class_names)这个脚本里有几个细节值得注意。第一,class_names的顺序决定了类别id的映射关系,一旦确定了就不能轻易改动,否则训练好的模型类别会全乱。第二,如果一张图片里所有目标都不在选定的类别列表里,这张图片和它的TXT文件都会被跳过,后续训练时要注意图片和标签的对应关系。第三,原图是2048×2048的大图,但yolov5训练时会resize到640×640或1280×1280,小目标的标注在resize后可能只剩几个像素,这对检测精度影响很大,后面会专门讲怎么处理。
3.3 数据集划分与类别筛选策略
TT100K的官方划分是训练集和验证集,但实际做训练时,我发现官方验证集的图片数量和标注质量参差不齐,建议自己重新做一次划分。我的做法是:把有标注的图片全部汇总,按照8:1:1的比例随机划分训练集、验证集、测试集,并且保证同一条路段上的图片尽量落在同一个集合里,避免数据泄漏导致评估指标虚高。
类别筛选方面,TT100K全量45个类别里,相当一部分类别的标注数量只有几十个,属于典型的长尾分布。如果全类别训练,模型会对样本量大的类别过拟合,对样本量小的类别基本学不到特征。我的建议是优先保留标注数量最多的前20到30个类别,比如限速标志、禁止通行、注意行人这些高频类别,这样训练出来的模型在实际场景中更有实用价值。
具体筛选时,可以先写个脚本统计每个类别的实例数量,画个柱状图看看分布,再决定保留哪些类别。这一步虽然耗时,但直接决定了模型最终能学成什么样,值得花时间去做。
4. 模型训练与超参数调优
4.1 数据集配置文件(TT100K.yaml)编写
yolov5训练前需要准备一个YAML配置文件,告诉训练脚本去哪里读数据、有多少类别、类别名叫什么。这个文件是训练的入口,写错一个路径或参数,训练就会报错或者跑出一个完全无效的模型。
我常用的配置文件模板:
# TT100K.yaml train: data/TT100K/images/train val: data/TT100K/images/val nc: 20 names: ['i100', 'i110', 'i120', 'p100', 'p130', 'p150', 'w100', ...]注意这里train和val指向的是图片目录,yolov5会自动在同级目录下找labels文件夹里的TXT标注文件。所以标准的目录结构是:
data/TT100K/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/如果你的数据集图片和标签不在这个标准结构下,一定记得在YAML里写对路径,或者用软链接把它们组织好。很多同学训练时提示found no labels,90%的情况下都是因为这个目录结构对不上。
4.2 训练命令与关键超参数解析
我在训练时用的命令大概是这样的:
python train.py \ --data TT100K.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 200 \ --device 0 \ --name tt100k_train逐个参数解释一下:--data指定数据集配置,--weights指定预训练权重,--img指定训练图片尺寸,--batch是批大小,--epochs是训练轮数,--device 0是用第一块GPU,--name是给这次训练起个名字,方便后续在runs/train/目录下区分不同的实验。
超参数里最值得花心思的是--img和--batch。--img默认是640,对TT100K这种小目标较多的数据集,我建议至少提到1280,这样小标志的细节能保留得更好,代价是训练时间几乎翻倍、显存占用翻倍。--batch受显存限制,24G显存跑yolov5s的1280分辨率,batch大概能开到8到16,如果你的显存不够,可以尝试用--batch 4配合--noplots减少显存占用,或者干脆用yolov5n这个更小的模型。
还有一个很多人忽略的参数是--cache,加上这个参数会提前把图片缓存到内存里,大幅减少训练时读磁盘的开销。如果你的数据集不是特别大,强烈建议加上,实测能提速30%以上。
4.3 训练过程监控与结果分析
训练一旦跑起来,yolov5会自动在runs/train/tt100k_train/目录下记录日志和评估结果。我习惯每隔几十个epoch就打开终端看一下输出,重点看三个指标的变化趋势:box_loss、obj_loss、cls_loss。这三个损失值应该逐步下降,如果某个损失值震荡剧烈或者长时间不降,说明学习率设置不合适,或者数据标注有问题。
训练完成后,weights/best.pt和weights/last.pt两个文件会自动保存。best.pt是验证集上mAP最高的权重,last.pt是训练结束时的权重,一般做推理和部署都用best.pt。
另外results.png这张图非常关键,它汇总了训练过程中所有损失和指标的变化曲线,包括mAP_0.5、mAP_0.5:0.95、精确率、召回率等。如果你的mAP_0.5能到0.8以上,说明模型已经相当不错了,放到实际街景里检测常见标志基本够用。
5. 推理实践与工程化部署
5.1 图片、视频与实时检测
模型训练完成后,最直观的验证方式就是用detect.py脚本跑推理。测试单张图片:
python detect.py \ --weights runs/train/tt100k_train/weights/best.pt \ --source test.jpg \ --conf 0.4 \ --device 0--conf参数控制置信度阈值,默认0.25。交通标志检测建议调到0.4到0.5之间,因为标志牌的纹理相对简单,低置信度的误检会比较明显。--source参数非常灵活,可以传图片路径、视频文件路径,甚至摄像头设备号,比如--source 0就是用第一个摄像头做实时检测。
实际测试的时候,我建议你去找几张TT100K测试集之外的街景图,最好是不同光线、不同天气条件下的,这样可以更客观地评估模型的泛化能力。如果你发现自己训练的数据集上效果很猛,换到真实场景效果暴跌,那大概率是过拟合了,需要回炉做数据增强或者正则化。
5.2 模型导出与嵌入式端部署方向
yolov5最让我满意的一点就是模型导出的便利性。训练好的best.pt可以直接导出成ONNX格式:
python export.py \ --weights runs/train/tt100k_train/weights/best.pt \ --include onnx \ --opset 12导出ONNX之后,就可以继续转换成各种推理引擎需要的格式。如果是做边缘端部署,现在很多同学会把它部署到瑞芯微RK3568/RV1106这类带NPU的板子上,流程基本是:PyTorch权重 -> ONNX -> RKNN格式,然后调用RKNN的Python SDK做推理。这个过程里最常遇到的问题就是某些算子不兼容,需要回头调整模型结构或者换一个更简单的模型变体。
如果你的项目只是毕设或者演示用,完全不需要走到嵌入式部署这一步,用detect.py做离线推理已经足够展示效果。但如果你想让项目在简历上有更大的亮点,部署到开发板甚至做一个Demo小应用,绝对是个加分项。
6. 常见问题与排查技巧实录
6.1 显存不足与训练中断
显存不足(CUDA out of memory)是训练yolov5最常见的报错之一,我几乎每次带新人都会遇到这个问题。解决策略从优到劣排列:
- 尝试降低
--batch,从16降到8,再降到4,直到能正常跑起来 - 同时降低
--img分辨率,从1280降到640,显存占用大概能降一半以上 - 如果在1280分辨率下训练小目标太吃力,建议换用多头注意力更好的yolov5m模型而不是强行上大图
如果是长时间训练中断,恢复训练也很简单,找到之前训练保存的last.pt,用--resume参数恢复即可:
python train.py --resume runs/train/tt100k_train/weights/last.pt6.2 精度低、不收敛怎么办
模型训练出来mAP只有0.3或者损失值震荡不降,这是我被问得最多的问题。排查思路可以参考下面这张速查表:
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 损失值降不下去 | 学习率过大或过小 | 检查训练日志里的lr,尝试降低到0.001或使用warmup |
| mAP很低但损失正常 | 类别映射错误或标注坐标错误 | 验证TXT标签内容和图片目标的对应关系 |
| 小目标完全检测不到 | 训练分辨率太低 | 把--img提升到1280,或使用SAHI切片推理 |
| 训练集精度高、验证集低 | 过拟合 | 增加数据增强,尝试--hyp自带的增强配置,或降低训练轮数 |
| 换场景效果暴跌 | 数据集过拟合 | 增加TT100K之外的补充数据,或者只保留通用性强的类别 |
这里要特别说一下,很多同学把标注格式转换脚本写完,没有做任何可视化校验直接训练,结果训练完才发现类别框全部偏离目标。我的建议是转换脚本写完之后,一定要用OpenCV或yolov5自带的utils/plots.py把标注框画出来,肉眼检查几组图片确认没问题再开始训练,这个习惯能帮你省下一天的时间。
6.3 标注转换与数据对齐的坑
TT100K还有一个比较隐蔽的坑:原始图片文件名和JSON里的imgs键名可能存在前缀不匹配的问题,比如文件名是0.jpg但JSON里是0。如果这种不一致没有处理好,转换脚本生成的标签就会张冠李戴。我在转换的时候会先打印几个样例,确认键名格式再写正式的转换逻辑。
还有一个问题是图片和标签文件的数量要对齐。yolov5训练时,如果一张图片没有对应的TXT标签文件,它会被直接跳过。如果你发现训练日志里参与训练的图片数量比总数少很多,就去检查是不是标签文件名和图片名没对上。文件名前缀不一致是很常见的原因。
6.4 资料包文档组织建议
既然是“源码+详细文档+全部资料”的形态,文档质量往往比代码本身更影响评估结果。我自己写毕设类文档的习惯是,至少包含六个部分:项目背景与需求分析、技术方案与整体设计、环境搭建与数据准备、模型训练与调优过程、实验对比与结果分析、总结与展望。
其中“实验对比”部分是拉开差距的关键。哪怕你只跑了一组yolov5s的实验,也可以在文档里加入:不同置信度阈值下的检测效果对比、不同天气场景下的检测效果分析、常见误检漏检案例分析。这些内容能充分展示你的工程素养,也方便答辩时应对老师提问。
数据资料方面,建议把数据集样本、标注可视化结果、训练过程截图、检测效果视频整理成独立的附件目录,这些“全部资料”在展示和后续复现时都很加分。
写在最后的一点体会
这个项目我最初做的时候,光是在TT100K标注转换和数据清洗上就花了两天时间,训练调试又花了两天,真正跑通时那种豁然开朗的感觉到现在还记得。回头来看,最大的心得有三条:数据质量比模型结构更重要,任何时候先可视化确认标注不出错再训练;版本锁定很重要,yolov5的代码、PyTorch版本、预训练权重三者匹配能省掉大量排错成本;不要迷恋大模型,yolov5s配合1280分辨率在交通标志识别任务上已经足够惊艳,先把效果打通比追求SOTA指标有意义得多。
如果你拿到类似的资料包,建议不要只停留在跑通Demo,而要按照本文的流程,把数据转换、训练、评估、部署的每一个环节都理解透彻,再试着改一改超参数、换一换模型规模,看看效果差异。做到这一步,你掌握的就不只是一个项目,而是一整套可复用的目标检测工程方法论。
本文还有配套的精品资源,点击获取