ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多任务人脸分析:疲劳、表情与年龄性别预测的实战指南

多任务人脸分析:疲劳、表情与年龄性别预测的实战指南

把疲劳检测、表情识别、年龄性别预测塞进同一个摄像头里,这句话听起来像是产品经理随口提的愿景,但真正落过地的人知道,它背后是三个模型、三份推理延迟、三套维护成本,以及一个永远对不齐的输出节奏。我前前后后做过几个人脸智能分析系统,从司乘疲劳预警到门店客群分析,最后得到的结论是:这三个任务不该被拆开做,但也绝不能简单焊在一起。这篇文章想把我在选型、训练、部署和踩坑过程中积累下来的东西讲清楚,希望能帮到正在做人脸视觉应用、或者想从跑通开源Demo迈向能上线系统的开发者。

先说清楚这套系统是什么:输入一路摄像头视频流,输出画面中每个人的疲劳状态、表情分类、年龄段和性别。三个任务共享同一个人脸检测器和特征提取主干,各自接一个轻量输出头,最终在同等硬件上能跑到实时甚至准实时。适合谁看?如果你手里已经有一个能跑通的人脸检测模型,想往上叠加更多属性分析,或者你正在评估多任务人脸分析方案到底划算不划算,这篇文章应该能给你省一周的调研时间。

1. 为什么要把三个任务做进同一套推理管线

1.1 场景需求反推系统设计

很多人一开始会这样想:疲劳检测是一个模型,表情识别是一个模型,年龄性别再是一个模型,反正现在框架这么成熟,三个模型串起来跑不就行了。

表面上确实可以,但你看实际业务场景就知道问题在哪。以常见的司机状态预警为例,摄像头装在驾驶室A柱附近,光线从正午烈日到凌晨隧道全程都能遇到,驾驶员还可能戴眼镜、戴口罩、偶尔扭头看后视镜。这时候如果只是把三个模型顺序执行,每一帧都要先做人脸检测,再分别跑三个模型。哪怕每个模型只花20毫秒,算上检测和预处理,单帧总耗时很容易超过120毫秒甚至更多。在连续视频流里意味着实际帧率不到10,疲劳判定的时序信息已经被拖垮了。

1.2 三个模型各自为战的问题

拆成三个模型部署,问题还远不止速度。

首先是工程复杂度。每个模型都有自己的预处理逻辑:有的要112x112,有的要224x224,有的要归一化到[-1,1],有的要保持RGB顺序,三套数据流同时存在,代码里到处都是分支判断。其次是内存占用。三个模型放在GPU里,显存至少多吃几百兆;放在边缘设备上,更是分分钟爆掉。最后是结果同步问题。三个模型独立跑,各自检测到的人脸框位置还不一定完全一致,你想把某一帧的疲劳结果和表情结果关联到同一个人脸上,先得多写一段人脸对齐的逻辑。

1.3 多任务不是银弹,要看清收益边界

但反过来说,这三个任务有一个天然优势:它们吃的都是同一张对齐后的人脸图像。检测是共享的,特征提取是共享的,只是最后输出头不同。这种结构带来的直接好处是,一次特征前向就能同时产出三组结果,速度变成加法而不是乘法。而且多任务训练本身有正则化效果,年龄和性别预测会迫使骨干网络学到更丰富的人脸结构特征,表情识别也能从中受益,这在样本量不大的时候尤其明显。

当然,你也要清醒一点:多任务共享只在任务输入高度一致的时候划算。如果你硬要把车型识别、车牌识别这种完全不同的输入塞进来,共享特征层反而会打架,性能两头都掉。所以这个思路适用于人脸这个域内的多个任务,不要无脑扩张。

2. 检测器、骨干网与推理框架的选型逻辑

2.1 人脸检测器:速度与小人脸精度的平衡

这一层是整个系统的地基。检测器选不好,后面的疲劳、表情、年龄全都在摇晃。

早期项目里我试过MTCNN,部署方便、速度快,但它的定位精度在侧脸、低头、逆光场景下明显不够,回头你提取眼周关键点时点位飘得很厉害。后来换到RetinaFace,精度确实好,但对边缘设备来说还是偏重。最近几个项目我比较倾向SCRFD这类轻量级高精度检测器,它把重参数化和解耦头玩得比较明白,在CPU上的推理时间能做到和MTCNN一个量级,精度却高出一截。如果你用的训练框架是PyTorch,导出ONNX很顺滑,部署也不费劲。

