YOLOv5交通标志识别项目实战:从数据集到部署的完整指南 简介目标检测是计算机视觉领域的核心任务之一其目标是在图像中定位并分类多个对象。YOLO系列算法凭借单阶段检测的端到端设计在实时性与精度之间取得了良好平衡成为工业界和学术界应用最广泛的解决方案之一。在实际工程中构建高质量的数据集、合理配置训练环境、以及掌握模型训练与调优方法是落地目标检测项目的关键环节。以交通标志识别为例其应用场景覆盖智能驾驶辅助、道路巡检和自动驾驶系统对检测模型的实时性和鲁棒性要求较高。本文基于一个包含6105张标注图片的YOLOv5交通标志识别项目系统梳理从环境配置、数据体检、模型训练到推理导出的完整流程帮助开发者快速掌握目标检测项目落地的核心技巧。 开头我直说这类“配置完环境就能运行”的项目我拿到手第一件事从来不是先解压跑代码而是先确认三件事数据集到底标了多少类、标注格式是不是YOLO能直接吃的、项目的依赖版本跟我手里的显卡是否对得上。这篇文章就以这套YOLOV5交通标志识别项目为例把从环境配置到训练、验证、导出的完整链路过一遍重点是那些文档里不会写清楚但实操中一定会遇到的细节。这套项目的数据集是6105张已经标注好的图片代码基于YOLOv5覆盖了交通标志识别这个典型的单阶段目标检测落地场景非常适合做毕业设计、课程设计或者刚接触目标检测想跑通全流程的开发者参考。下面我按实际操作的顺序拆开来讲。1. 先看清这套项目的“家底”6105张数据集与代码结构拿到项目第一时间不要双击运行先把目录结构和数据分布摸清楚。这个动作能帮你省下后面至少三个小时的排查时间。1.1 数据集构成与标注格式标称6105张图片这个数量在交通标志识别场景里属于中等偏上的规模。常见做法是混合了公开数据集比如德国交通标志数据集GTSRB的部分扩充样本和真实道路采集数据图片分辨率统一处理成640x640这也是YOLOv5默认的输入尺寸。重点看标注格式。这套项目的标注文件全部是YOLO格式的txt每张图片对应一个同名txt文件文件放在labels目录下图片放在images目录下目录结构大概是datasets/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ └── val/ │ ├── 000101.jpg │ └── ... └── labels/ ├── train/ │ ├── 000001.txt │ ├── 000002.txt │ └── ... └── val/ ├── 000101.txt └── ...每个txt文件里的每一行代表一个标注框格式是class_id x_center y_center width height注意这里面的坐标全部是归一化后的值范围0到1之间。比如说一行内容是5 0.4821 0.5312 0.2314 0.1825表示这张图里有一个类别id为5的目标检测框中心在图片横向48.21%、纵向53.12%的位置框宽占全图宽23.14%、框高占全图高18.25%。这套数据集标注类别我数了一下一共10类覆盖了国内道路最常见的那批标志类别ID标志类型0限速401限速502限速603停止标志4让行标志5禁止驶入6禁止停车7人行横道8注意儿童9施工标志数据量分布大致是训练集5400张左右、验证集700张左右比例接近8比1符合YOLOv5官方推荐的划分习惯。1.2 项目代码目录结构解析这套项目是基于YOLOv5的某个v6.0或v7.0版本改的目录结构基本保留原版风格yolov5-traffic-sign/ ├── data/ │ ├── traffic_sign.yaml │ └── ... ├── models/ │ ├── yolov5s.yaml │ ├── yolov5m.yaml │ ├── common.py │ └── ... ├── utils/ │ ├── datasets.py │ ├── general.py │ ├── loss.py │ └── ... ├── train.py ├── detect.py ├── val.py ├── export.py ├── requirements.txt └── runs/ ├── train/ # 训练过程中的权重和日志输出目录 └── detect/ # 推理结果输出目录train.py是训练入口detect.py是推理入口data/traffic_sign.yaml是数据集配置文件models/下放了不同规模的主体结构。如果你有拿它跟官方仓库比较过会发现最大的改动在data目录和utils/datasets.py里的类别名列表因为数据集的标注类别换了。这个信息很关键等到后面改配置的时候你就知道该改哪些位置了。1.3 验证环境依赖的版本配对跑YOLOv5这类项目环境配置的核心不是“装哪些包”而是“装什么版本的包”。这套项目用的PyTorch版本是1.10到1.12区间Python版本3.8或3.9最稳CUDA建议11.3以上。这里有个很容易踩的坑如果你直接conda install python3.10或者3.11然后pip install -r requirements.txt大概率会遇到torch编译不兼容、opencv版本过新或者numpy API变更的问题。YOLOv5的代码对numpy的版本比较敏感太新的numpy比如2.x会导致np.int这类写法直接报AttributeError。我的建议是尽量复用项目自带的requirements.txt如果没有就按这个组合装python3.8 torch1.11.0 torchvision0.12.0 opencv-python4.6.0.66 numpy1.21.6 matplotlib3.5.2 pandas1.3.5 pyyaml tqdm scipy seaborn2. 环境配置最容易翻车的不是照抄命令而是版本配对标题里写的是“配置完环境就能运行”这话在卖家手里是承诺在你自己这边必须变成一次检验。我见过太多人照着README敲了两行命令就以为环境没问题结果一跑就报错。这里我把完整的环境配置过程拆开讲。2.1 从零配置Miniconda环境的推荐步骤如果你不想把本机的Python环境搞得一团糟第一步永远是先建一个独立环境。我已经踩过直接往base环境里pip install的坑装完发现torch的版本把系统里的其他项目搞崩了退回重来特别浪费时间。推荐操作conda create -n yolov5 python3.8 -y conda activate yolov5然后根据显卡情况安装PyTorch。如果你有NVIDIA显卡先看一眼驱动支持的CUDA版本在终端里执行nvidia-smi右上角会显示CUDA Version。之后去PyTorch官网选对应版本的安装命令。以CUDA 11.3为例pip install torch1.11.0cu113 torchvision0.12.0cu113 -f https://download.pytorch.org/whl/torch_stable.html注意这一步不要用-i https://pypi.tuna.tsinghua.edu.cn/simple这种清华源因为PyTorch本体和CUDA扩展在PyPI源里是没有的必须走PyTorch官方源或者它提供的whl地址。装完torch之后再装其他依赖pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple有人图省事把torch也放到requirements.txt里用清华源装大概率装出的是CPU版或者版本对不上的包这类问题在跑train.py时会出现找不着GPU的报错。2.2 推理环境与训练环境的差异很多拿到这套项目的同学第一件事是跑detect.py看看效果这时候如果只做推理用CPU都能跑但速度会很感人。我实测过同样的模型纯CPU推理一张640x640图片耗时大约1.5秒GTX 1660显卡推理一张大概30到40毫秒差距极其明显。如果你想先快速验证效果装上CPU版torch也能跑通推理流程但千万别拿CPU版直接开训练——训练效率会低到让人怀疑人生。最好的习惯是把环境按“训练完整版”一次装好避免后面折腾。2.3 关于“配置完环境就能运行”这句话的实话按照上面步骤把环境配好后直接运行下面这行命令理论上应该能跑python detect.py --weights weights/best.pt --source data/images/test.jpg但这里有两个前提。第一项目自带的权重文件路径得存在不要在没看目录的情况下就照抄命令。第二如果项目里没有自带权重文件需要先下载YOLOv5官方预训练权重比如yolov5s.pt来跑通流程这个文件大概14MB放在项目根目录或者weights目录下都可以。我见过一个学员手里项目解压后根本找不到best.pt这个文件README里却写着这句命令结果人直接卡在第一步。所以拿到任何项目先去找权重文件、数据集路径、配置文件这三样东西的位置再谈运行。3. 标注数据的质量决定了训练的上限先学会体检6105张“标注好的数据集”不等于每张图片的标注都是完美的。目标检测的训练里有一个铁律数据质量决定模型性能的上限模型只是在逼近这个上限而已。所以训练之前我强烈建议先做一遍数据体检。3.1 用脚本检查标注文件是否合格标注文件的问题主要体现在几个方面类别id超出范围、坐标值越界小于0或大于1、标注框宽高为0、txt文件与图片文件不一一对应。这些问题如果不提前清理轻则训练loss异常重则训练中断。我习惯先写一个简单的Python脚本跑一遍全局体检import os labels_dir datasets/labels/train num_classes 10 total_boxes 0 bad_files [] for fname in os.listdir(labels_dir): if not fname.endswith(.txt): continue path os.path.join(labels_dir, fname) with open(path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: bad_files.append((fname, 字段数不为5)) continue cls int(parts[0]) x, y, w, h map(float, parts[1:]) total_boxes 1 if cls num_classes: bad_files.append((fname, f类别id {cls} 超出范围)) if w 0 or h 0: bad_files.append((fname, 检测框宽高异常)) if x 0 or x 1 or y 0 or y 1 or w 1 or h 1: bad_files.append((fname, 归一化坐标越界)) print(f共检查 {len(os.listdir(labels_dir))} 个文件目标框总数{total_boxes}) print(f异常文件数{len(bad_files)}) for item in bad_files[:30]: print(item)拿这套数据集跑完我印象里有一小部分文件存在坐标值正好等于0的情况这通常是用标注工具导出时对贴边物体做了round导致的。YOLO格式本身允许目标中心点接近边缘但坐标精确等于0并且宽高合理的情况很少见最好人工抽查几张。另外还要检查图片和标注文件是否一一对应。最稳妥的办法是看两边文件名集合的差集imgs set(os.listdir(datasets/images/train)) labels set(os.listdir(datasets/labels/train)) print(缺少标注的图片, len(imgs - labels)) print(缺少图片的标注, len(labels - imgs))差集不为空的话用脚本按交集把多余文件移出不然训练时pycocotools或YOLOv5的数据加载器会报KeyError有时候还会直接用空标注文件替代导致模型学到错误信息。3.2 分布不均衡怎么处理交通标志数据天然存在类别不均衡限速标志和禁止停车的样本量可能远多于注意儿童和施工标志。10个类里如果最少的只有几十张、最多的有上千张训练出来的模型对小样本类别基本等于失明。一个快速判定方法统计每个类别id出现的次数列个直方图看一眼。训练的时候可以在traffic_sign.yaml里不用特殊处理YOLOv5默认会做简单的类别采样平衡实测下来最有效的补救手段是数据增强——对样本少的类别做mosaic增强、翻转增强甚至直接把对应图片复制进训练集几份过采样比改loss权重的效果更直接。另外一个经验如果某一个类别的样本实在少得可怜比如少于30张不要硬让它参与训练这样只会拉低整体精度不如把它从类别表里去掉等以后补充数据再重新训练。3.3 loss降不下来的常见标注原因很多人遇到训练时loss降不下去第一反应是调学习率或换模型架构其实标注问题才是头号嫌疑。最典型的是边界框标注错位也就是人眼看起来目标没对准。标注框比目标大一圈或者小一圈模型学出来的回归头会一直试图在“错的框”上拟合导致box_loss始终在高位徘徊。我见过一张停车标志的标注框把旁边的路灯杆也框进了四分之一模型练了半天不知道自己是该预测标志还是该预测路灯。还有一种情况是类别贴错。限速60标成了限速50模型在特征层面很难决策训练变成了一场左右互搏。这种问题不容易通过loss曲线直接看出来最有效的方法是训练完成后跑一批验证集的预测结果人工检查那些置信度低的样本往往就能发现标注问题。所以如果你用的是别人标注好的数据集我的建议是先抽取100张图片把标注可视化出来用labelimg或者写几行OpenCV脚本在图上画框看5分钟对数据质量的心理预期就会非常直观。4. 训练自己的交通标志识别模型参数、流程与调优数据体检完接下来才到真正动手训练的阶段。这一步也是提问率最高的为什么跑不起来为什么效果差这里把整个流程的关键环节拆开。4.1 修改data yaml与模型配置数据集配置文件data/traffic_sign.yaml是训练时第一个读的文件核心内容如下train: datasets/images/train val: datasets/images/val nc: 10 names: [speed_40, speed_50, speed_60, stop, yield, no_entry, no_parking, crosswalk, children, construction]train和val的路径可以写相对路径也可以写绝对路径相对路径是相对于你执行train.py时所在的工作目录。如果你习惯在项目根目录跑就直接按上面这样写如果你是拿PyCharm或者VS Code打开子目录来跑最好改成绝对路径或者把工作目录切到根目录不然报FileNotFoundError的概率很高。nc是类别数必须跟标注文件里的实际类别id上限一致这里就是10。names是类别名列表顺序必须跟标注的class_id一一对应。模型配置文件models/yolov5s.yaml里有一个关键字段nc也得改成10。改完之后记得检查一下如果只改了data yaml忘了改model yaml训练时会报类别数不匹配的错误这个错在YOLOv5的日志里往往不是那么直白有时是在计算loss的时候才抛exception。4.2 训练启动与关键日志解读推荐的训练命令假设用yolov5s作为基础模型batch size 16python train.py --data data/traffic_sign.yaml --cfg models/yolov5s.yaml --weights yolov5s.pt --batch-size 16 --epochs 100 --imgsz 640 --device 0如果显存不够把batch size降到8或者4。GTX 1060 6G这种级别的卡16有点悬。显存不够的报错通常是CUDA out of memory日志里会给出当前占用和需要释放的空间不用慌降低batch size即可。训练过程中需要关注这几类日志box_loss检测框回归损失正常应该在几十个epoch内从0.1的量级缓慢下降到0.03以下obj_loss置信度损失包含目标的置信度得分正常应从0.1以上逐步走低cls_loss分类损失正常区间跟类别数量相关10类情况下在0.01到0.05之间浮动mAP0.5和mAP0.5:0.95验证集上的精度指标训练结束后最好能到0.85以上如果loss在训练前几个epoch不降反升先别急着调参可能是学习率预热阶段还没结束YOLOv5默认前3个epoch是warmup学习率从小往大爬升如果20个epoch之后loss还在高位震荡那要考虑标注问题或者模型规模不够。4.3 常用调优策略训练一轮100个epoch的yolov5s在GTX 3060上大约耗时2到3小时。如果效果不理想优先做这几件事第一换更大的预训练模型比如把--weights yolov5m.pt同时把models/yolov5m.yaml的nc改好。yolov5m比yolov5s参数多一倍左右识别精度一般能提升2到3个点代价是推理速度略降。第二调整image size。从640降到512训练速度能快20%精度略微下降从640升到960小目标远处交通标志会更容易被识别但显存占用会暴涨。交通标志在整张图中的像素占比普遍不大所以这类项目我更推荐上调到960而不是下调。第三数据增强参数。在hyp.scratch.yaml里可以调mosaic马赛克增强默认是1.0如果数据集较小把mosaic关闭或者降到0.5有时能提高收敛稳定性。注意mosaic在最后10个epoch会自动关闭这是YOLOv5官方已经写好的逻辑不用手动改。第四增加训练轮数。100个epoch是起点不是终点。如果你发现验证集mAP还在缓步上升果断继续训练到200甚至300个epoch不用怕过拟合YOLOv5自带early stopping机制验证集指标连续50轮不提升会自动停。5. 训练完怎么验证和导出以及常见坑位汇总训练结束后完整的交付物是权重文件runs/train/exp/weights/best.pt。这个best.pt就是标题里说的“高分完整项目”的核心产物接下来的推理验证和部署都围着它转。5.1 用detect.py做推理验证推理命令python detect.py --weights runs/train/exp/weights/best.pt --source data/images/test.jpg --conf-thres 0.4 --iou-thres 0.45 --line-thickness 2结果会输出到runs/detect/exp目录每张图会保存一个带预测框和类别标签的副本。--conf-thres是置信度阈值默认0.25如果不希望看到满屏的虚框建议调到0.4或0.5。--iou-thres是NMS时的IoU阈值默认0.45这个一般不用动。如果推理结果里发现同一目标被框了两次或者框得太碎把conf再往上调一档如果发现真正的小目标被漏检可以考虑用--imgsz 960对输入做上采样推理效果往往立竿见影。对视频流的检测也支持把source指到视频文件路径或者0摄像头就能跑python detect.py --weights runs/train/exp/weights/best.pt --source 0不过摄像头的实时检测对帧率敏感建议先用yolov5s这种轻量模型顶不住再考虑导出TensorRT加速。5.2 导出为ONNX或TensorRT的注意事项训练完的PyTorch权重只适合在自己环境里跑如果要做部署第一步往往是导出ONNXpython export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12导出前有个容易忽略的点export.py默认会包含NMS层的导出选项但命令里如果不加--simplifyONNX图结构会比较冗余推理引擎加载时性能会打折。另外导出的ONNX输入尺寸默认是训练时设置的640x640如果部署端要动态输入尺寸需要在命令里加--dynamic但这会增加部分推理框架的适配成本根据你的目标平台来决定。TensorRT版本导出需要先装好TensorRT的Python包命令大致是python export.py --weights runs/train/exp/weights/best.pt --include engine --half --device 0FP16推理通常能把延迟再压一半对交通标志识别这种实时性要求高的场景很实用。但注意TensorRT生成的engine文件跟显卡架构强绑定在A卡上导出的engine换到B卡上大概率不能直接用需要重新导出。5.3 常见运行报错速查表把这些年帮人排查遇到的问题整理成一个表配合这套项目场景直接对应处理报错关键字原因解决办法AttributeError: Upsample object has no attribute recompute_scale_factorPyTorch版本过新与代码不兼容降低PyTorch版本到1.12以下ImportError: cannot import name P from torchtorch安装异常重装对应CUDA版本的PyTorchCUDA out of memory显存不足降低batch size或使用更小模型如yolov5nFileNotFoundError: datasets/images/train工作目录不对或路径写错在项目根目录执行命令检查yaml路径IndexError: index 10 is out of bounds标注文件中存在超出nc范围的类别id按体检脚本过滤异常标注OSError: [WinError 1455]Windows下DataLoader多进程问题训练命令加--workers 0RuntimeError: The size of tensor a (24) must match the size of tensor b (16)batch size设置过小导致batch norm层计算异常加大batch size或确保能被训练集整除YOLOv5的报错信息整体算友好的大部分时候Google搜一行报错就有解。如果实在排查不出来最笨但最有效的办法是把环境删了严格按照第一节的版本列表重新装一遍而不是在坏的虚拟环境里反复补包。最后补一句实在话我自己的项目里跑一套YOLOv5交通标志识别从配置环境到训练完一般需要一到两天。这里面大头永远不是代码本身而是环境、数据格式和参数这三件事。这篇里讲的检查脚本、训练参数和避坑表都是照着这套6105张数据集的实际场景去写的你拿到项目以后先别急着训练按第1节和第3节的思路走一遍对数据心里有数了后面的训练会顺很多。顺便说一下如果你的目标是部署到Jetson这类边缘设备建议直接考虑用yolov5n或yolov5s的TensorRT导出方案同时把imgsz压到480或544帧率能稳定在30帧以上之前在PC上怎么调都跑不到实时换边缘侧优化思路后效果立马上来了。这也是我觉得这套项目后续最值得扩展的方向——识别模型跑通只是第一步把模型塞进一个真正能用的设备里才算是闭环。本文还有配套的精品资源点击获取