一张体育场馆的抓拍里挤了十几个人有的只露出半张脸有的手臂互相搭在一起还有小孩藏在大人身后。想一次性把所有人的关节都标出来最容易想到的思路是先用目标检测把人一个个框出来再对每个框单独回归关键点这就是自顶向下Top-Down。而这篇要聊的自底向上Bottom-Up走的是完全相反的路先在一整张图上把所有关键点全部检测出来再通过某种关联信息把属于同一个人的关键点拼回成完整骨架。作为2D多人姿态估计综述的第三部分这一篇会把Bottom-Up路线从问题定义、分组机制、骨干网络、训练细节到评价体系完整拆开讲。适合正在复现或改进这类模型的工程师也给刚入门、想真正分清两条路线差异的同学做一次系统梳理。1. 先把问题摆正Bottom-Up到底要解决什么1.1 一张图里既有检测又有分组自底向上方法最核心的认知是一个完整系统包含两个耦合的子问题检测和分组。检测阶段网络会在整张输入图像上输出所有关键点的热力图heatmap。假如数据集的关节点定义是17类那输出特征图的通道数就是17每个通道上的一团响应峰就表示这一类关节点可能在图像上出现的位置。这一步和单人姿态估计很像区别只是把多个人的关节混在一起检测。分组阶段才是Bottom-Up的独门考点。网络检测出几十上百个关键点候选之后如何把同一只左手肘和同一只左手腕连起来而不是把A的手腕接到B的手肘上这个问题在图像里只有一个人时根本不是问题但在密集场景下不同人的关节彼此交错、叠压单纯靠空间距离完全无法区分。整个Bottom-Up路线的发展史本质上就是一部如何设计更好的分组策略的历史。也因此我在看这一类论文时从来不只盯着热力图精度看而是重点看损失函数和后处理里怎么处理关联两个字。热力图AP高只能说明点找得到不能说明骨架拼得对。这两个能力又是强耦合的——分组信息如果设计得好反过头来还能帮助热力图把遮挡关节预测得更准。1.2 为什么Top-Down解决不了所有场景先给Top-Down一个公允的评价。它的好处非常明显把多人问题拆成单人问题每个检测框里的目标姿态更清晰上下文更完整模型可以专注感受野内那一个人精度自然容易做得高。所以很长一段时间里各大榜单靠前的位置几乎都是Top-Down类方法。但它有两个结构性缺陷是模型怎么调都很难绕开的。第一是计算量随人数线性增长。图像里有10个人单人的姿态网络就要跑10遍有50个人就要跑50遍。实时性和人数绑在一起在密集人群场景非常吃亏。第二是检测误差会直接传导。目标检测器一旦把某个小人漏掉或者两个重叠的人被框成一个框那这个框里的关键点后处理做得再好也救不回来。拥挤场景下检测框之间大量重叠还不得不引入额外的NMS和框质量筛选逻辑否则同一个人的关键点会在多个框里被重复预测最终还得做跨框去重。而Bottom-Up方法在这两个方向上天然占优。它的推理成本基本是恒定的图像里无论站着1个人还是20个人主体网络只跑一次。它也不依赖人检测框人挨得再近只要关键点检测和分组能处理信息就不会在先裁框这一步丢掉。1.3 用一张表看清两条路线的差异我用下面这张表总结过多次发给团队里新来的同学特别省事也放在这里。对比维度Top-Down自顶向下Bottom-Up自底向上基本流程行人检测 单人姿态估计全图关键点检测 关键点分组计算量随人数线性增长基本恒定与人数弱相关精度上限通常更高便于利用单实例上下文依赖分组算法关键点拥挤时吃亏速度优势人数少时可行人数多时明显下降多人/密集场景优势明显主要研发难点检测器召回率、跨框去重关联信息设计、多尺度召回典型适用场景少人、人框较清晰的图视频监控、体育动作分析、密集人群这张表里有一点需要补充解释Bottom-Up的精度上限在2020年前后已经慢慢追上来了不再像早期那样被Top-Down无悬念压制。差距缩小的关键就是分组机制和多尺度骨干网络的进步这也是这篇综述后续部分的主线。2. 分组机制的技术演进这是Bottom-Up的灵魂2.1 距离聚类这种朴素想法为什么走不远最早期的Bottom-Up方案最直接的分组思路就是距离聚类把二维空间上离得近的关键点归为同一个人。直觉上说得通——一个人的手肘和手腕本来就靠近两个人相隔较远时自然能分开。但真实图片很快会击穿这个假设。两个人擦肩而过时A的手肘可能比A自己的手腕更靠近B的手腕一个人把手叉腰时手和髋的距离也很近。固定距离阈值完全不成立因为骨架长度随人高、随远近、随视角都在变化。哪怕按比例动态调阈值也只是从一个固定错误变成一个动态错误。早期有些工作还要假设人数已知这在真实监控画面里完全不可用。所以这个方向后续的演进本质上是把所有手工规则逐步替换成可学习的关联表示。现在的模型里我们几乎看不到任何基于固定几何规则的起点式分组了取而代之的是下面要讲的两类主流方案。2.2 关联嵌入给每个关键点打上同一人的标签关联嵌入Associative Embedding的思路非常优雅。网络除了输出关键点的热力图还会为每个检测到的关节点输出一个额外的嵌入向量embedding可以理解成给这个点打一个身份标签。训练时约束只有一个属于同一个人的所有关键点它们的嵌入向量应该彼此接近属于不同人的关键点嵌入向量应该拉开距离。这个目的通过一对损失实现——拉近损失把同一个人内部的关键点标签往中心拉推开损失把不同人的标签互相排斥。推理时就不再需要人框了只需要把所有关键点按嵌入向量的距离做聚类距离近的归为一个身份距离远的拆开。听起来很简单实际训练时坑不少。第一个坑是嵌入向量容易饱和所有关键点学到最后都塌缩到同一个区域不同人之间没有区分度。这种情况通常要调大推开损失的权重或者把嵌入维度从一维升到二维、三维。第二个坑是单人内部的标签漂移一个关键点预测得准但它的嵌入向量偏了聚类时就会挂错人。我自己的经验是嵌入向量的质量往往比热力图精度更影响最终骨架准确度而这个指标又不会直接体现在热力图的AP上所以调试时必须单独盯。2.3 部分亲和场把肢体连接的方向也学出来如果说关联嵌入是给点打标签那部分亲和场Part Affinity FieldsPAF就是给边打标签。PAF的理念热力图只告诉关键点在哪而连接信息应该由网络单独学一个方向场表示每一个肢体连接比如左肘到左手腕上每个像素应该指向的连接方向。具体来说对于某一种连接类型网络会输出一个两通道的矢量场图。在真实肢体附近的位置上矢量方向约等于从一个关键点指向另一个关键点的单位向量。这样模型等于把这两个关键点疑似属于同一条肢体这种语义关系编码成了每个像素上的方向一致性。测试时对任意两个候选关键点比如一堆左手肘和一堆左手腕沿这两点连线采样路径上的PAF值计算每个采样的方向与连线方向是否一致。一致性得分高说明这条线穿过了一块像真实肢体的区域这两个点应该连在一起得分低说明它们只是位置碰巧接近方向完全对不上。之后整个图像上的所有连接投票问题建模成二分图匹配来求解。PAF这套机制我实际复现过之后最大的体会是网络结构本身差距不大差距往往在采样细节。采样路径上取多少个点、是否在关键点端点处截断、连线上矢量方向翻转时怎么对齐这些细节稍微写错分组结果就会明显变差。很多人复现出来热力图指标好看一测多人场景就开始乱接肢体基本都是采样逻辑出了偏差。2.4 从贪心匹配到全局匹配分组不是简单连线有了连接得分之后还面临最后一个问题全图那么多关节点怎么把匹配组合起来最简单的是贪心先把得分最高的连边确定下来再逐步加入得分更低的边直到所有可能连接都被安排。贪心的优点是快但问题是一旦前面某条高分支腿连错了后面的所有边都会跟着错。更稳的办法是用匈牙利算法做二分图匹配把让这些连接的总代价最小当成全局优化问题来解。代价中包含连接得分也常常包含一个方向的先验——比如左手肘应该配左手腕而不是左手肘配右手腕。到了2020年那个节点主流方法普遍不再满足于一次性全局匹配而是做多轮扩散先在高置信度连接上确定骨架主干再逐步向边缘关节扩散。这很像人在看复杂图片时的策略——先把最不可能弄错的大骨架确定下来再去处理手部、面部这些细节。分组问题的研究后来也一直在围绕这个方向深化但万变不离如何构造可信的关联信息这个母题。3. 骨干网络与多尺度设计让检测先站得住3.1 堆叠沙漏与感受野的意义检测阶段看起来只是输出热力图但前提是网络能在不同尺度上认出这团像素是个手肘。Bottom-Up方法有一个天然难点同一张图里远处的人可能只有30个像素的身高近处的人却能占满半个画面。一个固定感受野的骨干网络很难同时服务这两种极端情况。堆叠沙漏结构Stacked Hourglass这个设计本质上是把下采样提取全局语义和上采样恢复精确位置反复执行多次。每堆叠一次网络都重新在整个尺度谱系上加工特征下一层的沙漏模块能看到上一层在更小尺度上提炼出来的语义信息。它的优点正是Bottom-Up需要的在预测每个关节点时网络被迫看到更大的上下文可以捕捉到人与人的关系、部位之间的相对位置。缺点是模型又深又重训练显存占用大推理速度也不占便宜。我自己用这类骨干的时候会特别注意输出步长的设置步长直接决定热力图的分辨率。步长太大位置精度丢得厉害步长太小显存涨得快。通常要在8到16之间做权衡。3.2 高分辨率多尺度分支小尺度人的救命稻草堆叠沙漏在语义和位置的折中上做得不错但它本质上还是先降再升的路线。降采样过程一定会丢失细节信息对小尺度人尤其致命。2020年前后一个很重要的趋势是直接全程保持高分辨率特征同时把不同分辨率分支并行融合这条路线的代表性网络结构就是高分辨率多分支网络。一个关键改动是网络不再为了让感受野变大而牺牲分辨率而是用多个不同步长的分支并行计算高分辨率分支负责精确定位低分辨率分支负责语义上下文再用重复的多尺度融合把两边互相喂。这样小尺度人的关键点信号能直接从高分辨率分支里保留下来而不是在下采样过程中被抹掉。在当时的一篇关键工作里又叠加了特征金字塔的思路让这个高分辨率骨干的不同阶段各输出一个尺度的热力图并对每个尺度分别进行关键点监督。小尺度的人由低分辨率层和大尺度的人一起协同处理最终把Bottom-Up方法的人体关键点AP拉到了和Top-Down非常接近的水平而推理速度仍然保持优势。3.3 训练与推理时的多尺度策略除了网络结构训练和推理阶段也存在明显策略差异。训练时多尺度体现在数据增强。除了常规的随机旋转、翻转、裁剪很重要的一项是把输入随机缩放到不同尺度再喂给网络。这一步直接决定模型能否学到多尺度鲁棒性。还有一类做法是多尺度监督在网络的多个输出层都挂上关键点热力图的监督信号让浅层特征也学会预测小尺度的目标。对Bottom-Up来说这种多尺度监督几乎是必需品否则模型只会对训练集中出现最多的尺度敏感。推理时更常见的技巧是测试时多尺度融合。把原图缩放到几个不同尺度分别推理得到热力图然后上采样到统一分辨率做加权平均。这个操作对最终AP的提升非常明显尤其是对COCO评估中占比很大的小尺度目标。代价是推理时间成倍增加。我自己通常的做法是先跑单尺度看基线如果小尺度AP明显拖后腿再把两三个尺度用于测试观察提升幅度来决定是否值得。4. 训练细节Heatmap监督与分组损失4.1 GT Heatmap的正确生成方式训练阶段网络输出的热力图需要一个真值信号来监督而生成这个真值并不是把关键点坐标画成一个单点而是在每个关键点位置铺设一个二维高斯分布。选择高斯核的半径很讲究。半径太大热力图上多个关键点的峰会连成一片网络学不到锐利定位半径太小可学习的正样本像素区域太少训练很难收敛。实践中通常按关键点标注的方差设定再考虑热力图相对原图的分辨率缩放。输入图是512像素热力图如果下采样了8倍那一个3像素半径的高斯在热力图上对应的物理区域就很有限需要配合较小的步长使用。除MSE损失之外也有工作尝试用交叉熵或带权重的组合损失来监督热力图。这里更重要的一个细节是热力图监督信号到底应不应该是每个人逐个铺高斯再求和答案通常是应该但不能直接求和。因为多人关键点重合时直接求和会让多个峰合并导致一处响应过强而掩盖旁边的弱峰。更稳的做法是取最大值而非求和或者使用带衰减的求和保证重叠时每个实例的峰值都能保留一丝信号。4.2 分组损失怎么构造分组机制关联嵌入或PAF是Bottom-Up网络和普通单人姿态网络在训练时最大的区别。以关联嵌入为例它的损失通常写成两部分拉近项让同一个人的关键点嵌入均值互相接近推开项让不同人的嵌入均值互相排斥。这两个项之间需要平衡权重的。拉近项太强所有人都会被压在一起推开项太强同一人内部会分裂成好几块。常见的做法是先把推开项按固定间隔设定再在训练几步之后逐渐调高拉近项让网络先学会把不同身份分开再学会把同一身份的内部粘紧。PAF的监督信号则是真值矢量场的铺设。对于每一条肢体需要在连接两个关键点的线段上为每个像素标注一个指向目标端点的单位向量。线段的粗细覆盖半径是一个超参数太粗会把附近其他肢体也卷进去太细则真值区域覆盖不足。训练时的损失通常是预测矢量场和真值矢量场的逐像素L1距离而且只在线段覆盖区域内加权。有些工作还会引入中间监督在骨干网络的多个阶段都挂上热力图和关联信息的监督防止梯度在深网络中消失。这类细节在论文里往往只有一句话但对收敛效果影响很大我建议复现时把中间监督当成默认选项而不是开实验时才想起来加。4.3 数据增强和正负样本平衡Bottom-Up训练还有一个特有麻烦整张图参与计算但对损失贡献最大的永远是那些置信度低的小目标和被遮挡关节。如果不做针对性处理网络会倾向于把所有注意力都放在画面中央那些大而清晰的人身上边缘的小人几乎学不到。数据增强方面除了常规的随机旋转缩放有两招对Bottom-Up特别有效。一是随机裁掉图像的部分区域模拟遮挡提高对局部缺失的鲁棒性二是随机复制粘贴人像把某个人贴到另一张背景图或密集区域扩充训练集中的人群密度和交互类型。正负样本平衡策略上可以对热力图的背景像素做降权。因为真正有高斯峰覆盖的像素占比非常低直接用MSE时网络优化的主体被大量背景像素控制。一般在损失里加入一个加权掩膜让前景像素的梯度占比显著提高。这个细节看似小实测下来训练稳定度提升很明显否则常会出现训练loss一直在掉但关键点AP纹丝不动的怪现象。5. 推理后处理与评测从输出张量到AP数字5.1 从Heatmap拿到关键点坐标训练完成后网络输出的还是软化的热力图要得到最终的关键点坐标还需要后处理。标准做法是在每个热力通道上找局部最大值再通过二阶插值做亚像素细化。这一步虽然简单但直接影响最终坐标精度。很多排行榜最后百分之零点几的差距就是靠这种亚像素偏移优化挤出来的。如果网络额外预测了一个偏移量分支offset直接把热力图的低分辨率坐标映射回原图再叠加偏移量修正效果通常会比纯插值更好。还有个细节是处理多尺度融合后的热力图。如果不同尺度的热力图直接平均反而可能把峰摊平、降低定位精度。更稳妥的做法是先各自提取关键点再对候选位置做聚类和加权最后保留下置信度最高的那个峰值。这一步看起来不起眼但能避免非常多的重复检测问题。5.2 骨架组装的后处理分组输出的原始结果往往带有很多毛刺比如同一只手腕被两个手肘抢、某条孤立的手臂没有身体主干、单人内部出现环状连接。清洗这些毛刺属于Bottom-Up工程化绕不开的环节。常见处理是先按连接一致性得分过滤掉低置信度连接再用BFS广度优先把相连的关键点聚类成连通分量每个连通分量对应一个人。此时要做几个规则化操作禁止同一个关节被多个骨架占用除非得分特别接近去掉少于两个连接点的孤立肢体如果骨架内部出现闭环保留得分最高的树形路径。这些规则在论文里很少被当成贡献点写出来但实际部署时它们决定了输出是否可用。我记得第一次把模型接进视频流时看到一个人同时长了三根右臂就是从这一步规则不严谨开始的。5.3 OKS、AP、PCKh到底怎么读评测离不开指标。人体关键点最常用的评估基准是COCO它采用的关键点相似度指标叫OKS。OKS的值由预测点和真值点的欧氏距离决定再按目标尺度做归一化并按关节类别乘以一个权重系数。原因是鼻子这种特征显著的部位允许的误差和脚踝这种容易漂移的部位不一样。OKS公式大致是exp(-d²/(2s²k²))其中d是距离s是目标尺度k是关节相关常数。在这个基础上AP指标就是不同OKS阈值下的平均精度通常报告的是阈值从0.5到0.95每隔0.05的平均值。于是会看到AP、AP0.5、AP0.75、APM、APL这一组指标分别表示不同阈值下和小/中/大目标下的表现。调模型的时候千万不要只看总AP要一个子项一个子项看确认短板在中等目标还是小目标再针对性调整多尺度策略。MPII数据集上用的则是PCKh它以预测点与真值点的距离是否小于头部框长度的一部分作为判定标准。这套标准更宽松且训练集规模比COCO小通常只在以PCKh为主要指标的场合对比时才会用到。两套指标的判定逻辑差异导致同一个模型在两个数据集上的好坏感受可能完全不同。做技术选型时先想清楚业务方关心什么数据分布再选匹配的评测基准这一步比调网络本身更重要。6. 实战踩坑清单与调试思路6.1 小目标关键点总是漏检怎么调症状最终AP的APM、APL都还行但APS明显掉队小人的关节几乎找不全。这是Bottom-Up早期最常见的问题。排查路线按顺序走第一步确认推理时是否做了多尺度测试没做的话先补上通常情况下小尺度AP会立刻回升几个点。第二步检查输入分辨率是否太低很多实现默认用512甚至384的输入远处的小人在分辨率层面就已经不可分辨适当提到640或768会有帮助。第三步考虑更换更高分辨率的骨干网络或启用多尺度监督。如果这三步都做了仍不行大概率是训练数据里小尺度样本不够需要针对小目标增强或者做更难例挖掘。6.2 密集场景下骨架乱接根源八成在分组症状每张图里都会出现把A的手接到B身上的情况且总是发生在人群密集区域。热力图AP可能不错但最终可视化结果一团乱麻。这种问题不要急着改网络结构先做两步诊断。第一步单独抽出连接一致性得分的分布看误连接样本的得分是否和正确连接严重重叠。如果重叠严重说明关联信息本身学得不够需要加强分组损失或提高嵌入维度。第二步检查后处理的匹配策略当前是不是用的纯贪心。换成全局匹配后很多乱接现象会有明显改善。还不行就要回到训练层面增强密集人群的数据组合。6.3 推理速度和精度怎么平衡Bottom-Up号称速度快但那是相对Top-Down而言。实际跑起来多尺度测试、高分辨率输入、复杂的分组求解都会把速度优势吃掉一大半。做法上我一直遵循几个原则先把单尺度、原始分辨率下的精度作为基线优先优化这部分如果精度达标就不做多尺度测试把推理速度留给业务侧。分组算法尽量用向量化实现避免Python循环里跑贪心匹配这类代码在密集人群上会慢到无法接受。如果还要压缩考虑把骨干网络换成更轻量的结构或用量化部署。这里有一个容易被忽略的点对整个pipeline做性能分析时要把分组后处理的时间也算进去因为有些方案网络推理只有几十毫秒后处理反而占了上百毫秒。6.4 最后再分享一点我自己的体会入行这些年我对Bottom-Up方法的判断发生过一次重要转变。最早总觉得它精度比不过Top-Down工程上更愿意用先检测后单人估计这类稳妥路线。后来真正做了视频级的人体动作分析项目发现场景里一旦出现几十个互相遮挡的人Top-Down的检测框本身就成了最大的瓶颈漏检、误检、重复框问题交替出现。反而是Bottom-Up网络一次性出点、再做分组在数量波动极大的视频流里保持了稳定的推理时间。如果你现在准备在这条路线上做实验我的建议是不要一开始就扎进最新的花哨结构里先把PAF或关联嵌入这类基础分组机制吃透再逐步替换骨干网络和匹配策略。Grouping是Bottom-Up的灵魂模型结构只是让灵魂落地的手段。把这个顺序搞对后续的优化才不会总是边改边乱。