我有一个建议送给新人:别一上来就追检测器的极致精度。对这套系统来说,只要检测框不整体跑偏到背景上,后续关键点和分类头的修正空间都很大。真正要命的是检测器在低分辨率下丢人脸,一丢就是整帧全丢。

2.2 关键点数量与任务匹配

做疲劳检测不一定需要很多关键点。

很多人看到MediaPipe的468点就会觉得越多越精确,但在遮挡场景下,点数越多越容易有某个点突然飞掉,给你的眼周横纵比计算带来一个莫名其妙的尖峰值。我现在做疲劳检测基本只用68点方案里眼周6个点、嘴周几个点就够了,轻量又稳定。表情、年龄、性别任务也一样,它们关心的是整张脸的纹理和结构,对齐阶段用5点关键点做仿射变换到标准位置即可,不需要高密度的点云。

这里的取舍逻辑一句话:关键点数量应该由任务精度和稳定性共同决定,而不是越多越好。你在模型选型时如果看到两个关键点模型精度差不多,优先选点数少、速度快、对遮挡鲁棒的。

2.3 骨干网络与三个轻量输出头的设计

骨干网络我建议从MobileNetV3或GhostNet系列里选。做实时人脸属性分析,不需要上ResNet50这种级别的网络。MobileNetV3在ImageNet上的精度可能不算顶尖,但提炼人脸特征给下游三个头用完全够。

头的设计要留意一个细节:三个任务不要共用同一个全连接层输出,各用各的独立小网络。我在项目里是这样分的:在骨干网络最后一层特征图之后,分别接三个并行的分支。疲劳分支只取人脸中心区域(眼部和嘴部)的特征,表情分支取全局特征,年龄性别分支也取全局特征。这样既共享了底层计算,又保留了各任务关注区域的不同。

2.4 推理框架选型参考

推理框架决定了同一份模型在不同硬件上跑多快。我做过的几个系统里:

  • 纯CPU部署,且目标是Intel平台:首选OpenVINO,对自家CPU的图优化做得最狠。
  • 公司内已有NVIDIA GPU,或者要跑在工控机加显卡的环境:TensorRT,FP16精度下速度能再翻一翻。
  • 要跨Windows、Linux、ARM多平台:ONNX Runtime最省心,生态全,踩坑最少。

调试阶段大家通常都用PyTorch跑,但真上线我一般导出ONNX,再做平台适配。ONNX不仅仅是格式转换,你还可以在中间插入一些图优化,比如把BatchNorm折叠进卷积、去掉冗余的Transpose。这些优化看着不起眼,在边缘设备上能省出20%到30%的时间。

3. 疲劳检测的核心算法与报警状态机设计

3.1 眼睛纵横比:最经典的闭眼信号

疲劳检测里最经典、也最稳的一个特征就是眼睛纵横比(Eye Aspect Ratio, EAR)。它的思路非常直白:睁眼时眼睛高度和宽度的比值稳定,闭眼时比值急剧下降。

用68点关键点的话,眼睛区域取6个点。EAR公式是:

[ EAR = \frac{|p_2-p_6| + |p_3-p_5|}{2 |p_1-p_4|} ]

其中p1和p4是眼角的两个点,p2到p6依次是上下眼睑的轮廓点。左右眼各算一份,取平均就行。

我实际项目里的代码长这样:

import numpy as np from scipy.spatial import distance def eye_aspect_ratio(eye_landmarks): # eye_landmarks: 6个关键点坐标 vertical_1 = distance.euclidean(eye_landmarks[1], eye_landmarks[5]) vertical_2 = distance.euclidean(eye_landmarks[2], eye_landmarks[4]) horizontal = distance.euclidean(eye_landmarks[0], eye_landmarks[3]) ear = (vertical_1 + vertical_2) / (2.0 * horizontal) return ear

正常情况下EAR大概在0.25到0.3之间晃,闭眼时会掉到0.2以下,甚至更低。但这里有个新手必踩的坑:阈值不是固定的。摄像头安装高度、人脸距离、镜头畸变都会改变EAR的静态分布。我后来采取的做法是,初始化阶段取前100帧眼睛睁开时的EAR平均值作为基准,再在这个基准上乘一个0.6到0.7的系数作为当前环境的闭眼阈值。这样换摄像头、换安装位置都不用重新调参。

3.2 PERCLOU标准:别只看单帧闭眼

