☰
多目标人脸识别毕设实战:深度学习检测+识别全流程解析
2026/10/2 8:46:03 网站建设 项目流程

简介:面向毕业设计、期末大作业及课程设计场景,这份Python项目以深度学习多目标人脸识别为核心,提供了一整套可直接部署运行的项目方案。资源共121个文件,压缩包约32.31MB,涵盖Python源码、编译后的pyc文件、模型权重数据、训练图片、特征统计csv以及说明文档等,并附有可直接启动的Windows可执行程序和演示视频,便于快速预览项目效果。项目代码带有详细注释,结构清晰,适合新手理解人脸检测、特征提取与多目标识别的完整流程。系统功能完善、界面友好,支持同时识别多个目标,能够满足毕设演示或课程验收的实际需求。目前已有273人学习下载,对于需要借鉴完整项目架构、参考环境搭建方式或撰写设计文档的读者,这份资料能提供从源码实现到成果展示的切实参考。

1. 从下载到跑通:这个多目标人脸识别项目到底能给你什么

拿到“Python毕业设计基于深度学习的多目标人脸识别代码.zip”这类压缩包,你面临的第一个问题往往不是算法,而是“这个包到底能不能用”。很多同学下载完解压,看到 src、models、data、requirements.txt 一堆目录,直接懵掉;又或者在 vscode 里一跑,报错信息扑面而来,第一反应就是“是不是代码有问题”。实际上,这类基于深度学习的多目标人脸识别毕业设计项目,核心链路不外乎两段:先用检测模型从图片或视频帧里把每一张人脸框出来,再用识别模型把框出来的脸变成特征向量,最后跟库里的人脸做距离比对。本文会按照“原理 → 环境 → 跑通 → 改造 → 避坑 → 进阶”这条顺序,让你把这个 zip 里的代码变成真正能演示、能答辩、能写进论文的完整系统。这篇笔记适合准备做毕设的本科生,也适合想快速上手一个可运行人脸识别 demo 的开发者,照着操作即可,不需要你是深度学习理论专家。

2. 多目标识别的技术栈:为什么“检测 + 识别”是这套代码的标配组合

2.1 多目标的“多”字,卡在检测侧而不是识别侧

很多人第一次看到“多目标人脸识别”这个描述,会误以为难点在于识别阶段要区分很多人,所以算法一定要很复杂。其实说句实在话:人脸识别模型的单张推理逻辑是固定的,它只负责把一张 112×112 或 160×160 的人脸图变成一串 128 维或 512 维的特征向量;真正决定系统能不能处理“多目标”的,是检测模块能不能在一帧画面里把所有人的脸都找出来。常见做法是,检测部分用 MTCNN 或 RetinaFace,识别部分用 FaceNet 或 ArcFace 搭配 ResNet 骨干网络。MTCNN 的成名优势是轻量、稳定且容易安装,在 CPU 上也能跑;RetinaFace 对侧脸、遮挡和小脸更友好,但依赖更大。这个 zip 如果用的是 MTCNN + FaceNet,那它至少做到了“开箱即用”而不是“高配专属”。如果你拿到代码后第一时间打开检测模块,看到的是一长串 PNet、RNet、ONet 的调用,那基本可以确认走的是经典的 MTCNN 级联检测路线。

从工程的视角看,“多目标”对检测模型提出的要求是召回率优先,也就是你宁可多框出一个误检,也不要漏掉一个真人脸;漏检的后遗症会在识别阶段无解,因为后处理拿不到那张脸的输入。检测模块的下游通常接一个对齐操作——根据两只眼睛和鼻尖的坐标把人脸旋转到标准位置,再缩放裁剪送入识别网络。这一步是好多人容易忽略的“隐藏坑”:同样是一个人,歪头和正脸的识别分数可能差出一大截,不是识别模型不行,而是你没做对齐。MTCNN 本身就输出人脸关键点(左眼、右眼、鼻子、左嘴角、右嘴角),对齐代码基本是该包的标配,跑通后千万别为了省事把这一步注释掉。

2.2 模型选型的实际考量:为什么不用 YOLO 做检测

做毕设的时候,有同学会问“检测为什么不用 YOLO,现在 YOLO 这么火”。这个问题问到点子上了。YOLO 确实能做人脸检测,甚至检测速度更快,但它输出的是“框”,不直接给你人脸关键点。你后面做识别之前,还得另找关键点模型做对齐,等于在链路里多加一个依赖。而 MTCNN 这类专用人脸检测器是把“找框 + 找关键点”一次性做完的,识别前的预处理就顺了。所以,做“人脸识别”这个具体任务,专业人脸检测器是比通用目标检测更顺的选择。下面的表可以帮你快速判断手里的代码更接近哪种技术路线:

检测选型关键点输出侧脸/遮挡表现CPU 推理速度适合场景
MTCNN自带 5 点一般较快毕设、离线 demo、小规模库
RetinaFace自带 5 点/106 点好偏慢要求高一点的人脸识别
YOLO 系列无好快需要额外接关键点模型,链路长

从工程调参的角度,MTCNN 有三个核心参数值得你花时间看代码:minsize(检测最小人脸像素)、threshold(三个网络 PNet/RNet/ONet 的置信度阈值)、factor(图像金字塔缩放因子)。常见初始值是 min_size=20、threshold=[0.6, 0.7, 0.7]、factor=0.709,这三个参数直接决定检测框的数量和误检率,后面章节会专门展开讲它们怎么调。

2.3 识别侧:欧氏距离还是余弦相似度,代码里是怎么落地的

识别模型把人脸变成特征向量之后,比对的逻辑有两种流派。FaceNet 原文用的是欧氏距离,训练目标是让同一个人的特征向量在欧氏空间里靠得近;ArcFace 则是把特征归一化到超球面上,用余弦相似度判断。这个差异看起来只有一行代码,实际影响巨大——如果你拿的是 MTCNN + FaceNet 的代码,那阈值 0.75 一般指的是欧氏距离;距离小于阈值才判定为同一个人。而如果你改成 ArcFace,阈值逻辑就要反着来:相似度(比如 0.4 上下)大于阈值才算同一个人。这类项目的代码里通常有一段 predict/recognize 函数,里面写着 np.linalg.norm 或者 cosine_similarity,你拿到代码后第一件事就去搜索这两个函数,搞清楚你的包用的是距离还是相似度,再去调阈值。不少同学翻车,就是因为在余弦相似度代码里套了欧氏距离的阈值逻辑,结果所有人都匹配不上。

另外,识别侧还会有一个代表性操作:计算特征向量的“均值”作为一个人的注册特征。因为一个人往往不止一张注册照片,代码常见做法是把这个人的 3 到 5 张照片分别过模型,得到特征向量后取平均,存成一条身份库记录。这个操作的工程意义在于消掉表情、光线带来的随机扰动,是低成本的白嫖提高准确率方法,后面做自己的人脸库时一定要用上。

3. 把代码跑起来:从解压到看到第一帧检测框的最小命令集

3.1 环境准备:Python 版本、虚拟环境和依赖安装方式

这一节先解决能不能跑的问题。这类毕业设计代码大多基于 Python 3.6 到 3.8 写成,最新代码也可能要求 3.9。你要做的第一步不是急着 pip install,而是先看包里有没有 requirements.txt,有的话直接用环境装;没有的话,按通用组合装。我习惯的稳定做法是:新建一个干净的 conda 环境,指定 Python 3.8,因为 PyTorch、OpenCV、face_recognition 这类常见的依赖在 3.8 上踩坑最少。python 安装教程这件事我不展开,但强烈建议别直接用系统自带 Python,更别用 Windows 应用商店里的精简版,那种环境装上 dlib 或 facenet_pytorch 往往缺这缺那,哭都来不及。

在 vscode 里配置 Python 环境时,记得把解释器指定到 conda 环境的路径,否则你在终端里明明装好了依赖,vscode 里一运行还是 ModuleNotFoundError。如果你图省事,也可以用 PyCharm,配置逻辑一样。

拿到 zip 解压后,按下面的顺序建环境、装依赖:

conda create -n face_project python=3.8 -y conda activate face_project pip install --upgrade pip pip install numpy opencv-python pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu

依赖装好后,先确认核心包能正常导入再往下走,不要直接盲目跑 demo,养成“一次只验证一个环节”的习惯。上面这组命令里,torch 我特意指定了 CPU 版本,原因是很多毕设机器没有可用的 CUDA 环境,CPU 版先跑通,后面真有显卡再换 CUDA 版不迟。你如果用的是显卡机器,把第三行换成pip install torch torchvision即可,会自动装带 CUDA 的版本。

3.2 最小推理脚本:对着图片框出所有人脸

