ARTICLE DETAIL

资讯详情

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

YOLOv5+姿态估计+LSTM:两段式异常行为检测实践指南

YOLOv5+姿态估计+LSTM:两段式异常行为检测实践指南 简介面向本科毕业设计场景的YOLOv5异常行为检测完整项目聚焦驾驶异常等行为识别适合计算机视觉、人工智能方向学生参考算法改进思路与工程实现。项目源自山东大学本科毕设成果围绕YOLOv5给出四种改进引入GhostConv轻量化卷积降低参数量采用BiFPN融合中小型目标检测层加入CA注意力机制使目标框定位更精确并以Alpha-EIoU替换CIoU在几乎不引入额外参数的情况下大幅提升检测精度。压缩包共214个文件以105个yaml参数配置和45个Python脚本为主体辅以jpg样例图、txt标注、sh脚本、md说明、Dockerfile环境配置及ipynb交互教程整体仅2.8MB目录结构清晰、轻量易用便于按模块阅读和二次开发。附带标注示例与预测样例图有助于理解数据组织和检测效果。已有121人学习适合需要快速搭建异常行为检测基线、复现YOLOv5改进实验或准备毕设答辩的学生。1. 先把结论放前面YOLOv5 只负责“看见人”异常行为是另一套模型的事一个常见误区是既然标题叫“基于YOLOv5的异常行为检测”那是不是用 YOLOv5 把“打架”“摔倒”“翻越围栏”这些类别训练出来就行实际跑过一遍就知道YOLOv5 做的是稠密单帧分类它擅长在静止画面里找到“谁、在哪”但“推搡”和“拥抱”从单帧上看几乎一样区别全在时间轴上的位移和姿态变化。所以正经的落地路线通常是两段式先用 YOLOv5 输出人体框再用姿态估计把每个人变成一组关键点坐标最后喂给一个时序分类模型去判断当前这段动作是正常还是异常。这套思路适合做毕业设计快速出成果也适合生产环境里在边缘设备上跑实时检测。下文按这个管线一步步拆开讲每步都给足可复现的命令和参数。2. 两段式结构YOLOv5 只出框异常判定交给姿态序列2.1 为什么单帧卷积网络做不好异常行为识别把“异常行为检测”当成目标检测的扩展任务是很多初学者踩的第一个坑。你想让 YOLOv5 学会“摔倒了”这个类别但摔倒可能发生在画面任意位置人倒地后身体轮廓变成一条横线单看这一帧和“躺在地上休息”没有区别。同样打架场景里两个人互相推搡单帧上就是两个矩形框距离很近YOLOv5 的 Anchor 匹配和 NMS 逻辑里并没有任何关于“相对速度”的信息可以提取。异常行为在计算机视觉里的定义是“偏离正常时空模式的动作序列”。空间维度看姿态时间维度看轨迹。我见过的最小可用方案是YOLOv5 检测人体框 - 姿态估计模型输出 17 个关键点坐标 - 按帧拼接成时间序列 - LSTM 或 Transformer 分类。整条链路里 YOLOv5 的参数量占比不大但它决定了姿态输入的质量前置检测的漏检和误检会直接扩散到后面的时序模型所以你不能随便砍掉它。2.2 姿态估计模型怎么选算力、帧率和关键点数量的取舍姿态估计是两段式结构里最容易被低估的部分。常见的选型有这么几类OpenPose 最准但慢适合离线处理视频HRNet 精度高但模型体积大部署到 Jetson 上需要 TensorRT 加速MediaPipe Pose 在 CPU 上能跑实时关键点平滑做得不错适合快速原型YOLOv8-pose 和 YOLOv5 生态亲和可以在同一个工程里管理检测和姿态两个模型。我做毕业设计时会先问自己一个问题这个系统最终跑在什么设备上教室监控场景通常是普通 PC 加独显那就可以用 HRNet-W32 这类精度更高的模型如果是树莓派或者手机优先 MediaPipe 或轻量姿态模型宁可关键点抖动多一点也要保证帧率不低于 15 FPS。另外要注意YOLOv5 检测到的人体框要稍微向外扩 10% 到 20% 再传给姿态模型因为关键点往往在人框边缘裁得太紧会让手腕和脚踝拟合失败。2.2.1 关键点坐标必须归一化到固定帧率姿态模型输出的关键点坐标系取决于输入图像尺寸不同场景下摄像头分辨率不同同一个“挥手”动作在不同距离下像素位移差几倍。所以姿态序列在进时序模型之前必须做归一化。常见做法是取人体框左上角和右下角作为归一化基准把关键点坐标变成相对于框宽高的比例值。帧率也要固定摄像头可能从 30 FPS 掉到 20 FPS不统一会导致窗口长度对应的物理时间漂移。import numpy as np def normalize_pose(keypoints, bbox, fps30): x1, y1, x2, y2 bbox w max(x2 - x1, 1e-6) h max(y2 - y1, 1e-6) # 关键点shape (17, 3) - (x, y, confidence) kp keypoints[:, :2].copy() kp[:, 0] (kp[:, 0] - x1) / w kp[:, 1] (kp[:, 1] - y1) / h kp kp * 2.0 - 1.0 # 映射到 [-1, 1]减少尺度偏移 return kp def sliding_window(seq, window_size32, stride16): for i in range(0, len(seq) - window_size 1, stride): yield seq[i:i window_size]这段代码做了两件事一是把关键点按人体框尺寸归一化避免人和摄像头的距离影响特征二是滑窗切序列让分类器每次只看到 32 帧约 1 秒的动作片段。window_size是超参数摔倒这类突发动作可以设小到 16 帧徘徊这类慢动作建议设到 64 帧具体看你的行为类别。stride决定相邻片段的重叠度50% 重叠可以缓解片段边界处动作被切碎的问题。2.3 顺便厘清YOLOv5 在流程里的边界很多人把 YOLOv5 当成整个项目的核心写论文时也只讲检测器怎么改。但我要明确一个判断在这类两段式方案里YOLOv5 的好坏只决定“能不能稳定拿到人体框”。评估检测器要看 mAP但评估整个系统要看时序分类的 F1。如果你发现误报很严重先去查姿态模型在哪几帧丢点了而不是反复改 YOLOv5 的 Anchor。反过来说YOLOv5 的轻量版本选择直接影响后续框架的输入质量yolov5n和yolov5l的推理耗时差别可能超过 100 毫秒但精度差别在特征明显的监控场景里反而不大。3. 数据集、标注与时序模型把行为变成序列分类3.1 训练数据从哪里来公开数据集打底自采数据补场景行为识别领域有几份被反复使用的公开数据集UCF-Crime 覆盖打架、纵火、抢劫等异常事件上海科技大学 Campus 数据集提供固定摄像头下的拥挤场景异常帧。这些数据集适合做预训练和论文对比实验但要注意两个问题一是数据集的“异常”定义和你的毕设题目不一定一致很多公开数据集的异常是人群踩踏不是单人摔倒二是分辨率普遍偏低和高清监控画面差异大。所以生产级做法是公开集预训练加自采微调。自采时不需要把所有行为都从头标一遍你只需要标出异常片段的时间范围然后用滑窗把异常片段逐帧切出来正常行为从监控录像的空白时段随机截取。关键点数据可以直接从视频批量提取存成 NumPy 数组训练集就绪的速度比重新标图片快得多。异常行为关键特征姿态序列建议窗口长度备注摔倒髋关节中心快速下移躯干角度从垂直变水平16~24 帧容易与“蹲下”混淆必须看变化速度打架两人距离持续小于阈值手腕关节速度急剧增大24~32 帧单人挥拳容易误报需要双人联合特征翻越围栏髋关节高度先上升再消失伴随腿部关键点丢失32~48 帧依赖姿态模型的持续跟踪徘徊人体轨迹长时间保持在同一区域64~128 帧需要轨迹特征单靠姿态不够3.2 时序分类模型选型LSTM 为主Transformer 做对比姿态序列的维度并不高17 个关键点乘以 2 维坐标即使加上置信度也才 51 个数值。这种低维序列用轻量时序模型就够了。LSTM 是毕业设计里性价比最高的选择训练快、可解释性强论文里也好画结构图。GRU 参数更少效果和 LSTM 基本持平。TCN 适合对延迟敏感的场景因为时序卷积不需要等待完整序列跑完。Transformer 的自注意力能捕捉“先抬头再摔倒”这种长距离依赖但小数据集上经常不如 LSTM 稳定。我一般会定义一个输入维度为(batch, time_steps, features)的模型features包括 17 个关键点的 x、y 和置信度总计 51 维。这样写的好处是换姿态模型不用改时序模型接口。import torch import torch.nn as nn class PoseLSTM(nn.Module): def __init__(self, input_size51, hidden_size128, num_classes2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layers2, batch_firstTrue, dropout0.3 ) self.classifier nn.Sequential( nn.Linear(hidden_size, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, num_classes) ) def forward(self, x): out, (hn, cn) self.lstm(x) # 取最后一个时间步的输出表示整段序列的语义 last out[:, -1, :] return self.classifier(last)num_layers2是经验值一层偏欠拟合三层在小数据集上容易过拟合。dropout0.3放在 LSTM 内部和全连接层之间防止模型死记训练集的动作节奏。分类输出我习惯用两个类别的二分类正常/某类异常而不是一个多分类模型同时学七八种异常因为新加的异常行为类别往往样本很少会影响既有类别的学习多模型组合在生产里也更好维护。3.2.1 不要犯的错把关键点序列直接丢进 CNN有些论文会把关键点画成热图再扔给卷积网络这在姿态估计里合理在行为分类里绕远路。关键点坐标本身就是高度抽象的特征强行栅格化反而丢失精确位置。另一个常见错误是忽略了置信度通道。姿态模型对遮挡关节会输出低置信度的坐标这些位置本身就是异常行为的重要信号——比如倒地后腿部关键点大面积丢失这恰恰说明人不在正常直立状态。所以输入维度里第三维置信度不要省略。3.3 时序数据增强速度扰动和关键点遮挡模拟图像增强里常用翻转、裁剪、颜色抖动时序模型也有对应的增强手段只是很多人不知道。最有效的是时间速度扰动把同一个动作样本在时间轴上拉长或压缩 10%~30%模拟不同人的动作快慢差异。实现方式是使用基于样条的时间扭曲把原始的关键点序列重新采样到目标长度。import numpy as np from scipy.interpolate import CubicSpline def time_warp(seq, factor1.2): # seq: (T, C) 的归一化关键点序列 T seq.shape[0] old_time np.linspace(0, 1, T) new_time np.linspace(0, 1, int(T / factor)) warped [] for i in range(seq.shape[1]): cs CubicSpline(old_time, seq[:, i]) warped.append(cs(new_time)) return np.stack(warped, axis1)factor是时间缩放系数大于 1 表示动作加速小于 1 表示慢放。实际效果等价于同一个“摔倒”动作既出现在短片段的快速跌倒里也出现在长片段的缓慢晕倒里模型就不会对速度过分敏感。另外一个增强技巧是随机丢弃部分关键点坐标并置为 0模拟姿态模型偶尔丢帧的现象增强后模型对姿态输入质量差的鲁棒性会明显提升。4. 从 YOLOv5 训练到工程交付配置、超参数和 ZIP 里该放什么4.1 YOLOv5 训练自己的数据集环境配置和最小命令先把前置条件说清楚项目根目录下需要准备好dataset.yaml和images/、labels/目录。YOLOv5 的标签是每个图片对应一个 txt 文件每一行是class_id center_x center_y width height坐标值归一化到 0~1。用 LabelImg 或者 AnyLabeling 标注完划分训练集和验证集时确保同一个人不出现在两边否则验证集分数虚高。用 7-Zip 解压下载好的yolov5-master.zip后在项目根目录下先把依赖装齐cd yolov5-master pip install -r requirements.txt python train.py --data dataset.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640 --project runs/train --name falling_v1--img 640是训练输入分辨率监控画面如果人脸和人体都偏小建议--img 960回头把阈值降到 0.25 再试。--weights用 YOLOv5 官方预训练权重做迁移学习而不是随机初始化不然 100 轮以内很难收敛到可用精度。训练完成后看runs/train/falling_v1/weights/best.pt这个文件就是后续检测用的最终模型。4.1.1 YOLOv5 超参数里值得动手改的三个值YOLOv5 默认的超参数文件是data/hyps/hyp.scratch-low.yaml毕设场景我不建议整体重调但三个值必须手动确认。lr0初始学习率默认 0.01如果你的数据集只有几百张图降到 0.005 会更稳否则 Loss 前两轮就飞了。mosaic数据增强默认 1.0行人目标比较小时保留即可但要注意它会随机裁剪拼接人身体关键连接处有时候被切开姿态模型后面处理起来会别扭。hsv_h色调增强在监控场景里可以调低一点监控摄像头色彩通常稳定过强的颜色抖动反而让模型学到不真实的颜色分布。4.2 模型导出与部署ONNX、TensorRT 与摄像头联调训练完要落地成毕设演示不能每次检测都开 Python 训练脚本。YOLOv5 官方提供导出脚本一次可以把 PyTorch 权重转成onnx、engine、openvino多种格式python export.py --weights runs/train/falling_v1/weights/best.pt --include onnx engine --device 0--include onnx engine会依次导出 ONNX 和 TensorRT 的 engine 文件engine 格式只能在 NVIDIA GPU 上运行但推理速度比 ONNX 快 2~3 倍。导出后写推理脚本时用torch.hub.load(ultralytics/yolov5, custom, pathbest.pt)或 ONNX Runtime 加载都可以。我通常建议做演示视频时用 ONNX Runtime部署到 Jetson 时才用 TensorRT因为后者构建 engine 的时间可能比你训练还久。4.3 毕业设计 ZIP 包怎么交付文件结构、权重与大文件处理标题里带.zip我特意说下交付规范。你最终交给老师或上传的压缩包如果解压失败前功尽弃。GitHub 上直接点 Download ZIP 下载项目没问题但注意 YOLOv5 依赖的权重文件比较大yolov5s.pt约 14 MByolov5l.pt约 90 MB打包前确认它们是完好的。如果解压时提示error read zip archive排查顺序是先看压缩包下载是否完整用右键属性对比文件大小和页面标注是否一致再用校验和工具计算 SHA256最后考虑是压缩软件版本问题装最新版 7-Zip 重新解压。不要用系统自带右键“全部解压缩”去解包含中文文件名或特殊字符的包经常出现路径异常。表格是压缩包内最小可用结构尽量按这个组织老师看代码按图索骥文件/目录作用是否必须main.py入口脚本支持图片/视频/摄像头三种输入必须utils/pose_estimator.py姿态提取与归一化逻辑必须models/lstm_classifier.py时序行为分类模型定义必须runs/train/falling_v1/weights/best.pt检测模型权重必须weights/pose/hrnet_w32.pt姿态模型权重建议configs/dataset.yaml数据集配置与类别说明必须README.md环境说明、运行步骤必须requirements.txt依赖列表必须data/sample_videos/演示视频和标注好的行为片段建议4.3.1 模型权重太大放不进压缩包的替代方案超过 200 MB 的权重传到学校 FTP 或微信传输经常损坏常见做法是把权重文件单独提取出来在 README 里说明下载地址和校验值压缩包里只放一个weights/download_weights.sh脚本。这样最终交付的 ZIP 体积控制在 50 MB 以内也不容易出现上次提到的 zip 报错问题。脚本里用wget或 Pythonrequests下载均可下载后自动校验大小不匹配就重下。5. 上线前必查的三个技巧耗时拆解、摔倒阈值调整和异常行为定义5.1 用 perf_counter 拆掉每一帧的时间开销毕设答辩最容易回答不上来的问题就是“整个系统延迟多少”。别把time.sleep(0.03)当成帧率管理器直接用高精度计时器拆管线import time import cv2 cap cv2.VideoCapture(0) while cap.isOpened(): t0 time.perf_counter() ret, frame cap.read() t1 time.perf_counter() dets model(frame) t2 time.perf_counter() kps pose_est(frame, dets) t3 time.perf_counter() abnormal lstm(kps) t4 time.perf_counter() cv2.imshow(monitor, render(frame, dets)) print(fDET {t2-t1:.3f}s | POSE {t3-t2:.3f}s | LSTM {t4-t3:.3f}s | FPS {1/(t4-t0):.1f}) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()如果DET和POSE两项加起来超过 200 毫秒先考虑把姿态模型的输入分辨率降为YOLOv5框的 1.5 倍而不是全图缩放很多实现把全图缩小后关键点反而更容易飘。LSTM单次推理在 CPU 上用不了 10 毫秒很少成为瓶颈。5.2 摔倒检测里最容易误报的是“蹲下捡东西”摔倒和蹲下在关键点序列上很像髋关节高度都会下降。区分的关键在于下降速度、躯干横转角度、以及在低位停留的时间。三者的参考判据可以这样取髋关节中心高度在连续 5 帧内下降超过身高比例的 60%躯干与水平面的夹角小于 30 度且持续超过 0.5 秒同时关键点置信度大面积低于 0.3。蹲下捡东西一般是降低到固定高度持续几秒并且躯干不会完全水平。你可以在标注时单独把“蹲下”列为一类放到验证集里反复看时序模型的概率输出分布调 LSTM 输出的阈值而不是简单地默认 0.5。5.3 快速定义一种新异常行为的步骤清单毕设做完摔倒检测老师大概率会问“能不能加一个攀爬检测”。完整重训 YOLOv5 没必要姿态序列的本质特征不变你要走的是这条流程录一段 5 分钟包含目标行为的新视频用已有的 YOLOv5 权重抽出所有人体关键点序列保存成 NumPy 文件。用现有滑窗把序列切成 32 帧片段人工筛掉动作只有一半的边界片段。跑一次旧模型看正常数据上的召回率如果 95% 以上正常行为都被正确分类说明旧模型可能把新行为也判成正常这时只需要新类别的样本微调 LSTM 的最后两层。如果新增类别样本太少先用时间扭曲和关节丢弃增强到 500 段以上再把 LSTM 输出从二分类改成三分类类别权重给新类别设 2.0。最后用一段没见过的真实监控录像做盲测逐帧输出异常概率曲线看报警时间点和人工标注是不是对得上。这套流程的核心思路是YOLOv5 和姿态模型不重训窄化到“时序分类器可变”来完成新行为扩展毕设演示时现场加入新演示视频也不至于翻车。本文还有配套的精品资源点击获取
返回列表