只看单帧EAR有一个问题:正常人每分钟眨眼十几次,每次眨眼持续100毫秒到200毫秒。如果你把单帧EAR小于阈值当成疲劳信号,那每隔几秒就会触发一次假报警,整个系统根本没法用。

所以要用PERCLOS,也就是单位时间内眼睛闭合时间所占的百分比。疲劳检测领域常用的E70或E80标准,核心思想是记录眼皮遮住瞳孔超过70%或80%的事件,统计它们在单位时间内的累计时间占比,再和经验阈值比较。

具体实现上,我会维护一个固定时间窗口,比如60秒,窗口内的每帧都算出EAR,标记为闭眼或睁眼,累积闭眼帧数除以总帧数就是PERCLOS值。疲劳阈值通常定在0.4到0.5,意思是60秒里有超过40%的时间处于闭眼或半闭眼状态,基本可以判定为疲劳。

# 简化的PERCLOS窗口统计 ear_history = deque(maxlen=150) # 假设30FPS下5秒窗口 ear_threshold = adaptive_threshold() # 前面提到的自适应阈值 for frame_ear in video_stream: ear_history.append(frame_ear) close_frames = sum(1 for v in ear_history if v < ear_threshold) perclos = close_frames / len(ear_history) if perclos > 0.4: trigger_fatigue_alert("疲劳")

3.3 哈欠与点头:两个重要的补充特征

单纯靠眼睛判断疲劳,在强光下司机戴墨镜、或者低头角度大导致眼部特征被遮挡时就会失效,所以要加两个补充特征。

第一个是嘴部纵横比(Mouth Aspect Ratio, MAR)。哈欠的典型特征是嘴部高度突然增大且持续至少1到2秒。计算方式和EAR类似,取嘴周几个关键点的纵横比。需要注意区分哈欠和说话,说话时嘴部纵横比变化频率快、波形密集,哈欠则是单一宽脉冲。我在工程上会加一个持续时间约束,只有嘴部持续张开超过0.5秒才可能算哈欠。

第二个是头部姿态估计。通过人脸关键点配合solvePnP算法可以估算出头部的俯仰角、偏航角和翻滚角。疲劳时驾驶员点头动作会呈现出周期性的低头和恢复,表现为俯仰角反复小幅波动。但这里有个坑:驾驶员看手机、捡东西时也会低头。所以单纯低头不算,要看短时间内的重复频率。我之前定的经验值是,连续5分钟内低头次数超过8次且伴随其他疲劳信号才上报。

3.4 状态机与防抖:把单帧信号变成稳定事件

把上面的特征都算出来之后,不要直接根据数值触发报警。真实场景里每一帧的特征值都会有抖动,单帧误判的比例不低。正确做法是引入状态机,让系统从离散指标中提取出稳定的事件。

我的状态机定义大致是这样:正常状态、预警状态、确认疲劳状态、恢复状态。在正常状态下,某一帧出现EAR低于阈值、或者嘴部张开幅度较大,只是进入预警状态,并不触发报警。只有在预警状态下连续若干帧(比如0.5秒以上)持续满足疲劳特征,才切换到确认疲劳状态并触发报警。报警后需要一个冷却时间,比如2分钟内不重复报警,避免同一个疲劳事件被反复刷屏。

这种状态机设计的好处是:疲劳信号天然是持续性的,单帧抖动被窗口过滤掉,同时报警输出也规范了很多。你接到的是一个一个的离散事件,而不是一条连续的触发流,对接下游短信、语音提醒、后台看板都方便。

4. 表情识别:从分类标签到现场置信度的落地思路

4.1 数据集选择与类别不均衡

表情识别最常见的做法是分成7类:生气、厌恶、害怕、开心、伤心、惊讶、中性。这个体系主要继承自FER2013数据集。

FER2013最大的问题是标注噪声大,图像质量也参差不齐,拿它训练出来的模型在实验室数据集上还行,一到真实摄像头就原形毕露。我后续项目里更偏好用RAF-DB或者AffectNet。RAF-DB是真实场景的标注,带有人脸框和质量标签,比较贴近实际部署环境。AffectNet数据量大,但要自己处理类别不均衡,其中"中性"和"开心"占了绝大多数,"厌恶""害怕"等类别样本非常少。

类别不均衡会直接导致模型在推理时把大多数样本都预测成高频类别。我处理的方案有两个:一是在采样时按类别权重过采样,让低频类别也能被充分看到;二是在损失函数里给每个类别加权重,misclassified的惩罚和样本数量成反比。如果你觉得麻烦,至少把权重加上,效果立竿见影。

4.2 训练细节:标签平滑与数据增强

自然表情在类别边界上是模糊的,比如"惊讶"和"害怕"在表情肌运动上就有重叠。如果训练时用硬标签强逼模型输出0或1,模型的置信度会变得非常极端,稍有扰动就大幅反转。

我习惯在训练时做标签平滑,把真实标签从1改成0.9,剩余0.1平均分给其他类别。这个操作代价最小,但能让模型输出概率分布更平滑,推理时抗干扰能力强不少。

数据增强方面也有讲究。常见做法是随机裁剪、水平翻转、颜色抖动、高斯模糊。针对真实场景,我会额外加一部分随机遮挡增强,模拟口罩、刘海、帽子对脸部的遮挡。还有一个小技巧:在训练样本里模拟摄像头常见的偏色和低照度。真实摄像头和开源数据集的色彩分布差异很大,提前在训练期把这种差异打进去,能显著减少部署时的掉点。

4.3 部署形态:单帧分类必须要做时序平滑

表情识别在部署时的最大陷阱是:单帧模型太敏感。人在自然对话过程中表情本来就是连续变化的,每一帧的表情类别跳来跳去,如果直接把这个结果送给业务方,他们会觉得这个系统完全不可用。

解决方案就是做时间上的平滑。我在表情分支的输出层之前加了一个简单的时序缓冲:维护最近5帧的表情概率向量,逐类别取平均,再取最大值对应的表情作为当前结果。这一个小小的改动,能让表情分类变得稳定非常多。需要注意的代价是响应延迟变大了,大约会滞后3到5帧,但对于绝大多数业务场景完全可接受。

另外还要说清楚一个认知边界:表情识别结果不等于情绪状态。模型输出的是"这张脸上最接近哪一类表情",而不是"这个人心里在想什么"。做司乘系统或者门店服务评估时,这个区分一定要提前跟业务方掰扯清楚,否则后面验收时会被反复挑战。

4.4 现场表现为例:为什么中性和开心占大多数

我去过一个做门店客流分析的项目,业务方一开始希望模型能识别顾客的"满意度"。结果现场采集的数据统计出来,大部分人脸在绝大多数时间都是中性和开心状态,生气、厌恶这些类别的出现频率极低。这其实才是真实分布,不是模型坏了。

这种情况下,光看表情分类对业务没有太大价值。我后来把策略调整成:只统计特定表情事件,比如"微笑持续时间占比""皱眉出现频率",用这些事件化的指标来评估服务质量。这个思路同样适用于你的项目——表情识别的产出最好是事件和趋势,而不是单个标签值。

5. 年龄与性别预测:序数回归、标签噪声与实测干扰

5.1 年龄为什么不能直接当回归任务做

年龄预测最直观的建模方式就是回归:输入人脸图像,输出一个年龄值,用L1或者L2损失去拟合。听起来很合理,但实际训练出来的模型经常会出现一个现象:对于同年龄段的人脸,预测值会疯狂波动,30岁的人有时候能被预测成20,有时候被预测成40。

原因在于相邻年龄的人脸特征差异非常小,而回归损失把每一个年龄差异都当成同等的误差在惩罚,模型根本分不清19岁和20岁的边界在哪里。相比之下,把年龄当成分类问题来建模反而更稳:输出一个向量,每个位点代表一个年龄的概率,最后取期望值或者最大概率年龄。工程上更推荐把年龄划分成几个区间,比如常见的儿童、少年、青年、中年、老年几档,因为业务方关心的一般是群体画像而不是某一个人的精确年龄。

5.2 标签平滑怎么做才算对

年龄分类的标签平滑要特别注意"标签顺序"的关系。FaceID相关的论文里常用一个做法:把真实年龄附近的年龄概率也设成正数,离真实年龄越远,概率越低。我用过一个很简单的实现,给真实年龄前后各一个标签分配少量概率,比如真实年龄25岁时,标签设定为24、25、26分别对应0.1、0.8、0.1的概率。这样模型预测25岁和26岁之间的边界不会变得太绝对,而且相邻年龄的预测分布既平滑又合理。

年龄段分类相对简单,直接在区间粒度上做。可以参考公开的年龄段体系,把0到100岁粗略分成8到12个区间。隐藏的技巧是,训练时的软标签也要按区间跨度的距离来衰减,这样模型能学会"50岁的人和40岁的人之间出了错相对可以谅解"这个逻辑。

