资讯动态

端到端人脸多任务网络:3DMM参数层运动重定向实践

发布时间:2026/10/17 15:50:47 来源:尧图企业网站定制
简介一份面向计算机视觉与图形学研究者的学术论文聚焦人脸运动重定向问题提出可联合检测人脸边界框并回归3DMM参数的端到端网络。该网络由单面网络SFN与多面网络MFN构成先将3DMM参数分解为身份、表情、姿态和比例四类通过不同分辨率的多尺度特征强化参数组之间的解纠缠并结合Fire模块与SE模块提升实时性随后用SFN生成多脸图像中的弱标签再训练MFN实现同步检测与重定向。实验表明其人脸检测精度较高、对极端表情和姿态鲁棒且运行速度优于多数现有方法在移动设备上也可达到较高帧率可应用于人脸动画、面部表情识别等场景。资源包内含1个PDF大小约2.47MB已有97人学习适合需要复现或深入理解该网络架构的研究生、工程师及人脸相关应用开发者参考。1. 端到端人脸多任务网络为什么我把运动重定向做在3DMM参数层人脸多任务学习和运动重定向这个方向最初吸引我的场景其实很朴素一台人脸识别门禁机的联动屏上想让检测到的人脸实时做出指定表情或者做虚拟人驱动时想把一段真人视频里的表情、姿态搬到另一个人的脸上。传统做法是检测、关键点对齐、3DMM拟合、重定向四步各跑各的每一步的误差都会累积到下一步最终表情偏移超过肉眼能忍的范围。而这个端到端网络把多人脸检测、3DMM参数回归、运动重定向焊进一条链路输入普通RGB图输出重定向后的人脸参数不再有中间环节的修正机会误差反而比级联方式小。适合谁做虚拟人驱动、影视表情迁移、人脸数据集增强的从业者。这篇文章我按“参数结构 — 网络设计 — 训练策略 — 踩坑 — 验证”的顺序把它拆给你。2. 从3DMM参数看运动重定向为什么重定向要建立在参数空间而不是光流上2.1 3DMM 把人脸拆成身份、表情、姿态三个独立坐标系人脸运动重定向要的是“把A的表情系数搬到B的脸上同时保留B的长相”。这件事在几何网格上做很麻烦因为A和B的拓扑、顶点顺序、脸型都不同。3DMM三维可形变模型的思路是把人脸形状表示成一个线性组合平均脸加上身份基向量、表情基向量和姿态项的加权和。BFM这类公开模型通常把身份形状压缩到199个主成分、表情压缩到几十个主成分所有顶点坐标由这些系数唯一确定。所以我一般把3DMM全量参数看成一个“高维向量”——这也是热词里常说的“人脸识别图像进入神经网络到输出高维度向量的过程”。检测网络输出的是框和置信度3DMM回归头输出的是这个向量。向量头部是身份系数中间是表情系数再到纹理、姿态角度、平移量。重定向时只需要操作向量的某几段跟人脸长什么样、视频分辨率是多少都无关。2.2 参数空间重定向比光流重定向稳在哪光流法做运动重定向的痛点是遮挡和大幅形变。张嘴、闭眼这类动作会带来非刚性形变光流估计一旦出现空洞迁移到目标脸上就是扭曲的。参数空间没有这个困扰因为表情系数本身就是从真实人脸样本里统计出来的形变模式天然保证拓扑一致性。第二个原因是解耦。3DMM的身份子空间和表情子空间是分开的PCA基重定向的数学操作退化成一次矩阵乘加def redirect_3dmm(id_coeff_target, exp_coeff_source, mean_shape, id_basis, exp_basis): # 目标脸身份 源脸表情 identity_part mean_shape id_coeff_target id_basis.T expression_part exp_coeff_source exp_basis.T redirected identity_part expression_part return redirected这段代码的逻辑是先由目标人脸的身份系数重建出它的中性形状再把源人脸的表情系数叠加到这个中性形状上。id_basis和exp_basis是3DMM的正交基矩阵形状分别是(顶点数, 身份维数)和(顶点数, 表情维数)它们从BFM模型文件里读取后不再参与训练。重定向的精度上限由表情基的表达能力决定和网络本身无关。2.3 3DMM 系数拆分一个被很多人忽略的细节无论是训练时算损失还是推理时做重定向第一步都是把回归头输出的系数向量拆开。常见做法是按约定好的维度顺序切片def split_bfm_coeffs(packed_coeffs, n_id199, n_exp29, n_tex199): offset 0 id_coeffs packed_coeffs[offset: offset n_id]; offset n_id exp_coeffs packed_coeffs[offset: offset n_exp]; offset n_exp tex_coeffs packed_coeffs[offset: offset n_tex]; offset n_tex angles packed_coeffs[offset: offset 3] translation packed_coeffs[offset 3: offset 6] return id_coeffs, exp_coeffs, tex_coeffs, angles, translation注意这里踩过的坑不同训练集使用的3DMM版本不同表情维数可能是29、46、52甚至79。如果你的预训练模型来自某个基准库而重定向模块用了另一个库的维度切片身份和表情会错位重定向出来的人脸表情完全不对。我一般会在模型保存时把n_id/n_exp/n_tex三个值写进配置文件加载时强校验不匹配直接报错而不是悄悄跑。3. 联合检测与重定向网络主干、输出头与损失配比3.1 一个网络三个头检测、3DMM回归、重定向整体结构我倾向于这样组织共享主干采用类似ResNet的FPN结构顶部拉出三个分支。第一个分支是检测头输出多人脸的边框、置信度和用于NMS的额外特征第二个分支是3DMM回归头对每个人脸区域输出身份、表情、姿态系数第三个分支是重定向头它的输入是源人脸的完整3DMM系数、目标人脸的身份系数以及一个“驱动信号”。关键的选型理由是检测和3DMM回归共享底层特征多人脸场景下特征图上的每个锚点都能同时响应“这里有人脸”和“人脸长什么样”重定向头不直接吃图像而是吃参数所以这个分支很轻。实际实现里检测头用anchor-based还是anchor-free都行但3DMM回归头要对齐检测框我一般用RoIAlign从FPN的P2层抠特征因为人脸框普遍偏小P2层的感受野和高分辨率更适合抓五官细节。3.2 回归头输出解析把网络输出变成可重定向的参数下面这段是3DMM回归头在推理时的解析逻辑它在检测之后逐个人脸执行def decode_face_params(face_feat, delta_box): # face_feat: RoIAlign 后的特征形状 (C, 14, 14) out fc_layers(face_feat) # 得到全量3DMM系数 id_coeff, exp_coeff, tex_coeff, angles, translation split_bfm_coeffs(out) # 部分实现里姿态角不是直接用而是恢复旋转矩阵 import numpy as np rx, ry, rz angles Rx np.array([[1,0,0],[0,np.cos(rx),-np.sin(rx)],[0,np.sin(rx),np.cos(rx)]]) Ry np.array([[np.cos(ry),0,np.sin(ry)],[0,1,0],[-np.sin(ry),0,np.cos(ry)]]) Rz np.array([[np.cos(rz),-np.sin(rz),0],[np.sin(rz),np.cos(rz),0],[0,0,1]]) R Rz Ry Rx return id_coeff, exp_coeff, R, translation, delta_box逻辑说明fc_layers是全连接回归头输出维度由n_id n_exp n_tex 6决定。姿态角按Rz Ry Rx的欧拉角顺序转成旋转矩阵这个顺序必须和训练时数据标注的约定一致否则头部会绕错误轴旋转。重定向模块只取id_coeff和exp_coeff姿态由驱动源决定全网络只有这一处需要处理旋转矩阵。3.3 多任务损失配比权重不是拍脑袋定的多任务学习最容易被忽略的是损失函数之间的尺度差异。检测头的Smooth L1损失通常在0.01量级3DMM系数回归的L2损失可能在10以上直接相加会让检测头完全失去梯度。我的做法是给每个任务设独立权重同时对3DMM损失做按维度的归一化损失项公式初始权重理由L_detSmooth L1 (bbox cls)1.0检测是入口错框全错L_3dmmL2 (coeff_pred - coeff_gt) / dim2.0按维度平均避免高维主导L_redirectL2 (redirected_coeff - target_gt)3.0重定向是本网络最终目标L_id_consistency余弦距离 (id_coeff_pred, id_coeff_gt_target)1.5保证重定向结果不丢身份代码实现里重定向损失不是直接盯着渲染图算的而是监督重定向后的系数与真实目标表情系数之间的L2距离。身份一致性损失单独用余弦距离因为身份系数是方向性的欧式距离会惩罚整体缩放而这种缩放对身份无关紧要。def compute_multitask_loss(batch): det_loss smooth_l1(batch[pred_boxes], batch[gt_boxes], beta0.1) coeff_loss torch.mean((batch[pred_coeff] - batch[gt_coeff])**2) / COEFF_DIM redirect_loss torch.mean((batch[pred_redirect] - batch[gt_redirect])**2) / REDIRECT_DIM id_loss 1.0 - torch.cosine_similarity( batch[pred_redirect][:, :], batch[gt_identity][:, :]).mean() total 1.0 * det_loss 2.0 * coeff_loss 3.0 * redirect_loss 1.5 * id_loss return total注意最后一步pred_redirect的切片范围是完整的身份加表情段。这是有意为之——如果只拿身份段算重定向头会学成“无视表情、只复制目标脸身份”网络退化成身份编码器。4. 训练数据与分阶段训练联合网络翻车率最高的一环4.1 数据怎么凑、怎么对齐多任务联合训练的数据要求比单任务高得多同一张图要同时有检测框标注、3DMM系数标注和重定向真值。公开数据里很难找到三者齐全的常见做法是把几份数据混合起来用。检测标注来自WIDER FACE3DMM系数来自带参数标注的人脸数据集重定向真值来自不同身份、不同表情的配对数据。混合训练要注意采样比例我一般按检测数据25%、3DMM数据50%、重定向配对数据25%的比例采样每批数据只能激活对应的损失头。对齐问题的典型翻车场景是检测框来自WIDER FACE人脸的裁剪方式和3DMM数据集的裁剪习惯不同导致网络看到的人脸尺度不一致。解决方法是统一按检测框放大1.2倍裁剪再缩放到固定尺寸人脸中心偏移量作为检测头的额外回归目标。这一步看着小但没对齐好后面重定向头学到的身份系数会把背景纹理当成人脸特征。4.2 分阶段训练先把底层稳了再开全部头多任务网络直接联合训练容易梯度互相拉扯。更稳的做法是分三阶段阶段训练内容学习率轮数备注第一阶段检测头 主干1e-480不更新3DMM和重定向头第二阶段检测头 3DMM回归头5e-560冻结重定向头主干微调第三阶段全部头联合微调1e-5403DMM回归头学习率减半第一阶段的目的是让FPN学到稳定的人脸特征避免检测任务带着不成熟的3DMM梯度一起崩塌。第二阶段把3DMM回归头接到检测特征上检测错框会导致回归错位所以学习率调低。第三阶段才是完整的端到端训练此时检测和3DMM回归已经基本稳定重定向头的梯度不会像一开始那样剧烈震荡。还有一个数据技巧重定向配对样本里同一张源脸表情要和多个身份配对。这样网络学到的是表情的泛化转移而不是记住某个固定映射。每批里至少保证有一个身份是训练中没见过的否则重定向头会倾向通过身份检索来作弊。5. 避坑记录这个网络我踩过的五个坑按现象到解决写5.1 多人脸场景小脸漏检现象两张及以上人脸时较小那一张经常被漏掉重定向根本没机会执行。原因FPN的P3到P5层下采样倍数太大小脸在P5层特征里只剩2×2像素检测头的anchor匹配不上。这个网络在单人脸或大脸数据上表现尚可一上多人脸密集场景就翻车。解决把检测头锚定到P2层并增加小尺度anchor。同时把RoiAlign的特征分辨率从7×7提到14×14三个头共享。注意这会让显存增大后面有专门讲显存的坑。提示如果用预训练权重务必确认它的anchor尺度配置和你现网一致。之前直接复用基准模型P2层特征没人用过输出全是低置信度噪声框。5.2 表情和身份耦合重定向出来“像A又像B”现象目标身份的表情和源身份的表情同时出现在结果里五官是B的但表情幅度和纹理细节明显偏向A。原因3DMM回归头对身份和表情的系数分布学混了。身份基和表情基虽然正交但网络输出层是全连接没有显式约束两个子空间。PCA理论上正交在参数空间解耦容易在特征空间容易耦合。解决在3DMM回归头的输出层前加一个可学习线性变换矩阵把特征投影到身份和表情两个正交子空间并用正交性惩罚损失监督。简版做法是在损失里加id_coeff exp_coeff.T逼近零矩阵的项。这么做之后耦合现象明显减少重定向表情幅度更干净。5.3 重定向结果在连续帧里抖动现象单帧效果不错视频序列里表情幅度忽大忽小嘴的开合像低频振荡。原因重定向头逐帧独立回归没有时序建模。3DMM坐标拟合本身对噪点敏感检测框轻微抖动就会导致RoI区域变化系数波动被放大。解决偏工程的做法是推理阶段给重定向系数加一阶指数平滑def smooth_coeff(new_coeff, prev_coeff, alpha0.4): # alpha越大跟随越快越小越平滑 return alpha * new_coeff (1 - alpha) * prev_coeffalpha取0.3到0.4比较平衡取太低表情会拖尾、滞后明显。我一般还对人脸框做同样的平滑让检测框抖动和系数抖动同时被压制。5.4 显存不够多人脸加三个头直接OOM现象8张1080Tibatch size设为4FPN加三个头直接OOM。这在多人脸场景尤其常见每张图的人脸数量不定RoI特征显存开销随人数线性增长。原因重定向头本身不吃显存但3DMM回归头如果对每个RoI都过三四个全连接层参数量不小。同时P2层的高分辨率特征把激活值显存推到很高。解决第一是主干换成可分离卷积版本P2层通道从256降到128第二是开启梯度检查点gradient checkpointing以少量计算换显存第三是batch size降到2但不降图像分辨率分辨率对多人脸检测影响大batch size影响可以由累积梯度补偿。5.5 第三个阶段loss不降反升现象分阶段训练前两步loss都正常第三阶段联合微调loss震荡上升。原因要么学习率没按阶段衰减要么梯度冲突集中在检测头和重定向头之间。重定向头的梯度期望主干特征更强调表情相关纹理检测头的梯度期望更强调位置相关边缘两者在FPN的高层特征上直接冲突。解决除了把3DMM回归头学习率减半我对检测头做梯度裁剪梯度范数阈值设为5.0。更稳妥的替代是更新检测头时让重定向头的梯度不反向传播到FPN只更新它自己的几层相当于软冻结共享特征。6. 验证重定向效果自监督一致性检查与一个收尾习惯6.1 用合成数据做自监督验证重定向的真值难以采集但合成数据能闭环验证。做法是用渲染引擎生成同一目标身份在10种表情下的图像再生成一个源身份在同样10种表情下的图像。把源身份表情系数和目标身份中性脸输入重定向头输出应该和目标身份对应的表情系数一致。def verify_redirect(model, render_set, n_id199, n_exp29): errors [] for identity_target, identity_source in render_set: for exp_id in range(10): src_exp identity_source[exp][exp_id] tgt_neutral identity_target[exp][0] pred model.redirect(tgt_neutral src_exp, identity_target[id]) gt identity_target[exp][exp_id] errors.append(torch.cosine_similarity(pred, gt).item()) return sum(errors) / len(errors)这段代码的验证逻辑是重定向后的表情系数应该和目标身份在该表情下的真实系数高度接近。余弦相似度平均在0.93以上算合格低于0.90说明表情信息在重定向链路中丢失优先怀疑分支输出头的特征融合方式。6.2 一致性指标怎么结合渲染验证系数相似度高不代表像素级正确我会额外把重定向后的3DMM系数渲染成网格和真实目标脸图像做轮廓IoU以及嘴部、眼部区域的光流误差。轮廓IoU反映形状贴合度光流误差反映局部形变是否符合物理规律。两者都过合格线才敢说“这个重定向网络真的能用”。从那以后我每次训练完这种联合网络都强制走一遍“系数拆分 → 重定向 → 渲染 → 像素比对”的全流程不只看loss曲线哪怕系数损失已经收敛得很漂亮。因为系数空间差0.01在渲染后可能就是表情完全不同的两张脸。希望帮到你少走我这些弯路。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