资讯动态

深度学习人脸识别:从Siamese网络到Triplet Loss实战

发布时间:2026/10/21 13:21:00 来源:尧图企业网站定制
刷脸这件事放到深度学习里拆开其实没有大多数人想象的那么魔法。昨天一个学弟问我人脸识别是怎么记住那么多张脸的如果只是把照片当成普通图片去训练一个分类器那遇到新人注册怎么办难道每次都要重新训练吗这个问题正好戳中了我看吴恩达深度学习课程人脸识别那一节时反复琢磨最久的地方。那一节课程笔记我前前后后整理了好几版把自己后来动手搭 demo 时踩过的坑也一起归纳了进去。这篇文章就当作一份完整的学习笔记加实操复盘写给准备认真啃人脸识别的朋友也写给那些课程看懂了、但一上手写代码就发现对不上的开发者。这套人脸识别方案的核心一句话说就是不要让人脸网络去输出这个人是谁而是让它输出这个人长什么样的一串数学签名然后通过比较签名之间的距离来判断是不是同一个人。后面 for 循环穷举、阈值怎么定、为什么没人自己从头训练模型全部围绕这个核心展开。课程里用到的最关键三个知识块分别是 one-shot learning、Siamese 网络和三元组损失Triplet Loss下面我会一个个拆开讲并结合我实际动手验证过的流程把笔记变成可以直接参考的东西。1. 为什么刷脸不能当成普通分类任务来解人脸验证与人脸识别的本质区别1.1 人脸验证 vs 人脸识别先分清你要解决哪件事很多入门视频和博客都会把人脸验证和人脸识别混着说但它们的难度和算法处理方式差了很远。课程里最开始就明确区分了这两个任务人脸验证是判断这张脸是不是某某人本质是 1 对 1 的问题比如手机解锁、门禁闸机系统知道你声称的身份它只需要回答是或否人脸识别则是判断这张脸到底是谁本质是 1 对 N 的问题比如公司考勤系统从几百个员工里找出你是谁或者在安防场景里判断这个人是否在数据库中。如果只有一个人注册了系统那么识别和验证几乎一样无非是拿这张脸和唯一候选人的脸比一下。可一旦候选人数变成几千、几万识别就成了和每一个人分别做一次验证的叠加问题。也正因为这样人脸识别的实时性要求会更高——你不能让用户刷个脸等几十秒。从算法上这两个任务最终共享同一套核心能力提取人脸特征向量。所以课程里并不急着把验证和识别分开训练而是先训练出一个能对人脸做嵌入表示的模型然后在应用层通过不同的比对策略实现验证或识别。1.2 当类别数变成海量softmax 分类思路会撞上哪三堵墙刚接触图像分类的人很自然会想到把刷脸问题转化成分类问题把每个注册用户当成一个类别训练一个带 softmax 的分类器输入照片输出你是第几号人。课程里特意点出了这条路线的三堵墙这也是我最初自己做实验时才真实体会到的。第一堵墙是类别数不定。分类器的输出维度等于注册用户数来一个新用户就得改网络最后一层全连接并重新训练整个过程非常笨重。第二堵墙是样本不均衡。现实中不可能给每个注册人都收集几百张高质量照片大多数人的照片数可能只有一两张用分类器硬训会严重过拟合。第三堵墙是封闭集合与开放集合的本质区别。分类器学到的能力是把当前这批人脸区分开一旦出现一个从未参与训练的新面孔softmax 给出的概率分布完全没有意义它不具备我都见过谁、和谁长得像这种泛化判断能力。所以课程给出的人脸识别路线没有沿用分类—类别编号的范式而是转向了另一种思路网络不再输出类别编号而是输出一个固定长度的向量这个向量要能作为一张脸的数学签名。输入同一人的两张照片这些向量应该离得很近输入不同人的照片向量应该离得很远。把判断是谁变成判断向量像不像就是理解人脸识别这门课笔记的核心。2. Siamese 网络把人脸压缩成一组可以比对的数字2.1 两个输入共享同一套权重最终得到人脸嵌入实现上述思路的网络结构课程里重点介绍的是 Siamese 网络中文常翻译成孪生网络。它的结构并不神秘两张人脸图片分别经过同一个卷积神经网络网络结构和所有参数完全共享最后各自被压缩成一个向量比如 128 维。这里注意两个输入是同一个网络处理两次而不是各自用一套独立网络所以叫做共享权重。这样做的道理也很简单我们希望无论照片从哪个角度进来最终把它投影到特征空间的编码规则必须一致。如果 A 照片用网络甲、B 照片用网络乙那比较出来的距离根本没有意义了。为了直观理解可以把人脸嵌入想象成给每个人的脸生成一段声纹签名。就像声音通过麦克风被采样成波形再经过编码变成一个能代表说话人身份的特征向量人脸就通过卷积网络被采样和压缩最终变成一个能代表长相特征的高维向量。这个向量的维度是设计者定的常用的是 128 维或 512 维既不直接对应某些五官坐标也不是人类的可读信息它就是一组经过训练后具有区分力的数字签名。2.2 128 维空间里的距离到底在衡量什么神经网络最后得到的这个向量数学上叫嵌入向量embedding。课程里给的比较方式很简洁计算两个向量之间的 L2 距离也就是欧氏距离。如果两张脸属于同一个人它们的向量应该像两个几乎重合的点如果属于不同的人向量之间的距离应该明显拉开。用公式来表示就是[ d(x^{(1)}, x^{(2)}) | f(x^{(1)}) - f(x^{(2)}) |_2 ]这里 ( f(x) ) 表示人脸图片经过网络后输出的嵌入向量。对于只有周末时间折腾深度学习的我来说最开始很难想象 128 维空间长什么样。实际上可以把维度降低到 2 维来感受一下把几百个人的照片经过网络输出成 2 维向量然后画在坐标图上会发现同一人的照片聚成一团不同人的照片分散开。128 维就是这种聚类关系在高维空间里的自然延伸只是高维空间能保留的细节信息远比 2 维多能容纳的人脸差异也更多。2.3 不要从零训练课程里反复强调的默认操作看到这里很多人第一反应是既然这个嵌入网络这么核心那我应该找一个人脸数据集从零开始训练一个属于我自己的模型。课程里非常明确地表示现实中没有多少人会真的从零开始训练这样一个网络。因为要学到足够稳定的人脸嵌入需要的数据量通常是百万级人脸图片还要配套做高质量的人脸对齐、清洗和边界样本筛选一般团队根本凑不齐这个数据规模和算力。更务实的做法是直接拿预训练好的人脸嵌入模型来用把对方已经训练好的那套投影规则迁移到自己的场景里。这就像你去一家公司上班不需要自己重新发明一套办公软件直接把成熟工具拿来配置好后就能干活。我在做小项目时直接加载了开源预训练权重输入自己拍的少量照片测试效果已经很稳定。这也印证了课程里那句话你的工作重心会从训练网络变成设计对比逻辑和处理工程细节后者反而才是落地时最花时间的地方。3. 三元组损失Triplet Loss训练人脸嵌入模型的核心公式与实操陷阱3.1 Anchor、Positive、Negative 与 margin 的准确含义如果要训练一个人脸嵌入模型怎么告诉网络什么样算像、什么样算不像课程引入了一个非常清晰的度量学习目标——三元组损失。每次从数据集里取三张图锚点图 AAnchor代表某个人的基准脸正样本图 PPositive是同一个人的另外一张脸负样本图 NNegative是另一个人的脸。我们希望网络输出的嵌入向量满足一个不等式[ | f(A) - f(P) |^2 \alpha | f(A) - f(N) |^2 ]也就是说同一人的距离要明显小于不同人的距离并且至少要小出一个安全间隔 (\alpha)这个间隔也叫 margin。损失函数写作[ L(A,P,N) \max\left( | f(A)-f(P) |^2 - | f(A)-f(N) |^2 \alpha, 0 \right) ]对不等式不满足的三元组网络会有梯度会往正确的方向调整参数对已经满足的三元组损失是 0梯度也是 0网络不会为它做任何改变。我用一个通俗类比去理解 margin它就像考试分数线不能让负样本只比正样本多 0.1 分就放行而是要求高出一定分数才算真的安全。课程里建议 margin 通常在 0.2 到 0.3 之间太大容易让训练很难收敛太小又会导致学到的距离区分度不够。3.2 随机选三元组为什么容易训练了个寂寞难样本挖掘自己在复现三元组损失时最容易掉进去的坑就是随机挑选三元组。因为绝大部分随机抽出来的组合都是非常容易的比如一个金发外国人和一个东亚人放在一起即使网络还没有被充分训练它们的嵌入距离也可能已经大于锚点到正样本的距离。这时损失直接为 0梯度也为 0参数完全得不到更新。课程里专门指出这种局面训练需要刻意选择那些违反 margin 的难样本也就是让网络感到这个比较有挑战性的三元组。难样本挖掘的思路其实很朴素在一个 batch 里对每个锚点找出对应的正样本同时找出距离最近、最容易混淆的负样本用这种困难组合构成三元组去计算损失。我用随机三元组训练到一万步几乎不收敛改成难样本挖掘后同样一批数据和同样的模型结构几千步就能看到明显的聚类效果。这就是课程里提到的训练效率问题不是模型坏了而是你投喂的训练样本难度太低了。3.3 别忽略的训练细节batch 分组采样与嵌入维度选择三元组训练还有一个容易忽略的前置条件如果你的 batch 是纯随机采样很可能出现 batch 里所有人都不是同一个人压根构造不出正样本对。所以实际操作要按身份分组采样每个 batch 里至少保证挑若干个人并且每个人至少有 2 到 3 张照片这样才能组成有效的 (A, P, N) 组合。嵌入维度也不是越大越好128 维是经过实践验证的常用选择再高可以增加表达能力但也会加大存储和计算压力且对小数据集更容易过拟合。很多刚开始学的人会纠结如果我的应用场景只有固定几个人为什么不直接训练一个二分类器判断两张脸是同一人还是不同人呢课程里的度量学习思路可以覆盖更通用的场景。我做了个小对比对比项固定类别二分类度量学习嵌入向量注册新用户需要重新训练模型只需新算一个向量入库陌生面孔处理没有可靠输出距离普遍远可判为陌生人样本需求每个身份尽量多图借助预训练可容忍少量样本部署灵活性受限于类别数与类别数无关适合大规模识别从这里也能看出为什么即便只做一个几十人的考勤系统大家也更愿意走嵌入向量路线。它把新增用户这种常见需求从训练任务降级成了普通的数据库写入操作。4. 从课程笔记到线上部署搭建一个人脸入库与比对流程的完整步骤4.1 离线入库阶段检测、对齐、向量化一步都不能少课程主要讲网络结构但真实项目里你会发现模型前面还挡着一大堆图像预处理工作。我整理出来的标准入库流程是原始图像进来先做人脸检测找到人脸框然后做人脸对齐通常借助眼睛、鼻子等关键点把脸扭正接着裁剪并缩放到网络要求的输入尺寸最后送入模型得到嵌入向量存入一个内存索引或数据库。这个流程中人脸对齐是最容易偷懒也最不应该偷懒的步骤。同一个人的脸如果歪斜角度不同直接裁出来对比特征距离会被明显拉大误拒率飙升。这里要特别强调入库阶段和在线识别阶段必须走完全相同的预处理管线。假如入库时用的是高清正脸照识别时却是暗光下随手拍的小图那么即使后面模型一样距离分布也会整体漂移。课程里没有展开这些工程细节但实际操作中它可以决定你的阈值是否还能正常工作。4.2 在线比对阶段距离计算与阈值选择以及一个简单的阈值统计方法在线识别时拍一张照片经过同一条预处理和模型推理得到一个查询向量 q。然后和数据库里所有的向量逐一算 L2 距离找到距离最小的那个人。如果最小距离小于我们设定的阈值就返回对应身份如果大于阈值就判定为库外人士。课程里说的判定逻辑就是这样简单清晰但阈值不能拍脑袋定需要通过验证集来标定。我在实操时一般会收集两类样本一是同一人在不同环境下注册和抓拍的正样本对二是不同人之间的负样本对分别算出它们的距离分布。然后画一张示意表来理解阈值设置误识率把不同人放进来的概率拒识率把同一人拦在外面的概率过小如 1.0低高容易把本人拒之门外适中如 1.3中中过大如 1.6高不同人脸也放进来了低所以阈值调参本身就是产品策略选择。门禁系统希望宁可拒识也不要误识就把阈值调严一点大型活动快速通行则相反优先保证通过速度。这个取舍在课程里虽然只是一句话但真正落到图上你会发现大量时间都花在这里。4.3 工程上的致命细节预处理不一致、活体攻击、存储与更新策略排在第一位的工程坑是预处理不一致。我就犯过这样的错入库时用的检测器是较大的人脸框上线时换了另一个检测器裁出来的范围小了一圈结果同一个人两次识别的距离从 0.7 变成了 1.2直接被系统拒掉。要避免这个坑最好固定一个检测器、固定一套 keypoint 对齐代码、固定一个缩放尺寸任何环节都不要在运行阶段随意替换。第二个坑是活体检测。纯照片/视频攻击是人脸识别系统绕不开的问题课程里没有讲那么多但工程中必须考虑。一个折中方案是把活体检测作为前置环节只有通过了眨眼、摇头或者红外深度校验的图像才送入人脸嵌入模型去比对。这样不能让系统更聪明但能挡住大量低成本攻击。第三个坑是存储与更新策略。数据库里不应存原图而应只存嵌特征向量这样既节省空间又降低隐私泄露的风险。新增用户时只需多算一个向量写入数据库不用改模型这个操作与课程里强调人脸识别最重要的就是学嵌入表示完全呼应。真正上线前还要做数据收集告知和授权尊重用户的知情权这是底线。5. 我用这套思路实测时的踩坑复盘与复现路线建议5.1 同一个人的距离居然比不同人大排查才发现是预处理管线不统一我在做某个内部 demo 时出现过一次非常尴尬的情况同一人两张照片算出来的距离是 0.9不同人照片的距离反而只有 0.6直接把距离最近判定法推翻了。当时第一反应是模型权重不对后来一步步排查才发现问题不在网络而在于颜色预处理管线不一致训练方在生成嵌入时使用的是特定通道顺序和像素范围而我把自己采集的图片直接转成另一个通道顺序送进去导致特征偏移严重。这个经历让我彻底明白课程里公式和网络结构只是硬币的一面。嵌入模型很敏感它记住的不只是人脸结构还包括了输入图片的分布。只要是实际项目就必须把整套管线当成一个整体系统来对待所有环节保持一致而不是只盯着模型准确率。5.2 算力不够时的部署策略让向量缓存和批量推理替你省钱人脸嵌入模型的前向推理确实比普通分类模型更重一点但工程上有几个简单的优化手段。离线阶段入库本来就不要求实时可以把所有注册人的照片批量跑完生成向量存好在线比对阶段只需要做一次查询向量推理剩下的就是 N 次距离计算对 CPU 都很友好。在低算力设备上我会把模型的输入尺寸固定死尽量避免动态 shape 带来的额外计算抖动再进一步的话可以做量化把模型压缩到半精度甚至更低的整数精度换来接近一半的推理提速。我在实际部署时还会把特征向量缓存成本地文件并且只在新增注册时增量更新而不是每次启动都全量重新计算。这套思路配合课程里用嵌入向量做比对的思想可以让人脸识别服务跑在很便宜的设备上还保持流畅。5.3 如果我要把课程笔记落成一个完整实验会按什么顺序做考虑到课程主要是讲原理我建议第一次动手复现的人按这个顺序去推进而不是逆向去从头训模型。第一步找一个可靠的预训练人脸嵌入模型先把自己拍的几十张照片跑一遍向量用最近邻距离验证同类聚合。第二步准备一个小规模的验证集里面尽量包含同一人多角度照片和不同人照片用来做阈值分析。第三步如果发现预训练模型在你自己的场景上不够用才去考虑在私有数据上微调微调时也优先冻结大部分层只动最后一部分。整个过程里不需要一上来就非要复现百万级数据训练那样只会劝退自己。按照这个路线我从看课程觉得懂了到跑通一个能用的 demo实际只用了一个晚上。这验证了一个观点这类课程笔记的真正价值是帮你建立正确的技术框架让你知道该把精力集中在哪个环节剩下的事都是细节打磨和工程取舍。回到开头那个学弟的问题人脸识别确实不是记住很多张脸而是学会用一组数字描述一张脸然后比数字之间的距离。我再分享一个小技巧你在搭建验证集时记得把同一个人的不同姿态照片和不同人的相似长相照片尽量多放一点这样标定出来的阈值会比随机照片可靠得多。我这个习惯是从几次误判里总结出来的对后面调阈值帮助很大。

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

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

免费获取报价 →
↑