人脸检测做久了你会发现一个有意思的现象大部分工作都在琢磨“怎么把框回归得更准”但真正让人脸检测产生质变的往往不是框本身而是围绕“人脸”这个目标的额外监督信息。RetinaFace这篇论文标题已经把核心亮出来了——Single-stage Dense Face Localisation in the Wild单阶段、密集、野外人脸定位。这三个词每一个都不是白写的。它出现在两阶段检测器依然统治WIDER FACE榜单的年代却硬生生靠单阶段结构拿到了当时的最优结果靠的不是结构上的花活而是把“一个anchor上能回归的东西”做到了当时没人做到过的密度。这篇文章我前前后后读了三遍也复现过一轮。说句实话RetinaFace能成为人脸检测领域的常青树不是因为它某一项设计特别惊艳而是它把多任务监督、特征金字塔、自监督信号这些散落的零件以非常合理的方式组装在了一起。无论你是打算复现论文、做工程落地还是单纯想理解“什么叫好的多任务设计”这篇解读应该都能给你一些比原文更多的东西。1. 论文要解决的问题为什么单阶段人脸检测会被两阶段压制1.1 两阶段方法的核心优势特征对齐在RetinaFace出现之前WIDER Face榜单上排在前面的几乎清一色是两阶段方法。两阶段检测器比如Faster R-CNN那一系为什么能打关键就在ROI Pooling或者后续的RoIAlign。第二阶段会把第一阶段proposal对应的特征区域抠出来重新缩放、对齐再做一次精细的分类和回归。这个过程解决了一个单阶段方法长期头疼的问题特征和物体位置的对齐。单阶段检测器直接在特征图的每个位置上铺anchor然后回归偏移量。问题在于人脸尺寸变化极大同一个特征层上有的脸可能就几个像素有的脸占据几十上百个像素特征表达质量参差不齐。两阶段因为有“先粗筛、再精修”的结构天然多了一次纠正机会。这也是当时很多人的共识想刷精度就老老实实上两阶段。单阶段的优势只有速度精度上不去。1.2 单阶段方法的三重困境单阶段人脸检测当时被压制总结下来有三个核心痛点。第一正负样本极端不平衡。一张野外照片里人脸框可能只有几个而anchor则动辄上万。如果不做特殊处理训练时负样本的梯度会淹没正样本。第二anchor匹配策略粗糙。两阶段方法第一阶段只要做到“大概有脸”就行精修交给第二阶段单阶段方法则要求某个anchor直接回归到最终框。这对anchor的初始位置、尺度、长宽比都提出了很高要求小脸尤其吃亏。第三回归目标太“贫瘠”。之前的单阶段检测器每个anchor只回归一个框顶多再加个分类分数。但人脸不是一个简单的矩形框它有五个关键点有轮廓有姿态信息。这些信息如果能在同一个框架下被预测出来会极大提高模型对人脸结构的理解能力。可惜当时没人把这套东西体系化地加进单阶段检测器里。1.3 “Dense Face Localisation”到底在说什么RetinaFace标题里的Dense不是指dense anchor铺得多密而是指每个anchor的回归目标从“一个框”扩展成了“一整套人脸几何”。具体来说每个anchor除了分类分数和bounding box之外还要回归5个人脸关键点landmark以及一个密集的人脸轮廓点集dense box。这个问题从本质上改变了训练的监督信号密度。两阶段方法在第二阶段只对少数proposal做精细回归RetinaFace则对每一个正样本anchor都施加了来自框、关键点、密集轮廓三个层面的监督。信息量完全不在一个量级。RetinaFace用实验证明了单阶段打不过两阶段不是结构的问题是监督密度不够的问题。2. 网络架构设计特征金字塔和多任务头的搭配2.1 为什么ResNet-50加FPN就够了RetinaFace的主干网络没有走极端路线选的是ResNet-50加FPN这个在当时已经非常成熟的组合。现在的视角看这似乎平淡无奇但放到当时的人脸检测环境里这个选择其实很讲究。人脸检测和通用物体检测有个显著差异人脸尺度分布极广而且小脸占了很大比例。野外场景下远处的人脸可能只有十几甚至几个像素这对特征图的步长非常敏感。RetinaFace的FPN从P2开始往上构建P2对应原图1/4分辨率保留了最丰富的空间细节对小脸至关重要。很多复现实验也验证过如果把P2层去掉或者从P3才开始建金字塔Hard子集上的召回率会有明显下滑。FPN的横向连接则解决了浅层语义信息不足的问题。P2虽然空间分辨率高但经过的卷积很少语义抽象程度低。FPN把深层的语义信息逐级向下融合让P2层在保留细节的同时也具备足够的语义判别力。这个设计在RetinaFace里不是可选项而是整个多任务监督能够生效的基础。2.2 SSH context module给金字塔加上感受野增强FPN有了以后RetinaFace在每层特征后面还接了一个context module这个模块的灵感来自SSHSingle Stage Headless检测器。说白了就是在每个金字塔层上用不同 dilation rate 的卷积分支并行提取上下文信息再融合到一起。这么做最直接的好处是在不降低分辨率的前提下扩大感受野。为什么需要扩大感受野人脸检测经常遇到遮挡和模糊的情况。只看局部特征很难判断这是不是一张脸但如果能看到周围的肩膀、身体轮廓判断起来就容易得多。上下文模块让每个位置的预测既能关注到人脸本身又能感知周围环境对Hard子集上那些模糊、遮挡、极小人脸非常有用。RetinaFace的context module和SSH原版不完全相同它做了一些轻量化调整但核心思想一致用不同dilation的并联卷积替代堆叠卷积。后者增加感受野的代价是层数变多、计算量变大前者则是几乎零成本的感受野扩张。2.3 多任务head一个anchor输出的全部内容RetinaFace检测头部的结构非常直观。每个金字塔层经过context module之后接一个共享的多任务head输出四样东西分类分数判断这个anchor是否包含人脸。box回归4个偏移量修正anchor到真实人脸框。landmark回归5个关键点的坐标通常每点两个值共10个输出。dense box回归一组密集点坐标用于刻画人脸轮廓。这个设计从工程角度看非常简洁所有任务共享同一组特征头部只增加了少量卷积参数。从训练角度看这相当于强制特征同时具备“判别是否有脸”、“定位脸框”、“定位五官”、“刻画轮廓”四种能力。特征表达被多任务逼得更加丰富这也是为什么RetinaFace只用ResNet-50就能打赢当时很多更大网络的两阶段方法的深层原因。我在复现时发现一个细节landmark和dense box分支的输出层不要单独拉一条很深的子网络直接在共享特征后接一个1x1卷积输出对应通道就行。任务之间的信息共享正是多任务学习的价值所在。3. 像素级监督landmark和dense box是怎么生成和回归的3.1 5点landmark从“框住人脸”到“理解人脸”5点landmark左眼、右眼、鼻子、左嘴角、右嘴角在人脸领域是个非常成熟的定义。RetinaFace最值得注意的地方是它把landmark回归从“后处理优化”变成了“训练主任务”。之前很多方法把landmark当作一个附加输出训练时给个小权重意思一下。RetinaFace的做法是把它当作和box回归同等重要的监督信号。每个正样本anchor不仅要学会把框拉准还要学会把五个关键点放到正确位置。这个监督信号看似只是在学点坐标实际上逼着模型去理解人脸的内部结构。模型必须学会区分眼睛和眉毛、鼻子和嘴巴否则landmark的loss降不下去。从损失函数的角度看landmark回归给每个anchor提供了10个额外数值的监督这和小目标检测所需的精细空间感知能力是直接相关的。很多消融实验都显示加了landmark监督之后框的定位精度也会同步提升这就是多任务学习带来的正则化效应。3.2 dense box一条轮廓的监督密度dense box是RetinaFace比较有特点的设计。它不做5点就完了而是对每个正样本anchor回归一组密集点这组点勾勒出脸部的轮廓。论文实现里这组点被组织成一个类似盒子的形式Cover整张脸的面部区域。概念上可以理解为每个anchor不仅知道自己负责的脸框在哪还知道这张脸的轮廓线长什么样。dense box的回归目标从哪来这是RetinaFace解决的核心工程问题之一。公开的人脸检测数据集比如WIDER FACE只标注了框没有标注轮廓。RetinaFace引入了一个自监督的网格解码器先用3D人脸重建的方法预测一个3D mesh然后把mesh顶点投影回2D图像平面生成密集的2D轮廓点以此作为dense box的监督信号。这个思路的妙处在于它绕开了“人工标注密集点”这个不可能完成的工程。3D人脸重建技术已经能够对人脸形状做出合理预测把3D先验投影到2D相当于免费获得了大量面部的密集空间标注。用这些自动生成的标注来训练检测器检测器就能在推理时只凭单张图像直接输出密集轮廓而不需要额外的重建网络。我在实际训练中发现dense box的标签质量直接决定了这个分支有没有用。如果3D重建模块质量太差生成的位置图有大量空洞和错点训练出来的dense box输出基本是废的。所以如果你打算复现建议先用质量较高的预训练重建模型生成离线标签而不是训练到一半再去实时生成。3.3 自监督网格解码器更深的几何一致性约束自监督网格解码器解决了另一个更深层的问题2D关键点标注和3D人脸形状之间的一致性。一张人脸图像如果标注了左眼和右眼的位置严格来说我们并不知道它在空间中的真实3D位置因为缺少深度信息。但如果有一个3D人脸模型我们可以通过投影来找到“哪个3D顶点对应这个2D点”。RetinaFace利用这个原理让检测器预测的landmark和dense box和3D mesh投影结果尽量一致从而建立起2D监督信号与3D几何之间的桥梁。这项设计让检测器从“看见一个点”升级到了“理解一张脸的几何结构”。模型学习到的不再是孤立的关键点位置而是人脸表面在3D空间中的整体形状。正因如此RetinaFace的姿态鲁棒性明显优于当时没有这项设计的检测器。侧脸、遮挡、夸张表情这些情况模型依然能够输出稳定的关键点位置。3.4 多任务损失怎么平衡一次说清RetinaFace的损失函数由四部分组成分类损失、box回归损失、landmark回归损失、dense box回归损失。总损失可以写作L L_cls λ1 * L_box λ2 * L_landmark λ3 * L_dense其中分类使用softmax loss回归部分统一使用smooth L1 loss。smooth L1的好处是误差较小时梯度平缓误差大时梯度不会爆炸在关键点回归这种数值范围不稳定的任务上非常合适。损失权重的设置是个值得说道的点。论文里给不同回归任务设置了不同的权重力和box的权重明显高于dense box。这背后的逻辑是5点landmark是强语义信息检测器的核心输出权重必须给足而dense box属于辅助监督它的定位精度天然低于人工标注的landmark权重过高反而会把噪声放大。我复现时的经验是四个loss的比例大约在10.50.250.1这个量级是比较稳的起点。重点在于不要四个任务一视同仁。多任务设计的核心问题不是“任务越多越好”而是“每个任务输出的监督质量不一样权重必须有所倾斜”。如果某个分支生成标签本身噪声较大索性把它的权重压低让它作为辅助信号就好了。4. 训练细节KPB采样和跨GPU可变形卷积4.1 KPB已知像素块采样论文里有一个容易被略过但对训练效果影响很大的组件KPBKnown Pixel Boxes采样策略。普通anchor采样策略通常是在整张特征图上均匀分布采样这样小脸上匹配到的anchor数量会非常少。retinaface的做法是先找到所有包含人脸的区域在这些区域对应的像素位置密集选择anchor让正样本覆盖更充分。KPB和传统OHEM、focal loss这类方法有个本质区别OHEM和focal loss都是在“生成大量候选anchor之后再筛选”而KPB是在anchor分配阶段就从源头让正样本更密集。两者虽然可以同时使用但解决的是不同层面的问题。KPB解决“有没有正样本”focal loss解决“正负样本损失比例”。4.2 跨GPU可变形卷积工程上最麻烦的一环RetinaFace里还做了一个在论文中不太起眼、但复现时让人头疼的设计跨GPU可变形卷积。可变形卷积DCN能让卷积采样的位置不再是固定网格而是根据输入动态偏移。对人脸这种形变较强的目标DCN能有效提升特征表达能力。但标准DCN在分布式训练时有个麻烦每个GPU只保存自己那一份特征图如果某个采样点的偏移量指向了相邻GPU上的特征位置普通实现是取不到的。RetinaFace为此专门实现了跨GPU的DCN版本让不同GPU之间能够交换特征数据。这个模块的贡献对于当时的训练效果确实是实打实的。但如果你只是单卡复现完全可以直接使用标准DCNv2精度损失在可接受范围内。跨GPU DCN更像是一个分布式训练的工程优化项而不是检测器的核心算法组件论文里对它的着墨也不多。4.3 数据增强和训练配置值得抄的几点RetinaFace训练时用了随机裁剪、颜色扰动、水平翻转这些常规操作真正对精度影响较大的几个配置包括输入分辨率。论文训练和测试统一使用固定分辨率复现时不要随意改动。分辨率折半WIDER FACE Hard子集上的性能下降非常明显。样本匹配策略。每个真实人脸框会和多个不同尺度、不同位置的anchor匹配保证小脸也能获得足够多的正样本监督。分阶段训练策略。论文先在有框标注的数据集上训练基础检测能力再用包含landmark标注的数据微调多任务分支。这背后是数据使用效率的考量不是所有数据都有landmark标注硬凑反而降低检测性能。这些配置单独看都不难但组合起来效果非常拔群。如果你只记住了结构而忽略了采样和训练策略精度大概率达不到论文水平。5. 实验结果与消融分析到底是谁在起作用5.1 WIDER FACE三个难度档位一次说清楚WIDER FACE验证集和测试集按检测难度分成Easy、Medium、Hard三档。Easy档人脸大且清晰Hard档则充满了大量小人脸、遮挡、模糊和夸张姿态。RetinaFace在这三档上都取得了当时的最优结果其中Hard子集上的提升最为显著。这个结果的意义要从时间线看在RetinaFace之前Hard子集的榜首长期被两阶段方法霸占。RetinaFace以单阶段结构拿下第一首次证明了单阶段方法在小脸密集检测上也能做到顶级精度在很大程度上扭转了“精度靠两阶段”的行业惯性。5.2 消融实验拆解每个模块的真实贡献论文的消融实验值得认真看它把各个设计的贡献量化了出来去掉FPN只保留单层特征Hard档成绩大幅下降证明多尺度特征不可或缺。去掉dense box分支landmark精度下降检测框的定位精度也同步受损说明密集轮廓监督和关键点定位之间存在正向迁移。把dense box监督替换为普通人工标注数据效果反而不如自监督生成的密集逼近好说明监督的数量密度比单点精度更关键。去掉KPB采样小脸召回率下滑明显。这些消融实验串起来看指向的是同一个结论多任务监督带来的收益不是简单的“加了个辅助loss”而是让同一个特征表示同时具备分类、定位、关键点和轮廓预测能力特征本身的质量被整体拉高了。5.3 为什么能打赢两阶段监督密度弥补结构差距两阶段方法依赖ROI对齐解决特征错位问题这本质上是一种后验优化。RetinaFace换了一条路既然我没有第二阶段做精修那我就在训练时让每个anchor直接学习完整的人脸几何结构。当监督足够密集时单阶段网络本身就能学会空间对齐的能力不再需要ROI Pooling这样的结构来兜底。我觉得这是RetinaFace对整个目标检测领域最有启发性的地方结构上的劣势可以用监督密度来弥补。这给后面很多单阶段算法提供了底气也解释了为什么后来的一阶段人脸检测器层出不穷而两阶段方法在人脸领域逐渐式微。6. 从论文到落地复现与部署实操笔记6.1 复现环境的配置要点硬件方面RetinaFace的完整训练在当年的显卡上需要多卡并行但现在单张主流显卡基本也能跑通一个缩配版本。框架选成熟的深度学习框架即可重点在于几个细节backbone加载预训练权重。不管你是用官方权重还是自己下载的ResNet-50预训练模型一定要确认FPN横向连接的初始化是合理的否则训练初期会非常不稳定。anchor参数。论文里的anchor设置对尺度覆盖影响很大不要随意调整。小脸密集场景下必须保留最小的anchor来覆盖极小人脸。标签生成顺序。建议先离线生成landmark和dense box标签保存成文件再训练而不是训练过程中实时跑重建模型。后者看起来方便但会拖慢训练速度而且容易出现不同epoch之间标签不一致的问题。6.2 推理后处理容易踩的三个坑第一landmark和dense box的输出坐标一般是在输入图像分辨率下定义的不是原图分辨率。如果你的pipeline在检测前做了resize记得把landmark坐标按缩放比例映射回原图否则后续做人脸对齐或识别时会出现位置漂移。第二NMS的阈值调节。RetinaFace的分类输出和box回归质量不是完全相关的。有时候分类分数高但框定位一般NMS按分类分数排序可能会保留一些定位差的框。实际部署建议用分类分数和box置信度综合做排序或者直接放宽NMS的IoU阈值让重叠框先合并再按综合分数过滤。第三多任务分支的batch size不匹配。推理时检测、关键点、轮廓三个分支可以共享特征一次性算出但实际工程中常有人把检测和关键点拆成两个模型串行跑白白让速度翻倍完全没有必要。6.3 轻量化与部署建议RetinaFace的强大在于多任务监督这个设计思想并不要求你必须用ResNet-50。换成MobileNet或更小的backbone保留同样的FPN和多任务head依然能获得准确率和速度的平衡。我实际测试下来轻量backbone版本在移动端跑实时人脸检测关键点定位完全够用而且因为多任务监督的存在精度相比单纯训练一个轻量检测器要高出不少。这也是RetinaFace到现在依然活跃在各种人脸SDK底层的原因之一它不是一个只能刷榜的论文模型而是一套可以直接落地的算法框架。如果你要做的是人脸识别pipeline里的检测模块我的建议很直接不要从零训练先用官方预训练模型做fintune把你的场景数据特别是大角度侧脸和遮挡脸加进去配合合适的anchor设置重新训练一小段时间就好。从零训练RetinaFace的代价很大而多任务预训练带来的特征先验在真实场景中价值极高。RetinaFace这篇论文我最佩服的地方其实是它对“监督信号”的极致利用。大部分检测工作停留在“框准了就行”它却把一个anchor的预测目标拓展到了人脸几何结构的完整表达。每个人工设计的多任务模块都经过实验验证每个训练技巧都有明确的motivation这种论文读起来是真的很舒服。如果你要入门人脸检测的算法设计这篇是值得反复品的基础课。