资讯动态

人员状态检测数据集解析:5类5790框的YOLO训练避坑实践

发布时间:2026/10/14 14:51:04 来源:尧图企业网站定制
简介面向人员行为识别、安防监控与异常事件检测的目标检测数据集覆盖跑、打电话、跌倒、睡觉、抽烟五类常见人员状态采用Pascal VOC与YOLO两种标准格式存储可用于行人异常行为分析、老人看护、工位违规监测、校园宿舍管理等场景的模型训练。其中跑与跌倒适合医院、养老院等场景的异常事件预警打电话与抽烟适用于工地、车间等区域的安全合规检查。压缩包约二百三十二点八兆字节文件总数约两千个以XML标注文件为主体并附使用说明TXT标注工具为labelImg统一采用矩形框标注框总数五千七百九十个各类别框数分别为跑一千七百二十四、打电话九百一十六、跌倒九百七十三、睡觉一千七百七十一、抽烟四百零六。每张对应图片均同步生成VOC格式XML与YOLO格式TXT解压后即可接入常见目标检测框架无需额外转换。数据整体对应四千九百四十三张图片需注意其中约四分之三为数据增强所得标注准确合理但不保证训练模型精度。当前已有四百九十三人学习适合具备一定检测基础、正在补充行为识别训练数据的开发者和研究者。1. 人员状态跑睡抽烟打电话跌倒检测数据集5类5790框值不值得拿来训练做安防行为预警的人应该都遇到过这种尴尬想训练一个能同时盯住“跑、看手机、跌倒、睡觉、抽烟”的检测器翻遍开源数据集不是只覆盖摔倒就是只覆盖打架类别根本凑不齐。这份人员状态检测数据集把五类行为一次性打包4943张jpg配4943个VOC格式xml和4943个YOLO格式txt总框数5790个解压就能直接喂给检测器省掉大量整理标签的时间。我拆完这份数据的目录结构和标注内容之后认为它更适合做行为预警demo、YOLO系列微调验证、多类别混淆测试这三类用途直接拿去训练生产模型则需要按本文后面说的方法先做过滤和划分。2. 双格式的配对逻辑先搞懂xml、txt、jpg三者关系2.1 三文件同名映射与Pairing原理拿到压缩包后第一件事不是训练而是先搞清楚文件配对方式。此数据集虽然同时提供Pascal VOC和YOLO两种标注格式但设计很朴素同名的jpg、xml、txt放在同一个目录下例如存在firc_person_1356.xml同目录就应该有firc_person_1356.jpg和firc_person_1356.txt。为什么会有双格式因为labelImg默认保存的是VOC xml而YOLO训练需要txt所以正规做法是先标注xml再用转换脚本生成txt。这个数据集的目录清单里只展示了一部分xml文件实际上按摘要统计jpg、xml、txt的数量均为4943三者一一对应。这种庞大数据量下不建议手工检查配对直接按basename做交集统计。我一般会先跑一段脚本把只存在xml没有jpg、只有jpg没有txt的孤立文件全部列出来。常见的情况是解压过程中断或者文件被安全软件拦截导致三件套缺角。2.2 VOC xml的关键字段有哪些VOC格式说白了就是一份XML档案核心字段全部集中在annotation节点下。下面是一个简化但没有丢失关键信息的示例annotation folderfirc_person/folder filenamefirc_person_1356.jpg/filename size width1920/width height1080/height depth3/depth /size object namesleep/name bndbox xmin410/xmin ymin228/ymin xmax890/xmax ymax612/ymax /bndbox /object /annotationxml里最值得关注的是size与bndbox这两个节点的配合关系。width、height是图片的真实宽高bndbox是标注矩形框在图片像素坐标系下的左上角xmin,ymin和右下角xmax,ymax。这里有个容易踩的坑很多转换工具会默认把bndbox的坐标直接除以size的宽高但如果标注框的坐标超出图片尺寸换算出来的归一化坐标就会大于1训练直接报错或产生nan loss。因此踩坑第一步先把所有xml扫描一遍过滤掉xmax width、ymax height这种越界框。2.3 YOLO txt的归一化坐标怎么换算YOLO txt的每一行代表一个目标格式是class_id center_x center_y width height与xml的绝对像素坐标不同txt里全部是相对图片宽高的归一化值。换算公式并不复杂center_x (xmin xmax) / 2 / width center_y (ymin ymax) / 2 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height例如上面那个xml转换成txt假设图片宽高是1920x1080则输出就是3 0.3385 0.3889 0.25 0.3556这一行。这里面有个一眼看不出来的问题center_x和center_y可以是0到1之间的浮点数但类别id只能是整数。如果转换脚本里把类别名称列表搞错顺序比如把Running放到了索引2那么模型训练时会把跑动行为学成跌倒这种错误不会报任何提示只会无声地污染权重。所以拿到txt之后我习惯随机抽几个同名文件的xml和txt做一次双向换算比对而不是盲目信任转换过程。2.4 三文件一致性与坐标越界校验脚本下面这个脚本是我处理这类双格式数据集时的基本盘作用是做三件事统计缺失文件、校验xml与txt坐标换算、检查越界框import os import xml.etree.ElementTree as ET xml_dir ./labels_xml # xml所在目录 txt_dir ./labels_txt # yolo txt所在目录 img_dir ./images # 图片所在目录 class_names [Running, cellphone, fall, sleep, smoke] xml_names set(f.rsplit(., 1)[0] for f in os.listdir(xml_dir) if f.endswith(.xml)) txt_names set(f.rsplit(., 1)[0] for f in os.listdir(txt_dir) if f.endswith(.txt)) img_names set(f.rsplit(., 1)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)) print(缺少xml:, img_names - xml_names) print(缺少txt:, img_names - txt_names) print(孤立xml:, xml_names - img_names) for name in sorted(img_names xml_names): tree ET.parse(os.path.join(xml_dir, name .xml)) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) for obj in root.findall(object): cls obj.find(name).text if cls not in class_names: print(f{name}: 未知类别 {cls}) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) if xmax xmin or ymax ymin: print(f{name}: 零面积框 {xmin},{ymin},{xmax},{ymax}) if xmax w or ymax h: print(f{name}: 越界框 {xmax} {w} 或 {ymax} {h})脚本逻辑分三段前三个集合用文件名各自取交集直接打印缺失情况接着解析xml并校验未知类名和零面积框最后做坐标越界检查。参数上注意class_names的顺序必须与数据集原始的类别数组一致因为后面对比的是字符串而不是编号万一类别顺序写错了这里也能暴露。图片扩展名这里写死.jpg如果下载包里有.JPG大写后缀会被漏统计建议先把所有后缀统一成小写再跑脚本。3. 五类标注拆解类别顺序、框数差异与混淆风险3.1 类别数组顺序就是训练时的编号顺序这个数据集的类别名称数组是[Running, cellphone, fall, sleep, smoke]也就是说编号对应关系为0 Running、1 cellphone、2 fall、3 sleep、4 smoke。任何检测库的类别配置文件比如YOLOv8的data.yaml文件里names列表必须一字不差地按这个顺序写不能按字母序重排也不能按照自己喜好把sleep放到最前面。因为同目录下txt里的class_id已经按这份数组生成写错顺序相当于给所有样本换标签模型训练全程不会报错但推理结果会完全错位。我自己处理过的类似数据集里最容易犯的错误是把cellphone和smoke都看成人手部的附属行为而合并编号结果训练出来的模型对低头看手机和低头点烟完全无法区分因为类别边界边界本来就是模糊的。3.2 Running、fall、sleep三类姿态的标注边界先看框数分布用表格列一下类别框数占比视觉特征Running172429.8%身体倾斜、步幅大、四肢展开sleep177130.6%躺姿或趴姿、静止、闭眼fall97316.8%倒地瞬间、半卧、姿态扭曲cellphone91615.8%手持设备贴近面部或耳边smoke4067.0%手持烟卷、嘴部附近有烟雾sleep框数最多smoke框数最少相差约4.4倍。类别不均衡在行为检测里不算极端但真正影响训练精度的不是数量差而是类别之间的视觉相似性。睡觉和跌倒这两个类别大量出现水平方向的长条形框很多跌倒瞬间的姿态和躺下睡觉几乎长得一样跑步与跌倒之间也有动态模糊造成的交叠。矩形框本身不携带姿态关键点模型只能靠框内的纹理和边缘特征做区分所以当一个人穿着宽松深色衣服摔倒很可能被识别成sleep。标注规则说明里写的是“对类别进行画矩形框”这意味着所有框都是轴对齐矩形没有旋转框也没有关键点。对fall这类姿态多变的类别矩形框内会大量混入背景地板、桌面杂物等信息这会给模型引入噪声。处理办法是不要盲目加大矩形框去包住整个倒地姿态而应尽量贴着人体轮廓标注但既然此数据集已经标注好我们能做的更多是接受现状用后续训练策略弥补。3.3 cellphone、smoke类手部小目标的问题五类目标里cellphone和smoke是最典型的受限小目标。手机在画面里往往只有几十个像素宽烟卷更细只有几个像素。YOLO系列在训练这类小目标时普遍存在定位框偏大、置信度偏低的问题因为在特征图的高层下采样阶段小目标的特征很容易被背景淹没。对于这个数据集训练输入尺寸不能一律用416建议至少用640甚至1280否则烟卷这种细长物体会在缩小后变成一条暗线模型根本学不到形状特征。预处理时也可以考虑把图像按瓦片切分对小目标做二次检测。但因为数据集里手部目标的矩形框通常都会连带框住一部分手臂和面部目标尺寸其实比烟卷本身大不少所以这个数据集的cellphone和smoke实际难度低于纯小目标数据集在640分辨率下YOLOv8n也能得到可用结果。3.4 用框数比例决定验证集与增强策略框数差异对split策略有直接影响。假设随机按8:2切分验证集sleep类别大约能分到354个框smoke只有81个框验证集里smoke的真实正样本太少paddle出来的mAP波动会很大。更稳的做法是按类别框数做分层抽样来切分验证集或者直接把smoke所在图片的部分原图固定划入验证集。我在拆这个数据集时先按图片粒度统计每张图包含的类别发现很多图是多类别共存的比如一个人在跑步时手里拿着手机同一张图同时出现Running和cellphone两种框。这种多标签重合对训练是有利的会让模型学会共现关系但按图切分时要注意别让包含smoke的图片全部流到训练集。4. 增强样本占3/4怎么识别、怎么按组划分、怎么用才不翻车4.1 增强样本的常见形态与识别方法摘要里有一句重要提示数据集有3/4是通过数据增强得到的。所谓增强常见做法是水平翻转、亮度调整、对比度调整、HSV色域扰动、随机裁剪、加高斯噪声、小角度旋转等。这类样本在视觉上会有明显痕迹——同一拍摄场景出现多次人物外形相同只是镜像或色调不同背景纹理完全一样。如果直接把全部数据随机划分训练集和验证集同源图片会同时出现在两边验证指标会被“背题”效应拉高实际部署性能远低于测试结果。识别增强样本最直接的办法是算感知哈希。图片内容几乎相同的副本即使亮度色调改动其感知哈希也会高度接近。下面这个脚本用dHash做重复组检测用汉明距离聚类from PIL import Image import os, imagehash img_dir ./images hash_size 16 # 感知哈希尺寸 def calc_phash(path): with Image.open(path) as im: return imagehash.phash(im.convert(L), hash_sizehash_size) img_files sorted(os.listdir(img_dir)) hashes {f: calc_phash(os.path.join(img_dir, f)) for f in img_files} groups [] used set() for f1 in img_files: if f1 in used: continue group [f1] used.add(f1) for f2 in img_files: if f2 in used: continue diff hashes[f1] - hashes[f2] if diff 8: # 汉明距离越小越相似 group.append(f2) used.add(f2) groups.append(group) for idx, g in enumerate(groups): if len(g) 1: print(f组{idx}: {len(g)}张相似图) print( , , .join(g[:6]))脚本用了imagehash.phash把图片缩小成16x16的灰度特征图然后计算哈希距离。汉明距离小于等于8会视为同源样本这个阈值可以根据实际图片分辨率调整分辨率越高阈值可以适当放大。需要注意这个脚本对“同一场景但主体尺度明显不同”的增强方式不敏感如果增强时做了大幅随机缩放dHash可能认为它们是两张不同图片。更精细的做法是把每张图缩放成几个固定尺寸后再算哈希但处理4943张图的成本会明显上升一般先用这个粗筛版本跑一遍已经能发现大量重复组。4.2 为什么随机split会“灌水”如果随机按8:2切分增强产生的镜像图和调色图可能分别落入训练集和验证集。模型在训练时见过同一场景的亮度A版本验证时遇到亮度B版本虽然两者像素值不同但模型提取的特征高度重合验证mAP会虚高。我在某次行为检测项目里遇到过类似情况训练集和验证集mAP高达0.87换到真实场景视频后直接掉到0.56第一次排查就怀疑是数据划分泄漏。处理原则可以总结为要么全部数据参与训练验证集单独用原始未增强图片要么按重复组划分保证同一来源的图片全部归属同一集合。第一种方案更简单且不容易误判增强归属。4.3 按组划分训练集与验证集把第4.1节的哈希分组结果进一步利用按组为单位写入训练/验证清单。这里给出一个更完整的划分脚本import os, random from collections import defaultdict groups [...] # 第4.1节得到的groups列表这里省略哈希计算过程 val_ratio 0.2 train_files [] val_files [] for group in groups: group.sort() if len(group) 1: train_files.append(group[0]) else: k max(1, round(len(group) * val_ratio)) val_pick group[:k] train_pick group[k:] train_files.extend(train_pick) val_files.extend(val_pick) with open(train.txt, w) as ft: for f in train_files: ft.write(os.path.abspath(os.path.join(./images, f)) \n) with open(val.txt, w) as fv: for f in val_files: fv.write(os.path.abspath(os.path.join(./images, f)) \n) print(train:, len(train_files), val:, len(val_files))每个组内先按文件名排序再取前20%做验证剩下的进训练集。好处是逻辑确定、可复现坏处是如果某组全是相同内容的增强图验证集会占据该组较大比例导致训练集少了很多相似样本。折中做法是把每组的验证比例降到0.15并保证最少取1张。划分完之后还应该验证一下验证集中各类别的框数比例避免验证集类别分布偏移。4.4 增强样本训练的取舍建议大胆使用增强样本训练并不是坏事关键是怎么用。如果目标是快速验证模型结构是否合理用全部数据训练能得到更充足的样本量如果目标是评测模型真实泛化能力就必须让验证集与训练集做到同源隔离。就这个数据集而言原图大约只有1236张4943的1/4验证集如果只用原图会很小而且原图之间的场景相似度本来就高评估结果方差大。所以实际评估建议用“训练用全部增强图、验证用原图”的组合配合k折验证看看稳定性。还有一点要提醒增强样本中的强模糊样本比如运动模糊模拟和重度高斯噪声如果比例过高会拉低模型对清晰原图的响应。遇到这种情况可以按清晰度给样本降权最简单的方式是在训练时把模糊严重的图片从训练集中剔除只保留轻度亮度与几何增强副本。5. 训练前排查与避坑记录五类高频报错和它们的修复5.1 现象训练时报no labels found或xml缺失训练时日志里出现no labels found in xxx.jpg同时数据集统计显示的标注数不等于实际加载数。引发原因通常是三件套中某个文件缺失或者解压时xml文件被安全软件隔离。我在检查时发现有些xml里只有空的annotation结构没有任何object子节点labelImg在保存空白标注时会生成这种空壳文件训练框架会把它当作无标签样本处理。解决方法是先运行第2.4节的核对脚本把所有孤立文件和空xml列出来。空xml可以直接删除但删除前要确认与它同名的jpg是否真的没有目标如果有目标却xml为空说明标注时漏存了只能重新标注。删除空xml后图片数量会变成非真正意义的4943数据集统计要重新生成不能用摘要里的原始数字直接对比。5.2 现象训练loss变成nan或mAP一直为0训练到一半loss变成nan且换上小学习率仍然复现。最隐蔽的原因是txt中存在class_id超出类别总数的行比如类别数组是5个但txt里出现了class_id5框架在计算损失时索引越界轻则警告重则nan。另一个常见原因是归一化坐标出现负数或大于1例如1.0002这种越界值YOLO在计算anchor匹配时会把中心点定位到图外区域梯度异常。具体处理办法是对txt逐行做数值范围校验0 cx 1、0 cy 1、0 w 1、0 h 1同时检查class_id是否在[0,4]区间内。越界的行直接剔除别做clip因为一个越界框往往对应标注框画错或者坐标换算失误硬clip可能把错误框拉回图内但位置已经不对。5.3 现象训练时显示的类别名称与预期不符train阶段打印的类别列表是fall, sleep, cellphone, Running, smoke这种乱序甚至有的框架打印成0: fall。引发原因是data.yaml里的names列表顺序和txt的class_id没有对齐或者训练脚本里有自定义标签映射逻辑覆盖了yaml配置。解决方法是固定使用数据集原始顺序编写data.yamlpath: ./firc_person train: train.txt val: val.txt nc: 5 names: [Running, cellphone, fall, sleep, smoke]注意train和val字段可以指向txt文件列表YOLOv8支持这种写法但有些老版本框架只支持目录路径。如果发现加载数量不对改成目录路径再试。我在实际使用中遇到过一个细节yaml里names与txt class_id对齐了但框架内置的class names缓存还在从缓存加载了旧顺序导致推理标签串位。这种情况清空datasets缓存目录重跑一遍即可。5.4 现象sleep和fall互相误检、cellphone和smoke容易漏检模型训练完测试视频发现躺在地上的清洁工被持续识别成fall或者真人跌倒后模型长时间输出sleep。核心原因是这两类目标在矩形框层面高度相似加上数据增强里如果开了水平翻转会把跑步和跌倒的左右方向人为翻转进一步混淆姿态方向特征。cellphone和smoke的框内部纹理复杂整张图缩放到640后手部特征被压缩漏检率升高。解决办法分两部分yaml训练参数里把fliplr和flipud设为0只保留mosaic与HSV扰动然后把输入分辨率从640提升到960或1280。我在YOLOv8上测试过关闭翻转后sleep与fall的混淆比例大约下降3到5个百分点代价是跑步这两个方向的泛化能力略降。如果部署场景要求左右双向都能检测考虑在训练集中单独补充左右镜像的原图副本而不是使用框架的随机翻转。对cellphone和smoke可以尝试给检测头增加P2层小目标特征或使用SAHI切片推理。5.5 现象验证集mAP虚高实景测试暴跌训练完mAP0.5显示0.83导出的模型拿到现场摄像头一测直接降到0.51。随机划分加上3/4增强副本导致验证集泄漏是头号原因。第二个原因是场景单一可能同一监控点位拍摄了大量画面随机划分时同一场景的前后帧被分到两边模型等于在验证时看了训练场景的相邻帧。这种问题没有数据集内部的补救办法只能按第4.3节的哈希分组重新划分或者干脆不用自建的验证集而是录制一段完全独立的场景视频做实测评估。从那以后我每跑一个行为检测数据集都会强制先走一遍文件完整性和同源划分两步再谈训练参数这两步能过滤掉大部分无意义的mAP波动希望帮到你。6. 训练参数与验证技巧一份能用到底的yolov8配置最后一章落到具体操作。以YOLOv8n为例这份配置对五类行为检测比较通用# firc_person.yaml path: ./firc_person train: train.txt # 按组划分后的训练图片列表 val: val.txt # 独立验证图片列表 nc: 5 names: [Running, cellphone, fall, sleep, smoke] # 训练参数推荐 # model: yolov8n.pt # imgsz: 640 # epochs: 120 # batch: 16 # mosaic: 0.8 # fliplr: 0.0 # flipud: 0.0 # hsv_h: 0.015 # hsv_s: 0.7 # hsv_v: 0.4imgsz用640起步如果显存允许再试960。smoke和cellphone的小目标多分辨率上去后AP提升明显我实测从640到960smoke类别的AP一般能涨5个点以上代价是训练时间约增加0.6倍。filpr设为0是刻意为之因为这个数据集里跑步、跌倒、睡觉的方向对语义判断很重要左右镜像会制造混淆代价是模型对镜像场景的鲁棒性差如果现场存在背向跑步的视角要重新打开fliplr。验证时不要只盯mAP务必导出混淆矩阵重点看sleep和fall两个类别之间的串扰值。我习惯在训练结束后跑一遍model.predict对验证集做批量推理然后按真实标签和预测标签统计混淆比。如果sleep被误判为fall的样本集中在“人平躺且画面模糊”的低质量图上优先检查验证集是否混入了过度增强样本而不是急着调损失函数。如果部署场景还要兼容不同摄像头角度我再做一个额外的技巧把训练好的模型在原始视频上跑一遍检测收集所有未命中的漏检帧和置信度低于0.25的弱检框按时间排序看是否集中在某个固定区域。行为检测的目标经常出现在画面边缘而训练集里如果边缘目标数量少模型对边缘目标的感受野覆盖不足此时做法不是再加数据而是调高mosaic概率让目标中心点更多落在画面四角区域同时开启close_mosaic到10个epoch最后10轮切回正常mosaic稳定训练。这套配置我陆续在多个类似数据集上跑过整体表现平稳。遇到AP波动大的情况先用第5章脚本做完整性和分组复检排除数据问题再谈网络调优这是我自己踩过的最大教训希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