资讯动态

YOLO11-C3k2-FMB改进:深蹲动作识别与分类实战

发布时间:2026/10/19 12:49:25 来源:尧图企业网站定制
把YOLO11的C3k2模块改造成C3k2-FMB结构用来做深蹲动作的识别与分类这个项目我从数据采集一路做到模型部署前后折腾了将近两个月。先放结论在自建的深蹲动作数据集上改进后的模型相比原始YOLO11mAP50从0.814提升到了0.869mAP50-95从0.527提升到了0.596单帧推理时间基本持平这个精度提升在动作质量评估这种细粒度分类任务里已经相当可观。之所以把标题定为“YOLO11-C3k2-FMB改进_深蹲动作识别与分类_1”是因为整个工作就围绕两条线索展开一条是网络结构层面针对YOLO11的骨干模块C3k2做轻量化注意力融合改进引入FMB结构另一条是应用层面把改进后的模型落在深蹲这个具体动作上识别蹲得标不标准、有没有膝盖内扣、蹲幅够不够深。这篇内容会把从选型、改结构、做数据到训练、排查问题的完整链路都过一遍适合正在做动作识别、姿态分析、或者想在YOLO系模型上做结构改进实验的开发者参考。1. 项目概述C3k2-FMB在深蹲场景里到底做什么1.1 深蹲质量评估的业务痛点很多人觉得深蹲识别不就是检测人、分类动作吗真上手才发现完全不是这么回事。健身房里教练能一眼看出学员膝盖内扣、身体前倾、蹲幅不足但让机器做到同样的事难点在于这些“错误”之间差异非常细微——膝盖内扣可能只有十几度的角度偏差蹲幅不足可能只是髋关节低于膝盖那个临界点的几厘米差距。这种细粒度差异对模型的特征提取能力要求极高。项目落地场景是家庭健身和线上私教这两种模式用户对着手机摄像头做深蹲系统实时给出动作质量反馈替代真人教练的一部分监督工作。这个场景里有两个硬约束一是必须跑在普通手机或者低端边缘设备上不能依赖服务器端的大模型二是延迟要低反馈如果晚了两秒钟用户动作早都做完了点评毫无意义。所以模型选型从一开始就锁定了单阶段检测器YOLO系列是当前效率和精度平衡最好的选择之一。1.2 为什么目标检测比姿态估计更适合这个任务这里有个很容易走弯路的点。做动作分析常规思路是先用姿态估计网络比如某个关键点检测模型把人体关节坐标提取出来再根据关节角度规则判断动作质量。这个方案我之前也试过但在实际场景里暴露出两个问题第一姿态估计模型的关键点精度在自遮挡和运动模糊情况下掉得很厉害膝盖、脚踝这些关键点一旦偏移几像素角度计算就彻底失真第二规则判断的阈值需要人工反复调不同体型的用户在同一规则下表现差异很大。把思路换成目标检测之后逻辑就变成直接让网络学习“标准深蹲”“膝盖内扣”“蹲幅不足”这些类别在图像上的整体视觉特征绕开了关键点提取规则判断的两段式误差累积。动作质量分类本质上是细粒度视觉识别问题检测网络在框出人体区域的同时输出动作类别标签一步到位延迟也更低。这也是项目标题里“识别与分类”并举的原因——识别是定位分类是判断合在一起就是检测头干的事。2. 基线模型拆解YOLO11与C3k2模块2.1 YOLO11相比前代的演进逻辑在动手改进之前得先把基线模型吃透。YOLO11是YOLO系列在2024年底推出的新一代版本比起前代YOLOv8最大的变化集中在骨干网络和颈部结构。骨干部分用C3k2模块替换了原来的C2f模块颈部用C2PSA模块增强了跨尺度特征交互能力整体在检测头里还引入了类似注意力机制的动态处理。官方提供了n、s、m、l、x一共五个不同规模的版本参数量从2.6M一路到57.5M。选型时我对比过v8n和v11n这两个最轻量的变体。在同样的深蹲数据集上v11n的mAP50比v8n高出大概2.3个百分点推理速度反而还快了一点点。这主要得益于C3k2模块更高效的特征复用方式。我最后选了v11s作为改进基线原因是n版本在细粒度分类上的特征表达能力稍弱而s版本在手机端仍然可以跑到30毫秒以内的推理延迟这个平衡点比较舒服。2.2 C3k2模块的原理分析C3k2这个模块继承自CSPNet的设计思想核心目的是把特征图分成两条路径处理一条路径走较深的瓶颈层另一条路径通过shortcut直接传递最后把两路特征拼接融合。这种“分叉-融合”的结构能有效缓解深层网络的梯度消失问题同时提升信息流通效率。C3k2跟之前的C2f最大的区别在瓶颈层的实现细节。C2f里的瓶颈层固定使用3x3卷积而C3k2引入了一个可调节的kernel size参数瓶颈部分可以选择3x3、5x5或者更大感受野的卷积组合在代码里体现为一个控制参数。对于深蹲识别这种需要同时关注全身宏观姿态和局部关节细节的任务单纯增加卷积核尺寸并不能解决问题——大卷积核带来更大感受野但也增加了参数量和计算量而且对局部细节的捕捉能力并不会线性提升。这正是我决定在C3k2里引入注意力机制的动因。2.3 基线模型在深蹲场景下暴露的三个瓶颈直接拿原始YOLO11跑深蹲数据问题很快浮现出来。第一类是同类内差异过大同样是“标准深蹲”不同体型的人框出来的特征差异比“标准深蹲”和“膝盖内扣”之间的差异还要大模型容易把注意力放在体型特征上而不是动作特征上。第二类是细微姿态变化捕捉不足膝盖内扣十几度在图像上可能只反映为膝盖区域几像素的位移标准卷积核很难捕捉这种程度的局部变化。第三类是遮挡和截断深蹲过程中腿部经常被手臂遮挡或者用户站在画面边缘导致脚部被截断检测框内包含大量噪声信息。这三个瓶颈的共性指向同一个结论模型需要一种能在空间维度上自适应地强调“动作关键区域”、弱化“无关信息”的机制。注意力机制是方向但直接照搬SE或者CBAM效果有限因为这两种经典注意力模块更偏向通道维度的全局重标定对局部空间细节的敏感度不够。于是我设计了FMB结构专门解决这个问题。3. FMB模块设计改进思路与集成位置3.1 FMB模块的结构设计FMB全称Feature Modulation Block直译是特征调制块。设计它的出发点是在同一个模块内部用不同的感受野并行提取特征再通过可学习的门控机制动态决定融合权重。这个思路跟传统多分支网络有本质区别——传统多分支只是简单拼接或者相加而FMB引入了特征调制的过程让网络自己学会在什么情况下更相信哪个分支。具体结构上FMB接受输入特征图后先做一次通道分组分成两组。第一组走一条1x1卷积加一层简单的通道注意力负责提取全局性的通道相关性第二组先经过一个3x3深度可分离卷积做局部空间建模再接一个5x5的深度可分离卷积扩大局部感受野最后用一个sigmoid门控生成空间调制权重。两组特征在融合时不是简单相加而是把第二组的空间调制权重作用到第一组的通道特征上得到调制后的输出。这个设计的好处是通道维度的全局信息能精细地指导局部空间的注意力分配对深蹲这种“全身姿态局部细节”耦合的任务非常契合。3.2 如何把C3k2改造成C3k2-FMB改造过程不是简单地把FMB整个塞进C3k2就完事这里有个关键的集成位置问题。C3k2内部结构是输入先经过1x1卷积升维拆分出两条路径一条经过若干瓶颈层另一条直接skip连接最后在输出端把两路拼接。瓶颈层的数量由一个可选参数控制可以是一个也可以是一串。我的做法是把FMB作为瓶颈层的一部分插入把C3k2里默认的Bottleneck替换为Bottleneck-FMB组合即每个瓶颈层在原有3x3卷积后接一个FMB模块做特征调制。这样改法的好处有几个首先FMB只加在深层瓶颈路径上skip连接路径不受影响保证梯度流畅通其次FMB模块全部使用深度可分离卷积额外参数量被压得很低整模型只增加了约1.1M参数在s规格上可以接受最后这样的集成位置让FMB作用于网络提炼后的高层语义特征比随便挂在骨干末尾更有效。# 核心改造伪代码实际实现基于ultralytics源码结构 class Bottleneck_FMB(nn.Module): def __init__(self, c1, c2, shortcutTrue, k(3, 3)): super().__init__() self.cv1 Conv(c1, c2, k[0], 1) self.cv2 Conv(c2, c2, k[1], 1) self.fmb FMB(c2) # 新增的FMB调制模块 self.add shortcut and c1 c2 def forward(self, x): y self.cv2(self.cv1(x)) y self.fmb(y) # 特征调制 return x y if self.add else y改造完之后整个模型的骨干部分从输入到输出依次是连续的C3k2-FMB模块负责逐步提取不同抽象层次的特征每个阶段都带上了空间调制的表达能力。3.3 为什么这种改进对深蹲识别有效深蹲识别的本质困难在于区分“姿态相近但本质不同”的图像。标准深蹲和蹲幅不足这两个类别在图像上可能只有髋部下沉深度的细微差别标准深蹲和膝盖内扣之间差别出现在膝盖在垂直方向上的投影位置。这些细微差别通常集中在图像的中高频空间信息里。FMB的深度可分离卷积组合天然擅长捕捉这类信息3x3分支负责局部边缘和纹理5x5分支负责稍大范围的形态模式而sigmoid门控让模型能针对每个空间位置独立决定强调哪些特征。在训练过程中我观察到FMB调制后的特征图在膝盖区域和髋部区域确实产生了更强的响应这说明网络确实学会了把注意力放在动作评判的关键部位。从消融实验看单独把FMB放在骨干最后一个C3k2上提升有限而把它均匀插入所有C3k2模块后效果最好说明这种调制能力需要在多个尺度层级上持续起作用。4. 数据集构建深蹲动作采集与标注4.1 类别体系怎么设计才靠谱数据集的类别设计直接影响模型能力边界。一开始我设计了六个类别包括标准深蹲、膝盖内扣、蹲幅不足、重心前倾、弓腰、站姿恢复结果训练出来的模型在弓腰和重心前倾这两个类别上几乎无法区分。后来反思问题在于这两类动作在实际视频帧上确实高度相似——身体前倾时腰部压力大的人往往会下意识弓背视觉特征重叠严重。重新梳理后我把类别收敛为四个核心类标准深蹲standard_squat、膝盖内扣knee_valgus、蹲幅不足partial_squat、重心偏移off_balance。重心偏移这个类合并了原来的前倾和弓腰因为从自动评估的角度看它们都属于躯干稳定性问题反馈话术可以是统一的“注意躯干稳定”。类别少了类间差异更明确模型训练难度显著下降。这给后来者的建议是动作质量评估的类别设计一定要做可行性分析不要凭理想分类硬上类间视觉可分性是第一原则。4.2 数据采集与标注的实操细节数据来源我用了两条路并行一是从公开健身视频网站抓取深蹲教学和纠错视频按帧截取通过关键帧筛选去掉大量重复和模糊帧二是自己组织了几位不同体型、不同训练水平的人实际录制覆盖正面、侧面45度、正侧面三个拍摄角度同时在全实和半实两种光照条件下采集。标注是这类项目最耗时也最影响上限的环节。我用LabelImg工具标注规范严格定义检测框必须完整框住整个人体从头顶到脚底宁大勿小类别标签由三位标注者独立标注遇到分歧由我仲裁。特别强调一点膝盖内扣的标注必须同时参考相邻5帧因为深蹲过程是一个动态变化的过程单帧上膝盖内扣可能不明显但前后帧连起来看就能明确判断。最终数据集包含约8600张有效帧其中标准深蹲占39%膝盖内扣占24%蹲幅不足占21%重心偏移占16%按8:1:1划分训练、验证、测试集。整个标注过程花了大约两周这里没有捷径可走。4.3 数据增强策略与坑深蹲数据的增强策略跟常规目标检测不太一样。Mosaic增强我保留了它能提升模型对截断和遮挡的鲁棒性但这在深蹲场景有副作用Mosaic会把多个图像拼接在一起造成同一张图里出现多个人体模型可能学到“多个框互相抑制”的错误模式。我的处理办法是把Mosaic的开启概率从默认调低一些让模型更多学习单人的完整特征。另一个重要的增强是随机水平翻转这个对深蹲完全适用因为左膝内扣和右膝内扣在物理上是等效的。但千万不能做垂直翻转深蹲的上下空间关系是绝对的语义信息——头一定在上脚一定在下垂直翻转会把模型彻底练坏。我还加了HSV色彩抖动和小角度旋转正负10度以内这些对提升模型对光照和拍摄角度的鲁棒性很有帮助。有一个容易忽略的细节旋转增强的角度如果过大会让“蹲幅不足”这类依赖垂直距离判断的类别产生错误的训练样本所以旋转范围宁小勿大。5. 训练配置与实验过程5.1 环境与硬件配置训练环境我用了一块单卡GPU显存24GB对于s规格的YOLO11来说绰绰有余。软件层面用的是pytorch框架搭配官方YOLO11的训练管道Python版本3.9以上。我遇到过一个大坑在某个特定版本的pytorch下C3k2里新增的可变形卷积相关操作会触发CUDA内存碎片化问题训练到一半直接OOM。后来锁定pytorch版本和CUDA工具包的组合之后问题消失建议读者在改造网络结构前先把版本组合固定好。预处理部分输入图像统一缩放到640x640训练时batch size设为16。这种规模的数据集和模型300个epoch的训练大约需要6到8小时中间如果调整结构重新验证一个完整的消融实验组大概需要一整天。所以建议做结构改进实验时先跑一个精简版配置快速验证网络能正常收敛再跑完整训练避免浪费时间在无效实验上。5.2 关键超参数设置与理由优化器我选择了SGD配合动量0.937而不是很多人默认用的AdamW。理由是在目标检测任务里SGD经过充分训练后的泛化性能通常更好尤其在细粒度分类这类对特征质量要求高的场景。初始学习率设为0.01使用余弦退火调度总训练轮数300个epoch。权重衰减设为0.0005配合很小的0.0015 warmup比例让训练在前期稳定起步。损失权重方面类别损失和框回归损失采用了官方默认的比例配置但我做了一处针对性调整把类别损失中针对“膝盖内扣”这个类别的正样本权重提高了20%。原因是膝盖内扣在数据集中占比不是最高的而且它的误判代价最高——漏报一个内扣动作在健身场景相当于一次潜在的运动伤害风险。加权之后训练日志显示这个类别的召回率从原来的0.76提升到了0.83代价是precision轻微下降0.02这个trade-off我认为是值得的。5.3 训练过程的关键观察训练曲线有几个值得注意的转折点。前50个epochmAP50快速从0爬到0.6左右但mAP50-95几乎没动这是典型的“先学会粗定位、后学会细分类”的收敛过程。100到200个epoch之间mAP50增长趋于平缓mAP50-95持续稳定爬升这个阶段模型主要在精修特征表示不能因为mAP50不动就过早停训。我在第220个epoch左右观察到验证集loss出现了一次小幅反弹排查后确认是学习率余弦衰减末期出现的正常波动继续训练到300个epoch后loss重新回落。这里要强调的是动作识别这类细粒度任务跟常规的目标检测不太一样训练轮数不能太短特征调制模块的潜力需要足够长的训练周期才能完全发挥。6. 实验结果对比与部署落地6.1 改进前后精度与速度对比完整的对比实验在相同数据集、相同训练配置下进行消融分三组原始YOLO11、YOLO11加FMB模块但不改C3k2、完整的C3k2-FMB改进。结果如下表模型配置mAP50mAP50-95PrecisionRecall单帧推理耗时(ms)原始YOLO110.8140.5270.8020.77118.6YOLO11FMB0.8410.5510.8230.79519.2YOLO11-C3k2-FMB0.8690.5960.8510.82219.8从数据能明显看出两个结论第一FMB单独加在原始结构上就有提升说明调制机制本身有效第二把FMB深度集成到C3k2内部后提升幅度更大说明结构性的融合比“外挂式”加成更高效。推理耗时从18.6毫秒增加到19.8毫秒代价约6%在可接受范围内。如果部署在手机上可以通过ONNX导出加INT8量化推理耗时能压到12毫秒左右精度损失控制在1.5个点以内。6.2 部署端的实战经验部署环节我用ONNX格式导出模型再用推理引擎做加速。导出时有一个关键坑如果用自定义的Bottleneck_FMB模块ONNX导出需要确保深度可分离卷积等操作在onnx算子集范围内被正确映射否则会出现某些算子不支持导出的报错。解决办法是导出前用torch.jit或者静态ONNX导出接口做一次完整验证对所有输入shape做一次前向对齐测试。模型最终跑在一个安卓平板上输入是摄像头采集的1280x720画面先裁剪缩放至640x640送入模型。完整流程从摄像头取帧到画面叠加分类结果和反馈提示实测延迟约80毫秒其中模型推理占20毫秒剩余的取帧、缩放、后处理大约占60毫秒。这个延迟对实时健身反馈来说稍微偏长如果产品化还需要进一步优化前后处理的部分。7. 常见问题与排查实录7.1 相近动作类别互相混淆的根治方案上面提过最开始弓腰和重心前倾两个类别分不开这是这类动作识别项目最典型的“坑”。我的排查方法分三步第一步看混淆矩阵确认到底是哪两类在互相误判第二步随机抽取误判样本图人工观察视觉差异是否足够大第三步回到类别体系层面做合并或者重新定义。这个排查顺序非常重要很多人一遇到混淆就急着加数据、加模型复杂度但很可能根本问题是类别定义本身就存在视觉歧义。我最后的结论就是合并了两个可疑类别而不是继续硬训练。如果你合并后仍然有混淆再去考虑是否某个类别训练样本太少同类的两个子形态差异过大这种时候可以尝试把大类拆成多个小类来降低类内方差。7.2 小目标与遮挡场景下的检测失效深蹲检测里最头疼的检测目标其实是膝盖和脚踝这两个小区域。整体人框检测没有太大问题但分类依赖的细节特征往往集中在小区域上。有一次测试视频里用户穿着深色宽松长裤脚踝完全被裤脚遮住模型果断把动作判成了标准深蹲而实际上那是个膝盖内扣严重的错误动作。这个问题不能靠单纯增加训练样本解决我尝试了两个有效的措施一是训练时对膝盖区域做随机遮挡增强强制模型学会从整体姿态推断局部信息二是改在训练时额外引入一条分支对膝盖区域做辅助分类类似于多任务学习的思路。虽然辅助分支在推理阶段会被丢弃但它能引导骨干网络在特征提取阶段就关注到膝盖区域。实测这个辅助分支提升了膝盖内扣类别的召回率约4个百分点。7.3 过拟合与数据不平衡的组合处理8000多张图对四类动作识别来说数据集规模一点都不富裕尤其是重心偏移这类类别只有1300多张很容易过拟合。训练日志里一个典型表现是训练集loss持续下降但验证集loss在第260个epoch左右开始回升这就是过拟合信号。我采用的组合策略是第一类别加权损失已如前文所述提高小众类别的惩罚权重第二在FC层前加了dropout概率设为0.1这对细粒度分类任务是一个低成本高收益的正则化手段第三对少数类别使用过采样配合随机增强来补充样本。还有一个容易被忽略的做法是验证集不要光看mAP要单独看每个类别的precision和recall因为总体指标的改善可能掩盖某个关键类别的退化。写在最后的小经验整个项目做下来我感触最深的一点是结构改进的收益是有限的真正拉开差距的是对任务本质的理解。把C3k2改成C3k2-FMB本质上是让网络在特征提取阶段就具备“选择性关注”的能力这在深蹲这种细微差异决定判断结果的场景里正好对症。如果你也想复制这条路建议先在公开数据集上验证一下FMB模块的代码正确性再做迁移实验免得因为实现bug浪费整个训练周期。还有一个小技巧值得分享训练结束后务必把模型在完全不参与训练的真实场景视频上做一次长时间运行测试不要只依赖测试集指标。因为测试集里的数据分布跟实际场景还是有差距我之前就是靠这个长测试发现了深色裤子导致膝盖内扣漏检的问题。做动作识别这类项目真实场景里的雷远比训练指标能反映出来的多多花一天跑真实数据比多看十轮训练曲线有价值。

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

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

免费获取报价 →
↑