4. 人脸特征提取:从传统方法到深度学习的演进
说到人脸特征提取,我刚开始做这个方向时,用的还是传统方法。那时候深度学习还没火起来,大家靠手工设计的特征吃饭。说白了,就是想办法把一张人脸图片,变成一串能代表“这个人是谁”的数字。
这串数字,就是我们今天要聊的核心——Embedding向量。
核心概念:Embedding向量是人脸在特征空间中的“坐标”。好的特征提取方法,能让同一个人的坐标靠得近,不同人的坐标离得远。
4.1 传统方法:LBP与HOG
先聊聊传统方法。我2014年做安防项目时,用的就是LBP。那时候觉得这方法真巧妙——用局部纹理模式来描述人脸。
4.1.1 LBP(局部二值模式)
LBP的原理其实很直观:
- 对每个像素,比较它和周围8个邻居的灰度值
- 比它大的记1,比它小的记0
- 得到一个8位二进制数,转成十进制就是LBP值
举个例子:
原始灰度值: 120 130 90
110 100 80
95 105 85
比较结果: 1 1 0
1 0 0
0 1 0
二进制序列:11010010 → 十进制:210
嗯,这里要注意。LBP对光照变化比较鲁棒,但有个致命问题——它只描述纹理,不描述形状。我遇到过这样的情况:两个人脸纹理相似但五官不同,LBP就分不清了。
我的经验:LBP适合做人脸检测的粗筛,但不适合做精确识别。我在项目中通常用它做预处理,把明显不是目标的人脸快速过滤掉。
4.1.2 HOG(方向梯度直方图)
HOG的思路完全不同。它统计图像局部区域的梯度方向分布。说白了,就是看人脸轮廓的“走向”。
HOG的提取步骤:
- 计算梯度:用Sobel算子算每个像素的水平和垂直梯度
- 划分细胞单元:把图像分成8×8的小块
- 统计方向直方图:每个细胞单元统计9个方向的梯度强度
- 块归一化:把2×2的细胞单元组成一个块,做局部归一化
- 串联特征:把所有块的特征向量拼起来
一张64×128的人脸图,HOG特征维度大约是3780维。你想想看,这么高的维度,计算量可不小。
避坑指南:我曾经在嵌入式设备上跑HOG,发现帧率只有5fps。后来把图像缩放到32×64,特征维度降到756维,帧率才提到15fps。传统方法对计算资源很敏感,部署前一定要做性能评估。
4.2 传统方法的局限
我做了两年传统方法,越来越觉得力不从心。主要问题有三个:
| 问题 | 具体表现 | 影响 |
|---|---|---|
| 光照敏感 | 侧光、逆光下特征变化大 | 同一个人在不同光照下可能被识别为不同人 |
| 姿态鲁棒性差 | 人脸偏转超过30°时效果急剧下降 | 实际场景中很难要求用户正对摄像头 |
| 特征维度高 | LBP约256维,HOG约3780维 | 存储和计算开销大,不适合大规模检索 |
为什么会这样?因为传统特征是“手工设计”的,它只能捕捉到人脸的局部模式,无法理解人脸的整体语义。说白了,它不知道什么是“眼睛”,什么是“鼻子”。
4.3 深度学习的崛起:FaceNet
2015年,Google的FaceNet出来时,我整个人都震惊了。它用深度学习直接学习从人脸图像到Embedding向量的映射,端到端训练,不需要手工设计特征。
4.3.1 FaceNet的核心思想
FaceNet用了一个叫三元组损失(Triplet Loss)的玩意儿。它的想法很巧妙:
- 选三张图:锚点(Anchor)、正样本(Positive)、负样本(Negative)
- 锚点和正样本是同一个人的不同照片
- 负样本是另一个人的照片
- 训练目标是:让锚点和正样本的距离近,和负样本的距离远
损失函数长这样:
L = max(d(A,P) - d(A,N) + margin, 0)
其中d是欧氏距离,margin是个超参数(通常设为0.2)。
关键点:FaceNet输出的Embedding向量是128维。相比HOG的3780维,压缩了近30倍,但识别精度反而更高。这就是深度学习的魅力——自动学习最有效的特征表示。
4.3.2 我在项目中用FaceNet的体会
2017年,我在一个智慧园区项目里用了FaceNet。效果确实比传统方法好,但有个坑——三元组的选择太难了。
你想想看,如果随机选三元组,大部分都是“简单样本”:锚点和负样本本来就差别很大,模型学不到东西。我后来用了难样本挖掘策略:只选那些锚点和负样本距离比较近的三元组,强迫模型去区分难区分的对。
实用技巧:训练FaceNet时,建议每轮迭代都重新计算所有样本的Embedding,然后在线生成难三元组。离线生成的三元组容易过拟合。
4.4 更进一步的ArcFace
FaceNet虽然好,但三元组损失有个问题——它只关心相对距离,不关心角度。2018年,ArcFace提出了加性角度间隔损失,把问题从欧氏空间搬到了超球面上。
4.4.1 ArcFace的原理
ArcFace的核心改进:
- 把特征向量归一化到单位超球面上
- 在角度空间中增加一个固定的间隔m
- 让同类样本在球面上更紧凑,异类样本更分散
损失函数变成:
L = -log( e^(s*cos(θ_y+m)) / (e^(s*cos(θ_y+m)) + Σ e^(s*cos(θ_j)) ) )
其中θ_y是特征向量和真实类别权重的夹角,m是角度间隔(通常设为0.5),s是特征缩放因子(通常设为64)。
| 方法 | 特征空间 | 损失函数 | LFW精度 | Embedding维度 |
|---|---|---|---|---|
| LBP | 欧氏空间 | 无(手工特征) | ~85% | 256 |
| HOG | 欧氏空间 | 无(手工特征) | ~88% | 3780 |
| FaceNet | 欧氏空间 | Triplet Loss | ~99.2% | 128 |
| ArcFace | 超球面 | ArcFace Loss | ~99.8% | 512 |
注意:ArcFace的512维Embedding虽然比FaceNet的128维大,但精度提升明显。我建议在安全等级高的场景(如金融支付)用ArcFace,在实时性要求高的场景(如门禁)用FaceNet。
4.5 Embedding向量的本质
说了这么多,到底什么是Embedding向量?
我个人习惯把它理解成人脸在特征空间中的“身份证号”。但这个身份证号不是随机的,而是有数学意义的:
- 语义相似性:同一个人在不同条件下的Embedding向量距离很近
- 判别性:不同人的Embedding向量距离很远
- 紧凑性:用低维向量表示高维人脸信息
举个例子:
# 假设我们有两个人的Embedding向量
person_A = [0.12, 0.34, -0.56, ..., 0.78] # 512维
person_B = [0.15, 0.31, -0.52, ..., 0.81] # 512维
# 计算余弦相似度
cos_sim = dot(person_A, person_B) / (norm(person_A) * norm(person_B))
# 如果cos_sim > 0.7,认为是同一个人
嗯,这里要注意。Embedding向量的质量取决于训练数据的质量和多样性。我见过一个项目,训练数据全是亚洲人脸,结果在欧美人的识别上精度暴跌。数据分布一定要覆盖实际场景。
4.6 方法演进路线图
为了让你更直观地理解整个演进过程,我画了张图:
4.7 实际选型建议
最后,结合我的项目经验,给你一些选型建议:
| 应用场景 | 推荐方法 | 理由 |
|---|---|---|
| 低算力设备(如门禁) | FaceNet(128维) | Embedding小,匹配速度快 |
| 高安全场景(如支付) | ArcFace(512维) | 精度高,误识率低 |
| 大规模检索(百万级) | ArcFace + 量化 | 512维量化到128维,精度损失小 |
| 快速原型验证 | FaceNet预训练模型 | 开箱即用,不需要自己训练 |
我的建议:如果你刚开始做人脸识别,先用FaceNet的预训练模型跑通流程。等理解了Embedding向量的含义,再考虑用ArcFace提升精度。别一上来就追求最先进的方法,先把基础打牢。
从LBP到ArcFace,这二十年的演进,说白了就是一句话:从“人教机器看脸”变成了“机器自己学会看脸”。Embedding向量就是机器学会的“看脸方式”。理解了这个本质,你就能灵活运用各种方法了。