YOLOV8行人检测实战:从数据集训练到Gradio图形化系统

发布时间:2026/10/2 4:05:19
YOLOV8行人检测实战:从数据集训练到Gradio图形化系统
简介本资源面向计算机视觉入门与进阶开发者提供一套完整的YOLOv8行人检测实战方案覆盖从数据标注、环境配置到模型训练与推理的全流程同时兼容YOLOv3、v5、v9、v10等系列模型的训练需求适合希望快速上手目标检测项目的高校学生与算法工程师。压缩包共1642个文件约847.6MB包含169个Python源码、343个Markdown说明文档、76个YAML配置、9个pt权重文件及66张jpg示例图另附约5000张标注好的行人检测数据集可直接用于训练与验证。资源内还提供训练好的模型权重与一套图形化界面方便直观体验检测效果。目前已有4296人学习下载配套视频教程与联系方式可协助解决调试问题帮助读者掌握数据准备、模型训练、界面部署与排错思路快速构建可运行的行人检测系统。1. 行人检测项目落地从 YOLOV8 权重到图形化系统的完整链路行人检测是视觉落地里最容易被低估的一类任务。很多人以为拿 YOLOV8 官方权重跑一张街景图就算完事真到项目里才发现通用 COCO 权重对小目标、遮挡、密集人群的召回率根本不够看业务方还要一个能点按钮、能传图、能看框的图形化系统。这个标题指向的正是这条完整链路——代码、行人检测数据集、训练好的模型、图形化系统四件套打包。它适合两类人一类是想跑通 YOLOV8 训练全流程但缺一份干净行人数据集的算法新手另一类是手里有模型却卡在「怎么给别人演示」的工程师。下面按数据、训练、推理、界面、避坑的顺序把每个环节的参数和翻车点讲清楚。2. 行人检测数据集从原始标注到 YOLOV8 可训练格式2.1 为什么行人数据集不能直接拿来训行人检测数据集和通用目标检测数据集最大的区别在长宽比和遮挡密度。通用数据集里一张图可能只有一两个目标行人数据集一张图动辄几十个人且大量存在半身遮挡、背对镜头、夜间低照度的情况。如果直接把标注文件丢给 YOLOV8最常见的后果是训练 loss 震荡、mAP 卡在 0.5 上不去。原因通常不是模型不行而是标注框的宽高比分布和 anchor 匹配策略不匹配。YOLOV8 本身是 anchor-free 的理论上对宽高比不敏感但它的标签分配策略TaskAlignedAssigner仍然依赖分类和回归的联合置信度。行人框普遍是「窄高」型宽高比集中在 0.3 到 0.5 之间如果数据集里混入了大量非行人标注或者空标注正样本会被稀释。所以第一步不是急着训而是先做数据体检。常见做法是先用脚本统计所有标注框的宽高比和面积分布把异常框面积小于 32×32 或宽高比大于 1:5单独拎出来人工复核。这一步能省掉后面至少两轮无效训练。2.2 标注格式转换Labelme 到 YOLO 的脚本与边界坑很多行人数据集原始标注是 Labelme 的 JSON 格式需要转成 YOLO 的 txt。转换本身不难坑在坐标归一化和类别映射。下面这个脚本处理单类行人检测输入是 Labelme 生成的 JSON 目录输出是 images 和 labels 两个文件夹。import json import os from pathlib import Path from PIL import Image def labelme_to_yolo(json_dir, out_img_dir, out_lbl_dir, class_nameperson): json_dir Path(json_dir) out_img_dir Path(out_img_dir) out_lbl_dir Path(out_lbl_dir) out_img_dir.mkdir(parentsTrue, exist_okTrue) out_lbl_dir.mkdir(parentsTrue, exist_okTrue) for json_file in json_dir.glob(*.json): with open(json_file, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] img_name data[imagePath] # 复制原图到输出目录 src_img json_dir / img_name if src_img.exists(): Image.open(src_img).save(out_img_dir / img_name) lines [] for shape in data[shapes]: if shape[label] ! class_name: continue points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 过滤无效框 if x_max - x_min 2 or y_max - y_min 2: continue # YOLO 格式class_id cx cy w h全部归一化 cx (x_min x_max) / 2.0 / img_w cy (y_min y_max) / 2.0 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h # 裁剪到 [0,1] 防止越界 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if lines: txt_name json_file.stem .txt with open(out_lbl_dir / txt_name, w) as f: f.write(\n.join(lines)) if __name__ __main__: labelme_to_yolo(./raw_json, ./dataset/images, ./dataset/labels)逻辑说明脚本遍历 JSON 目录读取每张图的宽高把 Labelme 的多边形点集转成外接矩形再归一化成 YOLO 需要的class_id cx cy w h。参数上class_name默认是person如果你的数据集里行人标签叫pedestrian或walker改这个参数即可。min(max(...))那几行是防止标注点超出图像边界导致归一化后出现负数或大于 1 的值YOLOV8 训练时遇到越界标签会直接报错退出。注意Labelme 的imagePath有时是相对路径有时是绝对路径脚本里用json_dir / img_name拼接如果原图不在 JSON 同级目录需要手动调整src_img的查找逻辑。2.3 数据集划分与 data.yaml 的四个必填项转换完成后按 8:1:1 划分 train、val、test。YOLOV8 不强制要求 test但行人检测项目建议留一份 test 集因为验证集在训练过程中会被间接用于调参最终指标容易偏乐观。# 目录结构 dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml四个必填项path: ./dataset train: images/train val: images/val test: images/test nc: 1 names: [person]path是数据集根目录train/val/test是相对路径。nc是类别数行人检测单类就写 1。names必须和标注里的 class_id 顺序一致如果后面要加骑车人、推车人改nc和names即可但标注文件里的 class_id 也要同步改。提示划分脚本不要用随机种子固定后就不管了。行人数据集里同一段视频的连续帧如果被分到 train 和 val验证指标会虚高。常见做法是按视频源或场景划分而不是按帧随机划分。3. YOLOV8 训练参数怎么设、显存怎么省、曲线怎么看3.1 环境配置与模型选型n 还是 sYOLOV8 提供 n/s/m/l/x 五个尺度。行人检测在边缘设备上跑n 和 s 是主流选择。n 的参数量约 3.2Ms 约 11.2M。如果目标是 RK3588 或类似板端部署直接选 n因为 s 在 INT8 量化后精度掉得比 n 明显。如果只在服务器 GPU 上跑且数据集超过 5 万张s 的 mAP 通常比 n 高 2 到 4 个点。环境配置用 conda 最省事conda create -n yolov8 python3.10 -y conda activate yolov8 pip install ultralytics8.1.0 # 如果要用 GPU 训练确认 torch 版本匹配 python -c import torch; print(torch.__version__, torch.cuda.is_available())ultralytics版本建议锁在 8.1.x8.2 之后部分 API 有变动网上很多教程还是旧写法混用容易出KeyError。CPU 版本训练也能跑但行人数据集动辄几万张CPU 训一轮要几个小时只适合调试代码是否跑通。3.2 训练命令与关键参数含义yolo detect train \ data./dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ workers4 \ projectruns/person \ nameexp1参数逐个说imgsz640输入分辨率。行人检测如果目标普遍偏小可以提到 960 或 1280但显存占用按平方增长。640 到 960显存大约翻 2.2 倍。batch16批大小。8G 显存跑 640 分辨率n 模型可以到 32s 模型建议 16。如果报 OOM先降 batch 再降 imgsz。lr00.01初始学习率。YOLOV8 默认用 SGD 时 0.01用 AdamW 时建议 0.001。如果 loss 前几个 epoch 就炸到 nan先检查标注有没有越界再把 lr0 降到 0.001。lrf0.01最终学习率因子即最终 lr lr0 × lrf。这个值控制学习率衰减幅度行人检测这种单类任务0.01 到 0.05 都常见。patience20早停轮数。如果 20 轮验证 mAP 不升就停。行人数据集如果标注质量一般建议设 30 到 50给模型更多机会。workers4数据加载线程数。Windows 下如果报共享内存错误改成 0 或 2。训练开始后runs/person/exp1/下会生成results.csv和weights/。results.csv里记录了每轮的 box_loss、cls_loss、mAP50、mAP50-95。画曲线用 pandas 加 matplotlib 即可import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/person/exp1/results.csv) df.columns df.columns.str.strip() fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[0].plot(df[epoch], df[train/cls_loss], labelcls_loss) axes[0].set_xlabel(epoch) axes[0].legend() axes[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[1].set_xlabel(epoch) axes[1].legend() plt.tight_layout() plt.savefig(training_curve.png, dpi150)逻辑说明results.csv的列名前后可能有空格strip()是血泪经验不处理会报KeyError。左图看 loss 是否收敛右图看 mAP 是否还在涨。如果 box_loss 持续下降但 mAP 不涨大概率是过拟合检查训练集和验证集是否同分布。3.3 低显存与 CPU 训练的降级方案显存不够时按以下顺序降级先降 batch 到 8再降 imgsz 到 512再换 n 模型最后开ampFalse混合精度关闭反而省显存因为 AMP 会缓存一些中间变量。如果只有 CPU把devicecpuworkers0batch4imgsz416先跑 10 个 epoch 确认流程通再考虑上云 GPU。GTX1660Ti 6G 显存跑 YOLOV8n 640 分辨率batch 可以到 16但训练时如果同时开浏览器和 IDE容易 OOM。训练前关掉不必要的图形程序这是最容易被忽略的显存杀手。4. 图形化系统从推理脚本到可交互界面4.1 推理封装把 best.pt 变成可调用函数训练完成后runs/person/exp1/weights/best.pt是最终权重。图形化系统不需要重新加载模型而是把推理封装成一个函数输入图像路径或 numpy 数组输出带框的图像和检测列表。from ultralytics import YOLO import cv2 import numpy as np class PersonDetector: def __init__(self, weight_path, conf0.35, iou0.45): self.model YOLO(weight_path) self.conf conf self.iou iou def detect(self, img): # img 可以是路径或 numpy 数组 results self.model.predict( sourceimg, confself.conf, iouself.iou, classes[0], # 只检测行人 verboseFalse ) result results[0] boxes result.boxes detections [] if boxes is not None: for box in boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy().astype(int) score float(box.conf[0].cpu().numpy()) detections.append({ bbox: [x1, y1, x2, y2], score: round(score, 3) }) annotated result.plot() return annotated, detections参数说明conf0.35是置信度阈值行人检测场景下不建议低于 0.3否则背景里的柱子、树影会被误检。iou0.45是 NMS 的 IoU 阈值密集人群场景可以提到 0.5 到 0.6减少漏检。classes[0]强制只输出行人类别即使模型训练时有多类推理时也只保留行人。result.plot()返回的是 BGR numpy 数组可以直接给 OpenCV 或 Gradio 显示。如果要在界面上显示检测数量用len(detections)即可。4.2 Gradio 界面十行代码搭一个可演示系统图形化系统不需要从零写 PyQtGradio 足够应付演示和内部测试。下面是一个最小可用界面import gradio as gr from detector import PersonDetector detector PersonDetector(runs/person/exp1/weights/best.pt) def infer(image): if image is None: return None, 请上传图片 annotated, detections detector.detect(image) # Gradio 需要 RGB annotated_rgb annotated[:, :, ::-1] summary f检测到 {len(detections)} 人\n for i, d in enumerate(detections[:10]): summary f#{i1} 置信度 {d[score]}\n return annotated_rgb, summary demo gr.Interface( fninfer, inputsgr.Image(typenumpy, label上传图片), outputs[ gr.Image(label检测结果), gr.Textbox(label检测摘要, lines12) ], titleYOLOV8 行人检测系统, description上传图片自动检测行人并显示置信度 ) demo.launch(server_name0.0.0.0, server_port7860)逻辑说明gr.Image(typenumpy)让 Gradio 直接把上传图片转成 numpy 数组省去文件读写。annotated[:, :, ::-1]是 BGR 转 RGB不转的话显示出来颜色是反的这是 OpenCV 和 Gradio 配合时最常见的翻车点。server_name0.0.0.0允许局域网访问方便在另一台机器上打开浏览器测试。如果要做视频检测把gr.Image换成gr.Video推理时逐帧调用detector.detect再用cv2.VideoWriter写回。注意视频检测的帧率会受推理速度限制YOLOV8n 在 CPU 上大概 5 到 8 FPSGPU 上 30 到 60 FPS。4.3 界面与模型的解耦换权重不用改代码图形化系统最容易犯的错是把权重路径写死在界面代码里。正确做法是把权重路径、conf、iou 抽成配置界面只负责调用。这样换一个训练好的模型只需要改配置文件不用动界面逻辑。# config.py CONFIG { weight_path: runs/person/exp1/weights/best.pt, conf: 0.35, iou: 0.45, max_display: 10 }界面里从CONFIG读参数推理函数从CONFIG读权重。如果后面要支持多模型切换把weight_path改成下拉框选项即可。这个解耦在演示现场特别有用——业务方说「这个模型漏检太多」你直接换一个高召回权重界面不用重启。5. 避坑与排查行人检测项目里最容易翻车的五件事5.1 现象训练 loss 正常但 mAP 一直是 0原因data.yaml里的names和标注文件的 class_id 对不上或者标注文件里出现了nc之外的类别 id。YOLOV8 不会报错但所有正样本都被当成背景mAP 自然为 0。解决用脚本统计所有 txt 文件里第一列的唯一值确认只有 0 到 nc-1。如果有多余类别要么改nc要么过滤掉那些标注行。5.2 现象推理时框全图置信度还很高原因标注时把整张图框成了一个人或者转换脚本里多边形点集异常导致外接矩形覆盖全图。这种脏数据在训练时会教模型「整张图就是人」。解决在转换脚本里加面积过滤宽或高超过图像 90% 的框直接丢弃。训练前用labelImg或labelme抽查 50 张肉眼过一遍。5.3 现象Gradio 界面显示图片颜色发蓝原因OpenCV 读图是 BGRGradio 和 PIL 默认 RGB中间没有转换。解决在返回给 Gradio 之前做annotated[:, :, ::-1]。如果用的是result.plot()它返回的也是 BGR同样需要转。5.4 现象RK3588 部署后精度掉一半原因PyTorch 权重直接转 ONNX 再转 RKNN量化时用了默认的 KL 散度校准行人小目标在量化后特征损失严重。解决量化校准集里必须包含至少 200 张行人密集场景图且校准集分布要和测试集一致。如果精度还是掉改用混合量化对检测头部分保留 FP16。5.5 现象视频检测结果框闪烁严重原因逐帧独立推理没有做帧间平滑。行人走动时相邻帧的框位置会有几个像素的抖动视觉上就是闪烁。解决加一个简单的滑动窗口平滑对连续 3 帧的框做加权平均。或者用 ByteTrack 做跟踪跟踪 ID 稳定后框自然稳定。YOLOV8 官方已经集成了 ByteTrack加trackerbytetrack.yaml即可。6. 进阶技巧用验证集反推标注质量与模型上限训练完看 mAP 只是第一步真正决定项目能不能交付的是知道模型的上限在哪。我一般会做一件事把验证集里 mAP 最低的 20 张图拎出来逐张看标注和预测的差异。如果发现是标注漏标说明数据集还有提升空间如果发现是模型确实看不清夜间、严重遮挡那就不是训练能解决的需要补数据或换红外相机。具体操作跑一次验证把save_jsonTrue打开YOLOV8 会输出 COCO 格式的预测结果。然后用 pycocotools 按图片统计 AP排序后取最低的 20 张。yolo detect val \ modelruns/person/exp1/weights/best.pt \ data./dataset/data.yaml \ save_jsonTrue \ projectruns/val \ nameexp1_valfrom pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval import numpy as np ann_file ./dataset/annotations/instances_val.json coco_gt COCO(ann_file) coco_dt coco_gt.loadRes(runs/val/exp1_val/predictions.json) coco_eval COCOeval(coco_gt, coco_dt, bbox) coco_eval.evaluate() coco_eval.accumulate() # 按图片统计 AP precisions coco_eval.eval[precision] # [T, R, K, A, M] img_ids coco_gt.getImgIds() per_img_ap [] for idx, img_id in enumerate(img_ids): # 简化处理取所有类别和面积范围的平均 p precisions[:, :, :, 0, 2] p p[p -1] ap np.mean(p) if len(p) 0 else 0.0 per_img_ap.append((img_id, ap)) per_img_ap.sort(keylambda x: x[1]) worst_20 per_img_ap[:20] print(最差 20 张图的 image_id 和 AP) for img_id, ap in worst_20: print(img_id, round(ap, 4))逻辑说明precisions的维度是[IoU阈值, 召回率, 类别, 面积范围, 最大检测数]。这里取[:, :, :, 0, 2]表示所有 IoU、所有召回、所有类别、面积范围取all、最大检测数取 100。p[p -1]过滤掉无效值。按 AP 升序排列后最差的 20 张就是最值得看的。拿到 image_id 后去验证集里找到对应图片用标注工具打开对比预测框和真实框。如果真实框明显漏标补标后重新训练如果真实框没问题但模型就是检不到说明这类场景在训练集里太少需要针对性补数据。这个习惯帮我省过很多次「盲目调参」的时间——模型上限是数据决定的不是学习率决定的。我自己的习惯是每训完一个版本先不看 mAP 绝对值而是看最差 20 张图里有多少是标注问题。如果超过一半是标注漏标那这一轮训练的意义就是暴露了数据问题而不是模型问题。把数据修完再训mAP 通常会有一次跳变。希望帮到你。本文还有配套的精品资源点击获取