人脸识别与活体检测

本文系统讲解人脸识别与活体检测,回答 ArcFace 与 CosFace 怎么选、比对阈值怎么定、RGB 与红外活体如何分工等实战问题。覆盖人脸检测与关键点对齐、度量学习与嵌入归一化、误识率与阈值标定、人脸库向量索引、打印与屏幕翻拍等攻击类型、模型偏见与隐私合规,并给出余弦比对与质量评估两段可运行代码。

引言

人脸识别是 CV 里落地最广、也最容易被低估复杂度的任务。把「一张脸」变成「一个 ID」看似一步,实际要经过检测、关键点对齐、质量过滤、特征提取、向量比对、活体判定六道关卡,任何一关失效都会导致误识或拒识。金融与门禁场景还要额外面对「攻击」——照片、屏幕翻拍、面具、深度伪造。

工程上的核心难点有三个。第一是阈值标定:人脸识别的输出是相似度分数,必须把它映射成通过/拒绝的决策,而这个阈值要按业务的误识率(FAR)与拒识率(FRR)诉求来定。第二是活体检测:真实场景的攻击手段不断升级,纯 RGB 方案对高清屏幕翻拍越来越吃力。第三是合规:人脸是敏感个人信息,采集、留存、删除都有硬性约束。

本文按「检测对齐 → 特征嵌入 → 比对与阈值 → 活体 → 偏见与合规 → 部署」的顺序讲,重点给出可直接运行的代码与真实取舍。传统特征匹配的思路可对照 特征提取与图像匹配 ,关键点定位细节见 姿态估计与关键点检测 。

目录

  1. 人脸识别链路与任务边界
  2. 人脸检测与关键点对齐
  3. 度量学习:ArcFace 与 CosFace
  4. 嵌入归一化与比对阈值
  5. 代码:余弦比对与阈值标定
  6. 人脸库检索与向量索引
  7. 活体检测:攻击类型与模态
  8. 代码:图像质量与清晰度评估
  9. 模型偏见与公平性评估
  10. 隐私合规与数据留存
  11. 对抗攻击与深度伪造防御
  12. 端侧与部署
  13. 人脸属性与相关任务

1. 人脸识别链路与任务边界

先把四类任务分清楚,混用会导致方案错配:

任务输入输出典型用途
人脸检测图人脸框与关键点前置环节
人脸验证(1:1)两张脸是否同一人手机解锁、闸机
人脸辨识(1:N)一张脸 + 底库命中 ID 或拒绝门禁、考勤
人脸属性一张脸性别、年龄、表情分析类,合规敏感

1:1 与 1:N 的难度差一个量级:1:1 只需在单对分数上定阈值,1:N 的误识率随底库规模近似线性放大。底库 1 万人时单对 FAR 为 1e-5,整体误识概率就可能到 10%。所以 1:N 必须同时控制底库规模与单对阈值。

2. 人脸检测与关键点对齐

检测器常用 RetinaFace、SCRFD、YOLO-face 系列。它们同时输出人脸框与 5 个关键点(双眼、鼻尖、双嘴角),这 5 点就是后续对齐的依据。

对齐的做法是用相似变换(旋转 + 缩放 + 平移,无剪切)把关键点映射到标准模板位置,再裁出 112×112 的图。这一步至关重要:未经对齐的人脸,同一人不同姿态的嵌入距离会被姿态差异主导,导致比对失败。

import cv2
import numpy as np

# 标准 5 点模板(112x112,ArcFace 常用)
TEMPLATE = np.array([
    [38.2946, 51.6963], [73.5318, 51.5014],   # 左眼、右眼
    [56.0252, 71.7366],                        # 鼻尖
    [41.5493, 92.3655], [70.7299, 92.2041],    # 左嘴角、右嘴角
], dtype=np.float32)

def align_face(img, landmarks, size=112):
    # 用相似变换把关键点对齐到模板
    M, _ = cv2.estimateAffinePartial2D(landmarks.astype(np.float32), TEMPLATE)
    return cv2.warpAffine(img, M, (size, size), flags=cv2.INTER_LINEAR)

print("template ready:", TEMPLATE.shape)

对齐之外还要做质量过滤:模糊、过曝、遮挡、姿态过大的脸应当直接拒绝,而不是硬送进识别网络。质量分低的脸贡献的错误比对远多于其数量占比。

