
简介YOLOv5交通标志识别检测项目是一套面向毕业设计、课程设计与期末大作业的完整资源包含数据集、源码和预训练模型帮助开发者快速掌握目标检测项目全流程避免从零搭建环境的繁琐。资源共266个文件以Python源码、yaml配置文件、pt模型权重及jpg/jpeg图片数据集为主另包含训练日志、结果统计csv、Dockerfile等支撑文件压缩包整体约423MB目录结构划分清晰方便按模块训练、评估与部署。目前已有252人学习下载项目经严格调试确保可运行代码附有详细注释新手也能轻松复现。系统功能完善、界面美观、操作简单整合数据预处理、模型训练、实时识别与结果导出等环节个人手打98分、导师认可度高既可直接用于毕设答辩也可作为课程设计的高分范例和二次开发基础。1. 从数据集到部署YOLOV5交通标志识别检测的完整落地路径每年毕业季交通标志识别都是目标检测方向的热门选题但绝大多数开源项目只给你一套能跑的代码数据清洗、标注格式转换、超参数调整和模型评估这些真正决定论文和工作量质量的环节往往被一笔带过。交通标志识别检测和通用目标检测最大的区别在于目标尺寸跨度极大、类别间外观高度相似、实拍场景光照复杂直接用默认配置训练很难拿到可观指标。本文从数据集组织、标注格式转换、YOLOV5训练调优到模型部署给出一条经过验证的完整路径适合正在做毕设或准备参加竞赛的开发者也适合想系统掌握YOLOV5训练全流程的工程师参考。重点会放在数据准备和超参数选择上这两块是拉开分数差距的关键。2. 交通标志识别数据集构建从公开源到YOLOV5格式的转换全流程2.1 公开数据集选型与适用场景分析交通标志识别领域有几类公开数据集选型会直接影响模型最终效果。TT100KTsinghua-Tencent 100K是国内使用最广泛的交通标志数据集包含上万张腾讯街景图像标注了上百个类别适合中文交通场景和毕设场景德国GTSRB/GTSDB数据集规模大、标注质量高但类别和国内标志差异明显适合做预训练或算法验证LISA数据集面向美国交通标志遮挡和光照变化丰富适合做鲁棒性验证此外还有KITTI这类包含交通标志的自动驾驶数据集不过主要面向多任务场景。选择数据集的第一个判断标准是任务匹配度如果毕设面向国内场景TT100K是首选。第二个标准是类别体系TT100K原版有45类以上很多类别样本量不足100直接训练会带来严重的类别不均衡问题常见做法是筛选出样本量充足的类别合并相近类别比如把不同限速标志归为speed_limit一类把禁止类标志归为prohibition。第三个标准是标注格式这些公开数据集原始标注各不相同需要统一转换为YOLOV5默认使用的YOLO格式也就是每个图像对应一个txt文件每一行是 class_id x_center y_center width height坐标全部归一化到0到1之间。对于没有现成数据集、需要自采数据的场景常见做法是先用手机或行车记录仪采集视频抽帧后使用LabelImg或LabelStudio进行人工标注。自采数据的优势是场景可控能覆盖学校周边、城市快速路、乡道等具体环境但标注成本高一个包含5000张图的数据集至少需要两天的人力投入。所以更推荐的路径是公开数据集做主体自采数据做补充用自采数据专门覆盖公开数据集中缺失或稀少的类别。2.2 标注格式转换与数据划分的Python实现2.2.1 TT100K标注转YOLO格式的核心代码拿到的TT100K原始标注是JSON格式每个图像对应一个标注对象标注信息包含类别、边界框坐标和是否遮挡、是否包含在远距离组等属性。转换脚本的核心逻辑是读入JSON、筛选可用类别、过滤小目标、计算归一化坐标并写入txt文件。import json import os import shutil from pathlib import Path # 只保留样本量充足的类别并做类别合并映射 CLASS_MAP { i2: prohibition, i4: prohibition, i5: prohibition, p3: warning, p5: warning, p6: warning, ph4: guide, ph5: guide, pl100: speed_limit, pl120: speed_limit, pl40: speed_limit, pl50: speed_limit, pl60: speed_limit, pl80: speed_limit, pl30: speed_limit, pl20: speed_limit, pl5: speed_limit, pl15: speed_limit, i8: prohibition, i9: prohibition, i10: prohibition } def convert_tt100k_to_yolo(json_path, img_dir, out_dir, min_size20): 将TT100K JSON标注转换为YOLO格式txt文件 with open(json_path, r, encodingutf-8) as f: data json.load(f) # 建立类别名到数字id的映射 categories list(set(CLASS_MAP.values())) cat2id {c: i for i, c in enumerate(categories)} for img_name, annos in data[imgs].items(): img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): continue txt_path os.path.join(out_dir, labels, Path(img_name).stem .txt) # 读取图片尺寸用于归一化 from PIL import Image w, h Image.open(img_path).size lines [] for obj in annos[objects]: category CLASS_MAP.get(obj[category]) if category is None: continue xmin, ymin, xmax, ymax obj[bbox] # 过滤过小目标避免噪声标签 if (xmax - xmin) min_size or (ymax - ymin) min_size: continue # 归一化到[0,1]区间 x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h width (xmax - xmin) / w height (ymax - ymin) / h lines.append(f{cat2id[category]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if lines: os.makedirs(os.path.dirname(txt_path), exist_okTrue) with open(txt_path, w) as f: f.write(\n.join(lines))这段代码有几个关键参数需要说明。min_size用来过滤小于20像素的边界框交通标志在街景图中经常只有十几像素大小这些样本人眼都难以辨认直接用来训练会干扰特征提取。类别映射CLASS_MAP是决定模型能力的核心合并相近类别能显著提升单类样本量对最终的mAP影响远大于模型结构的选择。坐标归一化必须基于原图尺寸计算如果图像被resize过需要在转换前同步修改尺寸信息。2.2.2 训练集/验证集划分与目录结构组织YOLOV5训练时会读取数据集配置文件中的train和val路径标准的目录结构是images和labels两个平级目录各自下面再按train/val分目录。数据划分要保证类别分布一致不能简单随机切分否则某些小样本类别可能全部落在训练集或验证集中。import random from pathlib import Path import shutil def split_dataset(img_dir, label_dir, train_ratio0.85, seed42): 按类别分布进行分层划分 random.seed(seed) images list(Path(img_dir).glob(*.jpg)) # 按标签文件中的第一个数字类别id分组 from collections import defaultdict class_groups defaultdict(list) for img in images: label_file Path(label_dir) / (img.stem .txt) if not label_file.exists(): continue with open(label_file) as f: first_line f.readline().strip() if first_line: cls_id first_line.split()[0] class_groups[cls_id].append(img) train_imgs, val_imgs [], [] for cls_id, items in class_groups.items(): random.shuffle(items) split_idx int(len(items) * train_ratio) train_imgs.extend(items[:split_idx]) val_imgs.extend(items[split_idx:]) # 复制到目标目录 def move_files(img_list, split_name): for img in img_list: dest_img Path(dataset/images) / split_name / img.name dest_label Path(dataset/labels) / split_name / (img.stem .txt) shutil.copy(img, dest_img) shutil.copy(Path(label_dir) / (img.stem .txt), dest_label) move_files(train_imgs, train) move_files(val_imgs, val) print(fTrain: {len(train_imgs)}, Val: {len(val_imgs)})这个分层划分逻辑保证了每个类别在训练集和验证集中的比例一致避免出现过拟合到某一类的情况。seed42可以复现实验论文中需要注明随机种子。划分比例train_ratio0.85适用于中小规模数据集如果数据量小于2000张建议提高到0.9毕竟验证集本身样本少会导致评估指标方差大。2.3 数据增强策略Mosaic与自动增强的取舍YOLOV5内置了Mosaic、MixUp、HSV扰动、随机翻转等多种增强策略。Mosaic把4张图拼接成一张相当于一个batch内同时看到4个不同场景对提升小目标检测和跨场景泛化效果明显。对于交通标志这种小目标密集的场景Mosaic是必须开启的。但Mosaic在交通标志场景有两个副作用需要留意。第一是拼接边界处的标志可能被切断导致标注框与图像内容不对齐默认的裁剪概率在毕设数据上建议调低可以在数据配置文件里单独设置。第二是Mosaic对归一化坐标的精度要求更高如果原始标注框本身有偏差增强后会放大误差。HSV扰动对交通标志这种颜色语义强烈的目标很有帮助因为实际场景中同一个标志在晨昏、逆光、阴雨下的色相和饱和度变化明显。建议hsv_h0.015、hsv_s0.7、hsv_v0.5比默认值略高。翻转策略中左右翻转对大部分标志是安全的但限速标志中的数字会被镜像如果类别区分依赖数字内容建议关闭翻转或只对非数字类使用。3. YOLOV5模型选型与训练配置从权重文件到命令行参数的逐项拆解3.1 模型规模选择s/m/l/x与输入分辨率的关系YOLOV5有n/s/m/l/x五个版本区别主要在深度倍数和宽度倍数。交通标志识别对实时性要求通常低于自动驾驶但对小目标召回率要求高所以一般选yolov5s或yolov5m作为基线。yolov5s在COCO上mAP约37模型体积约14MB推理速度快yolov5m体积约40MB精度提升约3个百分点但训练时间翻倍。选型还要看显存。输入分辨率设为640时yolov5s的batch_size为32大约需要8GB显存yolov5m需要12GB左右。如果只有6GB显存建议yolov5s加batch_size16或者开启梯度累积。输入分辨率对交通标志的检测效果影响很大默认640可以满足大多数场景但TT100K中有大量小于32像素的标志把输入分辨率提升到960或1280能显著改善小目标召回代价是训练时间增加到原来的2到3倍。最新版的YOLOV5也提供了--weights参数支持加载不同预训练权重。COCO预训练权重对通用特征有很好的初始化作用交通标志虽然是新类别但底层纹理、边缘、颜色特征可以迁移强烈建议使用COCO预训练权重而不是随机初始化训练。如果数据集中类别和COCO差异较大可设置--freeze-layers微调前面几层但其实现有YOLOV5在全部层参与训练时收敛也很快一般不需要冻结层。3.2 自定义数据集的yaml配置与目录映射创建数据集配置文件traffic_sign.yaml放在yolov5/data/目录下# 数据集的根目录路径推荐写绝对路径避免相对路径带来的问题 path: /home/user/projects/traffic_sign_dataset train: images/train val: images/val # 类别数量与类别名称列表顺序必须与标注txt中的数字id一致 nc: 4 names: 0: speed_limit 1: prohibition 2: warning 3: guide这个文件里最容易出错的是path字段使用相对路径时YOLOV5会基于当前工作目录去拼路径如果在其他目录下启动训练就会报错。建议直接用绝对路径。nc必须和标注文件中的最大类别id1相等如果标注中出现了id4但nc写了4训练会在数据加载阶段直接报错或静默忽略。names的顺序不需要和任何外部规范一致但一旦开始训练就不要修改否则模型输出的类别含义会全部错位。3.3 训练命令的关键参数解读与推荐配置训练启动命令是python train.py \ --data traffic_sign.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --cache ram \ --device 0 \ --name traffic_sign_run1每个参数的选择理由如下。--img 640是精度与训练速度的平衡点如果验证集中出现大量mAP0.5为0但人工检查其实检测到了的情况说明小目标丢失可以提升到960。--batch-size 16在8GB显存下是比较安全的配置如果显存溢出优先降batch而不是降分辨率。--cache ram把图像一次性读入内存避免每个epoch都从磁盘读取能缩短30%左右的训练时间代价是占用约等于数据集大小的内存。--epochs 100对交通标志数据集通常足够如果训练30个epoch后验证集mAP还在明显上升续跑即可。3.4 超参数文件中的必调项学习率、锚框与损失权重YOLOV5的超参数文件hyp.scratch-low.yaml里有几十个参数但真正需要手工调整的只有几个。lr0初始学习率默认0.01配合batch-size16通常没问题但如果batch-size降到8梯度估计噪声增大建议lr00.005。lrf是最终学习率与初始学习率的比值默认0.01表示余弦退火最终降到0.0001。锚框计算是交通标志场景容易忽略的一环。YOLOV5默认锚框基于COCO数据集计算COCO中的目标普遍偏大而交通标志是小目标直接使用默认锚框会导致回归头需要更大的偏移量才能拟合真实框。训练时添加--noautoanchor可跳过自动计算但更推荐让YOLOV5在训练前自动用k-means在训练集上重算锚框这个逻辑在utils/autoanchor.py中训练时会打印新的锚框值并提示anchor与目标尺寸的匹配度。配合锚框还需要关注--multi-scale参数开启后输入尺寸在0.5到1.5倍之间随机变化对小目标检测有正则化效果但训练时间会增长。4. 训练过程监控与精度调优解决过拟合和小目标漏检的实战手法4.1 训练日志与tensorboard监控的关键指标解读训练启动后会输出每个epoch的loss、精度、召回率和mAP。需要重点关注三个值box_loss代表边界框回归损失持续不降说明定位能力弱obj_loss代表置信度损失交通标志类别少但背景复杂这个值通常会偏高cls_loss代表分类损失如果这个值在后期仍高于box_loss说明类别间特征区分度不足。使用tensorboard查看更直观tensorboard --logdir runs监控时看几个关键信号。第一个是验证集mAP0.5是否持续上升但mAP0.5:0.95停滞说明预测框定位还不够精细第二个是训练集loss和验证集loss的gap是否超过30%超过说明过拟合需要增强数据或减少epoch第三个是每个类别的AP直方图如果某个类别AP显著低于均值说明该类样本量不足或特征与其他类别混淆。4.2 针对小目标的三个有效手段SAHI切片、锚框重算与多尺度训练交通标志检测的典型痛点是mAP0.5可以到0.8以上但mAP0.5:0.95只有0.3左右原因就是小目标占比高。实测有效的手段有三个。第一个是SAHISlicing Aided Hyper Inference推理时把原图切成多个有重叠的切片每个切片单独检测再合并结果。这种方式对小目标检测提升非常明显TT100K上采用SAHI后mAP0.5:0.95能提升8到12个百分点代价是推理时间翻倍。from sahi.model import Yolov5DetectionModel from sahi.predict import get_sliced_prediction model Yolov5DetectionModel( model_pathruns/train/traffic_sign_run1/weights/best.pt, confidence_threshold0.3, devicecuda:0 ) result get_sliced_prediction( image_pathtest_image.jpg, detection_modelmodel, slice_height320, slice_width320, overlap_height_ratio0.2, overlap_width_ratio0.2 )slice_height和slice_width需要根据目标尺寸设定交通标志如果大多小于64像素切片尺寸设为320效果较好overlap_ratio用于避免目标被切片边界切断0.2是经验值。切片数量越多推理速度越慢所以SAHI更适合离线评估或对速度不敏感的预测场景。第二个手段是重新计算锚框。在train.py训练时如果看到打印的anchors与实际目标尺寸匹配度不高可以手动运行python utils/autoanchor.py --data traffic_sign.yaml --img 640查看建议锚框然后在yaml的anchors字段中替换默认值这相当于给回归头一个更好的初值能加速收敛。第三个手段是开启多尺度训练python train.py --data traffic_sign.yaml --weights yolov5s.pt --img 640 --multi-scale多尺度训练本质是数据增强的扩展让模型在不同输入尺寸下都能保持检测能力对小目标鲁棒性提升明显。如果训练时间紧张可以只在前30个epoch开启后面关闭继续精调和稳定收敛。4.3 早期停止与最优权重保存策略YOLOV5默认保存last.pt和best.pt两个权重best.pt是基于验证集mAP0.5:0.95的最高分保存的。实际操作中训练后期可能会出现验证集AP微降但实际图像预测效果反而更好的情况这是因为mAP指标与主观感受存在偏差。我的做法是保存最后10个epoch的权重用一段固定的测试视频或图片集侧试选出视觉效果最好的版本作为最终模型。早期停止可避免过拟合。在train.py中patience参数控制连续多少个epoch验证集指标不提升就停止训练默认100对毕设时间预算太长建议设为30。如果训练在第40个epoch因为过拟合开始下降这时回退到best.pt并降低学习率续跑20个epoch往往能再提升1到2个点的mAP。4.4 常见训练报错与显存、路径问题的排错清单训练过程中最常见的报错是CUDA out of memory。解决顺序是先降batch-size到8再降--img到512最后考虑开启--gradient-accumulation梯度累积。梯度累积的语义是用多个小batch模拟大batch比如--batch-size 8 --gradient-accumulation 4等效于32的batch但每个batch内的BatchNorm统计仍然基于8张图所以累积不会完全等同真32。交通标志数据集目标普遍较小BatchNorm统计量受影响有限可以接受。路径类报错集中在FileNotFoundError和AssertionError: train: No labels in ...。前者通常是因为yaml中path写错或labels目录结构不对后者是因为图片有对应文件但labels目录为空或标注txt的格式不正确。检查思路是随机挑一张图人工查看其对应的txt内容cat dataset/labels/train/xxx.txt如果txt内容为空文件说明标注在转换时被过滤掉了检查过滤条件里的最小目标尺寸如果txt内容是10 0.5 0.5 0.1 0.1这样的格式类别id10超出nc范围检查类别映射。另外要注意Windows和Linux的路径分隔符差异同一个yaml在Windows训练时用\在Linux上要用/建议统一使用pathlib处理。5. 模型部署与推理脚本从best.pt到实际检测的完整链路5.1 使用detect.py进行批量推理与结果输出训练完成后最直接的使用方式是通过YOLOV5自带的detect.py脚本。python detect.py \ --weights runs/train/traffic_sign_run1/weights/best.pt \ --source test_images/ \ --img 640 \ --conf-thres 0.3 \ --iou-thres 0.45 \ --save-txt \ --save-conf \ --project runs/detect \ --name test_output--conf-thres是置信度阈值交通标志检测建议设在0.25到0.3之间因为小目标在降采样过程中特征弱置信度普遍偏低设太高会漏检。--iou-thres是NMS的IoU阈值0.45是默认值当标志密集排列时例如连续多个限速标志可以降到0.4减少相邻框被抑制的情况。--save-txt会同时输出检测结果的标注txt文件方便后续评估或生成可视化报告。推理结果会保存为带标注框的图片和对应txt文件txt每行是class_id x_center y_center width height conf这个格式可以直接作为伪标签进行半监督学习迭代。5.2 模型导出与推理加速ONNX与CPU部署训练得到的best.pt是PyTorch格式在嵌入式设备或CPU环境下直接推理效率不够。常见做法是先导出为ONNX格式再用ONNX Runtime推理。python export.py \ --weights runs/train/traffic_sign_run1/weights/best.pt \ --include onnx \ --img 640 \ --opset 12导出后可以用下面的脚本做ONNX推理import cv2 import numpy as np import onnxruntime as ort session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img cv2.imread(test.jpg) img_resized cv2.resize(img, (640, 640)) blob cv2.dnn.blobFromImage(img_resized, 1/255.0, (640, 640), swapRBTrue) # 输入形状为 [1,3,640,640] outputs session.run(None, {input_name: blob})ONNX模型不包含训练时的预处理逻辑所以输入图像的归一化和通道顺序必须自己处理。cv2.dnn.blobFromImage的swapRB参数是因为OpenCV默认BGR顺序而YOLOV5训练时用的是RGB。解码输出时ONNX的输出形状是[1, 25200, 5nc]25200是三个尺度特征图上锚框的总数95是85580类在COCO预训练模型中的值如果从yolov5s.pt从头训练4类最后维度就是9写解码逻辑时需要按实际类别数修改。ONNX导出后可用onnxruntime-gpu或tensorrt进一步加速。TensorRT导出需要--include engine生成过程会做层融合和量化推理速度比ONNX Runtime快数倍适合部署到jetson nano这类边缘设备。如果使用--half开启FP16半精度速度还能继续提升但需要注意个别算子可能不支持半精度导出后要实测验证输出与FP32差距是否在可接受范围内。5.3 基于训练结果的增量扩展新增类别与转向检测完成一个基础版本的交通标志识别系统后通常会被要求增加夜间场景识别或新增类别。新增类别时不需要重新训练全部数据推荐做法是保留已有模型权重在原有权重基础上用新类别数据继续微调训练。将原有数据集中新类别的标注补上修改yaml中的nc和names直接运行--weights best.pt继续训练即可。这里有一个细节原有权重输出层的维度与新配置不匹配所以YOLOV5会自动裁剪掉最后一层重新初始化。这种增量训练比从COCO权重冷启动快得多而且旧类别知识不会遗忘太严重。夜间场景的增强可以在推理阶段处理而不是重训模型。推理前用autoencoder或CLAHE做光照归一化再把增强图送入模型。实测在TT100K夜间子集上仅做CLAHE增强就能让AP提升3到5个百分点。如果追求极致效果可以采用昼夜域自适应用训练好的白天模型在少量夜间数据上做半监督teacher-student蒸馏这已经超出毕设范畴但作为延伸方向非常契合高分答辩的加分点。本文还有配套的精品资源点击获取