ARTICLE DETAIL

资讯详情

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

YOLOv5交通标志识别实战:从数据集训练到模型部署全解析

YOLOv5交通标志识别实战:从数据集训练到模型部署全解析 简介针对交通标志识别任务提供一套基于YOLOV5的完整工程包含6105张已标注数据集、训练与推理源码及模型权重文件。代码经本地编译调试配置好环境即可直接运行尤其适合计算机专业毕业设计、课程设计或深度学习实战练习。资源共1568个文件以Python脚本为主辅以模型配置yaml、预训练权重pt、标注图片jpg及训练日志等压缩包大小282.71MB目录结构清晰便于按需调用。目前已有208人学习下载。项目中包含训练过程记录与多个阶段的权重文件方便对比不同迭代效果配套的类别配置和数据结构说明可帮助快速完成迁移学习与结果可视化为论文实验或答辩演示提供充分支撑。1. 一套 YOLOv5 交通标志识别项目6105 张标注数据与可运行代码的真实边界搞计算机视觉的毕业设计绕不开 YOLOv5 交通标志识别这个经典题目交通标志从整车目标检测里切出来十来个类别却把目标检测最磨人的小目标问题全暴露了。这套带 6105 张已标注数据集的 YOLOv5 交通标志识别项目正好是针对这个场景准备的完整代码包环境配好就能直接训练。它适合两类人一类是毕设选了图像识别方向、需要快速拿到可复现、可答辩的工程代码另一类是刚开始学 CV、想借一套真实标注数据把“数据集 → 训练 → 检测”整条链跑通的人。项目基于 YOLOv5 的 yolov5s 主干配合交通标志专用 data.yaml 和预先处理好的 images/labels 目录结构。当然“配置完环境就能跑”不代表你什么都可以不看数据格式、训练参数、检测阈值这三处看不明白模型照样训不出来。下面按环境、数据、训练、避坑、落地五步拆开。2. YOLOv5 环境配置从 conda 建环境到 PyTorch 选型按这个顺序装不会错对这套项目来说环境问题的优先级高于代码问题。绝大多数“训练报错”其实都发生在环境层面torch 装成了 CPU 版、依赖包版本被默认行为改了、pycocotools 编译失败。所以我会把环境配置放在第一步而且只讲一个能稳定复现的顺序。2.1 版本选型为什么是 Python 3.8 与 PyTorch 1.8YOLOv5 官方对 Python 3.7 到 3.9 都支持但我一般会推荐 3.8。原因很实际numpy 和 pycocotools 在 3.8 下遇到 wheel 兼容问题的概率最低网上大量现成的报错记录也都基于这个版本。PyTorch 方面YOLOv5 的老版本代码里大量默认参数是按 1.8/1.9 调的这个区间里 1.8.0 的 cu111 版属于最不出错的一档和 1.9.0 相比没有明显的检测精度差异。组件推荐版本说明Python3.8与依赖包兼容性最好用 conda 直接创建PyTorch1.8.0 / 1.9.0cu111 构建对应 CUDA 11.1 驱动torchvision0.9.0 / 0.10.0必须与 torch 同源安装不能混版本显卡驱动的 CUDA 支持11.1 及以上驱动向下兼容不需要单独装 CUDA Toolkit显卡显存4GB 以上没有 N 卡也能跑只是训练时间会拉长很多这里有一个容易误解的点装了 cu111 版的 PyTorch不等于你必须手动安装 CUDA 11.1。只要显卡驱动支持相应版本PyTorch 会把运行时需要的那部分 CUDA 组件一起带过来。如果你的机器是纯 CPU 环境也可以把 torch 换成 CPU 版训练流程能正常跑只是速度慢到没法接受通常不建议拿 CPU 版本训练这套数据集。2.2 conda 建环境与安装顺序我的操作顺序从来都是固定的先建虚拟环境再装 PyTorch最后装项目依赖。顺序乱掉的典型后果是先装 requirements.txt、再补 torchnumpy 等核心库会被来回替换版本最后 import 时弹出莫名其妙的冲突。conda create -n yolov5 python3.8 -y conda activate yolov5创建名为 yolov5 的 Python 3.8 虚拟环境这一步其实就是把系统 Python 和项目 Python 隔离。很多初学者图省事直接在 base 环境里装等第二个项目要用 PyTorch 2.x 时两个项目的 numpy、OpenCV 版本就会互相打架到时候清理起来比重新建环境还费劲。激活环境后命令行前缀会变成(yolov5)。如果前缀没出现说明 activate 没生效Windows 上常见原因是没执行过 conda init重启终端再试一次即可。pip install torch1.8.0 torchvision0.9.0 --extra-index-url https://download.pytorch.org/whl/cu111这条命令里的--extra-index-url是指定 PyTorch 官方 wheel 源。如果不加pip 默认从 PyPI 拉取很可能装成 CPU 版一旦装成 CPU 版torch.cuda.is_available()永远是 False训练代码不报错但性能惨不忍睹。装完这一步建议顺手打印一下版本确认两个包来自同一个构建python -c import torch, torchvision; print(torch.__version__, torchvision.__version__)如果打印出的 torchvision 是 CPU 版本而 torch 是 cu111就是版本对不齐重新把两个包写在一起安装一遍即可。这个问题我在帮别人排查环境时见过很多次基本都是分开装导致的。然后回到项目根目录装 YOLOv5 本身的依赖cd yolov5-master pip install -r requirements.txtrequirements.txt 里已经写好了 YOLOv5 需要的包和版本区间不用自己逐个挑。中途如果有包因为网络超时失败重跑同一条命令即可pip 会跳过已安装项只补装剩余部分。2.3 验证环境是否真正就绪环境装完不能直接开训先做一个 30 秒的连通性验证。第一确认 torch 能看到 GPU第二确认 YOLOv5 能正常加载预训练权重并完成一次推理。python -c import torch; print(torch.__version__); print(torch.cuda.is_available())期望输出是1.8.0和True。如果你看到 False不要往下走先解决 GPU 不可用问题否则后面训练一个 epoch 的时间会让人崩溃。接下来跑一次官方示例图python detect.py --weights yolov5s.pt --source data/images/bus.jpg --img 640第一次运行会自动下载 yolov5s.pt 权重。跑完后打开runs/detect/exp目录下的输出图能看到行人、公交车都有检测框说明环境、权重、源码三者已经打通。如果这一步直接报错优先检查权重是否下载完整再回过头看 requirements 是否装干净。注意detect 每次运行都会生成新的 exp 序号目录不会覆盖旧结果适合保留多组对比输出。3. 数据集格式与划分理解 6105 张图里的 images/labels 再重拆拿到这套项目后第一件事不是看训练代码而是打开数据集目录把 images 和 labels 的对应关系读明白。标注好的数据不是拿到手就能直接用文件名匹配、坐标格式、类别顺序任何一个地方出错训练时都不会立刻报错只会让 mAP 悄悄掉下去。3.1 先对照目录结构完整项目里的数据集通常是这样组织的datasets/traffic/ ├── images/ │ ├── train/ │ │ ├── img_00001.jpg │ │ └── ... │ └── val/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── img_00001.txt │ │ └── ... │ └── val/ │ └── ... └── traffic.yaml这里有一个常见误区图片在 images/train 下对应的标签必须在 labels/train 下并且文件名相同、后缀不同。如果标签放错了子目录train.py 不会报“找不到标签”而是静默跳过这张图片等价于你丢了一部分训练样本。3.2 标签文件里的五个数字打开任意一个 txt 标注文件内容长这样0 0.300781 0.254883 0.044047 0.042969 1 0.682424 0.925781 0.032422 0.037109 2 0.507812 0.710938 0.019531 0.021484每一行对应一个目标框。第一个数字是类别 id从 0 开始后面四个值依次是 x_center、y_center、width、height。四个数值都用图片宽高做了归一化范围在 0 到 1 之间。交通标志在这套数据集里普遍偏小所以框的宽度通常只有 0.02 到 0.05这是小目标检测的典型特征也决定了后面训练时不要轻易把输入分辨率降到 320。第一列类别 id 必须与 data.yaml 中 names 列表的索引对齐。如果 txt 里写的是 3而 names 第三位是 crosswalk模型训练时就把这个框当成行人横道标志处理。类别不对齐不会报错只会让验证阶段的混淆矩阵彻底乱掉这个细节值得多花两分钟核对。3.3 先做一次数据体检即使是标注好的数据集也未必百分百干净。缺标签、空标签、坐标越界是最常见的三类问题训练前先用脚本把它们找出来import os from pathlib import Path img_dir Path(datasets/traffic/images/train) label_dir Path(datasets/traffic/labels/train) total 0 missing 0 empty 0 invalid 0 for img_path in sorted(img_dir.glob(*.jpg)): total 1 label_path label_dir / (img_path.stem .txt) if not label_path.exists(): missing 1 continue lines label_path.read_text().strip().splitlines() if len(lines) 0: empty 1 continue for line in lines: parts line.split() # 必须正好五个字段class x y w h if len(parts) ! 5: invalid 1 break try: x, y, w, h float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if x 0 or y 0 or w 1 or h 1 or w 0.9: invalid 1 break except ValueError: invalid 1 break print(ftotal{total}, missing{missing}, empty{empty}, invalid{invalid})脚本逻辑很直接图片存在但同名 txt 不存在记为 missingtxt 存在但没有任何行记为 empty坐标格式不对或归一化范围明显越界的记为 invalid。如果三个指标都是 0可以放心进入训练环节如果有脏数据先清理再继续不要让异常样本干扰 loss 计算。3.4 重新划分 train/val 的脚本如果拿到的项目里数据集合并成了一个总目录就需要自己拆分成 train 和 val。常用的划分比例是 9:1用固定随机种子保证每次划分结果一致import os import random from pathlib import Path root Path(datasets/traffic) images sorted((root / images).glob(*.jpg)) labels sorted((root / labels).glob(*.txt)) assert len(images) len(labels), 图片与标签数量不一致 random.seed(42) random.shuffle(images) split int(len(images) * 0.9) train_imgs images[:split] val_imgs images[split:] for split_name, img_list in [(train, train_imgs), (val, val_imgs)]: img_dir root / images / split_name label_dir root / labels / split_name img_dir.mkdir(parentsTrue, exist_okTrue) label_dir.mkdir(parentsTrue, exist_okTrue) for img in img_list: label root / labels / (img.stem .txt) if not label.exists(): continue os.replace(str(img), str(img_dir / img.name)) os.replace(str(label), str(label_dir / label.name))我之所以先断言图片和标签数量一致再打乱是防止划分后的子集里出现大量匹配不上的文件。用os.replace而不是 copy是为避免同一张图同时出现在两个目录中被重复读取。固定random.seed(42)的做法是常见的可复现手段下次重新执行脚本能得到完全相同的划分。还有一点容易被忽略如果某个类别的目标很少9:1 随机划分可能把这类样本全部分到训练集验证集里完全看不到。保险做法是统计每个类别在 txt 中的出现次数保证 val 至少包含每个类别一例如果类别不均衡严重就得考虑按类别分层抽样。4. 跑通训练data.yaml、超参数、预训练权重一次讲清训练阶段要做的事其实就三件确认配置文件、选对预训练权重、给出合理的训练参数。这套项目已经自带了交通标志专用的 data.yaml但路径和类别名还是要自己核对一遍直接开训经常会在第 3 个 epoch 后才发现数据没对上。4.1 data.yaml 的四个关键字段train: datasets/traffic/images/train val: datasets/traffic/images/val nc: 9 names: [speed_limit, no_entry, stop, crosswalk, no_parking, do_not_enter, right_only, left_only, yield]train 和 val 的路径是相对 YOLOv5 项目根目录写的不是相对 datasets 目录也不是绝对路径。如果你把项目文件夹换过位置绝对路径就会直接失效。names 里的类别名建议全部用英文中文类别名在部分系统上会出显示乱码不影响训练但影响日志排查。nc 必须与 names 列表长度一致不一致时 train.py 会直接抛异常这个还算友好。如果你不确定类别顺序打开一个 labels 下的 txt看第一列最大值是几再去和 data.yaml 的 names 对齐。我自己碰上过标注文件从 0 到 8、而 data.yaml 里写了 10 个类别的情况训练不报错但 mAP 始终上不去浪费了一整天。4.2 训练命令与参数选择推荐起点是 yolov5s 权重它在精度和速度之间最平衡。交通标志背景相对简单不需要一上来就用 yolov5l 这种大模型。python train.py \ --data datasets/traffic.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 120 \ --patience 20 \ --cache ram \ --project runs/train \ --name traffic_v1参数值作用datadatasets/traffic.yaml数据集配置入口weightsyolov5s.pt预训练权重推荐从 s 开始img640训练输入边长交通标志小目标建议保持 640batch16显存占用4G 显卡改 8epochs120训练轮数patience20连续 20 轮验证无提升就早停cacheram把图片缓存进内存减少磁盘 IO参数里最值得解释的是 img。交通标志在整张图中的像素占比很小如果为了省显存把 img 降到 320小目标的特征会在下采样过程中被稀释最终表现为漏检。显存不够时优先降 batch而不是降分辨率。batch 减半后收敛速度会变慢可以顺带把 epochs 加到 150python train.py --data datasets/traffic.yaml --weights yolov5s.pt --img 640 --batch 8 --epochs 150如果你的显卡显存只有 4Gbatch 8 能跑通但训练时间会明显变长再不够就换--cache disk用磁盘缓存换内存占用。4.3 训练中断恢复训练过程中会因为断电、系统休眠、显存溢出被 kill这不代表前功尽弃。YOLOv5 每次保存 last.pt恢复训练用一条命令python train.py --resume runs/train/traffic_v1/weights/last.pt它会自动读取训练状态包括当前的 epoch、优化器参数、学习率调度器位置。我一般习惯把 project 和 name 固定下来这样中途断掉时能准确找到 last.pt 路径不用翻来翻去。4.4 训练日志怎么看训练开始后每轮终端会打印 Precision、Recall、mAP_0.5 等指标。主要看 val/mAP_0.5 有没有随 epoch 稳步上升。如果前 30 轮 mAP 一直低于 0.1先别急着怀疑模型排查顺序是data.yaml 的 nc 和 names 是否对齐、数据集目录路径是否正确、labels 里是否有空文件。这三个问题不解决换任何模型都白搭。5. 避坑训练和验证阶段我最常处理的四个翻车现场这部分每一条都是真实遇到过的现象、原因、解决方式一起写省得你一个个去搜错误码。5.1 现象训练一启动就报 FileNotFoundError训练开始后约 3 秒报FileNotFoundError: datasets/traffic/images/train。原因基本是 data.yaml 里用了绝对路径而整个项目文件夹在下载后移动过位置或者是 Linux 下路径大小写不一致Windows 不区分大小写但在 Ubuntu 上会直接失败。解决方式是先把 data.yaml 里的路径改成相对 YOLOv5 项目根目录的写法然后确认目录真实存在ls datasets/traffic/images/train没有输出就是路径写错了先手动把数据集目录放到项目根目录下再改 yaml。5.2 现象训练 loss 变成 NaNtrain 日志里 loss 在前几个 epoch 直接变成 nan之后 mAP 一直为 0。常见原因有三个一是显存不够时进程被系统杀掉但日志表现为 NaN二是 learning rate 超出模型承受范围三是 labels 里存在空 txt 或全 0 坐标导致 loss 计算出现异常。解决的顺序很明确先把空标签和脏数据清掉再检查学习率。default hyp 文件里的 lr0 是 0.01如果自定义过 hyp 可以换成更保守的配置python train.py --data datasets/traffic.yaml --weights yolov5s.pt --img 640 --batch 8 --epochs 150 --hyp data/hyps/hyp.scratch-low.yamlhyp.scratch-low.yaml里的学习率更低适合数据集样本质量参差的情况。如果换成 low 之后 loss 恢复正常说明你之前用的学习率偏高了。5.3 现象验证集 mAP 很高但实际图片里小标志全部漏检验证 mAP 能到 0.85可拿一张随手拍的照片跑 detect远处的限速标志完全没有框。原因在 detect.py 的默认参数--conf-thres默认 0.25小目标在低分辨率下置信度天然偏低超过 0.25 的很少--iou-thres默认 0.45 也可能让密集小框互相抑制。解决方式是检测阶段降低置信度阈值python detect.py --weights runs/train/traffic_v1/weights/best.pt --source test.jpg --conf-thres 0.05 --iou-thres 0.45 --img 6400.05 会把大量低置信度框也放出来如果误检太多再逐步回调到 0.1。部署场景里可以做二次筛选而不是先提高阈值把真目标滤掉。5.4 现象OpenCV 读不进中文路径的图片Windows 下图片或视频文件名带中文时detect.py 会被 cv2.imread 卡住报类似imread_的底层错误。原因是 OpenCV 的 C 接口对中文路径支持不完善不是 YOLOv5 本身的问题。解决方式是用 numpy 读入再转成 BGR 格式绕开 cv2.imread 的路径解析import cv2 import numpy as np data np.fromfile(测试图片.jpg, dtypenp.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR)这是一段通用小工具我习惯把它封装成read_image.py之后所有需要读图的地方都走这个包装函数。遇到批量文件带中文名时能省掉大量手动改名的功夫。6. 模型真要落地还得会这招摄像头推理与 ONNX 导出训练完的模型如果只停留在验证集指标上离“能演示”还差一步。交通标志识别的实际演示通常有两种场景用摄像头实时识别或对一段视频批量标注。YOLOv5 的 detect.py 把这两个入口都留好了关键是参数怎么配。python detect.py --weights runs/train/traffic_v1/weights/best.pt --source 0 --conf-thres 0.4 --img 640--source 0表示读取本机摄像头。直播场景和测试场景的取舍不一样摄像头上可以把阈值调高到 0.4 来抑制误检因为画面是连续帧偶尔漏一帧没关系。如果是视频文件把--source 0换成视频路径即可对长视频建议配合--vid-stride跳帧处理。如果要在没有 PyTorch 的机器上部署或者想进一步提速可以把权重导出成 ONNXpython export.py --weights runs/train/traffic_v1/weights/best.pt --include onnx --opset 11导出后用 ONNX Runtime 推理几乎不需要修改数据的预处理逻辑但推理速度和部署体积都会明显改善。三种方式的取舍可以简单对比如下方式依赖速度适用场景detect.py 原生推理PyTorch最慢调试模型ONNX Runtimeonnxruntime中等跨平台部署TensorRTN 卡 TensorRT最快边缘设备我的习惯是每次训练完先跑一遍测试集的图片再把 conf-thres 调到 0.05 验证真实场景视频确认没有系统性漏检后才把部署阈值调回到 0.4。从那以后我每次拿到这类 YOLOv5 项目都会强制把“环境、数据、阈值”这三个环节走一遍再谈优化。希望帮到你。本文还有配套的精品资源点击获取
返回列表