
简介本资源面向计算机视觉入门与进阶开发者提供一套可直接运行的YOLOv5车辆行人检测完整方案解决交通场景下person与car两类目标的检测需求。包内含yolov5s与yolov5m两种训练好的权重mAP达90%以上并附PR曲线、loss曲线等训练过程记录方便复盘调参效果。资源共约2000个文件以jpg图像、xml与txt双格式标注、py源码为主另有pt权重、yaml配置、ui界面文件及少量mp4演示视频压缩包约640MB目录按数据集与代码分文件夹存放结构清晰。配套PyQt界面支持图片、视频与摄像头三种检测模式可自由切换输入源。同时提供5000余张交通场景行人车辆数据集标注同时保留txt与xml两种格式便于直接用于训练或格式转换。目前已有5802人学习下载适合课程设计、毕业设计及检测项目快速落地参考。1. 从一份 5000 张的车辆行人数据集说起YOLOv5 检测 PyQt 界面到底能落地成什么手上有一批 5000 张标注好的车辆行人检测数据集想把它变成一个能双击运行、点按钮就能出检测框的桌面工具这是很多做毕设、做安防小工具、做园区车辆统计的工程师真实的需求场景。YOLOv5 车辆行人检测这条路线之所以被反复提起不是因为它最新而是因为它足够稳模型结构成熟、训练脚本开箱即用、导出 ONNX 或 TorchScript 后接 PyQt 界面几乎没有额外依赖负担。整套方案要解决的核心问题就三件事——用 5000 张车辆行人检测数据集把 YOLOv5 练到一个可用的 mAP把练好的权重封装成推理接口再用 PyQt 做一个能选图、能选视频、能实时显示检测结果的界面。适合谁适合已经会一点 Python、装过 conda 环境、但没完整跑通过「数据集 → 训练 → 推理 → 界面」全链路的人。下面按我实际做过的顺序把每一步的参数、坑和验证方法讲清楚。2. 数据集与标签格式5000 张车辆行人数据怎么整理才不返工2.1 先确认标注格式再决定要不要转换拿到一份车辆行人检测数据集第一件事不是急着写 data.yaml而是打开标注文件看格式。常见的有三种Pascal VOC 的 XML、COCO 的 JSON、YOLO 的 txt。YOLOv5 只认最后一种每行是class_id x_center y_center width height且全部归一化到 0~1。如果数据集给的是 VOC XML就得转如果是 COCO JSON也得转。我一般会先跑一段统计脚本把类别分布和框的宽高分布打出来因为车辆行人检测里「人」的框通常又高又窄「车」的框又宽又扁这个分布直接决定后面 anchor 和输入尺寸怎么选。import os, glob from collections import Counter # 统计 YOLO 格式标签的类别分布与框宽高 label_dir datasets/vehicle_person/labels/train cls_counter Counter() w_list, h_list [], [] for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue # 跳过空行或异常行 c, x, y, w, h int(parts[0]), *map(float, parts[1:]) cls_counter[c] 1 w_list.append(w) h_list.append(h) print(类别分布:, cls_counter) print(平均框宽:, sum(w_list)/len(w_list)) print(平均框高:, sum(h_list)/len(h_list))这段脚本的作用是给你一个量化依据如果某一类样本数不到总数的 5%训练时就要考虑过采样或者调类别权重如果平均框宽高都小于 0.05说明目标偏小输入尺寸就不能压到 416得用 640 甚至更大。参数上label_dir指向你的标签目录类别 id 从 0 开始车辆行人两类一般是 0person、1vehicle具体以你数据集为准不要照抄。2.2 目录结构与 data.yaml 的写法YOLOv5 对目录结构有约定我一般整理成下面这样训练、验证、测试三份互不重叠datasets/vehicle_person/ ├── images/ │ ├── train/ # 约 3500 张 │ ├── val/ # 约 1000 张 │ └── test/ # 约 500 张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml5000 张按 7:2:1 切是比较稳的比例验证集少于 500 张时 mAP 波动会很大看不出真实水平。data.yaml 只需要四行核心内容path: ../datasets/vehicle_person train: images/train val: images/val test: images/test nc: 2 names: [person, vehicle]nc是类别数names的顺序必须和标签里的 class_id 严格对应顺序错了模型会把车认成人而且 loss 还能降这种玄学问题排查起来最费时间。切分脚本我一般用随机种子固定保证每次复现一致import random, shutil, os random.seed(42) # 固定种子保证切分可复现 imgs sorted(os.listdir(raw/images)) random.shuffle(imgs) n len(imgs) splits {train: imgs[:int(n*0.7)], val: imgs[int(n*0.7):int(n*0.9)], test: imgs[int(n*0.9):]} for split, files in splits.items(): for f in files: shutil.copy(fraw/images/{f}, fdatasets/vehicle_person/images/{split}/{f}) shutil.copy(fraw/labels/{f[:-4]}.txt, fdatasets/vehicle_person/labels/{split}/{f[:-4]}.txt)提示切分前先检查有没有同一段视频抽帧出来的近似重复图重复图跨 train/val 会导致验证指标虚高实际部署时掉点。2.3 数据增强的取舍车辆行人场景别乱开YOLOv5 默认开了 mosaic、HSV、翻转、缩放。车辆行人检测里水平翻转基本安全但垂直翻转要慎用——人倒过来在真实监控里几乎不出现开了反而引入噪声。mosaic 对小目标友好但如果你的数据集里车和人普遍偏大mosaic 把四张图拼一起会让目标变小训练前期 loss 震荡明显。我的习惯是前 50 个 epoch 开 mosaic后面关掉做微调用--close-mosaic 50控制。HSV 增强里hsv_h别超过 0.015色相变化太大会让车身颜色失真对以颜色区分车型的任务不利。3. YOLOv5 训练从环境配置到练好的权重3.1 环境配置与依赖版本YOLOv5 对 PyTorch 和 CUDA 版本比较敏感我踩过的坑是 torch 版本和 torchvision 不匹配导致torch.load报错。稳妥做法是用 conda 建独立环境先装匹配显卡驱动的 CUDA 版 torch再装 requirements。conda create -n yolov5_veh python3.9 -y conda activate yolov5_veh # 按你的 CUDA 版本选对应命令这里以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt装完先跑python -c import torch; print(torch.cuda.is_available())返回 True 才算环境通了。这一步不通后面训练会默默退回 CPU速度差几十倍很多人以为是模型慢其实是环境没配好。3.2 训练命令与关键超参数车辆行人检测我一般从 yolov5s 起步5000 张数据量用 s 或 m 足够n 容易欠拟合l/x 容易过拟合且推理慢。训练命令python train.py \ --data datasets/vehicle_person/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --close-mosaic 50 \ --device 0 \ --project runs/train --name veh_person_s参数逐个说--img 640是输入尺寸前面统计过框偏小就别降--batch 16按显存调8G 显存跑 640 大概能到 16爆显存就减半并配合--accumulate--epochs 150对 5000 张是够的配合--patience 30早停--hyp用 low 增强配置车辆行人场景不需要太激进的增强--close-mosaic 50表示最后 100 个 epoch 关 mosaic让模型在真实分布上收敛。--weights yolov5s.pt是官方预训练权重从零训在 5000 张上很难收敛好。3.3 训练过程看什么指标训练日志里重点盯三个box_loss、obj_loss、mAP0.5。box_loss 持续下降说明框回归在学obj_loss 不降反升通常是标签有问题比如有框越界或宽高为 0mAP0.5 在验证集上到 0.85 以上对车辆行人这种两类任务算可用0.9 以上算好。如果训练集 mAP 高但验证集低就是过拟合减 epoch、加增强、或者换更小的模型。我一般会在runs/train/veh_person_s/下看results.png和confusion_matrix.png混淆矩阵能直接告诉你车有没有被认成人。# 用验证集跑一次评估拿到每类 AP import subprocess subprocess.run([ python, val.py, --data, datasets/vehicle_person/data.yaml, --weights, runs/train/veh_person_s/weights/best.pt, --img, 640, --batch, 16, --task, val ])val.py输出的metrics/mAP_0.5和metrics/mAP_0.5:0.95是判断权重好坏的硬指标best.pt是按验证集 mAP 存的last.pt是最后一轮部署一定用 best.pt。3.4 练好的权重怎么验证再交付拿到 best.pt 别急着接界面先用测试集跑一批图肉眼过一遍。我一般抽 50 张覆盖白天、夜间、遮挡、密集场景看漏检和误检集中在哪。如果夜间漏检多说明数据集夜间样本少要么补数据要么在推理时降 conf 阈值。验证脚本import torch model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/veh_person_s/weights/best.pt) model.conf 0.25 # 置信度阈值 model.iou 0.45 # NMS 的 IoU 阈值 results model(datasets/vehicle_person/images/test, size640) results.save(runs/detect/test_out)conf调低召回高但误检多调高反之车辆行人场景我一般从 0.25 起调iou控制 NMS 合并程度密集人群里调低到 0.4 能减少框被吞。这一步的输出图就是你和界面之间的验收标准图不过关界面做得再漂亮也没用。4. PyQt 界面把练好的模型包成能点的工具4.1 界面功能拆解与线程模型PyQt 做检测界面核心矛盾是「推理耗时」和「界面不能卡」。主线程只负责画界面和收信号推理必须丢到 QThread 里否则点一下按钮整个窗口就假死。功能上我一般做四块选图片、选视频、开始/停止、结果显示区。图片走单帧推理视频走逐帧推理两者共用同一个推理函数。下面是最小可用的推理线程from PyQt5.QtCore import QThread, pyqtSignal import cv2, torch class DetectThread(QThread): frame_signal pyqtSignal(object) # 把带框的帧发回主线程 def __init__(self, model, source, conf0.25, iou0.45): super().__init__() self.model model self.source source self.conf conf self.iou iou self.running True def run(self): cap cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame cap.read() if not ret: break # YOLOv5 接受 BGR numpy内部自己转 results self.model(frame, size640) results.conf, results.iou self.conf, self.iou annotated results.render()[0] # 画框后的帧 self.frame_signal.emit(annotated) cap.release() def stop(self): self.running False self.wait()frame_signal用object类型传 numpy 数组主线程收到后转 QImage 显示。running标志位是停止按钮的关键直接 kill 线程会崩。results.render()返回的是画好框的帧省得自己写画框逻辑。4.2 主窗口与信号槽连接主窗口负责布局和把线程信号接到显示控件上。用 QLabel 显示帧QPushButton 触发QSlider 调 conf 阈值。from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QFileDialog from PyQt5.QtGui import QImage, QPixmap import sys, torch class MainWindow(QMainWindow): def __init__(self): super().__init__() self.model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/veh_person_s/weights/best.pt) self.model.eval() self.label QLabel(等待选择文件) self.btn_img QPushButton(选择图片) self.btn_vid QPushButton(选择视频) self.btn_stop QPushButton(停止) layout QVBoxLayout() for w in (self.label, self.btn_img, self.btn_vid, self.btn_stop): layout.addWidget(w) container QWidget(); container.setLayout(layout) self.setCentralWidget(container) self.thread None self.btn_img.clicked.connect(lambda: self.start(image)) self.btn_vid.clicked.connect(lambda: self.start(video)) self.btn_stop.clicked.connect(self.stop) def start(self, mode): if mode image: path, _ QFileDialog.getOpenFileName(self, 选图片, , Images (*.jpg *.png)) else: path, _ QFileDialog.getOpenFileName(self, 选视频, , Videos (*.mp4 *.avi)) if not path: return self.thread DetectThread(self.model, path) self.thread.frame_signal.connect(self.show_frame) self.thread.start() def show_frame(self, frame): h, w, c frame.shape img QImage(frame.data, w, h, w*c, QImage.Format_BGR888) self.label.setPixmap(QPixmap.fromImage(img).scaled(self.label.size())) def stop(self): if self.thread: self.thread.stop() if __name__ __main__: app QApplication(sys.argv) win MainWindow(); win.show() sys.exit(app.exec_())QImage.Format_BGR888对应 OpenCV 的 BGR 顺序用错格式颜色会反。scaled保持显示自适应窗口。信号槽连接必须在thread.start()之前否则第一帧可能丢。4.3 打包成 exe 与依赖处理界面跑通后要交付用 PyInstaller 打包。YOLOv5 的坑在于torch.hub.load会去拉仓库打包后没网就崩。解决办法是提前把模型加载改成直接加载本地权重不走 hub# 打包前把模型加载改成显式路径避免运行时联网 import torch ckpt torch.load(weights/best.pt, map_locationcpu) model ckpt[model].float().eval()打包命令pyinstaller -F -w main.py --add-data weights;weights-w去掉控制台--add-data把权重打进去。打包后体积会到 1G 以上这是 torch 的代价接受不了就换 ONNX Runtime 推理能压到几百兆但精度要重新对齐。5. 避坑与排查这套方案最容易翻车的五个地方5.1 验证集 mAP 很高实际用起来全是漏检现象训练日志里 mAP0.5 到 0.92接上界面跑真实视频人和车漏一大片。原因验证集和训练集同分布但真实场景的光照、角度、分辨率差异大模型没泛化过去。解决从真实场景抽 100 张重新标一小份混进训练集做微调或者至少用这批图做一次独立评估别只看验证集数字。5.2 界面点开始后卡死任务管理器显示 CPU 占满现象点按钮后窗口无响应风扇狂转。原因推理写在了主线程或者 QThread 里又调了阻塞的 GUI 操作。解决确认推理在 QThread.run 里主线程只做显示另外torch.hub.load首次加载会联网也会卡提前加载好模型再启动线程。5.3 检测框颜色和类别对不上现象车被画成人的颜色或者标签文字错位。原因names顺序和训练时不一致或者 render 用的颜色表按索引取索引和类别没对齐。解决核对 data.yaml 的 names 顺序界面里显示类别时用model.names[int(cls)]取别硬编码。5.4 视频推理越来越慢内存持续上涨现象跑几分钟后帧率掉一半内存占用只增不减。原因每帧都新建 tensor 没释放或者 results 对象累积。解决推理循环里用with torch.no_grad():每帧处理完手动del results视频用cap.set(cv2.CAP_PROP_FRAME_WIDTH)降分辨率也能显著提速。5.5 打包后报找不到 best.pt现象源码跑得好好的打包成 exe 双击就报文件不存在。原因PyInstaller 打包后路径变了相对路径失效。解决用sys._MEIPASS拼资源路径或者把权重放 exe 同级目录用绝对路径读--add-data的路径分隔符在 Windows 是分号Linux 是冒号别写错。6. 进阶把 conf 阈值做成自适应让夜间和白天都不翻车固定 conf 阈值是这套方案最大的短板。白天光照好0.25 够用夜间噪点多同一个阈值会冒出一堆误检。我后来改成按帧亮度动态调阈值先算帧的平均亮度低于某个值就把 conf 提到 0.4高于则降回 0.25。这个技巧不复杂但实测能把夜间误检压掉三成左右。import cv2, numpy as np def adaptive_conf(frame, base0.25): gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) brightness np.mean(gray) # 0~255 if brightness 60: # 夜间/暗光 return min(base 0.15, 0.6) elif brightness 180: # 过曝 return base 0.05 return base亮度阈值 60 和 180 是我在几个监控场景里试出来的经验值你的数据如果偏室内得重新标定。另一个进阶方向是把 PyQt 界面里的 conf 和 iou 做成滑块实时调用户自己找平衡点比写死更实用。滑块拖动时只改model.conf不用重载模型响应很快。验证这套自适应有没有用别只看感觉拿同一段夜间视频分别跑固定阈值和自适应统计误检框数量数字会告诉你答案。我自己踩过的最大教训是一开始迷信验证集 mAP觉得 0.9 以上就万事大吉结果真实场景一跑全是问题。后来养成习惯任何权重交付前必须过一遍真实场景抽帧指标只是参考眼睛看到的才算数。希望帮到你。本文还有配套的精品资源点击获取