资讯动态

AU面部动作单元识别:基于FACS的表情分析原理与工程实践

发布时间:2026/9/20 13:09:17 来源:尧图企业网站定制
简介以FACS理论为基础AU_Recognition-master 是面向情感计算与计算机视觉研究者的面部表情单元识别工具可对人脸眼部、嘴部等局部区域的肌肉动作单元进行检测与强度分析弥补传统表情识别只能区分喜悦、悲伤等整体情绪的粒度不足。资源共28个文件压缩包仅1.54MB核心为8个Python脚本涵盖AlexNet、ResNet、Inception、VGG等模型实现及数据加载器另含2个.ddd模型文件9张.bmp与4张.png用于展示网络结构、预处理流程和识别效果1个ipynb提供交互式演示并配套Markdown说明、License等文档。项目内容覆盖AU识别完整流程从面部图像预处理、特征提取到动作单元回归预测和结果可视化代码结构紧凑且目录清晰便于快速复现实验也适合在此基础上调整模型或接入自有数据。目前已有540人学习可用于人脸表情分析入门基线搭建也可为中等以上开发者提供FACS相关模型设计参考。 我第一次在 GitHub 上刷到AU_Recognition-master这个仓库时正被一个需求反复折磨公司要做一套摄像头分析系统要求不仅能输出“高兴、难过、生气”这几种大表情还得把“真笑”和“假笑”区分开。普通表情识别模型当场抓瞎——两种表情的输出标签都叫“高兴”可判断依据藏在眼轮匝肌、嘴角这些极细小的肌肉动作里。AU_Recognition这类面部表情单元识别工具就是解决这个问题的它不预测情感标签而是把一张脸拆成一组可量化的动作单元编号告诉你眉毛抬了多少、嘴角动了多少、眼睛周围有没有收缩。这篇文章我会从 FACS 基础讲起把 AU 识别的难点、数据准备、模型设计、完整实操流程和真实部署中容易踩的坑一次说清楚。适合两类人看一类是想复现开源项目做毕设或论文实验的研究者另一类是正在选型情感计算/疲劳监测方案的工程师。1. 为什么AU识别比“猜整脸表情”难一个量级1.1 从FACS说起微笑不是一个标签而是一组编号FACS面部动作编码系统上世纪七十年代由 Paul Ekman 和 Wallace Friesen 提出核心思想很简单不管人类面部表情多复杂都可以分解成有限个“动作单元”简称 AU。每个 AU 对应一组特定肌肉的运动比如AU1眉毛内侧上抬AU2眉毛外侧上抬AU4眉毛下压、皱眉AU6脸颊上升/眼轮匝肌收缩也就是眼周出现笑纹AU12嘴角向外上方拉动AU17下巴上抬看出关键点了吗一个发自内心的“杜兴式微笑”其实是 AU6 AU12 同时激发而礼貌性假笑往往只有 AU12眼周没有动静。普通表情分类把这两者都归为“happy”但 AU 识别会老老实实地把[AU61, AU121]和[AU60, AU121]区分开。这种“原子级”的粒度是 AU 识别最大的价值。1.2 多标签、区域局部、类别不平衡AU任务的三重考验我刚上手时犯过一个错误把 AU 识别当成普通的图像分类任务去做结果训练一堆 epoch 后平均 F1 惨不忍睹。后来才意识到AU 识别和表情分类有三个本质差异。第一它是多标签分类不是多分类。一张脸可以同时出现多个 AU输出是一个[0/1, 0/1, ...]向量而不是一个 softmax 后互斥的类别索引。这意味着分类头、损失函数、评估指标全都不一样。第二AU 具有极强的区域局部性。AU4 的判别信息集中在眉毛和眉心AU12 集中在嘴角周围AU17 集中在下巴附近。如果网络用全局池化把 7×7 的特征图压成一维向量很多局部细节已经丢了。这也是很多论文专门做“区域注意力”的根本原因。第三类别不平衡非常严重。公开数据集中 AU6、AU12 这类动作出现频率高而 AU15、AU17 这类相对少见。直接拿 BCE 损失训练模型会学会“大部分预测为 0”因为这样已经能拿到很高的准确率。这三重考验决定了 AU 识别不能直接套用 ImageNet 分类的那套写法从数据到模型都要专门设计。2. 数据准备AU模型真正“吃”的是什么2.1 常见公开数据集差异与选择AU_Recognition这类开源项目通常会给一个默认数据集选项但跑之前你得先搞清楚各个数据集的脾气。我列个表格方便对比数据集AU数量是否含强度标注可靠性适用场景BP4D12个AU是0-5专家人工标注较可靠通用AU识别训练/评估DISFA8个AU是0-5专家人工标注较可靠强度估计、AU单元研究EmotioNet数百个AU部分自动标注噪声偏大大规模预训练CK离散表情触发AU否专家标注表情到AU的对照实验我第一次做实验直接用 BP4D因为它的视频帧数多、AU 覆盖较全而且标注是帧级别的适合训练图像模型。DISFA 也常见但它只标注 8 个 AU如果想覆盖 AU2、AU17 这类动作数据就不够用。EmotioNet 数据量大但自动标注的噪声会让模型学到不少错误关联我通常只拿它做预训练再用 BP4D 精调。2.2 人脸检测、对齐与多标签标注的组织不管哪个数据集训练前必须先把人脸区域归一化到统一尺度。标准流程是关键点检测68 点或 106 点- 根据眼睛位置对齐到标准模板 - 裁剪出固定大小的人脸 - 归一化像素。这一步做不好后面全白搭。你想想如果人脸在画面里的位置、角度、尺度五花八门模型的一部分能力就要浪费在“找脸”上而不是用来学 AU 特征。对齐之后一个训练样本就变成了“人脸图像 AU 标签向量”。例如某帧人脸同时有 AU1、AU4、AU12那么标签向量对应位置就是 1其余为 0。存成一个 CSV 或 JSON 索引文件每个样本一行配合图像路径训练时按索引读取即可。两个数据划分细节值得注意。一要按被试者subject划分训练/验证/测试集不要让同一个人的帧同时出现在训练集和测试集里否则模型很容易通过记忆“这个人长什么样”来作弊验证指标会虚高。二要谨慎对待水平翻转增强。大多数图像任务里随机翻转是安全且有效的但 AU 是带有方向语义的部分数据集中存在左右不对称的标注一旦翻转AU 编号对应的左右侧就反了。我的习惯是先验证数据集的 AU 定义是否双侧对称不确定的时候宁可不做翻转或者只做小概率翻转。3. 模型结构全局特征之外为什么大家都在做局部注意力3.1 为什么不能把表情识别模型改改就直接用这是新手最容易踩的坑。拿一个训练好的人脸表情分类模型把最后的 softmax 分类头换成 sigmoid 多标签头看似能跑实际效果很一般。原因回到第一节说的区域局部性。普通表情分类模型经过多层卷积和全局平均池化后空间信息已经被压扁了模型只关心“整体上这张脸像不像高兴”。而 AU 识别需要知道“眉间这块区域有没有向下挤压”“嘴角那一小块有没有向外上拉”。全局特征丢失了这些“在哪里”的信息所以直接把分类模型改成多标签输出通常只能达到勉强能看的水平F1 距离论文里的 SOTA 差一大截。3.2 主流方案的演变ROI、热图注意力、图关系建模要解决局部性问题学术界和开源项目里大致有三类方案按实现复杂度递增第一类是ROI 区域裁剪。利用人脸关键点把眉毛、眼睛、嘴角、下巴等区域分别裁剪成 patch每个 patch 单独过特征提取器再拼接或加权融合。实现最简单也最直观既然 AU4 看眉毛那就把眉毛区域给它。第二类是关键点热图注意力。典型代表是 JAA-Net它在网络里同时预测人脸关键点热图并用热图作为注意力权重去调制 AU 特征。相当于让网络自己学会“该往哪里看”比硬裁剪更灵活。AU_Recognition这类项目里如果用了类似结构训练时一般会要求同时提供关键点标注或者预训练的 landmark 检测结果。第三类是图神经网络/Transformer 关系建模。AU 之间不是相互独立的AU6 和 AU12 经常共同出现AU1 和 AU4 同时出现的概率很低。EAC-Net 这类工作把 AU 当作图上的节点用图注意力学习它们之间的共现关系Transformer 方案则把每个局部 patch 当作 token用自注意力挖掘区域之间的关联。效果通常更好但对数据和算力的要求也更高。选型建议很直接如果你是自己做实验先跑通 ROI 裁剪或者热图注意力的版本核心是把“局部特征”这件事做对等 baseline 稳了再上 GNN 或 Transformer 刷点。一上来就怼大模型很容易在数据处理细节上翻车。3.3 损失函数与评估指标调F1而不是调准确率训练时损失函数一般用BCEWithLogitsLoss也就是把 sigmoid 和 BCE 合并在一起计算数值上更稳定。类别不平衡怎么办两个常用手段# 为每个AU设置不同正样本权重 pos_weight torch.tensor([negative_count[i] / positive_count[i] for i in range(num_aus)]) criterion nn.BCEWithLogitsLoss(pos_weightpos_weight)另一个思路是 Focal Loss它对难分类样本给更大梯度权重也能缓解不平衡。实际中我更喜欢先用 pos_weight参数好调、效果稳定。评估指标千万别用“准确率”。想想看10 个 AU 全为 0 的样本占多数模型全预测 0 也能有 80% 准确率看起来很好实际上什么 AU 都没识别出来。正确做法是看每个 AU 的 F1再算平均 F1通常叫 Avg F1有时候也看所有正样本上的总 F1。F1 能同时惩罚漏检和误检对不平衡任务公平得多。4. 跑通AU_Recognition-master的完整实操流程4.1 环境准备与项目结构下载AU_Recognition-master之后先别急着 python train。把依赖装好再说。这个项目通常是 PyTorch 系我建议的环境组合是Python 3.8 或 3.9PyTorch 1.10带 CUDA 11.xopencv-python、numpy、pandas、tqdm、matplotlib人脸检测/关键点库dlib 或 face-alignment部分版本用 MTCNN/RetinaFacegit clone https://github.com/你的地址/AU_Recognition-master.git cd AU_Recognition-master pip install -r requirements.txt项目结构通常长这样data/放数据索引与预处理脚本models/放网络结构定义utils/放评估指标和可视化函数根目录下有train.py、test.py、inference.py。不同人的组织习惯不一样但整体链路大差不差。4.2 从数据预处理到训练一条线这是我最建议你认真看的部分。整个流程可以拆成四步下载并整理数据集。以 BP4D 为例下载后把视频抽帧或者直接用官方提供的帧图生成索引 CSV。每一行至少包含image_path和au_label两列。离线预处理或在线预处理。我的经验是人脸检测和关键点对齐很耗时如果每轮 epoch 都重新做一次训练效率极低。建议先把所有人脸检测、对齐、裁剪好保存成 numpy 文件或图片缓存训练时只读缓存。缺点是占磁盘优点是快。启动训练。典型命令类似python train.py --dataset BP4D --backbone resnet50 --batch-size 32 --epochs 50 --gpu 0训练过程中重点盯两个东西每个 epoch 的验证集平均 F1以及各个 AU 的 F1 是否出现“大部分 AU 不工作”的现象。如果 F1 长期在低位徘徊先查数据对齐结果而不是换网络。 4.保存 checkpoint 与评估。训练完用验证集上 Avg F1 最高的模型跑test.py拿到每个 AU 的 F1、平均 F1 和混淆矩阵方便写报告用。4.3 单张图片和实时视频推理训练完之后的推理链路很清晰检测人脸 - 关键点对齐 - 裁剪 - 模型前向 - sigmoid 得到概率 - 阈值化成 0/1。伪代码如下model.eval() with torch.no_grad(): out model(aligned_face) # shape: [1, num_aus] probs torch.sigmoid(out).cpu().numpy()[0] aus [i 1 for i, p in enumerate(probs) if p thresholds[i]] print(检测到AU, aus)这里的thresholds很重要后面第五节会细说。实时视频推理时不要每帧都重新检测人脸那样 CPU 占用会非常高。视频流里用跟踪器锁定人脸位置每隔几十帧重新检测一次中间的帧直接用上一帧的人脸框做对齐裁剪速度能快很多。5. 部署踩坑记录从数据集下载到真实场景泛化5.1 数据集格式“方言”太多先统一标注再训练BP4D、DISFA、EmotioNet 这三个数据集的 AU 编号并不完全一致有的标 12 个 AU有的标 8 个 AU标签文件格式更是五花八门。我第一次训练时就吃过亏脚本默认读 CSVDISFA 给的是文档行格式解析直接报错。排查链路是这样的先打印第一条数据对照原数据集文档确认字段含义再检查 AU 映射表把数据集自带的编号映射到项目模型统一的 0-based 索引最后做一个简单的数据完整性校验看看正样本数量是否符合预期。建议所有数据集解析完都统一输出成同一种格式比如image_path,au_idx,value的长表或标准多列 CSV后面训练脚本只认这一种格式省心很多。5.2 高F1的模型一到摄像头就“翻车”这是最让我头疼的一个坑。BP4D 验证集上 Avg F1 明明还行一到公司实际摄像头画面里就频繁误报画面稍微暗一点或者人脸侧一点AU4皱眉就开始乱触发。原因仔细想想也很明白BP4D 是实验室场景正面、光照均匀、表情做作实际摄像头画面是自然光照、有侧脸、有遮挡、表情强度也小得多。这是典型的分布偏移。我的处理方式有三招先把所有 AU 的判定阈值在验证集上做网格搜索不要用默认 0.5。类别不平衡时0.5 往往不是最优阈值对正样本稀少的 AU阈值要降到 0.3 甚至 0.2 才合理。再用时序平滑。单帧预测噪声大但 AU 在几百毫秒内是连续的。用一个滑动窗口对概率做均值或者 EMA 平滑比单帧硬判稳得多。最后做数据增强。给训练数据加光照抖动、小角度旋转、部分遮挡random erasing能在一定程度上模拟真实场景。5.3 资源受限下的训练技巧如果只有一张消费级显卡显存装不下大 batch不要直接投降。我常用的三个手段第一是梯度累积代码里用一个小 batch 多次前向把梯度累积起来再更新参数等效于增大 batch size只是训练时间变长。第二是混合精度训练PyTorch 自带 AMP显存占用能少一半速度还更快。第三是降低输入分辨率。AU 识别虽然依赖局部细节但也没必要一开始就用 224×224。先用 160×160 把流程跑通再慢慢加分辨率。这三个手段组合起来一张 8G 显存的卡也能训得动 ResNet50 级别的 AU 模型。6. 从研究到落地AU识别还能用在哪些地方6.1 疲劳驾驶、心理评估与医疗辅筛疲劳驾驶检测是我比较看好的落地场景。通过 AU26/AU27下颌下拉、张嘴和眼部相关动作的持续时长可以判断打哈欠、闭眼这些疲劳信号比单纯用眼睛纵横比判闭眼更鲁棒。在心理评估方向研究里有不少工作把微笑时的 AU6 和 AU12 联合出现作为“真实积极情绪”的指标用于抑郁倾向辅助筛查或者人机交互中的情绪反馈。注意这不是诊断工具而是给专业人员提供一个客观测量参考。工程上做这类系统时一定要先把数据隐私和伦理边界想清楚别把“表情分析”包装成“读心术”。6.2 虚拟形象驱动与表情迁移现在很多 3D 虚拟人、直播数字人需要实时驱动面部表情。AU 作为中间表示很合适先识别用户当前的真实 AU 状态再映射到虚拟形象的表情参数比直接迁移关键点坐标更稳定也不容易产生恐怖谷效应。这套方案在换脸、表情重演类应用里也能看到类似思路。6.3 我的落地建议先从单摄像头正面场景切入我个人踩过不少坑之后的体会是AU 识别很强大但它不是万能的。如果你想在真实产品里用最好先从一个相对理想的环境切入。固定摄像头、人正对镜头一到两米、光线可控这三个条件满足后再做性能调优成功率会高很多。另外在工程上别指望模型每帧输出一次“精确无误”的 AU 集合就万事大吉合理的做法是把 AU 概率当中间特征结合时序规则和业务逻辑一起判断比如连续 N 帧 AU6 持续激活才算一次真实微笑。最后再分享一个小技巧跑通项目之后先自己录一段视频把每一帧的 AU 预测可视化出来你马上就能发现模型在哪种姿势、哪种光照下会失常这比看多少论文里的指标都管用。本文还有配套的精品资源点击获取

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

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

免费获取报价