资讯动态

基于卷积神经网络的人体动作跟踪项目实战与踩坑复盘

发布时间:2026/10/20 3:24:30 来源:尧图企业网站定制
动作跟踪这个方向这几年在监控安防、智能健身、人机交互圈子里被反复讨论但只要动手做过的人都知道真正落地比想象中难不少。我近期刚完成一个基于卷积神经网络的人体动作跟踪研究项目前后踩了不少坑也把几个关键环节的细节都捋顺了。这篇博文就把整个经过完整复盘一遍从任务定义、数据准备、模型结构、训练配置到推理阶段的跟踪策略和排障经验都会覆盖到。如果你正准备自己做类似的项目或者刚接手一个动作识别相关的课题这篇文章应该能让你少走不少弯路。先说清楚这个项目是什么。我们要做的不是单纯的分帧分类而是把一段连续视频里的人体定位出来、判断动作类别、并在整个时间段里稳定保持同一个人物的身份最终输出“谁在哪段时间做了什么动作”的可视化结果。这种能力直接对应安防异常检测、健身动作计数、康复训练评估、老人跌倒监测这类实际场景。适合来读这篇博文的人包括刚入门想找完整实践路线的同学也包括已经有分类模型经验但没碰过完整跟踪链路、想知道数据标注和后处理怎么做的人。1. 项目核心设计思路为什么把宝押在卷积神经网络上1.1 先把“动作跟踪”这个词拆清楚很多刚上手的人会把动作跟踪和姿态估计、行为识别混在一起结果方案做着做着就跑偏了。项目立项之前我们课题组内部反复讨论过一个最基本的问题这个“跟踪”到底要解决什么最终定下来的目标很明确给定一段视频先定位画面里的人体再判断人体在做哪个动作并且在帧与帧之间保持同一个人的身份一致。拆开看就是三件事“空间定位”“动作分类”“跨帧关联”缺了任何一环都不叫完整的人体动作跟踪。只做分类没有定位是动作识别只做定位没有跨帧身份是目标检测只有把三件事全部闭环才能算真正的跟踪。为什么要死死咬住“跨帧一致性”这个条件因为实际业务输入是连续的视频流不是一张张孤立的图片。举个例子在监控场景里如果每一帧都重新检测一遍人动作标签会发生明显的抖动一段“跑步”在某几帧被识别成“快走”下几帧又变回“跑步”。没有跨帧平滑的ID和类别标签下游的告警逻辑、统计逻辑全部没法用。所以我们内部从一开始就强调这个项目最终交付的不只是一个模型而是一套能把动作标签稳定贴到个体身上的系统。1.2 CNN方案选型对比一圈之后的真实结论任务想清楚之后马上要解决的就是算法选型。我们当时对比了好几类主流路线也实际跑了简单实验最后才锁定卷积神经网络这个方向。当时对照的结果大致是这样。技术路线核心思路主要优势主要劣势传统手工特征提取HOG、光流、轨迹描述子计算开销小可解释性强泛化能力差动作复杂就失效2D CNN对单帧图像抽取空间特征成熟稳定预训练权重大把缺少时间维度建模能力3D CNN在时空维度同时做卷积时空信息同时建模计算量大显存压力突出双流CNNRGB分支加光流分支时间建模清晰直观精度高光流计算有额外开销CNN加RNNCNN抽特征RNN编序列序列记忆能力强训练慢长序列梯度容易出问题Transformer自注意力机制建模全局关系长程依赖处理效果好对数据量和计算资源要求高对课题组当时的条件来说最好上手且容易出效果的是双流CNN。它把空间信息和时间信息分开处理空间分支管“这个人是什么外观、在什么环境里”时间分支管“这一小段时间内的运动状态”两个分支在融合层汇合最后由分类头输出动作类别。这个设计非常贴合人对动作的直观理解你判断一个人在跑既依赖他出现在跑道上的画面又依赖他身体轮廓在时间轴上的位移速度。我们最终选的主干是残差网络结构原因很朴素预训练权重好找、结构成熟、网上可查的调参经验多。相比直接用3D卷积从原始视频里硬学时空特征双流CNN的中间结果更容易可视化比如能直接看到光流分支对哪些运动区域反应强烈。如果你也是第一次拿CNN做动作跟踪项目硬件资源又有限我建议直接选双流CNN起步别一上来就堆3D卷积或者大型Transformer否则大概率会被训练周期和显存问题拖垮。1.3 软硬件环境与项目边界的界定选型的同时我们也把项目边界和实验环境定了下来。硬件上只用了一张消费级GPU显存不算富裕所以后面很多配置都是围绕这个约束条件设计的。软件层面用深度学习框架训练模型推理阶段自己写后处理逻辑不依赖某一家大厂的整套闭源方案保证中间每个环节出了问题都能打开来看。项目边界也做了限制。我们不做全身关键点级的人体姿态估计因为那是姿态估计方向的独立问题。我们只需要提供人体级别的框和动作标签框可以来自现成的目标检测器重点自研的部分是动作分类与跨帧关联。这个边界划清楚之后团队内部分工很明确有人负责检测器接入有人负责双流CNN训练有人负责关联后处理互不干扰。2. 数据准备最容易被低估的地基工程2.1 数据从哪来格局怎样定数据是整个项目的地基这一步如果偷懒后面所有环节都会反噬到你头上。我们最初也想直接拿公开视频分类基准集来灌模型但实际动手发现了一个问题普通的视频分类数据集只给一整段视频打一个动作类别标签可动作跟踪项目需要知道“第几秒到第几秒、画面里哪个区域在做哪个动作”。没有时空粒度的标注训练根本没法端到端进行。实际做法是分两条线走。第一条线用公开的视频动作基准集做模型预训练。这类数据规模大、场景覆盖广非常适合让网络先学会通用的视频特征后面在特定场景下微调时收敛会快很多。第二条线针对我们真正想落地的几个动作类别包括走路、跑步、抬手、蹲下、跌倒用自采集的室内监控视角视频做微调。我们找了一间会议室固定机位拍摄大约积累了120分钟的多场景录像。后期把录像切成视频片段逐帧标注人体框和动作类别。标注阶段有个强烈的建议统一标注格式。所有框都按目标检测领域通用的框格式保存也就是左上角的X坐标、Y坐标、宽度、高度所有样本保持完全一致。我们项目里这里就踩过坑一开始两位参与标注的同学用了不一样的格式一个用左上角加右下角两个点一个用中心点加宽高。数据读取之后边界框整体偏移检测头长期不收敛最后花了好几天做数据清洗和格式统一才纠正过来。从第一天就统一格式能省下的工作量非常可观。2.2 预处理管线抽帧、光流、增强一步步怎么搭训练数据要经过怎样一条预处理管线才能进网络这里面的细节直接决定模型稳不稳。首先是抽帧。自采视频按25帧每秒全部抽出再按固定时间窗口切成若干视频片段。每个片段默认取8帧在窗口内均匀抽取。时间窗口的长度很讲究太长会引入大量冗余帧训练效率低太短又感知不到动作变化跑步和快走很容易混淆。其次是统一尺寸和归一化。所有帧缩放到224乘224像素像素值归一化到0到1之间。不同分辨率、不同压缩率的视频如果不做统一处理卷积神经网络会在前面几层积累大量分布偏移训练过程会显得非常“暴躁”loss曲线反复震荡。然后是计算光流。双流CNN的时间分支离不开光流图我们按片段内部相邻帧对计算横向、纵向两个光流分量最后把光流分量和原始RGB帧一起灌进网络。光流计算本身是有开销的这点后续还会提到。数据增强方面具体做了以下几组随机水平翻转。注意如果视频画面里出现文字、非对称标志要先评估翻转会不会带来语义错乱。随机裁剪和缩放模拟目标在画面中距离远近不同、位置不同的情况。时序增强把片段内部帧顺序在小范围内随机抖动模拟真实场景中帧率不稳定或运动节奏变化。亮度、对比度扰动让模型不要过度依赖固定光照条件。有一段时间我们把裁剪幅度调得特别夸张结果很多正常站立的人被切掉了上半身模型把“站”和“蹲”都学歪了精度反而下降。增强的目的是提升多样性不是人为制造大量极端负样本。把握这个度需要多观察增强后图像的实际效果而不是只看配置数字。2.3 数据平衡问题动作类别数量不均怎么办自采数据里动作类别天然不平衡。走路、跑步这类常规动作采集了大量样本跌倒、蹲下这种出现频率低的动作样本数量明显不足。如果不处理模型会严重偏向多数类跌倒样本几乎识别不出来而这恰恰又是监控场景最关心的异常动作。我们在数据层面做了两个操作。一是对少数类样本做过采样复制并加以扰动让网络在训练时能见到更多跌倒、蹲下的样本。二是调整损失函数的类别权重给少数类更高的权重值让模型对少数类的误判产生更大的惩罚。实际操作中发现类别权重不能拍脑袋设需要根据验证集表现反复调整权重过大反而会让模型过度敏感把正常的弯腰都判成跌倒。如果时间允许更推荐的做法是补充采集少数类数据哪怕换个机位再拍几次都行数据本身的质量和多样性永远比权重调整更可靠。权重调整只是补救手段不要当成主要方案。3. 模型搭建与训练细节从网络结构到参数配置3.1 双流网络结构配置说明模型结构这块直接给出我们最终实施时的配置照着搭可以跑通一个可工作的版本。空间分支输入的是RGB帧尺寸224乘224乘3骨干网络用残差网络结构取到最后一个卷积阶段的特征做全局平均池化后降维到固定长度的特征向量。时间分支输入的是光流图尺寸224乘224乘2因为光流只有横向和纵向两个通道采样的时候要把输入通道数改掉骨干网络同样用残差网络结构。两个分支分别产生特征向量后在融合层拼接再过两层全连接层中间穿插Dropout最后一层输出动作类别数。最初也试过把两个分支的特征直接相加效果没有拼接好。拼接保留了“当前动作是偏静态姿态还是偏明显位移”的完整信息分类器可以自己判断空间特征和时间特征各该信多少。直接相加等于提前强行做了一个线性融合信息损失比较明显。Dropout的位置和大小也很关键。双流网络特征拼接后维度不低非常容易对训练集死记。我在融合层后面用的是0.5的Dropout验证集表现比不加的时候稳得多。如果你也遇到训练精度上得去、验证精度差距大第一件该做的事就是检查Dropout和增强强度。训练时两个分支的骨干网络都加载图像分类任务的预训练权重这一点对收敛速度影响很大。刚开始做实验的时候时间分支因为光流输入和自然图像分布差异大团队里有同学主张从零初始化结果时间分支的loss收敛极慢后面还是统一改成预训练初始化效果才正常。预训练权重提供的底层边缘、纹理、运动感知基础比从零开始学省力太多。3.2 训练超参数与损失函数选择下面这些超参数是我们多次跑出来的经验值具体场景要根据硬件和数据量调整。输入片段长度8帧批大小16优化器带动量的SGD动量取0.9初始学习率0.001配合余弦退火调度训练轮数30轮损失函数交叉熵损失这里说一个经验判断。图像分类任务里SGD虽然要手动调节参数但配合余弦退火后泛化效果通常比Adam更稳。Adam省心收敛前期快但训练后期容易在小范围内反复震荡。我们这种标准的动作分类任务用SGD更可控跑完一个完整训练周期基本能看出来模型朝哪个方向走。学习率这一关要格外小心。用预训练权重初始化后初始学习率0.001已经算是偏高如果改成0.01训练前几轮loss就会直接发散。别急着把学习率调大可以先跑5个epoch的小规模试探观察loss曲线的趋势再决定量级。跑一趟小实验的时间成本和完整训练完全不在一个数量级这笔账要算清楚。训练主流程的骨架大致是这样。# 模型训练主流程伪代码非完整可运行脚本 for epoch in range(epochs): model.train() running_loss 0.0 for rgb_batch, flow_batch, label_batch in train_loader: rgb_batch rgb_batch.to(device) flow_batch flow_batch.to(device) label_batch label_batch.to(device) optimizer.zero_grad() logits model(rgb_batch, flow_batch) loss criterion(logits, label_batch) loss.backward() optimizer.step() running_loss loss.item() scheduler.step()实际项目里还会加上混合精度训练、梯度裁剪、EMA指数移动平均这些手段但核心骨架就是这个。混合精度主要解决显存不够的问题也能让训练速度小幅提升。梯度裁剪针对的是训练早期loss突然飙升的情况一般设一个1.0左右的阈值就够了。3.3 特征可视化与中间层检查训练过程中除了看loss和精度还要学会看中间层特征。这个步骤很多人会跳过但它往往能在训练失败之前就给出预警。我们最常用的可视化手段是激活图分析。把某一段输入交给训练中的网络提取最后一个卷积层的特征图叠加回原始帧上观察模型到底是靠哪些区域做判断。比如处理“跌倒”动作时如果激活区域集中在人体和地面的交界处说明模型学到了可利用的空间线索如果激活区域散落在背景墙上那基本是模型找到了数据里的巧合模式后期很可能会过拟合。另一个检查手段是直接查看光流分支的特征均值。把时间分支输出做统计对比不同动作类别之间是否有明显差异。如果跑步、走路两类动作在时间分支特征上几乎重合那就说明光流提取或时间窗口长度有问题需要回头调整预处理而不是盲目加大模型复杂度。特征可视化不需要特别复杂的工具打印特征图再叠加图像就能实现。关键是形成习惯每次调参之后都看一看。我们有过一次很典型的情况训练精度在涨但验证精度不动查了激活图才发现模型在学习背景纹理而不是人体动作根源是数据划分泄露。这种问题只看数值曲线永远找不到原因。4. 推理阶段把单帧预测变成连续跟踪4.1 检测、关联、预测三步走训练好的CNN模型只能对单个片段输出动作概率离“连续视频里的人体动作跟踪”还有一大截。我们把推理逻辑拆成三步。第一步是目标检测与ID关联。每一帧先用目标检测器把人框出来然后按检测框之间的交并比做目标匹配。上一帧某个ID和当前帧某个框的重叠比例超过0.5就认为仍然是同一个人。如果多目标之间出现遮挡、重叠简单贪心匹配容易出错就要交给匈牙利算法做全局最优匹配避免ID被频繁抢夺。第二步是片段级动作预测。以当前目标为中心取一个时间窗口内的若干帧组装成CNN输入得到该目标的动作概率分布。窗口长度对效果影响很大窗口短则反应快但抖动多窗口长则标签稳定但有延迟。我们最终用8帧窗口加1帧预测步长在实验中既不会让人觉得动作标签反应迟钝也不会在跌倒这种瞬时动作上错过关键报警时机。第三步是类别平滑。把CNN输出的概率序列做指数移动平均每次更新时用历史概率的0.7作为权重与当前概率融合。这个方法非常简单但非常实用。同一个人偶尔被误判成相近动作时平滑可以避免标签在相邻帧之间来回横跳。有一个容易被忽略的顺序问题ID和目标框关联必须在动作类别预测之前完成。如果先预测动作再根据动作去关联目标同一个人的动作历史会被框的抖动割裂成两段后续平滑的权重计算就乱了套。4.2 平滑策略与置信度处理后处理阶段还涉及置信度阈值的设计。不要把阈值设得太高否则很多正常动作会被归到“未知”类。我们实测后把置信度阈值定在0.6低于阈值时直接沿用上一帧的类别标签而不是清零。这个策略在快速转身、短暂遮挡这类场景里非常关键保证动作标签不会闪断。平滑参数也要跟着场景调。如果项目更看重及时告警比如跌倒检测那么历史权重应适当降低让模型对最近的输入更敏感如果项目更看重统计报表比如运动时长统计那么历史权重可以调高让标签更稳定。没有一套参数通吃所有场景要养成每次换场景就重新验证平滑参数的习惯。如果后续想升级可以在时间分支后面再接一个序列建模层把连续若干片段的预测结果综合后再输出最终分类效果会优于简单加权平均计算量增加也不多。我们当时因为工期没有做但这是一个性价比很高的扩展方向。4.3 边缘部署与性能优化思路我们项目跑通之后做过一次部署性能摸底。端到端FPS包含检测、双流推理、关联后处理三个环节在单张消费级GPU上约18帧每秒做离线批量处理绰绰有余做实时演示勉强可用。优化性能最容易想到的是缩小输入尺寸。把224乘224降到160乘160FPS明显提升准确率大约降两个百分点。如果场景里目标普遍偏大、没有太多小目标这个方案是划算的。另一个手段是减少时间分支的计算频率光流不是逐帧都要重算可以每两帧算一次中间帧沿用最近的光流结果时间分支的推理压力能降下来很多。如果部署环境连GPU都没有只能跑CPU建议把时间分支砍掉只用空间分支加检测器动作类别精度虽然会下降但至少能在15帧左右的低分辨率输入下跑起来。这种取舍在工业项目里很常见先保可用再谈精度。5. 实验结果与调参记录不只看准确率5.1 评估指标怎么定才算完整没有科学的指标项目做得再热闹也拿不出手。我们一共盯了三组指标动作分类的准确率与混淆情况、目标跟踪的ID稳定性与轨迹连续性、端到端推理速度。动作分类先看整体准确率但整体准确率会掩盖很多问题必须配合混淆矩阵看。我们实验中发现最容易混的三对动作是走路和跑步、蹲下和跌倒、抬手和挥手。原因很直观低帧率下走路和跑步的视觉差异本来就小蹲下和跌倒的开端都是重心快速下移抬手和挥手在单帧画面里几乎完全一样。这个结论告诉我们空间分支不管怎么做都有上限必须靠时间信息把这几对区分开。目标跟踪侧看两个数ID切换次数和跟踪片段完整率。ID切换次数越少越好跟踪片段完整率指的是目标从出现到消失全程没有发生身份断片的片段占比。这两个指标比单纯的目标检测mAP更能反映项目实际效果。推理速度则用端到端FPS衡量从读视频帧开始计时到输出动作标签结束。这个数字直接决定这套系统能不能上实时应用。5.2 一次过拟合问题的完整复盘调参过程中最让我印象深刻的是一次顽固的过拟合问题。训练开始没多久模型在训练集上的准确率已经冲到99%验证集却一直卡在63%差距非常刺眼。按常规思路先加Dropout再加数据增强效果有一点改善但没有本质变化。后来我们往数据划分方向查终于找到根因自采集视频是按一个固定机位连续拍的当训练集和验证集随机按帧划分时同一段录像里相邻的帧一部分进了训练集、一部分进了验证集模型实际上“背”住了这条视频的时序特征根本不是在做通用动作识别。解决问题的方法很粗暴但有效按录像片段划分数据而不是按帧划分。同一段连续动作的所有帧全部放进同一个集合训练集和验证集完全隔离开。改动之后的验证集准确率直接跳到81%。这个经验极其重要做视频相关的深度学习项目时数据连续性极强随机按帧划分几乎是自杀式做法。5.3 最终效果的量化结果经过两轮完整调优我们最终版本的实验结果大致如下。指标基线版本最终版本说明验证集整体准确率63%81%主要受益于数据划分修正与增强跌倒动作召回率51%74%受益于类别权重调整ID切换次数每千帧28次每千帧19次受益于运动外推匹配端到端FPS2218最终版本增加了后处理平滑这张表能看出一个明显规律精度的提升不完全来自模型结构更多来自数据治理和后处理。模型结构当然重要但如果没有正确的数据划分、合理的类别权重、稳定的关联策略同等结构下的效果差异会非常大。还做了一组时间窗口长度实验。把8帧窗口改到16帧动作分类准确率提升大约两个百分点但端到端FPS掉得明显。这个取舍没有标准答案取决于项目要精度还是要实时。我最终保留了8帧配置因为在我们这个监控场景里连续刷新的动作标签比偶发的精度提升更有实用价值。6. 高频问题与排查技巧把这些坑一次性讲完6.1 问题速查表最后这部分是我最想分享的全是实操里反复遇到的坑整理成台账方便对照。现象可能原因排查与解决方案训练loss不下降学习率过大或过小输入未归一化先跑5轮小批量试验观察loss曲线检查数据归一化是否正确生效验证集过拟合明显数据划分不当Dropout不足按视频片段划分数据提高Dropout加强数据增强光流计算太慢逐帧光流没有做帧间隔优化换大步长光流或只对关键帧计算光流动作标签频繁抖动时间窗口太短缺少平滑加指数移动平均适当延长时间窗口同一目标ID经常跳变关联阶段只用了单帧IoU加运动外推预测框结合位置预测做匹配近距离双人互相遮挡检测框变形剧烈匹配失效用多目标跟踪匹配策略遮挡帧沿用历史框跑步和快走混淆时间分辨率不足检查帧率和窗口长度考虑加大光流分支的权重显存不足输入尺寸或批大小太大降低批大小启用梯度累计使用混合精度训练动作类别整体偏移标注格式不统一或标签错位打印输入张量与标签做人工核对还有一个非常反常识的坑早期我们把分类分支做得特别复杂加了很多全连接层结果精度没提升训练越来越慢。后来精简了分类头验证集不掉点推理速度还提升了。分类头只需要足够表达即可骨干网络才是提取特征的核心过度堆分类头只会带来过拟合和资源浪费。排查问题有一条原则永远先确认数据流再怀疑模型。我们有好几次训练结果异常最终定位到都是数据读取出问题比如光流通道顺序反了、某一类动作标签整体偏移。在代码里加一段打印把输入张量和标签张量拉出来人工核对成本极低但能省出大量反复训练的时间。6.2 一次完整的线上故障排查实例我们项目演示前一天模型突然出现大面积误报几乎每一段正常行走的视频都被标记成跌倒。现场有点紧张因为白天还跑得好好的。第一反应是查模型权重和训练配置结果都没问题。后来怀疑是测试视频的帧率发生了变化演示录像用手机拍摄是30帧每秒模型训练用的是25帧每秒。打开数据流打印一看抽帧模块按固定的时间间隔抽帧但不同帧率下同一个动作的实际时间跨度对不上原本轻快的走路在24帧里被压缩模型看到的就是一个节奏异常的快速动作。修复方案不复杂把推理端的抽帧逻辑改成按时间戳抽帧而不是按帧序号抽帧然后重新跑一遍测试视频误报消失。这次经历让我深刻意识到模型在训练数据分布内表现良好不等于在真实采样条件下也表现良好。推理端任何一点配置上的差异包括帧率、分辨率、编码格式都可能让模型效果崩掉。另一次项目交付时客户反馈某个通道的跟踪总是断线排查后发现是该通道视频存在大量画面冻结也就是视频流里出现连续重复帧。检测阶段还能稳定输出结果但光流计算在重复帧上得到零向量时间分支完全失效。解决方式是在预处理阶段检测连续重复帧跳过这些帧的光流计算或者直接对该时间段的动作标签沿用历史结果。结尾我最深刻的体会是动作跟踪真正的难点并不在单帧识别而在时间连续性和目标一致性。CNN模型解决的是“看到什么”跟踪系统解决的是“记住谁并一直跟着谁”两者缺一不可。很多人在模型结构上花大量精力最后却被数据划分、帧率适配、后处理平滑这类环节绊倒这不是偶然而是证明了工程化思维比追新模型更重要。如果你准备做类似的方向我给一个非常实际的建议先做一个小而全的闭环别一上来就追求大模型、大数据集。把“检测人、分类动作、关联ID”这条链路用最小配置跑通再逐步替换其中的模块。这样任何环节出问题你都能清楚知道源头在哪而不是陷入一比一复现别人整个系统的泥潭。数据统一格式、按片段划分、推理端帧率对齐这三件事在项目开始前就做对后面能省下至少一半的返工时间。

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

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

免费获取报价 →
↑