装完依赖,我建议你不要直接去跑项目的完整 GUI 或摄像头程序,而是先用一段最小脚本验证模型和推理链路是通的。这样出了问题能快速定位是模型文件损坏、依赖缺失还是代码逻辑问题。你在右键新建一个test_detect.py,写入下面的代码:

import cv2 from facenet_pytorch import MTCNN # 初始化检测器:keep_all=True 表示把图片里所有检测到的人脸都返回 detector = MTCNN(keep_all=True, min_face_size=20, thresholds=[0.6, 0.7, 0.7]) # 读取本地图片,OpenCV 默认读成 BGR 格式 img = cv2.imread("group_photo.jpg") img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 检测人脸框和五个关键点 boxes, probs, landmarks = detector.detect(img_rgb, landmarks=True) # 在图像上逐人绘制矩形框 if boxes is not None: for i, box in enumerate(boxes): x1, y1, x2, y2 = [int(v) for v in box] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 在框上方标注置信度,保留小数点后两位 cv2.putText(img, f"{probs[i]:.2f}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) else: print("没有检测到人脸,检查图片里人脸是否太小或太模糊") cv2.imwrite("result.jpg", img) print("检测完成,结果已保存到 result.jpg")

这段脚本的逻辑不复杂:用 MTCNN 的detect方法同时拿到人脸框和关键点;keep_all=True控制的是“回传所有检测结果”,如果你只想要置信度最高的一张脸,就改成keep_all=False。min_face_size=20表示低于 20 像素的人脸直接忽略,thresholds三个值分别对应 PNet、RNet、ONet 的置信度门槛。这些参数是毕设项目里最常见的初始值。如果你的测试图片里人脸比较大、数量少,这三个参数基本不用动;如果图片里人脸又多又小,就要把min_face_size降到 10 或 15,代价是误检会变多。

参数说明到这里,还有一个容易被坑的细节:cv2.imread对图片路径里的中文支持很差,Windows 用户如果把测试图片放在“我的文档”“测试图片”这类中文路径下,img很可能读出来是None,程序不报错但后续代码全崩。习惯是把图片和代码放在全英文路径下,能省掉一堆玄学问题。

3.3 接摄像头实时流:从单张图片扩展到视频多目标

单张图跑通之后,再上摄像头就顺理成章了。实时视频流的本质就是不断读帧、送进检测器、画框、显示。你要注意 MTCNN 在 CPU 上的速度大概每帧 20 到 40 毫秒,USB 摄像头读帧本身也花时间,所以视频流代码里最稳妥的是“隔帧检测”:这一帧检测并画框,下一帧直接显示上一帧的结果,帧率能提升接近一倍。看下面的示例:

import cv2 from facenet_pytorch import MTCNN detector = MTCNN(keep_all=True) cap = cv2.VideoCapture(0) # 0 代表第一个摄像头 frame_skip = 1 boxes_cache = None while True: ret, frame = cap.read() if not ret: break frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if frame_skip % 2 == 0: # 偶数帧做检测,奇数帧沿用旧框 boxes_cache, _ = detector.detect(frame_rgb) if boxes_cache is not None: for box in boxes_cache: x1, y1, x2, y2 = [int(v) for v in box] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow("Multi-Target Face Detect", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break frame_skip += 1 cap.release() cv2.destroyAllWindows()

这段代码有一个重要的工程取舍:frame_skip控制检测频率,偶数帧才检测,奇数帧直接用上一帧的框结果画。好处是 CPU 占用明显下降,坏处是人快速运动时框会有一点“拖影”。毕设演示时我先用隔帧策略保证流畅,答辩时再用满帧检测让大家看到精确框,两种模式都写在同一个文件里,用变量切换即可。如果cap.read()一直返回 False,先检查摄像头是不是被其他软件占用,或者把VideoCapture(0)改成VideoCapture(1)试另一路设备。

4. 把代码改造成自己的毕设:数据集、训练与身份库落地

4.1 自定义人脸库的组织方式:文件夹名字就是身份标签

毕业设计一般不允许直接拿现成数据集仓库交差,老师往往会要求你自己采集或组织一定的数据。如果你不想采集,可以用公开数据集,比如 LFW(野外人脸数据集)或 CASIA-WebFace 的一部分;但更省事、也更贴合“系统是自己做的”这个视角的做法,是给每个身份建一个文件夹,放 3 到 5 张不同角度的照片,整个数据集长这样:

face_data/ zhang_san/ zhang_san_01.jpg zhang_san_02.jpg zhang_san_03.jpg li_si/ li_si_01.jpg li_si_02.jpg li_si_03.jpg wang_wu/ wang_wu_01.jpg wang_wu_02.jpg

这种组织方式的妙处是:文件夹名可以直接当身份标签用,代码遍历目录时用os.listdir()拿到目录名作为姓名,再读取目录下的所有图片生成特征,不用额外维护 CSV 或数据库。对毕设演示来说,这是最直观、最不容易出错的方案。采集照片时注意尽量保证光线均匀、人脸占画面比例不要太小,否则后面注册出来的特征质量差,识别阶段会疯狂误判。

注册特征入库的代码如下,注意它做了“特征平均”:

import os import torch import numpy as np from facenet_pytorch import MTCNN, InceptionResnetV1 # 加载预训练模型,vggface2 数据集上训练好的权重 encoder = InceptionResnetV1(pretrained="vggface2").eval() detector = MTCNN(keep_all=False) # 身份库用一个字典存:姓名 -> 平均特征向量 face_db = {} for identity in os.listdir("face_data"): identity_dir = os.path.join("face_data", identity) if not os.path.isdir(identity_dir): continue feature_list = [] for img_name in os.listdir(identity_dir): img_path = os.path.join(identity_dir, img_name) img = cv2.imread(img_path) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) face = detector(img_rgb) # 检测并裁剪出单张人脸,返回 3xHxW 张量 if face is None: continue # 检测不到人脸的跳过 # 把模型置为推理模式,不计算梯度 with torch.no_grad(): embedding = encoder(face.unsqueeze(0)).numpy()[0] feature_list.append(embedding) if feature_list: # 求所有特征向量的平均,提升鲁棒性 face_db[identity] = np.mean(feature_list, axis=0) print(f"已注册 {identity},有效照片 {len(feature_list)} 张") else: print(f"警告:{identity} 没有有效照片") np.save("face_db.npy", face_db, allow_pickle=True) print("身份库已保存至 face_db.npy")

这段代码有几个容易出错的地方需要特别留意。detector(img_rgb)返回的是已经裁剪好、缩放成 160×160 的人脸张量,而且它会自动做关键点对齐,所以你不需要在送入encoder前自己切图。如果某张照片里检测不到人脸,face为None,必须做跳过处理,否则程序会直接崩溃。注册阶段还有一个很小的比例问题:如果同一个人的照片里有一张特别模糊,导致特征向量离群,平均之后整体特征会被带偏,所以注册前最好肉眼看一遍照片,把明显模糊的删掉。这是纯粹的数据质量问题,任何算法都救不回来。

4.2 阈值判定与陌生人的区分:d 距离的两种打开方式

身份库建好后,识别阶段就是把相机里抓到的人脸特征跟库里做匹配,选距离最近的,然后跟阈值比较决定是否放行。这里有一个毕设论文里一定会被老师追着问的点:你是怎么区分“认识的人”和“陌生人”的?答案就是阈值。如果最近距离比阈值还大,说明库里任何人都匹配不上,判定为陌生人。代码实现常见做法是:

def recognize(face_embedding, face_db, threshold=0.85): min_dist = float("inf") best_name = "unknown" for name, db_emb in face_db.items(): dist = np.linalg.norm(face_embedding - db_emb) if dist < min_dist: min_dist = dist best_name = name if min_dist > threshold: return "stranger", min_dist return best_name, min_dist

np.linalg.norm(face_embedding - db_emb)计算的是欧氏距离,阈值 0.85 是 FaceNet 在野外数据集上比较常用的分界点。但你别直接抄这个数——实际阈值跟你的数据复杂度密切相关。如果你采集的照片全是同一个摄像头、同一个角度,特征向量会明显聚拢,距离普遍偏小,阈值可以压到 0.6;如果数据来源杂、光线变化大,距离整体变大,阈值反而要放宽到 1.0 左右。正确做法是:把一部分照片留出来当测试集,跑一遍所有比对距离,画出正负样本的距离直方图,取交叉点做阈值。这个操作在论文实验章节里非常好用,也是后面避坑章要展开的内容。别嫌这一步烦,阈值定得不准,识别率再高的模型也会被你搞到全员陌生人。

4.3 训练环节:要不要自己重训识别模型,怎么演示“深度学习”含量

老师一般会要求毕设里有“自己训练”的成分,但这里有个认知分歧需要说清楚。从头训练一个 FaceNet 或 ArcFace,需要几十万张人脸照片和多卡 GPU 跑好几天,这在本科毕设里既不现实也没必要。常见做法是采用迁移学习:加载预训练权重,在自建的小数据集上微调最后几层分类层。代码里只需要把 InceptionResnetV1 当作特征提取器,把它的输出接一个自定义分类层。

用一个简单的 PyTorch 训练循环说明:

import torch.nn as nn import torch.optim as optim # 复用预训练模型,但不要它的原分类头 base_model = InceptionResnetV1(pretrained="vggface2") base_model.last_linear = nn.Linear(512, 3) # 假设你有 3 个类别 optimizer = optim.Adam(base_model.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss() for epoch in range(5): for batch_x, batch_y in train_loader: optimizer.zero_grad() output = base_model(batch_x) loss = criterion(output, batch_y) loss.backward() optimizer.step() print(f"epoch {epoch} loss: {loss.item():.4f}")

这个微调方案的逻辑在于:预训练模型已经把底层的人脸纹理、轮廓特征学得很好了,你要学的是“你数据集里这几个人长什么样”的个性化分类边界。训练集很小的情况下,学习率一定要调低(0.001 以下),不然会把预训练的特征冲掉,现象是训练集 loss 低、测试集识别率反而更差。论文里你可以理直气壮地写“基于预训练模型 + 小样本微调”,这是业内认可度很高的做法,不是偷懒。如果你连微调都不想做,那在论文里至少要有“使用基于 VGGFace2 预训练的 FaceNet 模型作为特征提取器,冻结权重并提取特征用于分类实验”这种描述,学术上也是站得住的。

5. 避坑指南:六个让毕设翻车的常见问题与排查思路

5.1 大合照里永远检测不出后排小脸

现象:一张 1920×1080 的合照,前排人脸能检测到,后排人脸特别小,一个都检不出来。原因:MTCNN 的min_face_size默认 20,代表最少 20 像素边长的人脸才会被送入网络级联;后排人脸缩放到只有 15 像素左右,直接在第一层 PNet 就被抛弃了。解决:把min_face_size降到 10,同时把thresholds里的第一个值从 0.6 降到 0.4,但这样误检也会明显增多,建议只在小脸场景临时调,平时保持默认。如果发现误检增多导致系统不可用,就在检测后加一个过滤规则:人脸框的宽高不能小于整张图宽度的 2%,低于这个比例的框直接丢弃。

5.2 同一个人的两张照片比对距离高达 1.3,怎么都匹配不上

现象:注册时用了一张证件照,识别时用的是手机随手拍,结果两张图的距离大于阈值,认不出。产生这个现象的原因很典型:证件照和手机自拍的光线、角度、表情差异太大,远超模型能“无视”的范围。解决:注册时不要只传一张图,至少传 3 张不同光线和角度下的照片;然后在你的测试集上重新计算同类距离和异类距离,把阈值取两者的中间值。这条是血泪经验,很多人拿到项目代码后只换数据不换阈值,结果识别率一塌糊涂还以为是模型坏了。

5.3 摄像头上检测 CPU 占用 100%,画面卡成 PPT

现象:视频流跑起来后,风扇狂转,帧率不到 5 FPS,画框严重滞后。原因:MTCNN 本身就有三级级联,在 CPU 上每帧大约要 40 到 60 毫秒,如果每帧都跑,CPU 根本扛不住。解决:三个招数同时用。第一,用前面说的隔帧检测,检测频率减半;第二,把输入帧缩放一半再送检测器,检测耗时能降到原来的四分之一;第三,把图像金字塔 factor 从 0.709 改成 0.5,减少金字塔层数。这三招下来帧率基本能回到 15 FPS 以上,完全满足演示。毕设答辩现场电脑性能不可控,建议无论如何都保留一个低分辨率模式,用快捷键一键切换。

5.4 保存人脸库后重新启动,加载 npy 文件报错 object arrays cannot be loaded

现象:注册阶段生成的face_db.npy下次启动时np.load()报错,提示没法直接赋值。原因:字典里每个值是一个长度不等的数组,直接np.save会存成一个 object 数组,读取时容易踩序列化兼容问题。解决:不用 npy,改用 JSON 保存一份中间格式,或者读取时带allow_pickle=True。最稳的是直接换一种存储结构,把特征归一化后拼成一个大矩阵,另存一个names.npy存名字顺序,矩阵的每一行对应该名字的特征。这样后面做增量注册也方便,不用每次重算全库。

5.5 pip 安装 facenet_pytorch 时找不到合适的版本

现象:Python 3.10 以上环境执行pip install facenet_pytorch,装到一半报编译错误或依赖冲突。原因:这个库的部分依赖编译文件没有维护新 Python 版本的预编译包,特别是 Windows 上更容易中招。解决:用 Python 3.8 建环境;如果项目代码本身要求 torch,就别用 numpy 1.24 以上的版本,因为旧 torch 跟新 numpy 存在符号兼容问题。实在不行,退路是把检测部分换成opencv自带的 DNN 人脸检测器,识别部分单独用onnxruntime加载 ONNX 模型,这两个包对新 Python 的兼容性都很友好。

5.6 打开摄像头程序黑屏但没报错

现象:程序正常启动,cap.read()一直返回 True,但显示的窗口是全黑的。原因:笔记本摄像头被驱动设为隐私模式,权限没交给命令行或 vscode 进程;也可能是摄像头被另一个软件(比如腾讯会议)独占了。解决:先关掉其他占用摄像头的软件,然后去 Windows 设置里把“允许桌面应用访问摄像头”打开。如果代码里cap.isOpened()返回 True 但画面黑,多半是权限问题;返回 False 才是设备索引问题。

6. 把毕设做出彩的三个进阶操作:指标、阈值校准与加速导出

当你的代码能够稳定跑通,你已经完成了“能演示”这个基本目标。但毕设答辩里老师大概率不会只看你演示,还要问“你的系统识别率多少、误检率多少、凭什么说它好”。所以接下来这三件事,是把普通项目变成高分项目的关键。

第一件事,给系统做一个可量化的评估。在 LFW 或者你自建数据集上跑一遍批量识别,统计准确率(Accuracy)、精确率(Precision)、召回率(Recall)和 F1 分数。最简单的方法是把你识别脚本里的单张推理封装成一个函数,然后对测试集循环调用,把预测结果和真实标签输进 sklearn 的 classification_report。代码大概长这样:

from sklearn.metrics import classification_report y_true = [] y_pred = [] for identity in os.listdir("test_data"): for img_path in test_images[identity]: embedding = encode_image(img_path) name, _ = recognize(embedding, face_db, threshold=0.75) y_true.append(identity) y_pred.append(name) print(classification_report(y_true, y_pred, digits=4))

注意recognize返回的阈值得用下面第二件事的校准结果,不能用猜的数。这一步做完,你的论文实验章节就有了第一张数据表。

第二件事,做阈值校准而不是拍脑袋定阈值。把你测试集里所有同类和异类的距离值都收集起来,分别做直方图。你会在图上看到两个分布:左边是同一个人距离集中的峰,右边是不同人距离的峰。取两个峰交界处的距离作为阈值,是理论上等错率最优的做法。简单实现:

same_dists = [] diff_dists = [] # 遍历身份库与测试集,计算距离,按是否为同一个人归档 import numpy as np threshold = np.mean([np.min(same_dists), np.max(diff_dists)])

实际中两个分布往往有重叠,所以更科学的是把重叠区域的最小值点作为选定的阈值。如果重叠太多,说明你的照片质量不行或者注册照太少,先去补数据再谈调阈值。

第三件事,把识别模型导出成 ONNX 提速。facenet_pytorch的模型在 CPU 上跑的瓶颈主要是 PyTorch 框架的调度开销,导出成 ONNX 用 onnxruntime 推理,单张特征提取能从约 50 毫秒降到 20 毫秒左右。推理代码也从torch.no_grad()变成了session.run,整个拉通之后,摄像头识别模式可以做到每帧都识别而不用隔帧。这一步不是必需的,但做完之后你的系统运行速度明显快于原版代码,答辩演示效果会好很多。

最后说一个我自己习惯的验收方法:不要一上来就拿着摄像头对着人连测十分钟,那样你根本分辨不出是识别错了还是检测漏了。正确做法是先把测试集里的静态图片全部跑一遍,记录失败案例并分类——如果失败集中“检测不到脸”占大头,就是检测模块的问题;如果“检测到了但匹配错”占大头,就是识别阈值或注册库的问题。这样定位问题比瞎调参快得多。这个习惯让我少走了很多弯路,也希望帮到你后续改代码时不被突如其来的结果搞糊涂。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询