5.3 性别与年龄联合训练的好处

性别和年龄不是完全独立的,同一个年龄段的男性和女性衰老特征曲线完全不同。这也是多任务系统的一个隐藏加分项:共享特征提取层让年龄分支在推理时可以隐式利用性别信息,性别分支也能利用年龄信息。

我在训练时会让性别分支和年龄分支共享骨干网络的浅层和中层,只在最后两个分支各自用少量卷积层+全连接层独立预测。损失函数按比例加权,性别损失和年龄损失的权重初始设为1:1,训练到中间阶段再适当降低年龄权重,避免年龄损失主导整个模型。

5.4 实测干扰与掉点应对

年龄性别预测在真实场景里最容易被以下几样东西干扰:妆感会显著影响年龄判断,大概率会把女性年龄往小里判;墨镜和口罩会拿掉脸部大量判别性特征,性别分支在只剩半张脸时表现退化;大幅度侧脸和低头会明显加大误差;不同人种和不同光照下的年龄感知偏差也一直存在。

项目里如果业务方要求必须在戴口罩场景下做年龄性别预测,我一般给出的建议是:把预期指标主动下调。遮挡下人脸本身的信息量就是不够的,任何模型都无法凭空补出被遮住的信息。你宁可把置信度阈值调高、输出"无法判断"的比例高一些,也不要用一个低置信度结果去污染统计报表。

6. 工程整合:跳帧检测、异步推理与性能优化

6.1 单线程串行处理的死结

把检测、对齐、三个分支推理、跟踪、报警全部写在同一个循环里的系统,我见过太多。这种写法在演示Demo时没问题,一旦进入真实部署,任何一步慢下来都会拖垮整条链路。

问题出在串行依赖上:如果这一帧的检测耗时突然增加,那么后续的疲劳判定、表情输出和报警全都会跟着延迟。视频流还讲究时序一致性,延迟高到一定程度,疲劳时长统计就失真了。

我的建议是把整个处理流程拆成独立线程:采集线程只管收帧和放时间戳,检测线程负责低频率的人脸检测,属性分析线程从检测结果里取人脸框做对齐和推理,最后状态机线程消费属性结果输出事件。线程之间用带时间戳的队列传递,避免互相等待。

6.2 跳帧检测与追踪ID绑定

人脸检测在整条链路里占用的耗时最大。一个常见优化是跳帧检测:不是每一帧都做人脸检测,而是每3帧做一次,中间的帧用跟踪算法来延续人脸位置。跟踪用简单的IoU关联加Kalman滤波就够,如果场景里人特别多、经常交叉,可以上ByteTrack这类 MOT 扩展。

但跳帧检测有一个副作用:新进入画面的人可能要被等一两帧才会被发现。对疲劳报警这种场景问题不大,对需要极速响应的场景就要权衡。我一般会根据实际帧率动态调整跳帧频率:端到端帧率高于20时跳3帧,低于12时跳1帧甚至不跳。

多人场景下,人脸框必须和跟踪ID绑定。否则一旦检测器漏检了一帧,重新检测出来的人脸如果匹配错了ID,疲劳状态和表情状态就会张冠李戴,报警和报表都可能出错。这个细节在单目标Demo里根本体现不出来,但在一路摄像头下坐着四五个人的客车上,你会庆幸提前做了ID绑定。

6.3 模型导出与量化:FP16和INT8怎么选

部署阶段我基本不用PyTorch原生推理,统一导出ONNX。导出时最需要注意的是动态轴设置,否则后面一换输入分辨率或者batch size,模型就报错。

量化方面,我的经验是:FP16对精度影响极小,能开就开;INT8省得更多,但它对网络每层的敏感度不一样。在整套系统里我观察到,人脸检测和年龄性别分支做INT8量化之后,掉点在可接受范围内,但疲劳检测涉及的关键点精度对量化非常敏感,眼周坐标稍微偏移1到2个像素,EAR值就可能产生质变。所以最终方案里,检测器和分类头走FP16,关键点相关的部分宁可保留FP32,或者单独做一个高精度分支。

6.4 一组实测性能参考

下面是我在一套模拟项目X的参考数据,硬件配置分别是一台普通办公笔记本CPU和一块入门级GPU。跑的是640x480输入,检测跳3帧,属性分析每帧跑:

部署环境端到端FPS单帧p95延迟CPU/GPU占用备注
CPU-only + OpenVINO14280msCPU约60%属性分支用FP16
CPU-only + ONNX Runtime10340msCPU约55%交叉平台测试用
入门级GPU + TensorRT3855msGPU显存约1.2GB检测与属性均FP16

数据只是一份基准参考,不同硬件差异很大。但你可以看到趋势:同一份模型,不同推理后端和硬件组合下能差到3到4倍。上线前一定要在你实际要用的机器上测一遍,不要信别人报的任何数字。

7. 上线前绕不开的五个实测坑

7.1 报警反复横跳

第一个上线系统时,我们报警逻辑做得太粗暴,某一帧EAR低于阈值就拉响警报。结果测试员只是正常眨眼,系统就不停报警。后来加了状态机和窗口统计,情况好了很多,但要提醒你:窗口长度也不是越大越好。窗口太长,真实的疲劳事件会被淹没在正常状态里,报不出来。我之前调过的参数是闭眼持续0.5秒以上算一次预疲劳,配合冷却时间2分钟。

7.2 帧率不稳对疲劳判定是致命的

疲劳检测的所有时序指标都严重依赖稳定帧率。如果系统在CPU负载高的时候掉到8帧,PERCLOS的统计窗口就会拉伸,闭眼时长也会被压缩,原本算好的报警阈值全部失效。

解决办法有二:一是尽可能稳定帧率,宁可统一抽帧到10FPS做疲劳分析,也不要让它忽快忽慢;二是所有判定都要基于时间戳而不是帧数。例如判定"闭眼持续0.4秒",应该记录这一帧的时间戳,用时间差来判断,而不是依赖"连续12帧闭眼"。时间戳方案天然抵抗帧率波动。

7.3 眼镜、墨镜与反光

戴眼镜的人,镜片反光经常会让眼周关键点在几个像素范围内来回横跳。单纯的EAR值偶尔会出现一个巨大尖峰,帧率和状态机都可能被误导。

处理手段我排过优先级:优先用关键点置信度过滤,当眼周点置信度低于阈值时,这一帧的EAR不参与统计;再不行就上红外摄像头,红外下眼镜反光和可见光完全不同,也是一个靠谱的后备方案。单纯加大图像平滑不能根治,因为反光带来的跳动既随机又剧烈。

7.4 多人同框时的ID绑定问题

单摄像头多人的场景比想象中常见,网约车后排、门店闸机、教学场景,全都是多人同框。如果检测和跟踪做得不够稳,同一个人脸上的属性标签会频繁串号。

我的做法是用一个轻量跟踪器维护每个人脸的最近N帧位置和外观特征,每帧检测结果先和现有轨迹做IoU匹配,匹配不上的再按外观相似度匹配,仍然匹配不上才创建新轨迹。这一套逻辑在普通CPU上额外耗时不超过2毫秒,但对整体稳定性的提升是质的。

7.5 开源测试集和现场数据的差距

最后说一个所有做视觉落地的开发者都躲不开的问题:模型在FER2013、Adience这类公开测试集上可能已经很漂亮了,但一放到现场摄像头底下,准确率能跌十个点以上。

差距来源主要就是数据域迁移:你的是低照度、广角畸变、运动模糊的摄像头画面,数据集里是网络爬来的正脸图。我的体会是,项目上线前一定要预留一到两周做现场数据采集,哪怕只采几百张,用半自动方式做标注,然后用这些数据对模型做微调,效果都会好非常多。这一步没有捷径,越多现场数据,模型越不容易翻车。

7.6 顺带说一句:别忽略摄像头安装角度的细节

这个看起来最简单,却经常被排在最后才处理。摄像头装在头顶俯拍和装在正前方平视,模型看到的完全是两个世界。头部姿态估算、关键点坐标、表情幅度全都会变。每次改动摄像头安装位置,都要重新采集一批数据验证,不要认为模型会自动适应。你宁可多花半天调整安装角度,让正脸占比尽量大,后面的算法会省心很多。

这套系统做到这个阶段,留给我的一个深刻体会是:多任务人脸分析真正难的不是某个模型的精度,而是从单模型到多任务系统的组织和稳定性。疲劳、表情、年龄性别,三者共用一条管线,牵一发而动全身。如果你也想做类似项目,我的建议是从最小闭环开始:先串通检测加一个任务,跑通状态机和报警链路,再逐步把表情、年龄性别分支叠进去。每一步的小改动都要回到真实场景里验证,这个习惯替我省掉了无数次返工。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进