简介:这份资源围绕YOLOv5在行为识别中的应用展开,面向具备一定深度学习基础、希望将目标检测与行为分析结合落地的开发者与学习者。包内共6个文件,以2个Python脚本(detect.py、train.py)为核心,配合data.yaml配置、doc说明文档、README.md与LICENSE,压缩包约1.6MB,结构精简,便于快速理解项目组织方式。内容涉及YOLOv5的网络结构、Anchor机制、数据增强与Mosaic预处理,并延伸到特征提取、行为建模及LSTM、GRU等时序分类思路,同时提及UCF-101、HMDB-51等数据集的使用场景。已有572人学习,适合作为智能监控、安全预警等方向的入门参考,帮助读者梳理从目标检测到行为识别的完整流程与实现路径。
1. 从检测框到动作标签:YOLOv5 做行为识别到底在做什么
很多人第一次听到「基于 YOLOv5 的行为识别」,脑子里浮现的是模型直接输出「跌倒」「抽烟」「打架」这类动作标签。真上手才发现,YOLOv5 本身是个目标检测器,它只会给你一堆带类别的框,不会告诉你框里的人正在干什么。所以这个标题真正要解决的问题是:怎么把 YOLOv5 的检测能力,接到行为识别这条链路上,让它从「找到人」变成「看懂人在做什么」。
我一般把这件事拆成两条路线。第一条是「检测即识别」:把行为类别直接当成检测类别训练,比如把smoke、fall、fight当成和person并列的类,YOLOv5 一次前向就出结果。第二条是「检测加时序」:YOLOv5 只负责人体框,后面接跟踪或姿态估计,再用时序模型判断动作。前者落地快、延迟低,适合固定机位、动作差异大的场景;后者精度上限高,但要处理 ID 跳变和时序窗口,工程复杂度翻倍。
这篇文章面向的是想在自己数据集上跑通行为识别、并且准备往边缘设备部署的从业者。新手能照着把数据、训练、推理这条线走通,熟手能看到类别设计、后处理和部署上的边界。下面按「先立住原理和选型,再动手复现,最后讲坑」的顺序推。
2. 行为类别怎么定:把动作拆成 YOLOv5 能学的框
2.1 检测式行为识别的成立条件
YOLOv5 是单阶段检测器,输出的是(x, y, w, h, obj, cls)这样一组张量,经过后处理变成框。它没有时间维度,所以能学的行为必须满足一个前提:单帧画面里的空间特征足以区分这个动作。抽烟、打电话、跌倒、举手、戴安全帽,这些在单帧里姿态和物体共存,检测式方案能work。但「走路」和「跑步」单帧几乎一样,「挥手」和「招手」也难分,这类必须引入时序。
我判断一个行为能不能用检测式做,会问三个问题:这个动作在某一帧里有没有稳定的视觉锚点(比如烟、手机、倒地的人体长宽比)?不同行为的锚点会不会在同一帧里混淆?标注时标注员能不能只看一帧就给出确定标签?三个都过,才走检测式。否则老老实实上跟踪加时序,别硬塞给 YOLOv5。
类别设计上有个血泪经验:不要把person和行为类混在一套标签里还指望模型分得清。person是主体,smoke是行为,两者语义层级不同。常见做法是保留person类,行为类单独一套,推理时用人体框和行为框做 IoU 关联,或者干脆只标行为框、不标 person。我一般倾向后者,减少类别竞争,mAP 更稳。
2.2 数据标注格式与目录组织
YOLOv5 要的是 YOLO 格式:每张图一个同名.txt,每行class_id cx cy w h,坐标全部归一化到 0~1。行为识别数据集常见来源是监控视频抽帧,抽帧后要人工筛掉模糊和重复帧。目录按下面组织:
datasets/behavior/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── behavior.yamlbehavior.yaml是数据集描述文件,内容如下:
# 行为识别数据集配置 path: ../datasets/behavior # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 4 # 类别数,必须和 names 长度一致 names: # 类别名,顺序即 class_id 0: person 1: smoke 2: fall 3: phone这里nc和names的对应关系是最容易翻车的地方。names写成字典时键就是 class_id,写成列表时索引就是 class_id。标注文件里的第一个数字必须和这里对上,错一位整个训练就废了。我习惯标注完先跑一个校验脚本,统计每个 class_id 的框数量,数量为 0 的类直接删掉,否则训练时这类永远学不出来还拉低整体指标。
抽帧频率也要注意。行为动作持续时间不同,抽太密相邻帧几乎一样,模型过拟合;抽太疏动作中间态丢失。跌倒这类快动作,我一般按 5~8 fps 抽;抽烟这种慢动作,2 fps 就够。验证集要按视频源划分,不能按帧随机分,否则同一段视频的相邻帧同时进训练和验证,指标虚高,上线就露馅。
3. 环境配置与训练:conda 隔离 + 超参数怎么调
3.1 conda 环境与依赖安装
YOLOv5 对环境版本敏感,尤其是 PyTorch 和 CUDA 的匹配。我一般用 conda 建独立环境,避免和系统里的其他项目打架。下面这套流程在 Linux 和 Windows 上都跑得通:
# 创建 Python 3.9 环境,YOLOv5 对 3.8~3.10 兼容最好 conda create -n yolo5 python=3.9 -y conda activate yolo5 # 安装 PyTorch,CUDA 11.8 版本,按自己显卡驱动选 pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 克隆源码并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt装完先验证 GPU 是否可用:
import torch print(torch.__version__) # 期望 2.0.1+cu118 print(torch.cuda.is_available()) # 期望 True print(torch.cuda.get_device_name(0))如果cuda.is_available()返回 False,九成是 PyTorch 版本和驱动不匹配,别急着改代码,先换 PyTorch 的 CUDA 版本重装。这一步是黑匣子最多的地方,装错了后面训练报的错全是玄学。
3.2 训练命令与关键超参数
YOLOv5 的训练入口是train.py,行为识别数据集小、类别少,我一般从预训练权重起步:
python train.py \ --data datasets/behavior/behavior.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml \ --name behavior_s \ --cache参数逐个说清楚。--weights yolov5s.pt用 COCO 预训练权重,小数据集上比从头训收敛快得多;--img 640是输入分辨率,行为识别里小目标(烟、手机)多的话可以提到 960,但显存和速度要权衡;--batch 16按显存调,显存不够就降 batch 同时按比例降学习率;--hyp选超参文件,小数据集用hyp.scratch-low.yaml这种低增强配置,避免过拟合;--cache把图片缓存到内存,数据集不大时能明显加速。
超参数里最值得动的是这几个,在hyp文件里改:
| 参数 | 默认值 | 行为识别建议 | 作用 |
|---|---|---|---|
| lr0 | 0.01 | 0.005~0.01 | 初始学习率,小数据集调小 |
| lrf | 0.01 | 0.01 | 最终学习率系数 |
| mosaic | 1.0 | 0.5~1.0 | 四图拼接增强,行为场景可保留 |
| fl_gamma | 0.0 | 1.5 | focal loss,类别不平衡时开 |
| hsv_v | 0.4 | 0.3 | 亮度增强,监控场景别太大 |
行为识别数据集普遍类别不平衡,fall这种危险行为样本远少于person。这时候把fl_gamma开到 1.5 左右,让模型更关注难样本,比单纯复制样本效果好。但别开太大,2.0 以上训练容易不稳定。
3.3 训练过程看什么指标
训练日志里重点盯三个:box_loss、obj_loss、cls_loss。box_loss不降说明框回归有问题,检查标注框有没有越界或宽高为 0;cls_loss不降说明类别学不动,多半是类别不平衡或标签错位;obj_loss震荡说明正负样本分配不稳,可以调 anchor 或降学习率。
验证阶段看mAP@0.5和mAP@0.5:0.95。行为识别里mAP@0.5到 0.7 以上基本可用,但别只看这个数。我习惯把混淆矩阵导出来看,重点看行为类之间有没有互相误判。如果smoke大量被判成phone,说明两个类的视觉锚点太像,要么合并类,要么补更多区分性样本。
4. 推理与后处理:让检测框变成行为事件
4.1 推理脚本与结果解析
训练完拿best.pt做推理,最简方式是用detect.py:
python detect.py \ --weights runs/train/behavior_s/weights/best.pt \ --source test_video.mp4 \ --img 640 \ --conf-thres 0.4 \ --iou-thres 0.45 \ --save-txt \ --save-conf--conf-thres是置信度阈值,行为识别里我一般设 0.4,比通用检测的 0.25 高,因为行为误报的代价比漏报高;--iou-thres是 NMS 的 IoU 阈值,同一人身上多个行为框重叠时靠它去重;--save-txt把结果存成 YOLO 格式,方便后续做事件聚合。
如果要在代码里集成,直接用torch.hub加载:
import torch # 加载本地训练好的模型 model = torch.hub.load('./yolov5', 'custom', path='best.pt', source='local') model.conf = 0.4 # 置信度阈值 model.iou = 0.45 # NMS IoU 阈值 results = model('test.jpg') df = results.pandas().xyxy[0] # 转成 DataFrame,列含 xmin/ymin/xmax/ymax/confidence/class/name print(df[df['name'] == 'fall']) # 只看跌倒框results.pandas()这个接口很好用,把检测结果直接转成表格,按name过滤就能拿到某类行为的框。注意model.conf和model.iou是实例属性,改一次对后续所有推理生效,别在循环里反复设。
4.2 从单帧框到行为事件的聚合逻辑
单帧检测出fall不代表真跌倒,可能是弯腰捡东西被误判。要变成可靠的行为事件,得做帧间聚合。常见做法是维护一个滑动窗口,统计最近 N 帧里某类行为出现的频率:
from collections import deque class BehaviorTracker: def __init__(self, window=15, threshold=0.6): self.window = window # 滑动窗口帧数 self.threshold = threshold # 触发比例阈值 self.history = deque(maxlen=window) def update(self, names): # names 是当前帧检测到的行为类名列表 self.history.append(set(names)) # 统计窗口内每类行为出现的帧占比 counts = {} for frame_names in self.history: for n in frame_names: counts[n] = counts.get(n, 0) + 1 triggered = [n for n, c in counts.items() if c / len(self.history) >= self.threshold] return triggeredwindow和threshold是核心参数。跌倒这种瞬时动作,window设 10~15 帧、threshold设 0.5 左右,既能抓住又不会因为一两帧误检就报警。抽烟这种持续动作,window可以拉长到 30 帧、threshold提到 0.7,减少偶发误报。这套逻辑不依赖跟踪,实现简单,但同一画面多人时会把不同人的行为混在一起,需要配合人体框做空间关联才能分人。
4.3 边缘部署时的模型导出
要在树莓派 5 这类设备上跑,直接上 PyTorch 权重太慢,得导出成 ONNX 或 NCNN。YOLOv5 自带导出脚本:
# 导出 ONNX,opset 12 兼容性好 python export.py --weights best.pt --include onnx --opset 12 --img 640 # 导出 NCNN,适合 ARM 设备 python export.py --weights best.pt --include ncnn --img 640导出后务必用同一张图对比 PyTorch 和导出模型的输出,确认框的坐标和置信度差异在可接受范围。我遇到过导出 ONNX 后 NMS 结果对不上的情况,最后发现是导出时--img和推理时输入尺寸不一致,这种坑不对比根本发现不了。树莓派 5 上跑 NCNN 版 YOLOv5s,640 输入大概能到几帧到十几帧,具体看 CPU 占用和是否开多线程,行为识别如果不需要实时,抽帧处理完全够用。
5. 避坑与排查:行为识别落地时最容易翻车的五件事
5.1 指标很高但上线全是误报
现象:验证集mAP@0.50.85,部署到现场后误报不断,尤其是fall类。原因:验证集按帧随机划分,同一段视频的相邻帧同时进了训练和验证,模型记住了背景而不是行为。解决:按视频源划分数据集,训练集和验证集的视频不能有重叠;同时补一批现场负样本,尤其是容易误判的弯腰、蹲下、快速移动。
5.2 小目标行为类召回率极低
现象:smoke、phone这类依赖小物体的行为,recall长期低于 0.3。原因:输入分辨率 640 下,烟和手机可能只有十几个像素,特征在 backbone 下采样后基本丢失。解决:把--img提到 960 或 1280,同时在数据增强里关掉mosaic或降低概率,避免小目标被拼接到边缘裁掉;标注时确保小目标框贴合,别把背景框进去。
5.3 类别不平衡导致模型只学多数类
现象:person类指标很好,fall类几乎学不出来。原因:危险行为样本天然稀少,损失被多数类主导。解决:开 focal loss(fl_gamma设 1.5),对少数类样本做过采样,或者用copy-paste增强把少数类实例贴到不同背景上。注意过采样别太狠,否则少数类过拟合,验证集看着好现场还是不行。
5.4 导出模型后推理结果和训练时不一致
现象:PyTorch 推理正常,导出 ONNX 或 NCNN 后框位置偏移、置信度整体偏低。原因:预处理不一致,比如归一化方式、letterbox 填充的灰度值、通道顺序(RGB/BGR)对不上。解决:导出前后用同一张图逐层对比,重点检查预处理;NCNN 部署时确认输入是 RGB 还是 BGR,YOLOv5 训练用的是 RGB,很多部署框架默认 BGR,不改就全错。
5.5 多线程推理时结果错乱
现象:单张推理正常,多路视频并发时框和画面对不上。原因:YOLOv5 模型实例不是线程安全的,多个线程共用一个model对象会互相覆盖内部状态。解决:每个线程独立加载一个模型实例,或者用队列串行推理;显存紧张时用批处理代替多线程,把多路画面拼成一个 batch 送进去。
6. 把行为识别做稳的一个技巧:用姿态做二次校验
检测式行为识别最大的软肋是「形似神不似」——弯腰和跌倒、举手和挥手,单看框和物体很难分。我后来稳定下来的做法是:YOLOv5 出行为候选框后,对person框跑一个轻量姿态估计(比如 YOLOv5-pose 或 MoveNet),用关键点角度做二次校验。跌倒的判定不看框,看人体中轴线和地面的夹角,以及头部相对髋部的高度;弯腰时夹角小但头部没低于髋部,两者就分开了。
具体做法是给每个行为类配一组关键点规则,规则命中才最终输出事件。这套逻辑不复杂,但能把误报压下去一大截。参数上,姿态模型输入可以降到 256,和检测模型异步跑,检测每帧跑、姿态隔帧跑,整体延迟增加有限。我踩过的坑是姿态关键点抖动导致角度阈值频繁穿越,后来加了关键点置信度过滤和角度滑动平均才稳。
这套方案值不值得做,取决于你的误报代价。如果只是统计人流里的行为分布,检测式加滑动窗口就够了;如果是跌倒报警、危险行为告警这种误报会消耗信任的场景,姿态二次校验这一步省不得。我自己是从「先跑通检测式,再按误报情况逐步加校验」这个节奏过来的,一上来就堆时序模型,往往卡在数据和调参上出不来。希望帮到你。
本文还有配套的精品资源,点击获取