ARTICLE DETAIL

资讯详情

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

2125张已标注行人数据集:YOLO/VOC双格式训练实战与踩坑指南

2125张已标注行人数据集:YOLO/VOC双格式训练实战与踩坑指南 简介面向目标检测入门与毕业设计场景下载包提供CUHK Occlusion Dataset行人检测数据集的完整已标注版本。数据已按YOLO格式做好训练集与验证集划分并同时提供YOLO与VOC两种标签文件下载后可直接接入YOLO系列模型进行训练与评估免去自行整理和转换标注格式的麻烦。压缩包约315.63MB包含2125张jpg行人图像、2124个txt格式的YOLO标签、1062个xml格式的VOC标签及少量缓存文件图片与标注一一对应目录结构清晰适合计算机、电子信息工程、数学等专业学生用于课程设计、期末大作业或毕业设计。目前已有594人学习下载作者为某大厂资深算法工程师有十年计算机视觉与目标检测仿真经验资源附带的说明和代码注释较为详尽参数可调便于二次扩展。整体而言这是一套可直接跑通训练流程的实用数据集能帮助学习者聚焦模型调参与效果优化减少数据预处理时间。1. 2125张已标注行人图把目标检测的“数据门槛”直接消掉入门目标检测最常见卡点不是算法结构也不是显卡而是手里没有一份能直接开训的数据。很多人第一步就耗在找图、抽帧、用labelimg一张张画框上一标就是两三天还没碰到模型就累了。标题里这个zip解决的就是这个痛点2125张图像每张都带一份YOLO的txt标注和一份VOC的xml标注解压后目录结构清晰可以直接喂给训练脚本跑通完整流程。它适合两类人一是刚接触目标检测、想用最短路径跑出一个真实模型的入门者二是手头有业务数据但还没标好、需要先拿一份现成数据验证训练链路是否通畅的工程师。2125张对单类行人检测来说不算大但在预训练模型加持下足够启动一个正经项目关键是省掉了几天的标注时间。2. 拆开zip先读标注yolo的txt和VOC的xml分别存了什么拿到压缩包之后第一件事不是急着配环境而是把两种标注文件都打开看一遍。很多人死于“我以为我懂了”实际连class_id是从0开始还是从1开始都没确认训练一轮下来全在报错。这一章把两种格式的内部结构彻底讲清楚并给一段可直接运行的读取脚本帮你把数据集的家底摸明白。2.1 解压后的目录结构先确认文件组织方式这类数据集常见的组织方式有两种。一种是把两种标注分开放images/放jpglabels/放YOLO格式的txtannotations/放VOC格式的xml三种目录相互独立但文件名一一对应。另一种是VOC风格的目录JPEGImages/放图Annotations/放xmllabels/放txt。无论哪种第一步都是用命令行数一下三个目录的数量确认没有文件丢失。for d in images labels annotations; do echo $d: $(ls $d | wc -l) done这个命令分别统计三个目录下的文件数量。正常情况下三者数量应该完全一致都是2125份文件数。如果有出入说明打包时文件有缺失或者文件名对不上这种情况直接拿来训练会在数据加载阶段报错提前发现可以省掉一大段排错时间。2.2 YOLO的txt标注五个数字与归一化坐标系YOLO格式的每个txt文件与一张图片同名每一行描述一个目标框。典型内容是这样的0 0.520833 0.456481 0.208333 0.601852这行有五个数字含义分别是class_id从0开始计数的类别编号x_centery_centerwidthheight。后四个值全部是归一化坐标取值范围在0到1之间是框中心点或边长除以图片宽高之后的结果。这种设计让标注与图片分辨率解耦训练时无论resize到640还是960都不需要重新计算标注文件。要真正理解这五个数字可以把归一化坐标还原成像素坐标用下面的代码# 将归一化坐标换算回像素坐标便于直观检查 with open(img_0001.txt) as f: for line in f: cls, cx, cy, bw, bh map(float, line.split()) x (cx - bw / 2) * image_width # 左上角x像素坐标 y (cy - bh / 2) * image_height # 左上角y像素坐标 w bw * image_width # 框宽像素 h bh * image_height # 框高像素image_width和image_height来自图片本身的尺寸可以用PIL读出来。这段代码最直观的用途是验证标注是否合理换算出来的x、y应当大于0xw不应超过图像宽度yh不应超过图像高度。如果在换算过程中出现负数或超出边界的值基本可以判定这份标注文件有问题。2.3 VOC的xml标注绝对坐标与object节点VOC格式用xml文件描述标注它的关键字段是size、object和bndbox。打开任意一个xml文件结构大致如下annotation filenameimg_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameperson/name bndbox xmin100/xmin ymin90/ymin xmax320/xmax ymax740/ymax /bndbox /object /annotation与YOLO不同VOC里bndbox存的是绝对像素坐标即xmin、ymin是框左上角坐标xmax、ymax是右下角坐标。读取时直接使用不需要除以宽高。name字段是类别名的字符串形式YOLO里对应的是class_id数字这个对应关系在训练配置里需要显式定义。VOC格式与YOLO格式之间的转换也由此而来把xmin、ymin、xmax、ymax转成中心点加宽高的形式再分别除以图片宽高得到的就是YOLO的归一化坐标。如果压缩包里txt和xml同时存在大概率是同一批数据导出了两份二者应当一一对应。2.4 批量统计类别分布确认数据集只有一个标签标题说这是行人数据集但“行人”在不同数据集里的名字可能是person、pedestrian甚至people。训练配置里的类别名必须与标注文件里的名字完全一致否则模型训练出的类别和实际语义错位。用一段脚本批量读取所有标注统计类别名和各类别目标数import xml.etree.ElementTree as ET from pathlib import Path from collections import Counter xml_dir Path(annotations) name_counter Counter() for xml_file in xml_dir.glob(*.xml): root ET.parse(xml_file).getroot() for obj in root.findall(object): name obj.find(name).text name_counter[name] 1 print(name_counter)这段代码遍历所有xml文件统计每个name出现的次数。如果结果里只有一个类别名比如person那数据集的nc1如果出现多个名字你需要决定是合并它们还是删除部分。常见翻车情况是同一批数据里既有person又有people直接训练会变成两个类而业务上其实只想要一个类。这类问题在标注阶段就已经埋下了统计分析是排掉它的第一步。3. 把数据集接进YOLO训练校验、划分、配置、跑通一次完整流程这一章进入实操环节。从验证标注一致性开始到目录划分、写配置、跑训练一气呵成。以YOLOv8的命令行用法为示例但目录结构和yaml配置对v5、v7同样适用。3.1 先校验再训练txt与xml的对应关系检查很多人拿到zip直接把路径填进训练命令就开跑结果训练到一半报错“Can’t find labels”才发现某些图片没有对应的txt。更隐蔽的问题是txt里有class_id1但训练配置里只声明了1个类别模型训练时直接崩溃。from pathlib import Path images sorted(Path(images).glob(*.jpg)) labels sorted(Path(labels).glob(*.txt)) # 检查图片与标注文件是否一一对应 img_stems {p.stem for p in images} label_stems {p.stem for p in labels} print(有图片无标注:, img_stems - label_stems) print(有标注无图片:, label_stems - img_stems) # 检查txt里的class_id是否越界 max_cls 0 for txt in labels: for line in txt.read_text().splitlines(): cls int(line.split()[0]) max_cls max(max_cls, cls) print(最大class_id:, max_cls)运行这段代码后第一行输出如果有内容说明存在孤儿图片需要决定是补标注还是丢弃。最大class_id如果大于等于你计划的类别数说明txt文件里有未知类别编号必须回到xml里找到对应图片查看它是哪一类的目标。跳过这一步直接训练遇到的问题会在验证集上以mAP0的形态出现排查起来更痛苦。3.2 划分train/val目录随机抽样与固定随机种子很多入门项目会把全部2125张图都用来训练然后拿训练过的图片做验证得到的mAP高得离谱部署后立刻现原形。正确的做法是从一开始就把训练集和验证集分开。写一个划分脚本固定随机种子保证每次划分结果一致import random import shutil from pathlib import Path src_img Path(images) src_txt Path(labels) random.seed(42) # 固定种子保证划分结果可复现 imgs sorted(src_img.glob(*.jpg)) random.shuffle(imgs) val_ratio 0.15 # 2125张里留约320张做验证 val_imgs imgs[:int(len(imgs) * val_ratio)] train_imgs imgs[int(len(imgs) * val_ratio):] for split, split_imgs in [(train, train_imgs), (val, val_imgs)]: img_out Path(fdataset/images/{split}) txt_out Path(fdataset/labels/{split}) img_out.mkdir(parentsTrue, exist_okTrue) txt_out.mkdir(parentsTrue, exist_okTrue) for img in split_imgs: shutil.copy(img, img_out / img.name) txt src_txt / (img.stem .txt) if txt.exists(): shutil.copy(txt, txt_out / txt.name) print(ftrain: {len(train_imgs)}, val: {len(val_imgs)})这个脚本不移动原文件而是复制到新的dataset/目录下保留原始数据作为底稿。val_ratio取0.15而不是常规的0.2是因为2125张本身基数小验证集保留15%约320张已经能反映模型表现留更多反而让训练数据紧张。随机种子必须固定否则每次运行划分结果都不一样前后实验结果无法对比。3.3 写data.yaml路径、类别数、names三件套YOLO训练靠一个yaml文件描述数据集位置和类别信息。这个文件放在任何位置都行但路径必须指向正确最稳妥的方式是用绝对路径# data.yaml path: /root/pedestrian_dataset # 改为你解压目录的绝对路径 train: images/train # 相对path的子目录 val: images/val # 相对path的子目录 nc: 1 # 类别数量 names: [person] # 类别名与标注里的name一致train和val两个字段的值取决于你的目录结构。如果你按上一节的脚本划分目录是dataset/images/train/和dataset/images/val/那path就写成/root/pedestrian_datasettrain写images/train。这里最容易出错的是names列表里的名字必须与xml里的name字符串一致而不一定是person——上一章统计出来的类别名是什么就写什么。如果统计结果是pedestrian这里必须写[pedestrian]写person就会造成训练时类别语义错位。3.4 跑一个能出结果的训练命令完整参数与显存参考配置就绪后训练命令本身很简单。以YOLOv8为例yolo detect train \ data/root/pedestrian_dataset/data.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ device0modelyolov8n.pt会先加载COCO预训练权重这对2125张的小数据集至关重要。如果训练环境是离线内网需要提前下载好权重文件放到当前目录。epochs50对小数据集来说是一个相对保守的值训练过程中观察验证loss走向如果50轮后还在明显下降可以继续追加。batch16以8G显存为基准显存不足时先降到8不要一上来就调低imgsz因为目标检测里小目标对分辨率很敏感。imgsz640表示训练输入尺寸后面验证时可以用640也可以用960但要明白不同尺寸下mAP会有差异。训练结束后权重保存在runs/detect/train/weights/下best.pt是验证集mAP最高的那一份实际使用时应优先选它而不是last.pt。到这里你已经拥有一个在验证集上可评估的行人检测模型了。4. 训练不收敛的排查5个我实际踩过的标注数据坑拿到别人标注好的数据最容易被“已标注”三个字麻痹。标注存在但不代表标注正确训练中出现各种妖魔鬼怪的现象绝大多数根源都在标注本身。这一章把最容易踩的坑按现象拆解每条都是真实经历。4.1 现象验证集mAP一直是0训练loss正常下降但验证集上的mAP50始终是0精确率和召回率全是0。原因主要有两个一是验证集没有正常加载标注文件检查dataset/images/val/里是否有对应的txt二是你之前划分时用了没有固定随机种子的脚本验证集里混进了训练集的图片导致模型“见过”验证图但实际上文件路径错乱。解决办法是回归基础。先打印每条验证图片的label路径是否真实存在再检查yaml里的val路径和path拼接后是否正确。最经典的错误是把path写成数据集根目录的同时val字段又写成了绝对路径YOLO会按/root/pedestrian_dataset//root/pedestrian_dataset/...去查找自然什么都找不到。4.2 现象xml里的name与txt里的class_id对不上标注数据分类统计时发现xml里有person和people两种类别名但txt里的class_id只有0和1两个值。问题是两种不同的类别名被映射到了两个不同的分类编号比如person对应0people对应1但在训练配置里names: [person]只声明了一个类别于是所有class_id1的框直接越界。解决办法是把语义上同一类的不同名字合并成同一个class_id。对行人这类单类任务把people、pedestrian全部映射为0。常见做法是先跑第2章的统计脚本把类别名逐一确认再写一个映射表统一改写txt里的编号或xml里的name。注意同时保留两份格式时txt改了xml也要改否则后面对不上。4.3 现象txt坐标出现大于1的数字某个txt文件的一行是0 1.35 0.52 0.3 0.4中间的x_center超过了1。归一化坐标大于1或小于0意味着标注导出时没有正确除以图片宽高或者标注框本身已经超出了图像边界。这种文件在训练时不会直接报错但会严重拉偏损失函数。排查脚本很简单遍历所有txt把任何不在0到1范围内的值打印出来对应到具体图片打开原图查看那个区域是否真的有目标。如果目标确实超出画面比如半个人在画面边缘合理的做法是裁掉越界部分只保留画内的有效区域如果目标根本不存在就是标注漏删直接删除这一行。处理完后再跑一次训练。4.4 现象训练loss正常下降但验证集PR很差loss降到比较低的水平但验证集上的精确率很高、召回率很低意味着模型“漏检”严重。对2125张图片来说这是最常见的过拟合信号。模型记住了训练集里行人的外观和姿态遇到验证集里没见过的角度、遮挡或光照条件就认不出来。解决思路分三路第一降低训练轮数或提前早停避免最后几轮纯粹在记忆训练集第二调高数据增强强度YOLOv8默认的增强对单类小数据集可能偏弱把mosaic等增强参数适当拉大第三换更大的预训练模型从yolov8n换到yolov8s小模型本身就更容易在数据量不足时陷入局部最优。需要提醒的是只看train loss不验证指标的做法在小数据集上会给你一种假安全感。4.5 现象训练启动时报错“found no labels in these images”程序启动后提示找不到任何标注文件模型一直跑空数据。最常见原因是文件名大小写不一致很多相机拍出的图是.JPG后缀而数据检查脚本或训练框架只认.jpg于是所有图片都没匹配到同名的txt。另一个原因是txt的空文件问题标注软件导出了txt但内容为空或者图片本身没有目标留下大量0字节的txt。解决方案是统一格式。把目录里所有大写后缀改成小写再检查txt文件是否有0字节的有目标但没有标注的图片应当移出数据集避免被当作纯负样本参与训练。命令很简单# 统一后缀名为小写 find images -name *.JPG -exec rename s/\.JPG$/.jpg/ {} \; # 找出空标注 find labels -name *.txt -empty把这两步执行完重新启动训练即可。数据加载报错的问题90%出在文件匹配层面不要急着怀疑框架。5. 数据量只有2125张怎么扩充、怎么调整参数、怎么避免过拟合很多人拿到这个量级的数据会犯嘀咕2000多张够吗答案取决于你的目标。如果只是跑通流程、验证方案完全够如果要做一个能抗住各种街景场景的公开级检测器不够。这一章说清楚数据量的边界以及在这个量级下怎么把模型的性能往上推。5.1 2125张对行人检测意味着什么场景多样性比数量更关键单类别检测任务模型真正需要的不是无限多的图片而是足够的场景覆盖。行人在画面里的变化维度主要有几个方面尺度全身、半身、远处小目标、姿态正对、背对、侧身、遮挡被车挡、被人群挡、被树木挡、光照白天、夜晚、逆光。如果这2125张图全都来自同一批监控视频截图姿态和尺度高度相似那模型能学到的特征就很有限如果图片来自不同场景哪怕只有2000张泛化能力也会好很多。判断数据覆盖度有一个直接方法随机抽50张图数一下画面里行人的高度占图像高度的比例。如果大多数行人的高度占比集中在30%到80%之间说明数据里大目标居多模型对远处小目标的检测能力会明显偏弱。这一点会在验证阶段暴露但提前了解能让你的心理预期更准确。5.2 在线增强优先于离线扩数据YOLO内置增强的边界很多人的第一反应是多复制几份图片或者自己写脚本做旋转、加噪声来“增加数据量”。这个做法在小数据集上收益很低因为同一张图无论怎么变换模型学到的新信息都是有限的而且离线扩图和原图高度相关对提高泛化能力帮助不大。真正有效的是YOLO训练框架内置的在线增强。YOLOv8默认开启了mosaic、随机翻转、HSV扰动、平移缩放等增强手段每张图在每轮训练时都会以不同形态出现相当于把数据集的多样性放大了一个量级。对小目标问题mosaic把四张图缩放后拼在一起强制模型去学习小尺度行人。需要注意的参数是mosaic会在最后10个epoch左右关闭这是为了在训练末期让模型在接近真实分布的图像上稳定收敛不要手动把它全程关闭。5.3 需要额外扩充时视频抽帧、伪标签与数据清洗优先级如果确实需要扩充数据优先级顺序是视频抽帧大于合成图大于伪标签。视频抽帧时要注意相邻帧高度相似每隔30帧抽一张往往比连续抽帧有效得多。伪标签的思路是使用已经训练好的模型对未标注的图片生成预测框人工快速筛选后加入训练集这在半监督场景下确实可行但要严格控制置信度阈值低于0.7的建议直接丢弃错误伪标签对训练的破坏力远大于数据量不足。抽帧之后还会面临一个问题新数据的标注。如果新图片内容与原始数据集场景接近可以直接沿用原始模型的预测结果做人工修订如果场景差异大就需要用labelimg等工具做一次标注了。这也是为什么我一直建议把原始2125张留作底稿扩充数据时单独建目录不混在一起便于随时回滚。5.4 模型选择与训练参数在小数据量下的保守配置模型规模选择上2125张的规模最合适的是n或s级别。不要一上来就训练yolov8x参数越多过拟合风险越大训练时间也成倍增加对小数据量反而是负收益。以下是实际使用中比较稳妥的起点参数参数建议值理由modelyolov8n.pt预训练权重单类小数据首选nimgsz640均衡速度与精度检测小目标时可调960epochs50至100先跑50轮看验证loss拐点batch168G显存显存不足先降batch不改imgsz优化器autoYOLOv8默认自动选择mosaic1.0默认开启小目标增强利器weight_decay0.0005过拟合明显时适当增大选n还是选s的依据如果你的图片里行人目标偏小高度占比小于20%s的容量会有优势如果场景简单、目标居中偏大n足够。无论选哪个第一次训练都建议用默认参数跑通再根据曲线微调。6. 模型训练完别急着收工先跑这4类检查再说训练结束后runs/detect/train/目录下已经生成了best.pt你可以开始验证。第一个动作是批量推理一批完全没参与训练的图片最好是不同时间段、不同天气实拍的行人照片而不是数据集里的任何一张。yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_imgs/ \ save_txtTrue \ save_confTrue \ conf0.25save_txtTrue会把检测结果以YOLO格式保存在runs/detect/predict/labels/save_confTrue同时保存置信度。确认一下这批外部图片的检测效果重点看两个维度漏检的行人是什么形态远处小目标、遮挡、骑行误检的框都落在了什么物体上交通牌、树干、路灯。这两个问题的答案决定了下一步优化方向——漏检多就调高imgsz到960误检多就适当调高conf阈值到0.35或0.4。第二个动作是查看训练过程生成的混淆矩阵和PR曲线。对单类别检测来说混淆矩阵看不出类别间的混淆但能直观看到背景被误检的比率。PR曲线与水平轴围成的面积就是AP曲线末端召回率上翘但精确率掉得厉害说明模型在低置信度区间有很多误检。这时不要急着接受这个结果把预测出来的框按置信度排序逐个眼检你很快会发现模型最薄弱的部分。第三个动作是检查检测框与行人的贴合程度。行人不规则框的边界如果整体偏大或偏小是标注数据本身框得不准导致的。遇到大面积框偏差不用改代码回到标注文件修正那批数据的框坐标重新训练即可。第四个动作是用一段连续的视频做时域验证。静态图片检测稳定不代表视频里稳定相邻帧之间框抖动剧烈、同一行人一会儿检出一会儿丢这是单帧检测的通病。如果目标是部署到监控场景这部分成本需要提前算进去要么接受抖动要么引入跟踪模块。我自己的习惯是每次训练完都会在固定的20张盲测图上跑一遍记录漏检数、误检数并截图存档。训练参数每次只改一个变量记录它对盲测结果的影响而不是去看验证集mAP的波动。mAP是参考信号盲测图是真实反馈。反复改验证集参数骗自己最后部署时翻车的一定是你自己。这个习惯帮我避开了很多表面正确、实际不可用的模型。希望帮到你。本文还有配套的精品资源点击获取
返回列表