3. 度量学习:ArcFace 与 CosFace

人脸识别不用普通 softmax 分类,而是用度量学习:让同一人的嵌入向量尽量靠近,不同人尽量远离。

Softmax 的局限在于它只要求类间可分,不要求类内紧凑。改进路线是给角度加 margin:

方法核心做法特点
Softmax普通分类判别力弱
SphereFace乘性角度 margin训练不稳,需退火
CosFace余弦上加 margin(cosθ − m)稳定,易收敛
ArcFace角度上加 margin(cos(θ + m))几何解释清晰,最常用
MagFacemargin 随质量自适应兼顾质量感知

ArcFace 的直觉是:在超球面上,把目标类的角度减去一个 margin m(通常 0.5 弧度),迫使网络把类内角度压得更紧。它在大规模人脸基准(如 IJB-C、MegaFace)上长期是强基线。CosFace 与之接近,工程上常按训练稳定性选择。

4. 嵌入归一化与比对阈值

嵌入向量必须做 L2 归一化,比对时用余弦相似度(等价于归一化后的内积)。同一人的余弦相似度通常在 0.5 以上,不同人常在 0.3 以下,中间是灰色地带。

阈值标定要基于业务目标:

  • 高安全场景(支付):把 FAR 压到 1e-6 级别,阈值取高,接受更高的拒识率。
  • 体验优先(相册聚类):阈值取低,容忍少量误聚。
  • 阈值必须用独立的验证集标定,不能用训练集,否则分数分布失真。

评估时画 ROC 与 DET 曲线,重点看目标 FAR 下的 TAR,而不是笼统的准确率。人脸数据极度不平衡(正负对数量差异大),准确率几乎没有信息量。

5. 代码:余弦比对与阈值标定

下面演示归一化嵌入、余弦相似度、以及按目标 FAR 反查阈值。

import numpy as np

def normalize(x):
    return x / (np.linalg.norm(x, axis=-1, keepdims=True) + 1e-10)

def cosine_sim(a, b):
    return float(normalize(a) @ normalize(b))

# 构造正负样本对分数,标定阈值
rng = np.random.RandomState(0)
genuine = rng.normal(0.62, 0.08, 5000)    # 同一人
impostor = rng.normal(0.22, 0.09, 50000)  # 不同人
genuine, impostor = np.clip(genuine, -1, 1), np.clip(impostor, -1, 1)

def threshold_at_far(impostor_scores, target_far=1e-4):
    # 取使得误识率不超过目标的分位点
    return float(np.quantile(impostor_scores, 1 - target_far))

thr = threshold_at_far(impostor, 1e-4)
tar = float((genuine >= thr).mean())
print(f"thr={thr:.3f} TAR@FAR1e-4={tar:.4f}")

真实标定要用模型在验证集上的实际分数,并注意底库规模对 1:N 误识的放大效应:底库 N 张时,整体误识概率约为 1 − (1 − FAR)^N。

6. 人脸库检索与向量索引

1:N 辨识本质是向量近邻检索,与通用检索系统结构一致。

  • 暴力检索:底库几千张时直接用矩阵乘法,简单且无近似误差。
  • FAISS:十万级以上用 IVF 或 HNSW 索引,牺牲少量召回换速度。
  • 分片与预过滤:按性别、年龄段或区域先粗筛,缩小检索范围。
  • 向量量化:用 PQ(乘积量化)把 512 维压缩到几十字节,省内存。
  • 多模态检索:有时要图文互搜,此时嵌入空间与 视觉 Transformer 与多模态模型 中的 CLIP 思路相通。

工程上常见做法是「粗排 + 精排」:先用压缩向量粗排取 Top-100,再用全精度向量精排。这能把检索延迟压到毫秒级,同时保持召回。

7. 活体检测:攻击类型与模态

活体检测(Presentation Attack Detection,PAD)判断眼前是真人还是伪造。攻击手段分三类:

攻击类型例子有效防御
打印攻击纸质照片、打印面具纹理与反光分析、RGB 模型
重放攻击手机/平板屏幕翻拍摩尔纹检测、红外、动作配合
3D 攻击硅胶面具、3D 打印头深度相机、红外热成像
深度伪造换脸视频、注入攻击时序一致性、设备指纹

模态选择直接决定防御上限:

  • RGB 单目:成本最低,靠纹理、摩尔纹、光照一致性。对高清屏幕翻拍越来越吃力。
  • 红外(IR):屏幕在红外下与真人差异明显,是手机人脸解锁的标配。
  • 结构光 / ToF 深度:直接测三维结构,对 2D 攻击几乎免疫,对高仿 3D 面具仍有风险。
  • 动作配合:要求眨眼、转头、张嘴,提升重放攻击成本,但影响体验。
import cv2
import numpy as np

def laplacian_score(gray):
    # 拉普拉斯方差,衡量清晰度;过低说明模糊或翻拍
    return float(cv2.Laplacian(gray, cv2.CV_64F).var())

def moire_score(gray):
    # 频域高频能量占比,屏幕翻拍常有摩尔纹导致异常高频
    f = np.fft.fftshift(np.fft.fft2(gray.astype(np.float32)))
    mag = np.log1p(np.abs(f))
    h, w = mag.shape
    center = mag[h // 4:3 * h // 4, w // 4:3 * w // 4]
    return float((mag.sum() - center.sum()) / (mag.sum() + 1e-6))

gray = cv2.cvtColor(cv2.imread("face.jpg"), cv2.COLOR_BGR2GRAY)
print("sharpness:", round(laplacian_score(gray), 1), "moire:", round(moire_score(gray), 3))

这些是启发式信号,生产系统要用带标注的攻击数据集训练专门的 PAD 模型,并持续用新型攻击样本迭代。

8. 代码:图像质量与清晰度评估

质量评估决定「这张脸值不值得送比对」。质量差的脸不应硬比,而应提示重拍。

import cv2
import numpy as np

def face_quality(img_bgr):
    gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
    sharp = cv2.Laplacian(gray, cv2.CV_64F).var()
    brightness = float(gray.mean())
    contrast = float(gray.std())
    # 简单阈值打分,生产环境应训练质量回归模型
    score = 0.0
    score += 1.0 if sharp > 100 else sharp / 100
    score += 1.0 if 40 < brightness < 220 else 0.5
    score += min(contrast / 60, 1.0)
    return {"sharpness": round(sharp, 1), "brightness": round(brightness, 1),
            "quality": round(score / 3, 3)}

print(face_quality(cv2.imread("face.jpg")))

质量分还有第二个用途:作为 模型评估 的分层维度。把测试集按质量分桶,观察各桶的 TAR,就能知道模型是「在困难样本上差」还是「普遍差」。

9. 模型偏见与公平性评估

人脸模型在跨性别、跨肤色、跨年龄段上的表现差异是已知问题,且会带来法律风险。

  • 数据偏差:训练集某群体样本少,该群体误识率显著更高。
  • 评估缺失:只报整体指标,掩盖了分群体差异。
  • 缓解手段:按群体重采样、损失加权、以及在部署前做分群体评估。

工程上应固定输出一张「分群体指标表」,至少覆盖性别、年龄段、肤色深浅三个维度,并监控线上各群体的拒识率差异。监管机构对这类差异越来越敏感,把它纳入验收标准是必要动作。

10. 隐私合规与数据留存

人脸信息属于敏感个人信息,工程上要落实几条硬约束:

  • 最小必要:只采集业务必需的数据,不做无授权的后台留存。
  • 本地优先:能在端侧完成比对就不要上传原图,只上传特征或直接本地决策。
  • 特征不可逆:存储人脸特征而非原图,并声明特征的不可逆性。
  • 加密与访问控制:特征库加密存储,访问留审计日志。
  • 留存期限与删除:明确留存期,支持用户撤回与删除。
  • 联邦与隐私计算:跨机构协作时可用联邦学习或安全多方计算,思路与 联邦学习与隐私保护 一致。

合规不是事后补文档,而是要在系统设计阶段就确定「数据在哪里、留多久、谁能访问」。

11. 对抗攻击与深度伪造防御

除了物理攻击,还有针对模型本身的对抗手段:

  • 对抗样本:在图像上加微小扰动,让模型把 A 认成 B。防御靠对抗训练与输入净化。
  • 换脸注入:在视频链路上把他人脸部实时替换,绕过活体。防御靠时序一致性检测与设备指纹。
  • 人脸替换检测:用专门的 Deepfake 检测模型,关注频域伪影与眨眼节律。
  • 端到端加固:把活体与识别联合训练,避免两个独立模型各自被绕过。

实际部署中,攻击者往往选择最弱的一环。因此活体、识别、风控规则要作为一个整体评估,而不是各自达标就认为安全。

12. 端侧与部署

人脸场景大量在端侧(门禁机、手机、闸机),约束与云端不同。

  • 模型选择:MobileFaceNet、SCRFD-500M 这类轻量模型,参数量几 M,ARM CPU 可跑实时。
  • 量化:INT8 量化后精度掉点通常在 1% 以内,速度提升 2 到 3 倍。
  • 推理引擎:NCNN、MNN、TFLite 对端侧算子支持好,注意注意力算子的兼容性。
  • 安全:端侧模型容易被提取,必要时用模型加密与安全执行环境。
  • 缓存:把注册用户的特征缓存在本地,避免每次联网比对。

端侧与云端的分工建议:检测、对齐、活体在端侧完成,特征比对视底库规模决定本地还是上云。压缩与量化手段可参考 模型压缩与端侧部署 。

13. 人脸属性与相关任务

除身份识别外,人脸还有一组属性任务:年龄估计、性别分类、表情识别、视线估计、头部姿态。它们与身份识别共享检测与对齐前端,但训练目标不同。

  • 属性任务对偏见更敏感,年龄与性别估计在跨群体上的偏差常被报道。
  • 表情与视线对数据标注质量要求高,主观性强。
  • 合规上,属性推断在很多地区受严格限制,需评估法律风险后再做。

工程建议是把属性任务与人脸识别解耦:识别链路追求稳定与合规,属性能力按需单独评估与灰度。

权衡取舍

维度倾向代价
阈值高与低高安全取高阈值拒识率上升,体验下降
RGB 与红外活体高安全用红外或深度硬件成本与体积上升
端侧与云端比对小底库端侧,大底库云端云端有网络与合规成本
大模型与轻量模型精度优先用大模型端侧延迟与功耗上升
严格质量门槛与宽松严格提升准确率需要用户重拍,体验差
属性功能与纯识别纯识别合规风险低放弃部分分析价值

几条实用原则:

  • 先定业务能接受的 FAR,再反推阈值,不要反过来。
  • 1:N 场景必须评估底库规模对误识率的放大效应。
  • 活体与识别联合评估,单独达标不代表系统安全。
  • 端侧能做的尽量端侧做,既降延迟又降合规风险。

常见坑清单

  • 不做对齐直接比对:姿态差异主导距离,同人分数低于阈值。
  • 嵌入未归一化:余弦退化成点积,排序与阈值全乱。
  • 阈值用训练集标定:分数分布乐观,线上误识率远超预期。
  • 忽略底库规模:1:N 的误识率随底库增长,1 万人底库按 1:1 阈值配就是事故。
  • 活体只看 RGB:高清屏幕翻拍轻松绕过,需红外或深度配合。
  • 不做质量过滤:模糊与遮挡脸污染底库,后续比对持续出错。
  • 混淆检测与识别指标:检测 mAP 高不代表识别可用。
  • 忽略分群体评估:整体达标但某群体拒识率翻倍,引发合规问题。
  • 留存原始人脸图:违反最小必要原则,应只存特征并加密。
  • 对抗样本无防护:公开模型容易被针对性攻击,需对抗训练与风控。
  • 端侧模型未加固:模型被提取后攻击者可离线构造绕过样本。
  • 忘记 1:N 的候选排序:只取最高分不做二次校验,误识直接放行。

小结

人脸识别工程的主线是:检测对齐 → 质量过滤 → 度量学习嵌入 → L2 归一化 → 余弦比对 → 按目标 FAR 标定阈值 → 活体判定 → 分群体评估与合规审查。记住三个判断点:对齐决定上限、阈值必须按业务 FAR 反推、1:N 的误识率随底库放大。

活体检测要与识别联合设计,端侧优先完成检测与活体以降低合规风险。属性类任务单独评估,注意法律边界。当底库规模增长时,检索与索引优化会成为新的瓶颈,届时应转向向量检索与粗排精排的组合方案。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「计算机视觉」更多文章

  1. 检测与分割的评估指标
  2. 多模态视觉语言模型
  3. 3D 视觉:点云与深度估计