YOLOv9行人识别检测计数系统:从训练到部署的完整实践
简介面向深度学习目标检测与毕业设计场景这套基于YOLOv9的行人识别检测计数系统提供了从源码、训练好的模型到评估指标曲线的完整实现。包内共收录186个文件约62.46MB核心包括83个Python源码文件用于训练与推理、30个YAML配置便于自定义数据集与模型结构、3个pt权重文件可直接加载使用另有Jupyter Notebook、CSV评估结果与大量训练/验证批次图片方便复现训练过程并核对检测效果。当前已有237人学习下载适合计算机视觉、人工智能、自动化等相关专业学生及开发者快速搭建行人检测计数项目。配套的详细运行教程从环境配置、数据集准备、参数调整讲到测试流程训练好的模型和评估曲线能帮读者省去从零训练的周折直接查看精度指标并上手二次开发同时可通过重参数化脚本对模型进一步优化目录结构清晰便于按模块理解数据流转与推理输出。1. YOLOv9行人识别检测计数系统这套源码包的拆解与落地做目标检测项目最怕什么不是模型选型而是拿到一个号称能跑的源码包结果环境配三天、数据集格式对不上、训练完精度上不去最后连检测脚本都调不通。这套基于YOLOv9的行人识别检测计数系统源码包我拆完以后的感觉是它把从数据准备、模型训练、指标评估到推理检测的完整链路都收在一个项目里了内置了训练好的模型权重和评估指标曲线拿到手不需要再从零训练就能先跑通检测效果。适合三类人做毕业设计需要完整技术栈的学生、刚接触YOLO系列想对比v5/v8/v9差异的开发者、以及有行人计数需求但不想从头标注数据的工程人员。后面我会按实际复现顺序从环境配置讲到训练调参、推理检测再单独把重参数化这个容易被忽略的提点技巧拆开讲。2. 环境配置与数据集准备从零到跑通的完整路径2.1 环境搭建的两种方式与选型理由项目使用的是YOLOv9官方仓库的分支代码基于PyTorch框架实现。环境配置这块教程里推荐的是Anaconda PyCharm的组合这也是目前做深度学习项目最主流的搭配方式。Anaconda负责创建独立的Python虚拟环境避免不同项目之间的包版本冲突PyCharm则提供可视化的运行调试界面尤其适合学生和刚入门的人因为终端命令不熟的时候直接在PyCharm里点运行按钮比敲命令更容易上手。我实际复现时的建议顺序是先装Anaconda创建虚拟环境并指定Python版本YOLOv9官方要求Python 3.8以上建议直接用3.9或3.10然后激活环境安装依赖包最后在PyCharm里把Anaconda的解释器导入进来。创建环境的命令如下conda create -n yolov9 python3.9 -y conda activate yolov9创建好虚拟环境后接下来安装依赖。项目根目录下有requirements.txt文件里面列出了所有需要安装的包。安装时直接使用pip命令即可pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里用清华源是为了加速下载因为PyTorch、torchvision这类包体积比较大直接从官方源下载在国内网络环境下很容易超时。如果显卡驱动和CUDA已经装好PyTorch会自动调用GPU进行加速运算。没有NVIDIA显卡的机器也不用慌训练时指定device为cpu即可只是速度会慢很多后面会详细说。2.2 YOLO格式数据集的结构与制作方法这套行人检测系统使用的是YOLO格式的目标检测数据集。在开始训练之前必须先搞清楚这个格式的组织方式因为后续修改配置文件、训练脚本都和它直接相关。YOLO格式的数据集结构如下dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标注文件txt格式 │ └── val/ # 验证集标注文件 └── data.yaml # 数据集配置文件每张图片对应一个同名的txt标注文件文件内容每一行代表一个目标格式是class_id center_x center_y width height注意这里的坐标都是归一化后的值范围在0到1之间不是像素坐标。class_id从0开始编号对应data.yaml里names列表的索引。如果你手头只有VOC格式的xml标注或者COCO格式的json标注需要先转换成YOLO格式。这里我给一个XML转YOLO的Python脚本供参考import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_file, class_names, output_dir): 将VOC格式的XML标注转换为YOLO格式的txt标注 参数: xml_file: VOC XML文件路径 class_names: 类别名称列表索引即类别ID output_dir: 输出txt文件的目录 tree ET.parse(xml_file) root tree.getroot() # 获取图片尺寸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue # 类别ID在names列表中的索引 class_id class_names.index(name) # 获取边界框坐标 bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 转换为YOLO格式的归一化坐标 center_x (xmin xmax) / 2 / img_w center_y (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{class_id} {center_x:.6f} {center_y:.6f} {width:.6f} {height:.6f}) # 写入输出文件 base_name os.path.splitext(os.path.basename(xml_file))[0] with open(os.path.join(output_dir, f{base_name}.txt), w) as f: f.write(\n.join(lines)) # 使用示例 class_names [person] # 只有行人一个类别 convert_voc_to_yolo(annotation.xml, class_names, ./labels/train)这个脚本的核心逻辑是解析XML里的边界框坐标然后按照YOLO格式的公式进行归一化转换。转换时要注意两点一是类别名称必须和后续yaml文件里的names列表严格对应二是如果图片里有被截断的目标标注时仍然要标全否则训练时模型会学到错误的边界。2.3 数据配置文件data.yaml的修改要点代码里自带的data目录下有一个参考用的yaml配置文件格式如下# 数据集配置文件 train: data/images/train # 训练集图片所在路径 val: data/images/val # 验证集图片所在路径 # 类别定义 names: 0: person修改时重点是三个地方train路径指向训练集图片文件夹、val路径指向验证集图片文件夹、names列表换成自己数据集的实际类别。路径建议使用相对路径这样项目整体移动时不需要重新修改。如果你是使用绝对路径要注意Windows系统下分隔符是反斜杠容易和字符串转义冲突建议统一用正斜杠。3. 训练配置与调参train_dual.py核心参数逐项拆解3.1 训练脚本的整体逻辑与参数体系train_dual.py是这套系统的主训练脚本它基于YOLOv9官方训练代码二次封装而来核心逻辑和Ultralytics YOLO系列保持一致。脚本通过argparse接收命令行参数也支持在PyCharm里直接配置运行参数。为了方便理解我把最关键的几个参数按功能分组说明。参数类别参数名默认值作用模型配置weightsyolov9-s.pt预训练权重路径模型配置cfgmodels/detect/yolov9-c.yaml模型结构配置文件数据配置datadata/banana_ripe.yaml数据集配置文件路径超参数hyphyp.scratch-high.yaml学习率、数据增强等超参数训练设置epochs100训练轮数训练设置batch-size16批次大小受显存限制训练设置device0使用的GPU编号或cpu训练设置close-mosaic15最后N轮关闭马赛克增强训练设置img640输入图片尺寸其中weights参数如果你填入yolov9-s.pt脚本会自动下载对应的预训练权重到项目根目录。如果网络不好也可以先从其他地方下载好权重文件放到根目录脚本会优先加载本地文件。cfg参数是模型结构定义文件yolov9-c.yaml对应的是带CSP模块的完整版本yolov9-s则在精度和速度之间做了平衡显存不足时优先用s版本。3.2 直接运行与命令行运行的两种方式对比方式一是在PyCharm里直接配置参数运行适合新手和调试阶段。具体操作如下打开train_dual.py点击右上角的Edit Configurations在Parameters一栏填入参数例如--weights yolov9-s.pt --cfg models/detect/yolov9-c.yaml --data data/person.yaml --epochs 100 --batch-size 8 --device 0 --close-mosaic 15方式二是在PyCharm的Terminal终端窗口输入完整命令适合需要频繁调整参数或使用远程服务器的场景。官方示例命令为python train_dual.py --workers 8 --device 0 --batch 16 --data data/coco.yaml --img 640 --cfg models/detect/yolov9-c.yaml --weights --name yolov9-c --hyp hyp.scratch-high.yaml --min-items 0 --epochs 500 --close-mosaic 15对比来看方式一更适合本项目场景因为参数都可视化地展示在配置面板里修改起来直观。方式二适合批量跑实验时通过脚本循环发起多个训练任务。我一般建议学生先用方式一跑通再尝试方式二理解命令行参数的真义。3.3 关键训练参数的调整逻辑与失败排查实际训练中参数调整是最影响最终精度的环节。我这里把几个容易翻车的参数单独拎出来讲epochs训练轮数。数据集小几千张的情况下100轮基本可以收敛数据集大几万张建议200轮以上。判断方法很简单训练结束后看results.csv里的验证集mAP曲线是否已经走平如果没有走平就加大epochs继续训练。batch-size这是和显存强绑定的参数。batch-size乘以图片尺寸决定了一次前向传播占用的显存。8GB显存跑yolov9-s建议batch-size设为8如果报CUDA out of memory错误先把batch-size减半再试。device单张显卡填0多显卡填0,1,2,3没显卡填cpu。CPU训练速度大概比GPU慢20倍以上如果只是测试代码流程可以用CPU正式训练一定要用GPU。close-mosaic这个参数是YOLOv9新增的作用是在训练最后N轮关闭马赛克数据增强。马赛克增强会把多张图片拼在一起训练能显著提升模型鲁棒性但最后阶段不关闭会导致模型收敛不稳定。一般设置15比较合理。训练开始后在PyCharm控制台会看到进度条、loss值和mAP指标。当loss值持续下降说明训练正常当loss值震荡而mAP不动时可能是学习率太大或者数据标注有问题。训练结束后在runs/train/目录下会生成weights文件夹里面的best.pt就是验证集上精度最高的模型last.pt是最后一轮的模型。results.csv里记录了每一轮的精确率、召回率、mAP50等指标后面做评估可以直接读取。4. 检测推理与计数detect_dual.py的使用与结果输出4.1 推理脚本参数配置与执行训练完成后下一步就是用训练好的模型对图片或视频进行检测。项目中的detect_dual.py是推理脚本和很多YOLO项目的detect.py不同这个脚本额外集成了计数逻辑直接输出目标数量。修改参数时重点关注以下几项--weights runs/train/exp/weights/best.pt # 训练得到的最优权重 --source test_imgs/ # 待检测的图片或视频路径 --conf-thres 0.25 # 置信度阈值低于此值视为背景 --iou-thres 0.45 # NMS的IoU阈值source参数可以是单张图片、一个文件夹会遍历文件夹内所有图片、一个视频文件如test.mp4甚至是摄像头设备号如0表示笔记本自带摄像头。conf-thres决定检测的严格程度阈值越高漏检越多但误检越少阈值越低检测越多但会把背景误判为行人。行人检测场景我建议设置在0.3到0.4之间既保证召回率又不会太吵。4.2 检测结果的保存路径与格式说明运行检测脚本后结果会保存在runs/detect/目录下每次运行生成新的exp文件夹。文件夹里包含检测后的图片或视频图片上画有检测框、类别标签和置信度。如果你需要把检测结果导出为结构化数据可以在脚本里增加一个txt输出逻辑# 在detect_dual.py中找到结果写入的部分添加以下代码 from pathlib import Path results_dir Path(runs/detect/exp) output_file results_dir / detection_results.txt with open(output_file, w) as f: for img_path, detections in results.items(): f.write(f{img_path}: {len(detections)} persons\n) for det in detections: # det格式: [x1, y1, x2, y2, conf, class_id] f.write(f {det[0]:.1f},{det[1]:.1f},{det[2]:.1f},{det[3]:.1f},{det[4]:.2f}\n)这样输出的txt文件可以直接被后续的数据处理程序读取比如行车记录仪视频中统计每个时间点的行人数量。注意这里的results变量名是我为了举例写的实际脚本中对应的变量名是detect_output需要先打印出来看一下结构再改。4.3 视频流推流到摄像头的操作除了检测本地文件这套系统还支持接摄像头实时画面。只需要把source参数改为摄像头设备号启动后就会弹出窗口实时显示检测画面。这个功能很适合做演示但要注意摄像头分辨率过高会导致帧率下降建议先用1280x720分辨率的视频流测试。摄像头实时检测的完整命令为python detect_dual.py --weights runs/train/exp/weights/best.pt --source 0 --conf-thres 0.3这里source填0表示调用笔记本自带摄像头外接USB摄像头一般填1。运行效果会实时显示在屏幕上每帧左上角会显示当前帧的行人数量。我实际测试时发现如果摄像头画面里行人密集且互相遮挡计数会出现偏差这是单目视觉的天然局限不是因为代码有问题。5. 避坑与排查训练和检测中六个高频问题定位5.1 CUDA out of memory显存不足的三种处理方式现象训练刚启动或运行几个batch后控制台直接报错CUDA out of memory程序终止。原因batch-size过大加上图片尺寸和模型参数量共同决定的显存占用超过了显卡可用显存。YOLOv9模型比YOLOv5更大同样的batch-size占用显存更多。解决优先把batch-size减半甚至减到2其次把img参数从640改为512或416还不行就换yolov9-s的cfg配置。这三个操作按顺序尝试一般都能解决。如果显存还是不够最后一招是启用梯度累积在训练脚本里设置accumulate参数为4相当于每个batch内部累积梯度效果等同于batch-size乘以4的显存需求但速度会慢。5.2 训练loss为NaN学习率与数据问题排查现象训练过程中loss值突然变成NaN进度条消失模型参数全部变成无效值。原因最常见的是学习率初始值过大导致梯度爆炸其次是数据集中存在像素值异常的图片如全黑图、全白图或标注框坐标超出图片边界。第三种情况是训练时用了自动混合精度而部分GPU型号和PyTorch版本存在兼容性问题。解决先改hyp.scratch-high.yaml里的lr0参数从默认的0.01改成0.001或更小。然后检查数据集的txt标注文件中是否存在大于1或小于0的坐标值用以下命令扫描所有标注文件grep -rE [1-9][0-9]*\.|- labels/train/ | head -20如果输出结果中有坐标值异常的行说明数据集有问题需要用脚本过滤掉非法标注。最后如果是混合精度问题在训练脚本中把amp参数改为False。5.3 验证集mAP为0类别编号从头验证现象训练过程正常loss值下降但每轮结束输出的mAP50始终是0。原因绝大多数情况下是data.yaml中的names列表顺序和标注文件中的类别ID不对应。比如你的标注文件里类别ID是0但yaml里names列表第一个元素不是person就会导致模型认为所有预测都不匹配。解决打开一个标注txt文件看第一列数字是什么。如果标注文件里是0names列表第一位要是personnames: 0: person注意不能写成1: person否则类别ID强制从1开始和标注文件不匹配。5.4 训练速度极慢数据加载瓶颈定位现象GPU利用率长时间在20%以下训练一个epoch要几个小时明明GPU显存还有大量空闲。原因workers参数设置太小导致数据从磁盘读取到内存的速度跟不上GPU计算速度。也可能是数据图片体积过大每张图片几MB读取花费过多时间。解决把workers参数从默认值调大到8或16这个参数在train_dual.py的argparse里可以设置。图片过大则先做一次批量压缩统一缩放到640x640以下再存回数据集。另外检查train路径下是否不小心把标注文件和图片放在同一个目录YOLO读取时会卡在文件过滤上。5.5 检测效果差但训练正常置信度阈值与模型泛化现象训练时mAP60以上但检测实际图片时漏检严重甚至完全检测不到目标。原因conf-thres阈值设得过高或者训练集图片和测试集场景差异大比如训练的是白天场景测试的是夜间场景。还有一种可能是使用了best.pt而非last.pt当训练后期过拟合时best.pt反而更差。解决先把conf-thres降到0.1看检测框是否出现。如果出现但置信度在0.1到0.2之间说明模型已经学到了特征但测试场景差异大建议收集一些测试场景的图片加入训练集做微调。如果置信度接近1还是漏检说明模型没有学到有效特征需要检查训练集标注质量。5.6 计数重复计算视频连续帧中同一人多计现象用视频检测时同一个行人从画面左侧走到右侧计数器输出的累计数量远超实际人数。原因detect_dual.py的计数逻辑在没有跟踪模块的情况下每一帧都独立检测并累加同一人在每帧中都被当作一个新目标计数。解决这个场景有两个改法。简单方式是只统计当前帧的行人数量并输出不做跨帧累加。进阶方式是集成ByteTrack等跟踪算法给每个检测框分配一个ID跨帧时通过IoU和外观特征匹配同一个目标。代码层面改动量不大在每帧检测结果上套一层跟踪逻辑即可实现持续计数且不重复。6. 用reparameterization.ipynb做模型重参数化从yolov9-c到可部署的推理模型YOLOv9和其他YOLO版本最大的区别在于引入了可编程梯度信息PGI和广义高效层聚合网络GELAN这两个结构都依赖重参数化技术。简单说训练时的模型是一个带有额外辅助分支的复杂结构推理时要把这些辅助分支合并到主干网络里从而在减少计算量的同时保持精度。这就像是写完代码后做一次代码混淆压缩功能不变但体积和运行效率都优化了。项目根目录下的reparameterization.ipynb就是做这个转换的。有些博主说YOLOv9不需要重参数化就能直接部署那是因为Ultralytics代码里已经在推理时自动处理了。但如果你用的是官方YOLOv9仓库代码训练得到的pt文件如果没有做重参数化就用detect脚本推理速度会慢20%左右因为模型里还包含冗余结构。打开notebook后核心执行步骤是读取训练好的best.pt加载模型结构执行重参数化后保存为新权重文件。笔记本里的主要代码逻辑如下import torch from models.yolo import Model # 加载训练好的模型 model torch.load(runs/train/exp/weights/best.pt, map_locationcpu) model_model model[model] # 融合BN层和卷积层减少inference时的计算量 model_model.fuse() model_model.eval() # 保存重参数化后的模型 torch.save({ model: model_model, epoch: -1, best_fitness: model[best_fitness], names: model[names] }, runs/train/exp/weights/best_reparam.pt)这段代码最关键的一行是model_model.fuse()它会把卷积层后面的BatchNorm层合并到卷积层内部推理时少一次内存读取和计算。执行前需要切换到conda的环境yolov9因为notebook的kernel默认可能是base环境直接跑会报import错误我第一天就被这个问题卡了十分钟。好在切换kernel的操作很简单在PyCharm的notebook编辑界面右上角选择要求的解释器即可。重参数化后的模型可以用来对视频做实时检测。你还可以把它部署到服务端用Flask封装一个HTTP接口这样前端页面可以上传图片来请求检测结果。这个思路我去年给一个室内监控项目做落地时用过把小模型重参数化后部署到嵌入式设备上检测帧率直接翻了一倍效果立竿见影。再补充一个经验训练好的模型如果要在ONNX Runtime或者TensorRT上运行重参数化是必须的前置步骤。直接拿原始pt转换ONNX转出来的模型会包含一些推理解析时才支持的操作导致转换失败。所以正确顺序永远是训练 → 重参数化 → 导出ONNX → 部署。从那以后我每次做YOLO系列项目训练完后第一件事就是看有没有重参数化这个步骤确认做完了再谈部署和性能优化。如果你用的模型是yolov9-s这种轻量版本这个优化带来的速度提升没有yolov9-c明显但精度保持是一样的。整个系统我从环境配置到最终推理走了一遍最大的感受是YOLOv9的参数确实比v5要多但每个参数都有明确用途调参逻辑是通的。希望这篇拆解能帮你少走些弯路早日把行人检测计数系统跑起来。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →