ARTICLE DETAIL

资讯详情

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

YOLO行人检测实战:数据集校验、训练参数调优与踩坑全流程

YOLO行人检测实战:数据集校验、训练参数调优与踩坑全流程 简介面向YOLO系列算法开发者与初学者的行人检测数据集覆盖模型训练、验证与测试全流程可用于行人检测、人流统计等场景。压缩包共2000个文件以VOC格式XML标注文件为主同时配套YOLO格式TXT标签整体大小233.76MBYOLO标签采用“类别索引 中心点x 中心点y 宽度 高度”的归一化坐标数值在0到1之间类别索引从0开始方便扩展新类别可直接用于YOLOv5、v7、v8、v9、v10、v11等主流版本训练。XML文件则记录对象类别、边界框坐标及其他属性便于在需要更完整标注信息或转换标签格式时使用两种标签分文件夹存放可按需选择。数据集已经预划分好训练集与验证集并内置data.yaml配置文件解压后无需额外整理即可开始训练。目前已有138人学习下载适合希望快速获取行人标注数据、完成YOLO系列模型部署与效果评估的开发者。1. yolo算法遇上7203张行人图这份“人-主人”数据集该怎么落地行人检测大概是 yolo算法 在工程里被问得最多的任务安防摄像头、商圈人流统计、自动标注平台第一步几乎都是把画面里的“人”稳定框出来。你手上这套“行人数据集-7203张图像带标签-人-主人.zip”听起来只是一份压缩包可真正决定模型能不能用的不是 7203 这个数字而是图像和标签之间的配对质量。我拿到这类数据集的第一反应永远是先解压验证再谈训练。这篇按我从验收到训练完的完整流程讲重点落在标签格式核对、train/val 划分、YOLO 训练命令与参数设置、行人场景最常见的四类翻车最后给一个能落到实拍画面的验证方法。2. 解压后别急着训练先核对标签格式、文件数量与“人-主人”类别ID标题里写了“7203张图像带标签”但从 zip 落地之后要确认图像目录里的 jpg/png 数量、标签目录里的 txt 数量是不是都能对上。丢标签、框偏移、类别 ID 混乱是行人数据集最常见的三种脏数据。直接拿去跑训练轻则某个类别学不到重则验证曲线全是失真的。2.1 先看目录结构再数一次文件数量我习惯把解压路径固定到一个独立目录不要直接解压到项目根目录避免上一个数据集的 images 和 labels 混进来。常见的数据包目录结构也就下面这几种形态。目录/文件常见形态说明images/000001.jpg, 000002.png图像jpg 和 png 混用很常见labels/000001.txt, 000002.txtYOLO 格式标签每行一个目标框classes.txtperson, owner类别名清单部分包会单独带一份先看整体结构和文件数量cd ~/datasets unzip 行人数据集-7203张图像带标签-人-主人.zip -d pedestrian_raw find pedestrian_raw -maxdepth 2 -type d | sort find pedestrian_raw/images -type f | wc -l find pedestrian_raw/labels -type f | wc -lunzip -d指定解压目录避免压缩包内容散落一地find -maxdepth 2限制搜索深度先看结构而不是背出完整文件列表wc -l数图像数和标签文件数。这里的数量如果和标题的 7203 对不上先停一下排查是不是有隐藏文件、空目录或者打包时漏了几十张。图像扩展名可能不是纯 jpg我一般会顺手按扩展名归个类find pedestrian_raw/images -type f | sed s/.*\.// | sort | uniq -c这个命令把每个图像文件的扩展名提取出来然后统计输出类似“7200 jpg / 3 png”。数量差异不大时不用太紧张训练时 YOLO 能直接读 png差异超过几十个就得看看是不是有损坏文件。2.2 读几个 txt 标签搞清 YOLO 格式和“人-主人”到底几个类YOLO 格式的标签每一行是五个字段class_id center_x center_y width height坐标归一化到 01 之间。先随便看几个文件head -5 pedestrian_raw/labels/000001.txt如果看到类似0 0.52 0.63 0.31 0.78的输出说明是标准 YOLO 格式能直接进 YOLOv5/v8 的训练流程。如果看到person 120 300 45 90这种文本标签加像素坐标就是 VOC/COCO 风格的标注得先做格式转换。这时候最该做的不是训练而是把整个标签目录的类别 ID 分布统计出来。标题既然写了“人-主人”这个数据包很可能是两个类person 和 owner。owner 在不同来源里含义不一样也许是“随行主人”也许是某个特殊场景主体不管怎样以标签里实际出现的 ID 为准。# inspect_labels.py from pathlib import Path labels_dir Path(pedestrian_raw/labels) # 按实际目录改 cls_counter {} total_boxes 0 bad_lines 0 for txt in labels_dir.glob(*.txt): for line in txt.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: bad_lines 1 continue cls_id int(parts[0]) cls_counter[cls_id] cls_counter.get(cls_id, 0) 1 total_boxes 1 print(标签文件数:, len(list(labels_dir.glob(*.txt)))) print(总标注框数:, total_boxes) print(类别ID分布:, cls_counter) print(格式异常行数:, bad_lines)逻辑说明遍历所有 txt按行拆字段。标准 YOLO 标签必须是 5 列不足 5 列的行先记到bad_lines这类行训练时会直接报错或导致那一行的标注被忽略。统计结果如果只有{0: 15600}说明标签里其实只有 person 一个类如果出现{0: 9000, 1: 6600}说明 person 和 owner 两类都真实存在后面 data.yaml 里就要写nc2。参数说明labels_dir指向标签目录cls_counter是类别 ID 到框数量的映射total_boxes是平均每张图的框数可以用来快速判断数据密度——行人数据集一张图平均 13 个框比较常见平均超过 5 个框就得关注密集遮挡。2.3 画框回原图抽五个最容易出错的画面看一眼统计数字永远发现不了“框偏了”“漏标了”“人和人粘在一起只标了一个框”这类问题。要让标注“说话”就把框画回图像上。我一般会写一段小脚本# draw_boxes.py import cv2 from pathlib import Path image_path Path(pedestrian_raw/images/000123.jpg) # 换真实文件名 txt_path Path(pedestrian_raw/labels/000123.txt) img cv2.imread(str(image_path)) h, w img.shape[:2] for line in txt_path.read_text().strip().splitlines(): cls_id, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls_id)), (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(check_000123.jpg, img)逻辑说明YOLO 标签是归一化坐标必须乘回图像的宽高才能落在正确像素位置。cx - bw / 2算左上角 xcy - bh / 2算左上角 y再根据宽高推出右下角坐标。cv2.putText的y1 - 8让文字显示在框上方max(0, ...)防止文字越界到图像外。抽查的时候重点选五类画面密集人群、暗光、人被截断、远处小目标、两个人交错站着。这五类就是行人数据集标注错误的高发区。如果看到大量框没包住人或者两三个人挤在一起只给了几个框说明标签质量不达标。这时候优先做清洗而不是直接开训练。验证通过后保持图像和标签同名、放在两个平级目录里方便下一步切分。3. 划分 train/val 并生成 data.yaml做对这三件事再跑YOLO不少人的习惯是“7203 张图全扔进去让 YOLO 自己分验证集”。实际上yolo detect train不会自动切分数据包里也不一定自带划分。常见做法是写脚本按比例随机切分同时保证图像和标签同名配对并固定随机种子让结果可复现。3.1 建目录结构YOLO 只认 images/train 与 labels/val 这种路径YOLO 官方训练流程希望数据组织成下面这种结构datasets/pedestrian7203/ ├── data.yaml ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/datasets/pedestrian7203是这次训练的项目根目录。images 下放图像labels 下放同名 txttrain 和 val 的比例决定了模型在哪个尺度上过拟合和欠拟合。目录用 bash 一条命令建掉mkdir -p datasets/pedestrian7203/{images/{train,val},labels/{train,val}}3.2 写划分脚本按 basename 配对跳过缺标签图像切换脚本的核心逻辑不是“复制文件”而是“按文件名前缀配对”。图像叫000123.jpg标签就必须是000123.txtYOLO 完全靠 basename 找对应关系。# split_data.py import random from pathlib import Path import shutil src_img Path(pedestrian_raw/images) src_lab Path(pedestrian_raw/labels) out_root Path(datasets/pedestrian7203) val_ratio 0.15 random.seed(42) imgs sorted(src_img.glob(*.jpg)) sorted(src_img.glob(*.png)) random.shuffle(imgs) val_count int(len(imgs) * val_ratio) for split_name, subset in [(train, imgs[val_count:]), (val, imgs[:val_count])]: for img in subset: lab src_lab / f{img.stem}.txt if not lab.exists(): print(f警告: {img.name} 缺少标签已跳过) continue out_img out_root / images / split_name / img.name out_lab out_root / labels / split_name / lab.name shutil.copy2(img, out_img) shutil.copy2(lab, out_lab) print(划分完成 train:, len(imgs) - val_count, val:, val_count)逻辑说明先扫出全部 jpg 和 pngshuffle 打乱顺序按 15% 的比例切出验证集。注意判断lab.exists()一张图如果没有对应标签直接跳过而不是硬拷进去。硬拷的结果是 YOLO 会把这张图当负样本也就是说画面里的行人全部变成“该检测却没人管”的区域直接拉低召回率。参数说明val_ratio0.157203 张图大概切出 1080 张验证。数据量小、想更稳妥就改 0.2数据量大且来源单一0.1 也够。random.seed(42)是为了复现同一个划分结果方便下次对比实验。切片后做一次配对检查输出缺配对的样本名for img in datasets/pedestrian7203/images/val/*.jpg; do base$(basename $img .jpg) [ -f datasets/pedestrian7203/labels/val/$base.txt ] || echo missing: $base done3.3 写 data.yamlpath、train、val、nc、names 五项缺一不可YOLO 训练读的是 data.yaml不是读目录。这个文件五项配置项必须齐全# datasets/pedestrian7203/data.yaml path: ./pedestrian7203 # 相对 yaml 文件所在目录的路径 train: images/train val: images/val nc: 2 names: 0: person 1: ownerpath是数据集根目录写法上推荐相对路径避免换机器后绝对路径失效。train和val相对于path填写不需要加datasets/pedestrian7203前缀。nc是类别总数必须和names列表长度一致。前面统计脚本如果只出现了类别 ID 0这里就把nc改成 1、names只留0: person。类别顺序也要核对如果压缩包根目录有classes.txt以它为准从 VOC 格式转过来的数据VOC 里 person 排第几YOLO 的 ID 就是几不能自己随手换顺序。准备到这一步数据链路已经通了可以进入训练环节。4. 跑通YOLO训练命令imgsz、batch、epochs与nc对齐的四个必调参数训练命令不长但四个参数值得每次训练前都过一遍imgsz、batch、epochs、model 大小。我以 YOLOv8 为例这套参数同样适配 YOLOv5v9 等主流实现只是命令前缀略有差别。第一次跑这个数据集核心目标是“让数据流水线跑通”不是追指标。4.1 用最小命令验证数据和流程环境准备用pip install ultralytics就能完成。第一轮训练用最小的 n 模型yolo detect train datadatasets/pedestrian7203/data.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs80 \ patience15 \ projectruns/pedestrian \ nameexp1逻辑说明modelyolov8n.pt会加载 COCO 预训练权重对行人这类常见类别有很好的迁移起点没有网络环境时这条命令会卡在下载权重可以先本地准备好权重文件再写绝对路径。project和name指定输出目录训练日志、权重、曲线图都会落在runs/pedestrian/exp1下。训练起来后如果数据划分有问题日志里会出现found no labels in ...之类的警告这时候回去查配对而不是调大 epochs 硬跑。参数说明imgsz640是精度和显存的平衡点batch16在 12G 显存左右比较稳patience15表示验证指标连续 15 轮不提升就早停。4.2 imgsz 与 batch 怎么定行人多为中小目标别只看显存imgsz 对行人检测的影响比许多人预想的大。城市街道画面里大量行人的高度只有几十个像素imgsz640 时这些人在下采样后可能只剩几个像素特征几乎消失。想要小目标召回常见做法是提高 imgsz但显存会快速上涨。模型imgszbatch显存参考适合场景yolov8n640166G 左右第一轮验证数据yolov8s640168G10G常规精度需求yolov8s1280816G 左右远处小目标密集遇到 CUDA Out Of Memory优先减 batch 而不是减 imgsz。batch 减半对训练稳定性影响有限imgsz 一旦降低小目标问题就会回到原点。8G 显卡可以先batch8 imgsz640跑通后换 16G 以上的机器单独跑一版imgsz1280作对比。4.3 epochs、patience 与早停7203 张图不是越多越好7203 张图说多不多、说少不少。常规做法是 100150 轮配合早停。早停触发时看runs/pedestrian/exp1/results.png里的val/box_loss和val/cls_loss。如果停在很早期的轮次说明模型学习速度过快但验证在反弹这时候优先考虑增强、降低学习率或者换更小的 n 模型做基线。第一轮就该用最小的模型拿到一个能用的 best.pt再用它去评估数据质量而不是一开始上大模型。4.4 行人场景的两个超参微调YOLOv8 默认开启 mosaic 和左右翻转。行人场景建议微调两个点fliplr0.3左右翻转虽然不致命但行人朝向有语义翻得太多会让模型对方向不敏感mosaic0.5数据量不足时 mosaic 拼图会让小目标更碎调低一点往往更稳。把它们直接放在命令行尾部覆盖配置即可yolo detect train datadatasets/pedestrian7203/data.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs120 \ patience20 \ fliplr0.3 \ mosaic0.5 \ projectruns/pedestrian \ nameexp1_mosaic05参数说明fliplr和mosaic都是在 YOLO 已有配置基础上改的命令行参数优先于配置文件。5. 这个数据集最容易踩的四个坑从类别ID到val曲线反直觉的排查顺序训练三轮之后你对数据集的真实印象会逐渐浮出来。这里把行人数据集最常见的四个坑按现象、原因、解决三个环节写清楚。5.1 坑一训练日志提示某个 class 完全没有样本或某个类 mAP 永远是 0现象日志里出现class 1 has zero samples或者训练结束后某个类别的 PR 曲线没有任何数据。原因data.yaml 里写了nc2names 里写了 person 和 owner但标签文件里根本没有类别 ID 1。这在“标题写了人-主人、但实际标签只标了人”的包里非常常见。解决不要猜重跑一遍类别统计确认标签里到底有哪几个 ID。确认只有 person 时把 data.yaml 改成nc1、names只留0: person再重新训练。类别 ID 对不上会在训练初期埋雷越是拖到后期越难排查。5.2 坑二person 和 owner 两个类标签互相串一个物体被框两三遍现象画框抽查看到同一个行人同时被一个绿框和一个蓝框包住或者 owner 的框比 person 的框大出一圈把人和他身边的物体全包了进去。原因半自动标注器打包没有清洗干净同一个目标被多个人先后标过、类别命名又不统一。解决最简单高效的办法是合并类别。如果业务只关心“行人”把 owner 统一映射成 person重新写回 txt。改之前先备份整个 labels 目录这是必须做的后悔药cp -r pedestrian_raw/labels pedestrian_raw/labels_backup# merge_classes.py from pathlib import Path for txt in Path(datasets/pedestrian7203/labels/train).glob(*.txt): lines [] for line in txt.read_text().splitlines(): parts line.split() cls_id int(parts[0]) parts[0] 0 if cls_id in (0, 1) else parts[0] # 把 owner 合并成 person lines.append( .join(parts)) txt.write_text(\n.join(lines))逻辑说明逐行读标签把类别 ID 1 改成 0其他字段原样保留。合并之后类别不平衡问题也会同步缓解训练时不用再为一个样本稀少的 owner 类单独调 loss。注意训练集和验证集要同步合并否则验证时还有类别 1而模型只知道类别 0mAP 会直接乱掉。5.3 坑三val mAP 有 0.8但实拍画面里远处行人一个都检不到现象验证指标不算差把 best.pt 放到一段真实街道视频里20 米外的人全被忽略只检到近景的大目标。原因数据集里近景多、小目标少或者 imgsz640 把远距离行人压到几个像素。mAP50 对框的定位要求宽松人眼一看就知道“没检到”的地方它并不敏感。解决先用训练集统计小目标占比按框的宽度或高度算一下小于 32 像素的框有多少。占比超过 30%就要认真考虑把 imgsz 提到 1280或者干脆做切片推理。验证阶段不要只看总 mAP要拆开看你关心的尺度yolo detect val modelruns/pedestrian/exp1/weights/best.pt \ datadatasets/pedestrian7203/data.yaml \ imgsz640输出结果里 YOLO 会有按目标尺度分组的 AP重点看小目标那一行那才是这个数据集真实水平的反映。5.4 坑四训练 loss 正常收敛换一段视频就翻车现象results.png 里 loss 一路下降训练集上 mAP 逼近 1把模型放到真实业务画面里却把树干、路灯、广告牌人形全部误检成人。原因训练集的背景太单一来源可能是同一个摄像头或同一批监控点位模型学到的是“该场景下的行人”而不是泛化的“行人”。负样本太少是另一个常见原因——YOLO 允许标签文件为空即一张没有任何目标的背景图标记为负样本但很多数据集包里几乎全是正样本。解决从目标部署场景抽几百张没有行人的背景图复制到 images在 labels 对应位置放空的 txt 文件加入训练集。背景多样化能同时压误检和过拟合。加上负样本后观察 Precision 是否上涨漏检上涨不要太紧张这是 precision 和 recall 的常规博弈。排查顺序记住一句先数据再超参再模型容量。数据问题没解决前换更大的模型只会让模型更快地记住错误。6. 把 best.pt 接到真实画面验证指标、抽帧与部署提速训练完拿到runs/pedestrian/exp1/weights/best.pt先别急着上线用两个方法确认它真的可用。6.1 val 指标怎么读YOLO 训练结束会在验证集上算出几个关键指标它们各管一件事指标含义行人场景怎么用Precision检出框里真正是行人的比例误检多时盯它Recall真实行人里被框住的比例漏检多时盯它mAP50IoU 阈值 0.5 的平均精度框大概位置对就行松匹配mAP50-95IoU 从 0.5 到 0.95 的平均精度小目标、密集遮挡场景更敏感漏检多就去调 Recall误检多就去调 Precision。两个都差先回到第 5 章检查数据不要先怀疑模型容量。6.2 用一段实拍片段做视频级验证用一个 10 秒的真实监控片段或手机视频把模型跑一遍# video_detect.py from ultralytics import YOLO model YOLO(runs/pedestrian/exp1/weights/best.pt) results model.predict( sourcedemo.mp4, # 换成本地视频路径 conf0.4, imgsz640, saveTrue, projectruns/pedestrian, namedemo_out )参数说明conf0.4是置信度阈值。行人检测一般从 0.4 起步输出后人工看结果漏检多就降到 0.3误检多就升到 0.5。saveTrue会生成带框的输出视频直接肉眼数 50 帧里漏检和误检的数量比看任何指标都直观。这一步也是我最常做的验证方式——指标是概括画面是事实。如果部署端对帧率有要求常见做法是先把 best.pt 导出成 ONNX 再转 TensorRT在 Jetson 这类设备上用半精度推理帧率提升明显且精度几乎不损失。命令和推理脚本都可以用 ultralytics 的 export 接口完成权重文件不变只换运行后端。我自己的习惯是任何数据集到手哪怕时间再紧也先画十个框看眼标注再跑一个最小的 n 模型基线。被错标数据坑过几次之后越来越觉得这十分钟是花得最值的十分钟。希望帮到你。本文还有配套的精品资源点击获取
返回列表