ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLOv5跌倒检测数据集实战:5类标注与训练避坑指南

YOLOv5跌倒检测数据集实战:5类标注与训练避坑指南 简介在计算机视觉领域目标检测技术已从通用行人检测拓展到更细粒度的行为识别场景。跌倒检测正是这一演进的典型代表它要求模型不仅判断画面中是否有人还需通过姿态变化区分站立、行走、坐姿、弯腰与倒地状态。多类别数据集的语义设计成为平衡误报与召回的关键而YOLOv5作为工业界广泛使用的检测框架其标准目录结构与标签格式直接影响训练效率与模型效果。从5类别跌倒检测数据集的类别划分原则出发拆解YOLOv5目录组织、归一化标签坐标、data.yaml配置等核心环节并结合训练前的校验流程与数据泄露、标签ID混乱、类别不平衡等实战陷阱为跌倒检测工程落地提供可复用的技术参考帮助读者快速上手并规避常见问题。 跌倒检测这个场景很多人第一反应是“用行人检测不就行了”真上手做才发现完全不是那么回事。普通的行人检测模型只能告诉你“画面里有人”而当老人从床上滑下来、在卫生间倒地、在走廊突然晕厥时模型需要判断的已经不只是“有没有人”而是“这个人处于什么姿态、是否已经失去平衡”。这时候一个按YOLOV5目录格式整理好的行人跌倒检测数据集5类别就派上用场了——它把跌倒检测从“检测人”推进到了“推理姿态与状态”的层面这也是我这次要分享的核心内容。这篇内容适合两类人一是刚入门目标检测需要一个标准格式数据集来跑通整个YOLOV5训练流程的新手二是已经在做行人检测、但觉得“光检测人不够用”、想把场景细化到跌倒报警这类具体业务的人。我会从类别的语义划分、目录结构细节、训练前的校验动作到实际训练中容易踩的坑完整过一遍。1. 5个类别是怎么定的跌倒检测数据集的语义设计与标注边界1.1 为什么是5类而不是单纯的“跌倒/正常”二分类跌倒检测这个任务最直接的思路当然是“跌倒”和“非跌倒”两类。实际操作下来你会发现二分类模型在实验室里指标漂亮一上真实场景就疯狂误报问题恰恰出在“非跌倒”这个类别的边界太模糊。打个比方你告诉模型“画面里有人但没跌倒”模型记住的是“站立的人”这个单一形态。可现实中人还有蹲下系鞋带、弯腰捡东西、坐在轮椅上、躺在病床上休息这些状态这些姿态和跌倒瞬间的视觉特征高度重叠。弯腰捡东西时躯干前倾、头部下移和跌倒过程中的下坠阶段非常像坐在低矮板凳上时髋关节和膝关节的角度与跌倒前失稳下蹲也很接近。二分类模型没办法区分“主动下蹲”和“失控倒地”只能靠碰运气。所以这个数据集把类别细化成5类本质上是在给模型建立一套“人体姿态的连续语义空间”——从站立、行走、坐姿、弯腰到跌倒倒地每一类代表了人体稳定性的不同层次。模型学到的不是“跌倒/非跌倒”这个二元判断而是“当前画面里的人处于哪个姿态档位”当它看到画面里出现从站立突然跳变到倒地状态的组合时才有足够的上下文去判定跌倒事件发生。这也是为什么跌倒检测数据集普遍采用多类别设计而不是简单粗暴地做二分类。1.2 每类标签的判定规则和模糊边界处理由于原始描述没有明确5个类别的具体名称我按跌倒检测任务最常用的语义体系来做说明——这也是目前主流跌倒检测数据集如UR Fall Detection、Le2i等的通用做法。这个数据集的类别定义如下类别ID类别名称定义说明0standing 站立人体直立躯干与地面垂直或接近垂直无明显弯曲1walking 行走身体直立且有明显位移步态可见2sitting 坐姿臀部有支撑物椅子、床沿、轮椅髋关节与膝关节弯曲3bending 弯腰躯干明显前倾髋关节弯曲但身体仍处于自主平衡状态4fallen 跌倒倒地身体接触地面或床面失去自主站立能力无法维持直立姿态这个表看起来简单真正标注的时候每个类别都有灰色地带。先说standing和walking的边界从视频单帧来看站立和行走的姿态几乎一样区别只在于有没有位移。标注时如果只看单张图片走动中刚好停住的那一帧就很难归类。我的处理规则是只要该帧的前后几帧有明显的脚步位移就标为walking如果连续多个帧都静止不动才标为standing。sitting和bending的区分更麻烦。坐在高椅子上躯干直立这个没问题但坐在矮凳上时躯干会前倾髋关节弯曲角度很大从侧面看和弯腰的姿态很像。我的判定规则是看有没有支撑物——臀部或大腿有明显承托物就是sitting没有承托物且躯干前倾超过45度就是bending。还有一种特殊情况是“蹲姿”它介于bending和sitting之间数据集里没有单独设这个类别标准做法是蹲下时没有支撑物、重心向下按bending处理。1.3 弯腰、坐姿、跌倒这三个状态的区分逻辑这三个状态是整个数据集的灵魂也是跌倒检测误报率高低的分水岭。我在实际标注和训练中总结了一个核心判断逻辑看重心和支撑面的关系。站立和行走时重心在双脚支撑面之上人体稳定坐姿时支撑面转移到了臀部但支撑物椅子、床沿承受了大部分体重人体依然稳定弯腰时重心前倾但仍在双脚支撑面范围内人体可自主恢复直立跌倒倒地的本质是重心完全移出支撑面人体失去自主控制最终以身体其他部位背部、侧身、臀部接触地面或床面告终。这个逻辑落到标注边界上就产生了三条硬性规则弯腰和跌倒倒地的边界身体是否接触地面。弯腰时可能手撑地比如系鞋带单膝跪地但躯干没有大面积接触地面跌倒倒地是背部、侧身或臀部大面积接触地面且短时间内无法自主起身。坐姿和跌倒倒地的边界是否有支撑物承托。坐在床上、椅子上都算sitting但如果是从站立位置失去控制、身体重重摔落到地面上即使最后姿态像坐着也要按fallen标注。跌倒过程帧的处理从失稳到完全倒地中间有十几帧的“下坠过程”这些帧标的类别不统一会让模型学到错误特征。我的原则是——只要画面中人体已经明显失去平衡、躯干倾斜角度超过45度且正在加速下坠就直接标为fallen不单独设“falling”类别。原因是目标检测是单帧推理报警系统需要的是“已经倒地”的确定性判断而不是“正在倒”的概率预测把下坠帧也归入fallen相当于变相扩大了跌倒样本的占比让模型对跌倒早期的外观特征更敏感。2. YOLOV5目录格式到底应该长什么样目录结构、标签文件与配置文件逐一拆解2.1 基础目录结构YOLOV5的目录格式是整个训练流程的地基摆错了训练脚本连跑都跑不起来。这个数据集按标准格式组织完整结构如下dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ └── val/ │ ├── 000101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ ├── 000002.txt │ │ └── ... │ └── val/ │ ├── 000101.txt │ └── ... ├── data.yaml └── README.mdimages和labels两个目录必须严格同级train和val子目录名要保持一致。图片放在images/train下对应的标签txt文件必须放在labels/train下文件名要和图片文件名保持一致只差后缀否则训练时会提示找不到标签。一个容易忽略的细节是图片目录里应该只放图片文件标签目录里只放txt文件。YOLOV5在训练时会遍历目录下的所有文件如果images/train里混入了一张非图片文件比如误放的json文件、隐藏的.DS_Store可能会导致数据加载报错。我一般会在整理完数据集后跑一遍脚本把非.jpg/.jpeg/.png文件全部清掉。数据集根目录下放一个data.yaml是YOLOV5的标准做法后面会详细说。README.md里一般记录数据集的来源、类别说明和划分方式这个文件不影响训练但建议保留不然过两个星期你自己都会忘了各类别的标注规则是什么。2.2 标签txt的坐标格式与归一化计算YOLO系列标签格式和COCO、VOC的像素坐标格式差别很大很多从其他格式转过来的新手第一次看到标签内容会懵——txt里每一行都是这样的0 0.453125 0.617187 0.220703 0.481250每一行代表一个目标框5个数依次是类别ID、中心点x坐标、中心点y坐标、框宽度w、框高度h。注意是中心点坐标不是左上角坐标而且全部归一化到0~1之间。归一化计算方式很简单x_center (x_min x_max) / 2 / image_widthwidth (x_max - x_min) / image_widthy方向同理。比如一张1920x1080的图片里某个人物检测框的像素坐标是左上角(500, 200)、右下角(1200, 900)那对应的标签就是0 0.442708 0.509259 0.364583 0.648148计算过程是x_center (500 1200) / 2 / 1920 0.4427y_center (200 900) / 2 / 1080 0.5093w (1200 - 500) / 1920 0.3646h (900 - 200) / 1080 0.6481。这里有一个常见误区有人会把坐标写反写成左上角坐标除以图片宽高这样训练时模型的损失函数会变得异常大而且训练过程非常慢。YOLOV5在数据加载时读到标签会先判断坐标合法性如果出现中心点坐标大于1或者宽度高度为0的情况它会把这条标签当作无效目标直接跳过——但不会报错只会静默忽略最后你发现训练集明明有8000张图实际参与训练的目标却少了一大半。这种坑最烦人因为它不报错只会让你的模型莫名其妙地学不到东西。2.3 data.yaml的配置细节data.yaml是连接数据集和训练脚本的桥梁配置内容如下# 数据集根目录路径使用相对路径时以运行train.py的位置为基准 path: dataset/ # 训练集和验证集图片所在目录相对于path train: images/train/ val: images/val/ # 类别数量 nc: 5 # 类别名称按类别ID顺序排列必须和标签txt里的ID一一对应 names: 0: standing 1: walking 2: sitting 3: bending 4: fallen这里有几个需要注意的坑。首先是path字段YOLOV5有多个版本v5.0、v6.0、v7.0等不同版本对path字段的处理方式略有差异。v5.0版本要求train和val路径是相对于data.yaml所在的目录而v6.0及之后的版本是以运行train.py时的工作目录为基准。最简单稳妥的做法是把data.yaml放在数据集根目录下运行train.py时在数据集根目录的上一级目录执行path字段写dataset/train字段写images/train/。如果你把data.yaml放在项目根目录path就要写清楚到数据集文件夹的相对路径。其次是nc和names的顺序。如果你的标签txt里类别ID是0~4nc就是5names列表按0到4的顺序排列。如果你下载的数据集是1~5编号和COCO格式转换时的习惯有关那标签文件必须批量把ID减1否则训练时类别ID会越界。最后是类名不要有空格和特殊字符。YOLOV5会把names里的字符串作为日志输出的类别标签如果带空格后面打印混淆矩阵和PR曲线时看起来会非常乱。我用过带空格的类名“standing person”输出报告里表格对齐全是乱的后来改成standing一劳永逸。2.4 训练集/验证集的划分原则按视频片段切分而非按帧切分数据集的划分方式直接影响模型评估的可信度这也是很多从视频里抽帧做数据集的人最容易犯的错误。如果你的数据是从监控视频里按帧抽出来的那么相邻几十帧的画面几乎一样只是人物动作有微小变化。如果直接按时间顺序前80%做训练集、后20%做验证集那训练集和验证集里会包含大量来自同一段连续动作的高度相似帧。模型在训练集里见过的画面验证集里几乎原样再现评估出来的mAP会虚高可能达到0.95以上但一放到真实场景就原形毕露。正确的做法是先把视频按场景片段切分。比如一段3分钟的老人活动视频先切成若干个独立的动作片段每个片段内动作连续然后按片段为单位做划分——一部分片段进训练集另一部分片段进验证集。这样保证同一片段里的画面不会同时出现在训练集和验证集中评估结果才真实反映了模型的泛化能力。具体到这个跌倒检测数据集我建议是按“事件序列”划分把每条视频里“站立→行走→跌倒→倒地→起身”完整的事件链作为最小单元完整事件链全部进训练集或全部进验证集不拆分。一个公共安全项目里这种划分方式在真实场景测试时误报率比随机划分低了不少因为模型没有“背答案”的机会。3. 拿到数据集后的第一件事完整校验、可视化标注与快速试训练3.1 五步完整性检查拿到任何整理好的数据集先别急着跑训练花10分钟做一遍完整性检查能帮你省下后面调试的好几个小时。我每次拿到数据集都会按这五步走第一步检查图片与标签文件数量是否一致且一一对应。用脚本统计images/train下的jpg数量和labels/train下的txt数量应该完全相等且同名文件成对出现。import os img_dir dataset/images/train/ label_dir dataset/labels/train/ imgs [f.split(.)[0] for f in os.listdir(img_dir)] labels [f.split(.)[0] for f in os.listdir(label_dir)] only_img set(imgs) - set(labels) only_label set(labels) - set(imgs) print(只有图片没有标签:, only_img) print(只有标签没有图片:, only_label) print(总数: 图片, len(imgs), 标签, len(labels))第二步检查标签文件的类别ID是否在0~4范围内。遍历所有txt文件读出每一行的第一个数字如果是5类数据集任何ID大于4或小于0的数字都说明标签格式有误。第三步检查标签坐标是否越界。YOLO格式要求所有归一化坐标在0~1之间由于标注软件的精度问题偶尔会出现框的边缘超出图片边界导致坐标大于1或小于0的情况YOLOV5训练时会忽略这些标签。第四步检查图片尺寸是否统一。YOLOV5会自适应缩放输入图片不要求所有图片尺寸一致但如果数据集中混入了单通道灰度图和彩色图训练容易报通道数错误。用PIL打开图片看一眼即可。第五步检查是否存在空标签文件。有些图片可能画面里没有行人标注时会被跳过或生成空白txtYOLOV5训练时允许存在空标签文件但会影响训练效率建议尽量剔除。3.2 用脚本可视化标注框检查标签格式正确还不够我更建议把标注框画到图片上“看一眼”。格式正确只说明数据语法没问题但框的位置准不准、类别标没标错只有可视化之后才能确认。我常用的可视化脚本很简单import cv2 def draw_yolo_boxes(img_path, txt_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path, r) as f: lines f.readlines() for line in lines: data line.strip().split() cls_id int(data[0]) x_center float(data[1]) * w y_center float(data[2]) * h box_w float(data[3]) * w box_h float(data[4]) * h x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) color (0, 255, 0) if cls_id 4 else (255, 0, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img class_names [standing, walking, sitting, bending, fallen] img draw_yolo_boxes(dataset/images/val/000101.jpg, dataset/labels/val/000101.txt, class_names) cv2.imshow(check, img) cv2.waitKey(0)抽个20~30张不同场景的图片跑一遍重点看跌倒类fallen的框是否完整包住整个倒地人体弯腰类bending的框有没有把腿截断坐姿类sitting的框是不是包含了大半张椅子。如果这些有问题训练出来的模型在推理时框的位置会偏跌倒检测系统对框的完整性要求其实很高因为后续要基于框的高度和宽高比变化来判断是否跌倒框截错了下游判断全错。3.3 首轮训练的推荐参数和预期效果数据集校验无误后可以直接跑首轮训练。我建议用YOLOV5的s模型起步参数不要一上来就调得很激进python train.py \ --data dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --workers 8显存不够就把batch降到8或者用yolov5s的预训练权重配合--img 416先跑通流程。100个epoch对一个中等规模数据集来说基本足够看到收敛趋势如果loss在最后20个epoch还在持续下降再加50个epoch继续训。预期效果上一个标注质量良好的5类跌倒检测数据集在yolov5s、640分辨率、100个epoch的配置下验证集mAP0.5通常在0.85~0.95之间其中standing和walking这类外观差异较大的类别mAP会很高0.95以上bending和fallen这类容易混淆的类别mAP会低一些0.80~0.90。如果fallen的mAP低于0.75先不要急着调模型回头检查一下标注质量大概率是跌倒样本的边界框不完整或者把弯腰帧错误标成了fallen。4. 实际使用中的三个大坑数据泄露、标签ID混乱和类别不平衡4.1 数据泄露相邻帧被分进训练集和验证集这是做视频类检测数据集时最容易踩、也最难发现的坑。跌倒检测数据集很多是从监控视频里截帧来的如果划分数据时随手按文件名前80%做训练、后20%做验证那验证集的画面几乎都能在训练集里找到极其相似的“邻居”——因为视频每秒25帧第100帧和第105帧之间人物姿态差别可能只有几个像素。数据泄露的直接后果是训练过程loss下降正常验证集mAP高得离谱0.95以上但模型部署到真实场景后精准率直线下降误报漏报不断。我见过最极端的一次一个同事用按时间序划分的数据集训练跌倒模型测试集mAP达到0.98结果在真实养老院场景测试时几乎每个弯腰老人都被报成跌倒——因为模型根本没有学到“跌倒”的泛化特征只是背下了训练集里的画面。解决办法前面已经提到按视频片段或事件序列划分。如果没有原始视频只有图片文件可以通过文件名判断——像frame_0001.jpg、frame_0002.jpg这种连续编号的基本可以确定是抽帧来的建议按固定间隔抽帧后重新划分。还有一种做法是检查图片的感知哈希相似度把高度相似的图片聚类确保同一个聚类的图片全部进训练集或全部进验证集。4.2 标签ID从0还是从1开始一个小数字引发的连环报错数据集分享时为了符合人类的计数习惯有人会把类别从1开始编号也就是standing1, walking2……fallen5但YOLOV5要求类别ID必须从0开始。如果标签文件里出现了ID为5的标签而nc5合法ID是0~4训练会静默忽略这行标签。这个坑的隐蔽之处在于它不报错。YOLOV5的数据加载器碰到非法ID的标签行会打印一条Warning然后跳过但如果这种问题标签很多你训练的时候日志又刷得快很容易忽略掉。等到训练结束发现fallen类一个都检测不出来回头查才发现fallen的ID被标成了5全部被忽略。我之前写过一个通用的标签清洗脚本现在每次处理数据集都会先跑一遍import os def fix_label_ids(label_dir, valid_idsrange(5)): for filename in os.listdir(label_dir): if not filename.endswith(.txt): continue path os.path.join(label_dir, filename) with open(path, r) as f: lines f.readlines() new_lines [] for line in lines: parts line.strip().split() if not parts: continue cls_id int(parts[0]) if cls_id not in valid_ids: print(f{filename}: 非法ID {cls_id}已跳过) continue new_lines.append(line.strip()) with open(path, w) as f: f.write(\n.join(new_lines))有个容易被忽略的点标签文件最后一行的换行符。有时候空文件或者只有一行的txt末尾少了换行符某些脚本会在读取时把下一行的内容拼到一起导致标签解析错误。脚本里用\n.join()重新写入时每条标签单独一行能顺便把这个隐患消除。4.3 跌倒样本不足数据增强策略怎么调跌倒检测数据集天然存在类别不平衡的问题。在真实环境里人大部分时间是在站立、行走、坐着跌倒是一个低概率事件所以采集出来的数据里standing、walking、sitting的样本量通常是bending、fallen的好几倍。如果直接拿不平衡数据训练模型会倾向把不确定的样本预测为高频类别导致跌倒检测的召回率很低。处理类别不平衡有几个思路按推荐优先级排列第一个思路是数据增强。YOLOV5自带的mosaic增强、随机水平翻转、HSV色域扰动对缓解不平衡有一定作用但对于bending和fallen这类姿态敏感的类别增强幅度要控制好。比如随机旋转的角度如果超过±30度跌倒倒地的姿态会变得非常奇怪甚至看起来像贴在天花板上模型学到的是扭曲的姿态特征而不是跌倒本身。我建议基础增强用默认配置另外对fallen类单独做增强比如小角度旋转±15度、亮度对比度扰动、轻微模糊。第二个思路是类别加权。YOLOV5的损失函数支持按类别权重调整可以在数据集的data.yaml里增加不同类别的权重系数让模型对fallen类的误判付出更大代价。具体权重可以按各类别样本数量的倒数来算比如fallen只有standing的一半那fallen的权重就设为2。第三个思路是困难负样本挖掘。训练前期模型会把一些弯腰动作误判成跌倒把这些误判的样本挑出来确认是“弯腰捡东西”后重新标注为bending再加入下一轮训练。这算是一个主动的“模型反馈数据增强”流程我每做一个跌倒检测项目都会跑两三轮每轮能挑出几十个让模型混淆的困难样本针对性补标注后模型误报率下降非常明显。第四个思路才是考虑调整模型结构比如给fallen类单独加一个检测头但这属于进阶优化等前面几步都做完了还达不到指标再考虑。写在最后一个关于类别设计的体会这个数据集项目我前后迭代了好几版最深的体会是类别设计比模型调参更重要。第一次做跌倒检测我当时图省事只分了三类站立、坐姿、跌倒训练出来的模型在演示环境里效果还行一搬到真实场景就乱套——大量弯腰捡东西、蹲下系鞋带的画面被误判成跌倒因为模型根本不知道“弯腰”和“蹲下”这些中间姿态长什么样它只能把一切不是站立坐姿的形态往后仰的fallen类上靠。后来把类别扩到5类把弯腰、行走这些“中间状态”单独列出来误报率降了一大半。所以如果你也想自己做一个跌倒检测数据集我的建议是宁可分类细一点让标注工作量大一点也不要让模型在“弯腰捡东西”和“跌倒倒地”之间靠猜。每一个中间类别都是在帮模型划清一条安全边界。还有一个小技巧模型跑起来之后把推理置信度阈值调低比如0.25然后把所有低置信度的检测结果导出来人工复核一遍。你会发现很多模型犹豫不决的样本恰恰是标注边界最有争议的那部分——这比你自己凭空想“边界规则”要直观得多。根据这些样本去修正标注规则再迭代一轮数据集的整体质量会有实打实的提升。本文还有配套的精品资源点击获取
返回列表