ARTICLE DETAIL

资讯详情

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

YOLO行人数据集实战:标签转换与训练避坑指南

YOLO行人数据集实战:标签转换与训练避坑指南 简介面向YOLO系列目标检测算法训练与验证的行人数据集包含七千二百零三张已标注图像适合入门和进阶开发者快速开展行人识别实验。压缩包整体约二百三十三点七六兆共收录两千个标签文件并提供两种标注格式YOLO格式的文本文件与VOC格式的XML文件同时已划分好训练集与验证集附带数据配置文件data.yaml可直接适配YOLOv5至YOLO11等主流版本。目前已有138人学习下载。使用这份数据集可免去手动标注和格式转换环节直接进入模型训练、验证与测试流程清晰的目录结构和现成的配置文件也能帮助读者快速对比不同YOLO版本的行人检测效果适合作为项目起步数据或算法评测基准。资源包内文件命名规范便于批量管理可有效减少前期准备时间。1. 这个行人数据集到底是什么能帮你省下多少标注时间拿到别的团队标好的行人数据集是最省事的一条路。做目标检测最难的不是模型选型而是数据7203张图像带标签按一张行人图平均多框、复杂遮挡场景反复缩放标注来算一个人全职标也得两周起步而“yolo算法-行人数据集-7203张图像带标签-人-主人.zip”这类资源就是把这段人工工期直接换成了硬盘空间和几个小时的训练时间。但别急着解压拖进训练脚本——标题里那个“人-主人”的双类别划分、标签到底是不是YOLO原生txt格式、样本数量能不能支撑目标场景每一样都会决定你是否白忙一场。下面就把整个落地过程拆开。2. 把“人/主人”两类标签盘清楚格式识别、转换与合并2.1 解压后先查三样东西目录结构、标签格式、类别分布几行命令无论从哪个渠道拿到压缩包第一步统一动作解压后先看结构不要双击进去乱翻。常见的数据集会包含 images/ 或 JPEGImages/ 目录放图labels/ 或 annotations/ 目录放标注但也存在图片和txt混在一个文件夹里、靠同名后缀互相匹配的情况。unzip -q yolo算法-行人数据集-7203张图像带标签-人-主人.zip -d avail cd avail find . -type f | sed s/.*\.// | sort | uniq -c先看扩展名统计jpg/jpeg/png 和 txt 各有多少数量是否大致对应7203。这个阶段最怕的是图片7203张、标签只有一半或者标签文件名里带着奇怪的编号对不上图。文件数对不上直接在源头止损不用继续往下走。确认文件数量后打开一个标签文件看内容。YOLO格式的txt每行是“类别ID 中心x 中心y 宽 高”五个数字全部相对于原图尺寸做归一化范围在0到1之间。没有归一化、坐标大于1的多半是从标注工具导出后没做转换后面训练会直接出问题。用一段小脚本统计所有txt里每个类别各有多少个标注框顺便检查类别ID是否连续从0开始# count_classes.py from pathlib import Path import collections labels_dir Path(labels) counter collections.Counter() for txt in labels_dir.rglob(*.txt): with open(txt, encodingutf-8, errorsignore) as f: for line in f: parts line.split() if len(parts) 5: counter[int(parts[0])] 1 print(类别ID - 标注框数量:, dict(counter)) print(出现的类别ID:, sorted(counter.keys()))这段脚本的价值在于一次暴露两个问题第一类别数量是否符合预期第二ID是不是从0开始且没有断档。如果打印出来只有0和2、缺1说明原数据集在标注时删掉过某个类别YOLO训练时类别索引必须从0连续排列否则类别名称和标签彻底错位。出现断档不要手动猜把映射关系整理完再进下一步。2.2 标签格式识别与YOLO转换txt/XML/JSON三个分支的处理脚本txt格式是理想情况但实际拿到的数据集经常是VOC XML或者COCO JSON格式。判断办法很直接如果文本里每行是“class x y w h”那就是YOLO如果文件以annotation开头那就是VOC XML如果整体是JSON包着images和annotations两个数组那就是COCO格式。混用格式的压缩包我也见过一套数据里txt和xml并存不能只转一种就开训。下面按VOC XML转YOLO格式给一套完整转换脚本这也是行人检测里最常碰到的转换场景# voc_to_yolo.py import xml.etree.ElementTree as ET from pathlib import Path xml_dir Path(Annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) class_names [person, master] # 按类别ID顺序 for xml_file in xml_dir.glob(*.xml): 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) if img_w 0 or img_h 0: print(f{xml_file.name}: 图片尺寸为0跳过) continue lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 边界裁剪防止标注框超出图片范围 x1 min(max(x1, 0), img_w - 1) y1 min(max(y1, 0), img_h - 1) x2 min(max(x2, 0), img_w - 1) y2 min(max(y2, 0), img_h - 1) if x2 - x1 1 or y2 - y1 1: continue x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h lines.append(f{class_names.index(name)} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_file out_dir / f{xml_file.stem}.txt out_file.write_text(\n.join(lines), encodingutf-8)这段脚本有四个容易出事的位置。一是img_w和img_h从size节点读缺失或为0时直接跳过否则所有坐标算出来都是无效值。二是x1, y1, x2, y2做了min/max裁剪VOC标注经常出现xmax等于原图宽度、xmin等于0的情况如果不裁剪归一化后可能正好等于1.0训练时GT框落在边界外损失在某些版本里直接变成NaN。三是类别名到ID的映射用class_names.index(name)实现前一位加括号里的class_names.index(name)不能出现标--不存在的猫如是四行输出要确保每行五个值全对。四是过滤掉宽度或高度小于等于1像素的框这种框是脏标注保留会让损失函数在处理极小框时数值不稳定。转过之后把标签和图片做成同样的文件名前缀放到images/和labels/两个目录训练时YOLO靠“同名不同扩展名”找对应关系这一步对不上后面全白搭。2.3 “人”与“主人”要不要合并先可视化确认再决定标题里的“人-主人”两个类别很关键但不要只凭命名猜——这个数据集里的“主人”可能是指牵着宠物的主人、保安亭里的值守人员、或者某个特定场景里需要单独识别的主体原压缩包没有附带说明文档时最可靠的办法是先画出来看。画框脚本只需要OpenCV几分钟能写完# draw_yolo_boxes.py import cv2 from pathlib import Path images_dir Path(images) labels_dir Path(labels) class_names {0: person, 1: master} for img_path in list(images_dir.glob(*.jpg))[:50]: img cv2.imread(str(img_path)) if img is None: continue h, w img.shape[:2] txt_path labels_dir / f{img_path.stem}.txt with open(txt_path, encodingutf-8) as f: for line in f: parts line.split() if len(parts) 5: continue cls_id int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) color (0, 255, 0) if cls_id 0 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(fcheck_{img_path.name}, img)看50张左右的图就能得出两个结论两张标注框里的人是否真属于语义不同的目标两个类别的样本量差距是否悬殊得没法训练。如果“主人”类别本质上是行人的一个子集而且业务上最终输出只要“画面里有没有人”那就合并成一个person类不要因为压缩包里分了两类就强行保留。反过来如果目标场景就是区分普通行人和带宠物的行人那这两个类必须分开合并等于把模型的上限砍死。我的经验是这类“人-XX”双类数据集来自某个人群细分采集场景的概率远大于两类互斥目标先花20分钟可视化比训练完看测结果再返工划算得多。3. 在YOLO上训练行人检测从data.yaml到训练命令的参数3.1 划分train/val目录生成干净的data.yaml把标签格式和类别定下来之后下一步是划分训练验证集。划分时最容易犯的错是只挪图片不挪标签、或者用shutil.move时出异常导致文件对不上最好用一段固定随机种子的脚本一次做完。# split_data.py import random from pathlib import Path import shutil random.seed(42) images sorted(Path(images).glob(*.jpg)) val_count int(len(images) * 0.2) val_images set(random.sample(images, val_count)) for split in [train, val]: (Path(split) / images).mkdir(parentsTrue, exist_okTrue) (Path(split) / labels).mkdir(parentsTrue, exist_okTrue) for img in images: split val if img in val_images else train label Path(labels) / f{img.stem}.txt shutil.copy(img, Path(split) / images / img.name) if label.exists(): shutil.copy(label, Path(split) / labels / label.name)用random.seed(42)固定划分结果这样无论实验跑多少次训练集和验证集始终一致几个模型之间的对比才有意义。这里的比例是8:27203张图分出约5762张训练、1441张验证对行人检测来说验证集是够的。不要用shutil.move保留原始目录做备份万一划分错了还有后悔药。数据量足够的场景下不用再单独切测试集拿验证集当最终评估集用等到部署前再用一个独立采集的小场景集做盲测。然后写 data.yaml路径尽量放到纯英文目录下。Windows上如果解压目录带“yolo算法”这几个中文训练脚本的路径编码很容易在中间某一步崩掉建议直接把整个数据目录重命名为pedestrian_data/再跑。# data.yaml path: /home/user/pedestrian_data # 改成实际绝对路径 train: train/images val: val/images names: 0: person 1: masternames的排列顺序必须和标签文件里的数字ID一一对应。这里如果写反了person和master会互换训练不会报错但推理结果全错属于最难排查的“静默错误”。路径我用绝对路径避开相对路径在不同工作目录下找不到数据的问题。3.2 训练命令与必调参数epochs、imgsz、batch、lr0YOLOv8是当前最稳的起步选择环境配置好之后训练命令本身只有一行。模型用yolov8s而不是yolov8n行人类别小目标多n模型轻但漏检率会明显上升v100这类显存充足的卡上可以放心选s甚至m模型。yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs60 \ imgsz640 \ batch16 \ optimizerAdamW \ lr00.0005 \ cacheTrue几个参数按这个数据集的实际情况给理由参数建议值为什么这样设epochs60起步30轮能跑通链路但精度没到瓶颈60轮能看出过拟合拐点不够再加imgsz6407203张图足够喂饱模型640是速度与精度的平衡线待检目标整体偏小就上1280batch按显存上限减半16在12G卡上跑yolov8s刚好batch过小BN层不稳定loss曲线容易抖optimizerAdamWYOLOv8默认SGD收敛慢AdamW在小数据集上表现更稳lr00.0005默认0.01对预训练权重偏大行人框普遍小学习率太高会让回归分支震荡yolo的损失函数由边界框回归和分类两部分组成行人类别只有两个分类压力不大重心全在框定位上。小目标的中心点和宽高稍微偏一点IOU损失就会显著变大所以标签的归一化精度直接反映在loss曲线上。这也解释了为什么第2章要把坐标越界的框先修好——YOLO的标签分配策略anchor-free匹配会把所有GT框纳入计算一个坐标错乱的框会同时污染匹配附近的预测样本。除非已经对yolo环境配置和权重下载很熟否则第一轮训练建议先不调增强参数跑通一次拿到baseline再逐步加mosaic、mixup而不是一开始就把超参塞满翻车了都不知道是哪一项干的。3.3 训练完成后先看这四个输出混淆矩阵、预测图、PR曲线、loss曲线训练跑完不要急着看mAP数字先打开runs/detect/train/目录下的四样东西confusion_matrix.png、val_batch_pred.jpg、PR_curve.png、results.png这四张图能判断模型是真学会了还是瞎猜。混淆矩阵要特别注意一个现象两类别以上的模型混淆矩阵的行和列加起来往往不等于100%原因在于不同类别的预测框可能落在同一个GT附近或者多个预测框对应一个GT而矩阵在归一化时只按行或只按列做了一次归一化。看到“yolo混淆矩阵总合不唯一”不要立刻怀疑训练崩了先确认保存的图是行归一还是列归一但这不代表所有误差都能忽略如果person类大量被分类成master那就是类别难以区分该考虑合并而不是继续训。val_batch_pred.jpg直接给真相绿色框和红色框画在图上肉眼扫一遍就知道有没有把树干、路灯、商店橱窗里的人影当成人。PR_curve.png观察曲线是否在置信度0.5附近迅速掉头向下掉得越陡说明大量低置信度预测是误检。results.png里看loss曲线到后期是否平滑下降Box loss还在震荡就要加训练轮次但超过80轮还在下降的很少多数情况是数据集本身有噪声。4. 行人检测的避坑与排查遮挡、小目标、类别不平衡、训练崩坏4.1 标签越界与类别ID错位现象、原因、解决现象训练loss在正常下降的途中突然出现阶梯式跳变或者前几轮验证集mAP直接是0可视化训练样本时发现框画在图片外、框中心点在角落里、框尺寸巨大。原因数据集从XML/JSON转换时没有做边界裁剪归一化坐标出现了负数或者大于1的值也可能是标签的类别ID范围超过data.yaml里names的数量比如names只写了0和1两个类txt里却出现ID等于2的行YOLO在标签匹配时直接跳过或错配。另一种很隐蔽的情况names的顺序和标注时的类别顺序不一致但数字都对得上模型把person和master学反了。解决训练前跑一次全量检查脚本扫描所有标签文件的ID集合和坐标范围# check_labels.py import numpy as np from pathlib import Path labels_dir Path(labels) valid_ids {0, 1} # 根据data.yaml修改 errors [] for txt in labels_dir.rglob(*.txt): with open(txt, encodingutf-8) as f: for line in f: parts line.split() if len(parts) ! 5: errors.append((txt.name, 列数不等于5)) continue cls int(parts[0]) vals list(map(float, parts[1:])) if cls not in valid_ids: errors.append((txt.name, f非法类别ID {cls})) if any(v 0 or v 1 for v in vals): errors.append((txt.name, f坐标越界 {vals})) if errors: print(发现问题:, len(errors)) for err in errors[:20]: print(err) else: print(全部标签合法)发现问题直接跑第2章的转换脚本重新生成不要写临时代码打补丁——一次性补丁会把已有的问题覆盖掉后面再出问题更难看。类别ID错位的修复方法更简单把data.yaml的names顺序调整成标签里实际用的ID顺序两处对齐即可。4.2 远距离小目标行人漏检imgsz与切片推理现象验证集mAP看着不低但实际测试时距离稍远的行人全部漏掉或者检测框歪歪扭扭只框住上半身。看PR_curve.png时召回率在置信度0.7以上突然断层说明大量远距离目标根本没生成有效预测。原因YOLO的主干网络有五次下采样输入640分辨率时特征图上最小步长对应的原图区域就是8x8像素。远距离行人可能只有16x24像素在深层特征图上只占据2x3个格子语义信息已经被池化层抹掉。这是行人检测数据集里最普遍的问题尤其监控类场景目标小还不是少数。解决最直接的手段是把训练和推理的imgsz从640提到1280实测对中远距离行人AP提升最明显显存不够就先用yolov8n配1280跑一轮看效果再上大模型。另一个思路是推理阶段做切片把大图切块后逐块检测再合并结果常见做法是用sa-hi这类切片推理工具或者自己写滑窗逻辑。切片推理要注意边界位置的物体被切断切片之间需要预留重叠区域。这项优化放到最后做——先把训练分辨率提上去如果部署设备跑不动1280再切片。小目标问题靠硬调置信度阈值没用模型根本没生成预测阈值再低也捞不出来。4.3 “主人”类样本太少导致精度虚高类别权重与合并策略现象训练日志里总体mAP达到65%看起来不错但PR_curve.png里person类曲线和master类曲线差距极大master类的AP可能只有20%。实际用的时候master类几乎全部漏检模型输出的绝大多数框都是person。原因两个类别的标注框数量不在一个数量级“主人”在7203张图里可能只占几个百分点。YOLO默认按样本数量加权训练少样本类别在损失函数里占比太小模型倾向于把一切看起来像人的东西预测成person因为这样总损失最低。整体mAP被大类撑起来“精度虚高”就是这么来的。解决先明确业务上到底需不需要master这个类别。如果不需要直接把标签里的master合并到person模型只输出一类精度反而更高推理速度还略快。如果必须保留第一选择是把master样本做数据增强复制多拷贝几份到训练集里配合mosaic、mixup增强第二选择是在损失里给master类提高权重YOLOv8里可以通过修改配置文件的类权重参数实现。不要只简单重复master样本几十遍那会让模型过拟合到少数几个样本上测试时换个场景直接崩。最终评估必须逐类看AP不要看平均mAP掩盖问题。4.4 训练中loss冲高与BN崩溃参数与环境的双重排查现象训练到第20轮左右loss曲线突然从1.2飙到3.5甚至更大之后要么回不来要么直接出现NaN。用TensorBoard或者日志曲线看是单点突变不是缓慢上升。原因yolo训练中bn崩溃最常见的触发点是优化器切换会导致BN统计量剧烈波动尤其从默认SGD换成AdamW且学习率没降的时候其次是数据集里混入了损坏图片解码失败、全黑、全白这些图片的前向传播产生异常梯度。还有一些外部因素预训练权重下载不完整、多卡训练时batch size设置不一致、混合精度下loss溢出。解决按优先级排查。第一步把lr0降到原来的一半重新训练排除学习率问题第二步在训练命令里加cacheTrue并把batch改成8排除显存不足和IO异常导致的黑匣子问题第三步用一段脚本检查数据集的图片是否都能被OpenCV正常解码python -c import cv2 from pathlib import Path bad [p for p in Path(images).rglob(*.jpg) if cv2.imread(str(p)) is None] print(损坏图片数:, len(bad)) 有损坏图片直接剔除同时删掉对应的标签文件。BN崩溃的坑在于它在训练日志里出现得很突然让人以为模型没救了实际上一半情况是数据集里有毒样本换掉就恢复正常。养成一个习惯每轮训练前把训练log里的loss曲线截个图存到实验目录出问题时有据可查不用凭记忆复盘。5. 用测试集把行人模型压榨到上限推理、阈值与坏例复盘5.1 单张图片推理调置信度阈值而不是改模型训练完的best.pt先拿几张没见过的场景图跑推理观察效果再决定下一步yolo predict modelbest.pt sourcetest_scene.jpg conf0.25 iou0.45conf是置信度阈值低于这个值的预测框全部丢弃iou是NMS时的交并比阈值值越大越容易把两个重叠框合并成一个。行人密集场景里先把conf拉到0.4误检率肉眼可见下降如果发现漏检再把conf调回0.2或者更低看能不能捞回一部分框。如果调到0.1仍然漏检严重说明模型没学会特征跟阈值没关系。调阈值的本质是在误检和漏检之间找平衡点换场景时第一件事是重新搜阈值而不是改模型重训。5.2 用验证集统计mAP和每类AP坏例到底坏在哪验证集才能给出可信的量化数据单张图看得再仔细也只是玄学。用命令直接在验证集上评估yolo val modelbest.pt datadata.yaml batch32输出里重点关注两行mAP50-95和每个类目的AP50。行人类别AP50能达到80%以上才值得去部署如果只有60%先别优化推理回到第4章找原因。验证完把预测错误的图片挑出来按错误类型归档遮挡、模糊、远距离、截断、视角罕见统计每一类占比。这一步能明确告诉你后续要补什么数据——是补充遮挡样本还是提高训练分辨率。一开始直接闷头加训练集的轮次是低效的数据分布的问题用更多训练算力也治不好。我现在拿到任何数据集都会先花半小时做坏例统计而不是直接开一轮长训练这个习惯救过我很多次。5.3 导出engine做部署前的最终确认以及按场景剪裁习惯验证通过后如果要上NVIDIA设备部署把模型导出成TensorRT engine格式再跑一次推理yolo export modelbest.pt formatengine halfTrue device0engine是高度硬件相关的产物换一张GPU型号必须重新导出导出后拿同一批验证集图片重新测一遍确认精度没有因为半精度损失而明显下降。下一步是按业务场景剪裁只需要检测人的场景建议在训练阶段就合并类别、推理时固定conf0.35附近需要区分细分类别的场景则把conf放在0.25左右给下游留下更多候选框同时把每类AP的基线记录在实验日志里。版本迭代时只对比同一验证集上的mAP就好。本文还有配套的精品资源点击获取
返回列表