资讯详情

资讯详情

相似人脸识别系统实战:检测、特征提取与Faiss检索

先聊聊我为什么接这个话题。每年到了毕业季总有不少同学拿着人脸识别检测系统这个题目来找我聊方案有的是做相似人脸识别有的是做考勤打卡还有的是做人证比对。这个东西听起来好像被讲烂了但实际上真正能跑通的、能写进论文里的、答辩能讲清楚的项目并没有那么多。这个题目最妙的地方在于它是一个非常典型的工程与学术结合的选题——既要懂深度学习的基本原理又要能处理真实场景下的数据问题而且从数据处理、模型训练到系统部署整条链路非常完整非常适合作为深度学习方向的毕业设计。这篇文章我就以相似人脸识别这个方向为主线把整个项目的技术选型、数据准备、模型训练、系统实现到答辩避坑完整地拆给大家。如果你正准备做类似的毕设或者纯粹想系统地了解人脸识别落地怎么做这篇文章可以直接当参考框架用。1. 项目整体设计与技术选型思路1.1 核心功能定位先明确一下人脸识别检测系统相似人脸识别到底要做什么。它不是一个简单的人脸有没有出现在画面里的检测系统而是一个能够认人的系统。具体来说核心能力有三块第一人脸检测。给定一张图片或者一帧视频系统要能找到人脸在哪里用矩形框框出来。这一步相当于人的眼睛需要能应对不同角度、不同光照、部分遮挡等情况。第二人脸特征提取。检测到人脸之后要把人脸转化成一个向量——就是数学上的特征向量。这一步相当于人的大脑记忆系统并不是把照片存下来去比像素而是把人脸的独特特征压缩成一个定长的数字向量比如128维或者512维。第三相似度度量与识别。拿到两张人脸的特征向量之后计算它们之间的相似度比如余弦相似度或者欧氏距离。设定一个阈值如果相似度超过阈值就认为是同一个人否则认为是不同的人。这就是相似人脸识别的核心逻辑——它可以做一对一验证也可以做一对多检索。作为毕设项目我建议把这三个能力全部做出来不要只做检测也不要做只针对某一张固定图片的识别。完整的链路才能展示你对整个系统的理解。1.2 技术方案选型我这套项目最终采用的架构是RetinaFace人脸检测 ArcFace人脸特征提取 Faiss向量检索再包一层Python后端和简单的Web前端。为什么这么选先说人脸检测部分。目前常见的选择有OpenCV的Haar Cascade、Dlib的HOG、MTCNN、RetinaFace等。Haar Cascade和HOG属于传统方法优点是小、快、没有GPU也能跑但对复杂场景的鲁棒性差侧脸、暗光、遮挡情况下基本就废了。MTCNN是深度学习早期的经典方案三个级联网络速度快作为毕设已经够用但它在人脸极小、极度模糊的情况下还是会出现漏检。RetinaFace在精度和鲁棒性上更好特别是对侧脸和小脸的支持而且它的开源实现比较成熟推理速度在GPU上也能到实时水平。再说人脸特征提取。这是整个系统的灵魂。早期很多毕设是用人脸图片本身或者手工特征来做匹配比如直接用灰度图拉平之后算余弦相似度这种方案鲁棒性很差换个发型、加点噪声就不认人了。正确的做法是使用度量学习训练出来的人脸嵌入模型。常用的有FaceNetGoogle、ArcFaceInsightFace、CosFace等。考虑到是毕设场景且训练数据有限我最终选择了ArcFace这个方向——因为它把分类和度量学习结合得很好虽然在训练时需要类别标签但得到的特征判别性很强。实际使用时我直接加载了基于大规模人脸数据集预训练好的模型权重而不是自己从头训练。这里要提醒一下毕设不需要从零训练人脸识别模型预训练模型微调或者直接作为特征提取器是完全合理的做法论文里把原理讲清楚就行。最后说向量检索。相似人脸识别如果只是两张图对比那不需要Faiss。但如果要做以图搜图——在一个人脸库里面找出最相似的前K个人那就要用向量检索。Faiss是Meta开源的库专门为大规模向量相似度搜索设计支持CPU和GPU代码量很小。用它作为亮点在答辩时是加分项因为它体现了你对系统性能的考虑而不只是调个模型。1.3 系统架构整个系统的整体流程是这样的输入图片/视频帧 - 人脸检测 - 人脸对齐 - 特征提取 - 特征比对/检索 - 输出结果我在实现时把系统分成了三层数据层管理注册人脸库中的图片和对应的特征向量用Faiss建立索引索引文件保存到本地。服务层基于Python后端提供接口包括注册人脸、人脸对比和人脸检索三个核心接口。展示层一个简单的Web界面用户上传两张照片系统返回相似度和是否为同一人的判断也可以上传一张照片在库中检索最相似的Top-N。这个架构单看不复杂但好处是分层清晰论文的架构图好画答辩时也容易讲清楚数据是怎么流动的。而且每一层都可以单独测试出了问题容易排查。2. 数据准备与预处理实战2.1 数据集来源与组织方式人脸识别项目非常依赖数据。毕设阶段不可能自己去采集成千上万人的数据合理的策略是使用公开数据集加自建小数据集两部分。公开数据集方面我用了公开的CelebA部分样本作为注册底库的模拟数据再加上自拍的一些照片作为查询图片。这里要特别说明如果你不想引入真实人物隐私问题在论文中涉及的示例数据完全可以使用虚构人物照片、AI生成人脸照片或者同学授权采集的少量照片。我后来在演示版本中注册库使用的是AI生成的虚构人脸查询图片也是对应生成的既避免了隐私问题又保证了演示效果可复现。数据集的组织方式建议如下dataset/ register/ person_01/ 001.jpg 002.jpg person_02/ 001.jpg ... query/ query_01.jpg query_02.jpg每个人一个文件夹里面放该人的多张不同角度、光照条件下的照片。注册库建议每人3到10张图数量太少特征提取不稳定太多又显得冗余。如果某些人只有一张图也可以单张注册但你需要知道这会影响识别鲁棒性。2.2 人脸检测与对齐的细节预处理是整个项目中看起来不起眼但影响极大的环节。我前期踩过一个坑直接用检测框裁剪人脸送入特征提取模型结果同一张脸因为检测框稍微偏移一点提取的特征向量就差了很多。后来才意识到人脸识别模型训练时要求输入是对齐后人脸即两只眼睛在同一水平线、人脸居中。因此我在预处理阶段做两步处理第一步用RetinaFace检测人脸拿到5个关键点坐标——左眼、右眼、鼻尖、左嘴角、右嘴角。第二步根据关键点做相似变换将人脸对齐到标准位置。这一步我直接用OpenCV的estimateAffinePartial2D或者getAffineTransform实现目标位置设置为一个模板比如112x112的图像上两只眼睛分别在(38, 36)和(74, 36)鼻尖在(56, 56)左右。对齐之后的图像统一缩放到112x112ArcFace的标准输入尺寸再做一些归一化。这一套流程下来识别准确率提升非常明显。如果你时间有限也要至少做到检测并裁剪缩放不要跳过了对齐直接送进去。2.3 数据增强本科毕设阶段数据增强可以做得简单一些但做了比不做好。我在注册阶段对每张原始人脸图做了这些增强水平翻转人脸左右对称这个增强对训练很有效但注意一对图片如果都用翻转后的可能会增加识别难度需要谨慎使用轻微旋转正负15度亮度扰动高斯模糊这里不是要做成训练集而是为了在注册底库时让底库中的特征更稳定。一个人如果有3张原图我可以把它扩充成10张左右然后分别提取特征向量取平均这个人注册得到的代表向量就更稳。实践中取平均是个简单又有效的操作。3. 模型原理与推理实现3.1 检测模型RetinaFace的推理细节人脸检测模型我选择RetinaFace但毕设里我不建议自己去训练它直接用现成权重跑推理。需要安装的工具主要是PyTorch、OpenCV。RetinaFace输出每个人脸框的坐标、置信度以及5个关键点坐标。推理代码大致思路如下对输入图片做归一化处理保持长宽比缩放。通过模型前向传播得到输出。对输出做解码得到人脸框和关键点。根据置信度阈值过滤低质量框再用NMS去除重叠框。在实际调试时我把置信度阈值设为0.5左右比较合适。太高会漏检太低会出现误检。另外RetinaFace有一个参数控制是否执行NMS默认开启但如果画面里人脸密集NMS的阈值要调低一点防止两个人脸的框被合并。这里有一个特别需要注意的问题RetinaFace检测到的小脸数量可能会很多有些只有十几像素宽这种脸即使检测到了特征提取也基本不可靠。建议在送入特征提取前加一个最小尺寸过滤比如人脸框短边小于40像素就丢弃。加了这一个过滤系统的误识别率会低很多。3.2 特征提取模型ArcFace的使用方式ArcFace模型的本质是一个卷积神经网络它和普通分类网络的区别在于最后一层损失函数不同。ArcFace使用Additive Angular Margin Loss在人脸分类任务中它让同一类的人脸特征在超球面上更加聚拢不同类之间更加分散。这样一来训练出的特征向量在余弦相似度上就有很强的判别性。我们实际使用时并不需要自己复现这个损失函数只需要加载训练好的模型把倒数第二层输出的embedding拿出来作为人脸特征向量。ArcFace输出的特征维度一般是512这512个浮点数就是一张人脸的数字指纹。加载模型的方式需要注意因为预训练模型的训练框架存在版本差异有时会遇到权重格式不兼容的问题。稳妥的做法是直接使用InsightFace项目官方提供的推理接口它会自动处理图像的检测、对齐、特征提取全流程。如果不想依赖完整库也可以手动加载ONNX格式的模型用ONNX Runtime推理。ONNX格式的好处是不依赖PyTorch版本部署更干净答辩演示时不容易翻车。3.3 相似度计算与阈值设定这是相似人脸识别最核心的数学部分。特征向量之间的相似度通常有两种度量方式余弦相似度公式是similarity cos(A, B) (A·B) / (||A|| ||B||)余弦相似度的值域在-1到1之间越接近1说明两个向量方向越一致也就是人脸越相似。这是人脸识别中最常用的度量。欧氏距离也是一样可选但注意欧氏距离和余弦相似度并不等价——欧氏距离直接计算特征空间中的直线距离它对特征向量的模长敏感余弦相似度只关注方向不受模长影响。ArcFace训练后的特征向量通常在球面上归一化此时两者在一定顺序上等价但我统一使用余弦相似度便于解释。阈值怎么定如果使用ArcFace在主流数据集上的预训练模型同一个人的余弦相似度通常在0.5到0.9之间不同人的相似度可能分布在0.1到0.5之间。一个经验值是0.4到0.5之间。我不建议拍脑袋定阈值而是做一个简单的小实验准备10个人的照片每人2张凑成20对同人图片对。再准备不同人的图片凑成20对异人图片对。分别计算相似度画出分布。取两类分布之间的分界点作为阈值。我在实验中得到的数据是同人相似度均值0.62异人相似度均值0.21差距很明显。阈值取0.45时误报率很低取0.40时召回率变高但开始出现误识别。最终我定了0.42并根据场景提供宽松/严格两种模式供用户选择。4. 从命令行到Web系统的完整实现4.1 功能模块划分命令行版本是验证模型通路是否跑通的关键一步也是我建议所有毕设人先做的。不要一上来就写Web界面先把核心链路跑通确保输入一张图片能输出正确的特征向量和相似度。命令行工具建议包含以下几个子命令检测人脸输入图片路径输出检测结果图片。提取特征输入人脸图片输出512维特征向量到文件。两图对比输入两张图片输出相似度。注册底库扫描某个文件夹下所有图片提取特征并保存到Faiss索引。检索输入一张查询图片在底库中检索返回Top-K。这几个命令看起来简单但它们覆盖了整个项目的核心功能。Web界面只是把这几个命令包装成HTTP接口并加上直观的展示。4.2 用Faiss构建人脸检索底库注册底库之后需要把所有注册人的特征向量组织成索引供检索使用。Faiss中有一个非常基础的概念——IndexFlatIP即内积索引。因为我们在提取特征后做了L2归一化向量的内积就等于余弦相似度所以用IndexFlatIP计算相似度是完全匹配的。操作步骤很简单第一步把所有注册人脸的特征向量堆叠成一个二维数组形状是(N, 512)N是注册人脸数。第二步构建索引import faiss index faiss.IndexFlatIP(512) index.add(register_features)第三步检索时给定查询向量调用index.search返回相似度和对应的索引。对于小规模底库几千张脸IndexFlatIP已经是暴力精确搜索速度也很快。如果底库很大可以换用IndexIVFFlat做倒排索引加速检索但是会有一定精度损失。毕设阶段用IndexFlatIP即可但你在论文里可以提到FaceNet系统生产的百万级别底库时对检索性能的考虑这是一个加分点。踩坑提醒Faiss的索引格式和特征向量的dtype要求是float32而且是行优先排列。如果特征向量是从NumPy的float64直接传入会报错或精度异常必须先转成float32。4.3 Web演示系统Web部分我选择了Flask写后端前端只用了基础HTML/CSS/JS。为什么用Flask因为轻量、代码量少、文档多毕设阶段最容易跑通。不需要用Django这个项目规模用Django反而重了。后端需要实现三个接口POST /register接收一张图片和一个姓名检测人脸并提取特征放入底库。POST /compare接收两张图片返回相似度和是否为同一人。POST /search接收一张图片在库中检索最相似的前K个人。其中前端页面比较关键我做了三个标签页分别是双图对比、注册入库和以图搜图。界面不追求花哨但逻辑清晰每个操作的返回结果实时展示包括人脸框的绘制、相似度的分数条、检索结果的缩略图列表。在实际调试Web时有一个容易忽略的问题多线程环境下Faiss索引的查询操作是线程安全的但添加操作不是。也就是说如果一边有人调用注册接口一边又有人发检索请求可能出现数据不一致甚至崩溃。稳妥做法是给索引的更新操作加一把锁检索操作不加锁。这个细节写在论文里能体现系统设计的严谨性。5. 训练调优经验与错误排查5.1 要不要自己训练模型这是很多同学纠结的问题。我的明确建议是除非你的题目明确要求从零训练一个人脸识别模型否则不要自己从头训练。从头训练需要百万级别的标注数据和多卡GPU训练数天毕设的时间算力都不现实。合理的做法有两种第一种直接用预训练模型做特征提取完全不去改模型权重把重心放在系统构建和实验对比上。这种做法适合大多数毕设工作量已经足够了。第二种拿预训练模型作为起点用自己的小数据做微调。需要自己构造一个多头分类器人头数就是类别数用ArcFace损失函数去训练。这种做法适合你想在论文里加一点改进内容的场景。但微调也有风险——小数据容易过拟合一旦注册人的数量少微调后模型对底库数据过度拟合检索时对其他数据的效果反而变差。我最终选择的是第一种但为了论文的完整性我对预训练模型和自己微调后的模型在同一个验证集上做了对比实验。微调了一小轮结果是预训练模型对跨场景泛化更好微调模型对底库内数据更稳各有优劣。这个实验花不了太多时间但对答辩很有帮助。5.2 相似度阈值调整阈值问题我在前面提过一次这里展开细讲。因为很多同学在答辩演示时会出现同一人判断失败或拿两张不同人的照片却显示同一个人的尴尬情况根本原因就是阈值没校准。校准阈值不能只看一两张图。我建议做一个小工具自动扫描一个标注好的验证集计算阈值从0.1到0.9变化时系统的准确率和误报率变化画出一条曲线。平衡点可以选为阈值。实际经验如果你用的预训练模型和我的类似在生活自拍场景下阈值0.40~0.45比较均衡。如果底库全部是证件照风格光照均匀、正面、无表情阈值可以提高到0.50因为同人照片之间的相似度会很高。如果底库是随手拍的模糊生活照阈值要降到0.35否则很多同人照片会被误判为不同人。总之阈值不是一个固定常数一定要配合场景调整。5.3 常见问题与排查经验我把调试过程中遇到的典型问题整理成一个速查表你在自己复现的时候如果遇到同样问题可以直接对照排查。现象可能原因解决方案检测不到人脸输入图像过暗、人脸过小、角度过大提高置信度阈值下限、对图像做亮度增强、尽量输入正面大脸两张相同照片相似度极低特征提取前没有对齐确认预处理已做关键点对齐检索结果全是同一个人底库数据存在重复特征过滤底库中异常相似的样本避免重复注册Web页面长时间无响应模型推理耗时过长且Web服务是单线程使用异步任务或增加超时时间若硬件无GPU可降低检测尺寸Faiss搜索报shape错误特征向量未转为float32确保特征向量dtype为np.float32图片上传后返回500错误上传图片格式非RGB、含有Alpha通道统一用OpenCV解码不做转换转为RGB三通道身份证/证件照识别率低证件照与生活照风格差异大注册底库时尽量用与查询场景一致的图片另一个高频坑是编码问题。Python在读取中文路径图片时经常遇到乱码尤其在Windows环境下cv2.imread遇到中文路径会直接返回空。解决办法是用cv2.imdecode配合np.fromfile来读写文件时也要注意编码。这个坑看起来不大但实际开发中极易遇到。5.4 性能优化心得如果只有CPU跑RetinaFace检测一张图大约需要0.5到1秒ArctFace推理大约需要0.2秒这个速度做实时演示会很勉强。我在项目中实际采用了三个优化手段第一检测分辨率限制。RetinaFace内部会把输入图缩放如果图太大检测非常慢。通过设置max_size把长边限制在1000像素以内速度能提升数倍。第二使用ONNX Runtime替代PyTorch推理。同样是CPU环境下ONNX Runtime的推理速度通常能提升20%-50%而且内存占用更小。这是最简单有效的一步。第三批量推理。如果是底库注册场景有几百张照片需要提取特征不要一张一张跑循环把图片堆成batch一批一批送进去速度能提升好几倍。Faiss建索引本身就很快瓶颈主要在特征提取循环。如果有GPU当然更好但我建议不要依赖GPU因为答辩现场往往用的是主办方的电脑不保证有独立显卡。让系统在CPU环境下也能流畅运行是一个很实际的工程要求。6. 论文与答辩要点6.1 论文架构建议如果你按照上面这套方案来做论文结构的逻辑非常顺。我建议按照问题定义 - 相关技术 - 系统设计 - 实验与结果 - 总结展望的大框架来组织。可以特别设置一个系统实现章节把软件的架构图、核心代码接口、界面展示写清楚。实验章节要包含以下内容检测模块的测试选用若干张包含不同数量人脸的图片统计检测的精确率和召回率。特征提取的判别性测试计算同人相似度分布和异人相似度分布画出直方图。阈值敏感性分析不同阈值下的系统准确率变化。检索性能测试在不同底库规模下统计检索的平均耗时。对比实验将ArcFace特征与FaceNet特征在自建验证集上对比或者对比不同检测模型的效果。这里有两点要提醒第一不要篡改实验数据。答辩老师不一定能复现你的实验但一定会根据数据逻辑合理性提问编造的数据经常出现自相矛盾的漏洞。第二不需要追求SOTA指标毕设的重点是系统完整性和分析深度为什么效果不好也是一个有价值的分析点。6.2 答辩高频问题根据我做毕设指导的心得和旁听答辩的经验以下这些问题是高频问题你可以提前准备好答案为什么用ArcFace而不是FaceNet答ArcFace在损失函数中引入角度边界训练过程更稳定特征判别性更强现有的预训练模型也更容易获得。另外FaceNet的Triplet Loss在训练时需要构造三元组采样难度较高。人脸特征向量为什么是512维降维可以吗答512维是当前主流人脸模型的常用维度理论上维度太高会带来冗余但降维也会损失信息。在底库规模不超过百万时512维可以接受。底库中一个人注册多张照片怎么处理答提取多张照片的特征向量取均值作为代表性特征。如果照片质量差异大可以先过滤质量差的图片再取平均。遇到遮挡口罩怎么办答基于深度学习的检测模型在口罩遮挡下仍能检测到面部关键点不够时特征提取会受影响。可以在论文中作为未来改进方向提出但不必因为解决不了而不提。如何处理光照变化答预处理阶段的光照归一化能起到一定作用主流模型本身也对光照有一定鲁棒性但不完美。可以对光照异常图像做直方图均衡化但效果有所受限。6.3 几个求加分的小技巧在答辩演示时有几个加分项值得做第一准备一个错误案例。找一张识别失败的图片分析失败原因。这能展现出你的独立思考能力比一味说我的系统精度很高更有说服力。第二展示系统对视频流的处理能力。不用实时视频只需要把一段短视频抽帧逐帧做检测和识别然后把结果合成到输出视频里。这个功能代码量不大但演示效果极好。第三做一个简单的误识率-拒识率曲线就是调整阈值时系统的误识别率和拒识别率如何变化。很多同学的答辩PPT里没有这个东西加上它会让评委觉得你确实理解了系统的工作机制。7. 项目总结与后续扩展方向因为完整做了人脸识别系统我对整个流程有了比较系统的理解。最后把几个印象深刻的经验和扩展方向分享出来。第一个经验预处理的质量比模型的选择影响更大。我在实验中发现同一个特征提取模型做了对齐和没做对齐相似度分数差了将近10到20个百分点。很多新手一上来就纠结模型选型结果忽略了图像预处理这些基础环节效果自然上不去。第二个经验系统设计的完整度比单个模型的精度更重要。底库管理、阈值可调、批量注册、错误反馈这些看似不起眼的功能恰恰是让一个毕设项目从算法Demo变成系统的关键。第三个经验测试要覆盖真实场景。用证件照测试效果很好但用手机随手拍的照片测试就暴露了很多问题——光照复杂、角度偏移、清晰度不足。尽量提前采集多种场景的数据来测试别等到最后答辩才发现问题。说到后续扩展方向这个项目的延展性很强。比如把检索功能封装成HTTP接口配合人脸检测的摄像头采集就能扩展成一个人脸考勤系统比如把特征向量换成其他生物特征语音、步态就能做多模态身份识别再比如把底库换成百万级别的公开数据集在Faiss上使用IVF索引能够处理更大规模的检索场景。要我说人脸识别作为毕设题目最大的价值不在于识别有多准而在于它覆盖了深度学习项目的完整生命周期数据采集与清洗、模型选型与部署、系统开发与测试、论文分析与呈现。整个项目做下来你对深度学习工程化的理解会有一个非常实质的提升。如果在做的过程中你们有更好的思路或者遇到我这里没提到的坑欢迎多交流。人脸识别这个方向虽然卷但正因为它卷能真正把链路做通的人学到的东西才是实实在在的。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →