拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于深度学习的人脸识别考勤系统全流程实战与避坑指南

基于深度学习的人脸识别考勤系统全流程实战与避坑指南 简介一份面向计算机专业毕业设计与考勤应用场景的完整Python源码项目适合正在完成课程设计、期末大作业或希望从项目实战中提升识别系统开发能力的学习者。项目基于深度学习完成人脸识别考勤的核心流程压缩包共25个文件包含py程序文件、png界面与素材、ttf字体、ico图标及config配置等整体约239.41MBdocx使用手册可辅助快速理解项目结构与运行方式。资源经导师认可并获得98分评审目前已有166人学习。下载后可研读识别算法与界面代码复用背景图、字体和配置模板也可参考手册中的模块划分与设计思路用于扩展自己的考勤系统。包内另附人脸识别相关软件便于搭建运行环境适合作为毕业设计、课程设计或期末大作业的完整参考。1. 基于深度学习的人识别考勤系统源代码到底解决了什么、适合谁如果你的本科毕设选题恰好是「基于深度学习的人脸识别考勤系统源代码」先给你一句实话这套东西不缺开源方案缺的是能讲清楚、能复现、能扛住答辩的完整落地链路。市面上能搜到的人脸识别门禁机方案大多是烧录在嵌入式板子上而本科毕设版的源码通常是 Python 搭配深度学习模型跑在普通笔记本上接一个 USB 摄像头就能完成打卡识别。它能解决的问题很具体替代刷卡考勤、防止代打卡、把出勤数据自动落库。适合三类人——想快速拿到一套能跑通全流程代码的应届生、想在此基础上做功能打包的开发者以及需要给课程设计找一个完整项目模板的学生。这篇笔记会按「架构选型 → 最小系统复现 → 考勤逻辑落地 → 踩坑记录」的顺序把做这个方向的关键参数和边界一次讲透。2. 先把系统拆出骨架检测、识别、存储、界面四层怎么选型2.1 检测与识别算法选择为什么是 MTCNN FaceNet 而不是 YOLO ArcFace人脸识别考勤系统的核心链路是「检测出人脸 → 对齐人脸 → 提取特征向量 → 比对相似度」。常见的开源组合里第一组是 MTCNN 做检测、FaceNet 做特征提取第二组是 YOLO 做检测、ArcFace 或 InsightFace 做特征提取。绝大多数本科毕设源代码选的是前者原因是 MTCNN 和 FaceNet 的预训练权重小、环境依赖少、CPU 上能跑而 YOLO 加 ArcFace 的组合虽然识别精度上限更高但对 GPU 显存和 CUDA 版本的要求更容易让新手在半路翻车。检测与识别层的分工要明确。MTCNN 的输出不只是人脸框还带五个关键点坐标——两只眼睛、鼻子、嘴角。关键点的用途不是给界面画圈而是用来做人脸对齐把歪头、侧脸的人脸旋转归一到标准位置。这一步不做FaceNet 提取的特征向量波动会明显变大同一个人的识别相似度可能从 0.82 掉到 0.61。FaceNet 做的事情是把人脸图像映射成一个 128 维或 512 维的浮点向量学号录入阶段把每个人的多张照片转成一组向量存起来识别阶段把摄像头抓到的脸也转成向量然后和库里做距离比对。选型的时候还要考虑一个现实问题毕设答辩时老师大概率会问「为什么不直接用现成的人脸识别 API」。源代码方案的立足点应该是「全流程在本地运行数据不出内网」而不是「我们比大厂的算法更准」。所以在论文里写清楚选型理由时优先级排序是可控性、离线可用、二次开发空间最后才是识别率高低。2.2 存储与界面选型SQLite、PyQt5、OpenCV 的配合方式考勤系统的存储层很少会用 MySQL常见做法是 SQLite。原因是本科毕业设计的并发量极小SQLite 单文件、零配置、随代码一起提交就能跑省去了数据库服务的安装步骤。表结构一般设计成三张表员工表、考勤记录表、识别日志表。员工表存学号或工号、姓名、人脸特征向量的文件路径考勤记录表存打卡时间、日期、考勤状态正常、迟到、早退、缺卡、补卡识别日志表存每一次摄像头识别到人脸的原始时间点和比对的相似度分数这一张表在写论文时非常好用可以作为「系统运行日志」的实证材料。界面层用 PyQt5 是绝大多数源码包的选择。它不是最现代的界面框架但胜在稳定QTimer 控件和 OpenCV 的视频帧读取能配合起来信号槽机制也适合处理「摄像头画面实时刷新 后台比对线程」并行这样的典型需求。如果你看到某个开源项目用的是 Tkinter也不是不行只是做多页面切换和表格展示时会费劲一些。摄像头环节的坑要提前和你说清楚OpenCV 的 VideoCapture 读取本地 USB 摄像头时图像格式默认是 BGR而 PyQt5 的 QImage 需要 RGB每一帧都要做一次颜色通道转换。很多源码在这个转换处不做缓存导致界面显示的画面延迟严重。正确做法是只保留最新一帧的引用比对线程每次拿当前帧去处理而不是把所有帧都塞进队列。2.3 目录结构与数据流从摄像头到考勤记录的一次完整流转一个结构清晰的人脸识别考勤系统源码包目录应该是这样的目录/文件职责data/known_faces/按学号分文件夹存放录入照片data/embeddings.npy所有已录入员工的 128 维特征向量矩阵data/attendance.dbSQLite 数据库文件modules/detector.pyMTCNN 人脸检测与对齐封装modules/encoder.pyFaceNet 特征提取封装modules/attendance.py考勤判定与数据库写入逻辑ui/main_window.pyPyQt5 主界面scripts/collect_faces.py人脸样本采集脚本scripts/train_embeddings.py特征向量生成脚本main.py程序入口数据流向分为两条。录入路径摄像头抓拍人脸照片 → 保存到 data/known_faces/ 对应学号文件夹 → 批量读取照片、检测对齐、提取特征向量 → 保存到 embeddings.npy。识别路径摄像头实时帧 → MTCNN 检测出人脸框 → 对齐裁剪 → FaceNet 编码成向量 → 与 embeddings.npy 中的向量算距离 → 距离最小且低于阈值则判定为对应员工 → 写入考勤记录表并更新界面。这两条链路缺一不可而且必须用独立的脚本先跑通录入路径再跑实时识别路径否则会出现「认识全班同学却认不出你自己」的怪现象——因为你跳过了录入阶段的特征入库。3. 用自己的数据集跑通最小系统采集、训练、推理三步走3.1 环境搭建与依赖Python 3.8 PyTorch OpenCV 的版本组合开始写代码之前先把环境固定下来。这套方案我一般推荐 Python 3.8不是因为它最新而是因为 Python 3.8 对 PyTorch、OpenCV、PyQt5 三者的老版本轮子兼容性最好网上搜到的 python 安装教程大部分也是基于 3.8 或 3.10 的。如果你机器上没有独立环境先建虚拟环境再装依赖避免把系统 Python 搞乱。python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install torch --index-url https://download.pytorch.org/whl/cpu pip install facenet-pytorch pip install opencv-python pip install PyQt5 pip install numpy依赖装的顺序有讲究。先装 torch 是为了让 facenet-pytorch 在安装时能正确识别 PyTorch 的存在后装 opencv 是防止 OpenCV 的 numpy 版本要求和 facenet-pytorch 冲突。facenet-pytorch 这个库自带 MTCNN 和 InceptionResnetV1 的预训练权重初次运行时会自动下载权重文件如果你的机器网络受限记得去这个库的 GitHub Release 里手动下载权重并放到缓存目录否则第一次运行会在加载模型时报错。装完跑一句python -c import torch, cv2; print(torch.__version__, cv2.__version__)确认版本打印正常再继续下一步。3.2 人脸样本采集脚本摄像头自动抓拍与清洗这一步的目标是给每个人采集 3050 张不同角度、不同光照条件的人脸照片。照片质量直接决定后面的识别率宁可花十分钟把照片拍好也不要等到识别阶段才发现问题。import cv2 import os from facenet_pytorch import MTCNN student_id input(请输入学号) save_dir fdata/known_faces/{student_id} os.makedirs(save_dir, exist_okTrue) mtcnn MTCNN(keep_allFalse, post_processFalse) cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) count 0 while count 50: ret, frame cap.read() if not ret: continue # 检测人脸并在画面中画框提示用户调整位置 boxes, probs mtcnn.detect(frame) if boxes is not None and probs[0] 0.95: x1, y1, x2, y2 [int(v) for v in boxes[0]] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(collect, frame) if cv2.waitKey(1) 0xFF ord(s): aligned mtcnn(frame) # 返回对齐后的人脸张量 if aligned is not None: # 转成 numpy 再存为 jpg统一尺寸 160x160 img aligned.permute(1, 2, 0).numpy() * 255 cv2.imwrite(f{save_dir}/{count:03d}.jpg, img) count 1 print(f已采集 {count}/50 张) else: cv2.imshow(collect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码里有三个参数需要你留意。第一个是MTCNN(keep_allFalse)表示同一帧只保留置信度最高的一张脸单人录入时用 False后面做多人同框识别时才改成 True。第二个是mtcnn(frame)的返回值已经是裁剪并归一化到 160×160 的张量直接用permute把通道顺序从 C×H×W 换回 H×W×C再乘 255 还原成 0~255 的像素值存图。第三个是probs[0] 0.95的人脸置信度门槛采集时定高一点可以避免把模糊帧存进训练集。采集过程中要刻意转动头部方位——左右转头、低头、抬头各拍若干张只拍正脸会导致后续识别时稍微侧脸就认不出人。采集完的照片要做一次粗清洗。用文件管理器打开文件夹把模糊的、闭眼的、带大面积遮挡的照片删掉。这一步在代码里做自动清洗也行但要写模糊度判定函数本科阶段手动删除性价比更高50 张照片里删掉十几张是正常现象别心疼。3.3 生成特征向量把照片库变成一个人的身份模板「训练」这个词在毕设源代码里容易引起误解。基于 FaceNet 的常规方案并不是从头训练一个深度学习模型而是用预训练好的 InceptionResnetV1 网络把每张人脸照片编码成一个特征向量这个过程专业上叫推理但对毕设来说「训练特征库」这个说法更容易和论文里「模型的训练过程」对齐。真正会触发模型参数更新的操作只有当你想微调模型时才发生而大多数源代码并不需要微调。import numpy as np import torch from facenet_pytorch import InceptionResnetV1, MTCNN from PIL import Image import os device torch.device(cuda if torch.cuda.is_available() else cpu) mtcnn MTCNN(keep_allFalse) resnet InceptionResnetV1(pretrainedvggface2).eval().to(device) root data/known_faces embeddings, labels [], [] for student_id in sorted(os.listdir(root)): path os.path.join(root, student_id) if not os.path.isdir(path): continue student_embs [] for img_name in os.listdir(path): if not img_name.endswith(.jpg): continue img Image.open(os.path.join(path, img_name)).convert(RGB) face mtcnn(img) # 检测对齐输出 160x160 张量 if face is None: continue with torch.no_grad(): emb resnet(face.unsqueeze(0).to(device)) # 输出 1x512 向量 student_embs.append(emb.cpu().numpy()[0]) if student_embs: # 一个人取所有特征向量的均值作为模板增强鲁棒性 embeddings.append(np.mean(student_embs, axis0)) labels.append(student_id) np.save(data/embeddings.npy, np.array(embeddings)) np.save(data/labels.npy, np.array(labels)) print(f已生成 {len(labels)} 人的特征模板)几个关键点拆开讲。第一个是InceptionResnetV1(pretrainedvggface2)vggface2 是这个预训练权重的数据集名称比 casia-webface 的权重在亚洲人脸数据集上的表现通常略好本科生直接用 vggface2 是稳妥选择。第二个是resnet.eval()eval 模式会关闭 dropout 和 batch normalization 的运行时统计更新推理时必须加不加的话同一张照片每次编码出的向量都会有微小差异可能导致识别结果抖动。第三个是取均值作为模板而不是拿第一张照片的向量当模板均值模板能平滑单张照片的光照和角度噪声这是提高识别稳定性的性价比最高的一招。生成完 embeddings.npy 后打印一下形状比如30, 512确认有 30 个人、每人 512 维特征再进入下一步。3.4 实时识别的最小代码检测、编码、比对、标注一次跑通实时识别不能只靠 FaceNet必须让 MTCNN 先在前一帧或者当前帧把人脸找出来再交给 FaceNet 编码。为了让界面看起来流畅我一般把摄像头分辨率调到 640×480MTCNN 在这个分辨率下的检测速度能满足 15 帧每秒左右再高分辨率 CPU 就扛不住了。import cv2 import numpy as np import torch from facenet_pytorch import MTCNN, InceptionResnetV1 from scipy.spatial.distance import cosine device torch.device(cuda if torch.cuda.is_available() else cpu) mtcnn MTCNN(keep_allTrue, post_processFalse) resnet InceptionResnetV1(pretrainedvggface2).eval().to(device) embeddings np.load(data/embeddings.npy) labels np.load(data/labels.npy, allow_pickleTrue) threshold 0.75 # 余弦距离阈值小于阈值才算同一个人 cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) boxes, _ mtcnn.detect(rgb_frame) if boxes is not None: for box in boxes: x1, y1, x2, y2 [int(v) for v in box] face mtcnn(rgb_frame) # 返回当前帧中所有人脸的张量列表 if face is None: continue # face 是包含多张人脸张量的列表这里按单张处理 for f in face: with torch.no_grad(): emb resnet(f.unsqueeze(0).to(device)).cpu().numpy()[0] # 与库里每个模板算余弦距离取最小值 dists [cosine(emb, db_emb) for db_emb in embeddings] best_idx int(np.argmin(dists)) best_dist dists[best_idx] if best_dist threshold: name labels[best_idx] color (0, 255, 0) else: name unknown color (0, 0, 255) cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText(frame, f{name} {best_dist:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) cv2.imshow(attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()注意这段代码里mtcnn在初始化时设置的是keep_allTrue和采集脚本里的False不同因为实时识别时画面上可能出现多人需要保留所有人脸框。代码里mtcnn.detect(rgb_frame)用来拿框的位置mtcnn(rgb_frame)用来拿对齐后的张量两次调用会让检测重复执行一遍这是为了代码通俗易懂做的取舍。如果追求性能可以用mtcnn.detect返回的关键点坐标手动裁剪对齐省掉第二次检测。实际上 facenet-pytorch 的检测和编码可以封装成一次调用不过对新手来说先跑通再优化性能才是正路。跑通这段代码后你会面临一个实际问题画面里显示的名字和距离值已经出来了但考勤记录还没有入库。这就是第 4 章要解决的问题——识别正确不等于考勤成功中间还隔着打卡判定和去重逻辑。4. 考勤功能落地的工程细节打卡判定、阈值、去重、补卡逻辑4.1 打卡判定与相似度阈值距离和分数的换算是第一个必调参数人脸识别输出的相似度可以用多种度量方式表示。FaceNet 官方推荐用欧氏距离但实际工程里更通用的是余弦距离和余弦相似度。很多源代码里写的是「匹配分数」你需要搞清楚它是距离还是相似度否则调参时会闹出大笑话。余弦距离的取值范围是 0 到 2越接近 0 越像同一个人余弦相似度是 1 减去余弦距离越接近 1 越像。如果你的代码里写着if score 0.5判定为同一个人而 score 实际是余弦距离那么 0.5 意味着「有点不像但没那么不像」系统会把所有人都认成同一个人。我一般这样设计判定逻辑阈值区间余弦距离判定含义处理动作小于 0.6高度相似确定是本人直接打卡成功0.6 ~ 0.75中等相似可能是本人连续识别 3 帧确认后再打卡0.75 ~ 1.0低相似大概率不是本人忽略或标记为陌生人大于 1.0完全不像忽略连续识别 3 帧确认的机制在考勤系统里非常关键。单帧识别有两个问题一是人在走动时抓拍的脸可能是歪的或模糊的单帧相似度偏低二是场景里出现两个相似的人时单帧判断容易出错。连续多帧取稳定结果相当于给每一张打卡记录做了一层投票滤波。代价是判断耗时增加但对考勤系统来说等一两秒出结果是完全可以接受的。打卡成功后的时间点要注意写数据库的时间应该用打卡动作确认系统时间而不是读取摄像头采集时间。因为摄像头帧率可能只有 15 帧画面里的时间戳比真实时间晚几十毫秒到几百毫秒对考勤来说不重要但如果你的项目要求精确到秒级就统一用服务器本地时间。4.2 重复打卡与多人同框的处理策略同一个人的脸在画面里停留 10 秒识别模块每帧都能认出他如果不做去重数据库会被刷出几十条打卡记录。常见的去重策略是单次打卡间隔在考勤记录表里查询该员工当天最后一次打卡时间如果与当前时间的间隔小于 5 分钟就视为重复打卡不再写入新记录。这个 5 分钟间隔要根据考勤场景调整上午上班高峰 5 分钟合理下午下班时段如果人流量大可以缩到 1 分钟。更复杂的场景是多人同框。两个学生并排站在摄像头前画面里出现两张脸这时需要区分谁是真正来打卡的。一个常见做法是只处理「画面中央区域的人脸」离摄像头镜头中心越远的人脸越可能是路过的人。实现方式是给画面中心画一个打卡框只有当人脸框的中心点落在打卡框内时才触发考勤判定。这个设计在答辩时是个很好的功能亮点它避免了多人同时出现在画面里时误刷身份。多人同时打卡的正确姿势是每个人脸框都做检测编码和比对全部识别完成后一次事务写入数据库。注意不要让每张脸单独开一个数据库事务批量写入的性能和日志完整性都会更好。-- 考勤记录表结构示例 CREATE TABLE IF NOT EXISTS attendance ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL, name TEXT NOT NULL, date TEXT NOT NULL, time TEXT NOT NULL, status TEXT CHECK(status IN (normal, late, early, miss, makeup)), sim_score REAL NOT NULL, UNIQUE(student_id, date, time) );这个表结构里值得注意的字段是status和sim_score。status的五个状态对应考勤系统的完整业务规则sim_score记录的是识别时的最小余弦距离保留这个分数有两个用处一是复盘某条打卡记录是否可靠二是论文里可以统计不同光线条件下识别分数分布。UNIQUE(student_id, date, time)的约束是数据库层的兜底去重即使应用层忘记判断重复数据也写不进去。4.3 迟到早退判定与补卡逻辑判断迟到不能只靠打卡时间还得有课程表或排班表。最简单的方案是在 Python 里配置一个考勤规则字典比如上午第一节课的签到窗口是 08:00 到 08:1508:00 前的记录算 normal08:15 到 09:00 算 late09:00 之后算 miss。但考勤规则经常变写死在代码里不方便常见做法是把规则存到 SQLite 的 config 表里界面提供修改入口。对本科毕设来说代码里写一个常量字典也够用答辩老师更关心你的判定逻辑是否清晰而不是你有没有做成可配置化。补卡逻辑是容易被忽略但必然会用到的功能。学生人脸识别失败的场景远比想象中多刚起床脸肿了、额头长了痘、戴了口罩、眼镜反光。这时候必须有补卡通道。常见实现是化名验证——在界面上输入学号调取系统里已录入的摄像头历史抓拍图管理员确认后把一条 statusmakeup 的记录写入考勤表。不要做成直接改数据库否则系统缺失了一条完整的业务闭环答辩时会被问住。补卡记录在统计报表里单列一列不算迟到也不算缺勤但要记录补卡操作员和补卡时间。这个设计让系统有了审计能力而且只需要在考勤表里增加两个字段operator和operate_time实现成本极低但让整个系统的完整度提高了一个档次。5. 人脸识别考勤系统避坑指南5 个真实翻车场景与排查方法5.1 摄像头画面在 PyQt 界面里卡成幻灯片现象用 OpenCV 打开摄像头后在主窗口 QTimer 的槽函数里读取视频帧并显示界面帧率只有不到 5 帧画面一顿一顿识别响应也慢得离谱。原因QTimer 的触发间隔最短只有几十毫秒但槽函数里同步执行了「读帧 → 转换颜色格式 → 画框 → 更新 QLabel」的操作。OpenCV 的cap.read()本身有 IO 等待时间界面线程被阻塞自然卡顿。更隐蔽的问题是如果你在槽函数里又调用了 MTCNN 检测那一次检测就要耗时 100 毫秒以上整个 UI 全部冻结。解决办法把视频读取和人脸检测全部丢到独立线程界面线程只负责从线程的缓冲区取最新帧显示。具体做法是在线程里用一个实例变量保存最新帧和最新识别结果主界面的 QTimer 每次只读取这个变量不做任何计算。线程里 MTCNN 检测频率可以降为每 2 帧一次识别结果不需要实时刷新800 毫秒更新一次界面足够。5.2 识别率白天正常晚上全崩问题出在采集阶段现象白天开灯环境下识别率九成以上到了傍晚拉上窗帘只开台灯同一个人的余弦距离从 0.55 升到 0.8直接被判定为陌生人。原因FaceNet 预训练模型对光照变化比较敏感训练数据里主要是自然光照的人脸。考勤系统如果采集阶段只在一种光照下拍照特征模板被固化在那个光照环境里。晚上换了光源后人脸图像的整体亮度分布变了特征向量自然偏移。解决办法采集照片时刻意营造多样性——在自然光、日光灯、台灯三种光照下各拍十几张加上正面、左右侧脸、抬头低头多角度。如果已经拍完了可以用 OpenCV 对每张照片做 CLAHE 自适应直方图均衡化后再重新生成特征模板。要注意给用户操作界面上加一个「活体检测开关」选项某些考勤环境有防照片攻击的合规要求而纯深度学习的毕设源代码通常只做静默识别加眨眼判断动作指令交互的活体检测不在默认代码范围内。5.3 训练特征库时显存不足batch size 设得再小也崩现象执行第 3 章的特征生成脚本时单张图片推理可以跑但只要一循环到某几张图片就报 CUDA out of memoryTensorFlow 和 PyTorch 的报错信息都指向显存不足。原因你在循环里把 model 放到 GPU 上每张图片都调用了resnet(face.unsqueeze(0).to(device))理论上不会积累显存。真正的问题常常出现在 mtcnn 内部——MTCNN 在 GPU 上运行时它的三个子网络P-Net、R-Net、O-Net会在图像金字塔上产生大量中间张量加上 PyTorch 默认的显存缓存机制跑几十张后会越占越多。解决办法把 MTCNN 也放到 CPU 上运行只把 FaceNet 放到 GPU。或者在自己的代码里在循环体末尾加torch.cuda.empty_cache()但更科学的做法是给每个学生的照片数量做个限制一个人最多处理 60 张。如果机器显存只有 2GB干脆全部用 CPU 推理人脸考勤的数据量根本不需要 GPUCPU 单张推理耗时 200 毫秒左右完全够用。还有一个容易被忽略的点检查是否把模型初始化写在了循环体里很多人把model InceptionResnetV1(...)写进 for 循环导致加载了多个模型实例显存瞬间被吃满。5.4 阈值设成 0.5 后整个系统把所有人都认成同一个人现象把余弦距离阈值设为 0.5测试时发现随便来一个陌生人系统都会匹配到库里某一个人并显示打卡成功。换一个同学测试又匹配到另一个人反正没有 unknown 的提示。原因0.5 对于余弦距离来说是一个非常宽松的阈值。理解余弦距离的分布同一人不同光照下的距离通常在 0.5 到 0.7 之间不同人的距离在 0.8 到 1.2 之间两个分布之间存在重叠区。阈值定在 0.5等于把「人脸的相似性」放宽到只要是同一张脸型就过系统几乎无法拒绝任何人。解决办法不要拍脑袋定阈值用验证集来标定。做法是把每一张已录入照片随机抽取一张和库里所有人的模板算一遍距离画出 ROC 曲线选取误识率低于 1% 对应的距离值作为阈值。经验值参考在教室固定摄像头场景下余弦距离阈值在 0.65 到 0.75 之间比较合适如果你的摄像头分辨率高、光照稳定可以压到 0.6 以下。阈值不是越高越好0.75 意味着对戴口罩或角度偏大的场景比较宽容但也会增加误识率到底选哪个值需要按你的实际环境调。5.5 答辩时被问「源代码里训练部分在哪」答不上来现象答辩演示时系统运行得很好但老师翻代码发现里面没有传统意义上的训练模块没有 loss 曲线、没有 epoch、没有准确率图于是追问「你的深度学习体现在哪里」。原因源代码方案里用了预训练模型因为走的是迁移学习路线。如果不解释清楚「预训练权重是别人训练好的、我的训练过程是特征模板生成」答辩就容易陷入被动。解决办法在论文里把训练部分拆成两个阶段——预训练模型加载阶段和个性化特征库生成阶段。前者引用 FaceNet 在 VGGFace2 上的训练结果后者描述你如何用自己班上学生的照片生成专属特征库。可以补做一组实验来佐证随机抽取 10 个人的照片在 10 个不同光照和角度条件下测试识别率画一张准确率柱状图这是完全用你自己的代码和数据产出的结果答辩含金量比引用论文数据高得多。另外可以收集偏向于验证集的测试用例逐帧模拟识别过程生成标注后的视频片段作为答辩展示素材。6. 从「能跑」到「能答辩」阈值调优、周报生成与论文实验设计人脸识别考勤系统的价值不止于打点卡把数据变成报表才是这个项目真正完整的形态。我在设计考勤统计模块时坚持加入周报生成功能从 attendance 表里按周汇总每个人的正常、迟到、早退、缺卡次数输出成一张带图表的 HTML 页面。Python 标准库里的sqlite3加jinja2模板就能实现不需要引入额外报表工具。关键是统计口径要定义清楚一周内缺卡次数大于等于 2 次的人在报表里用红色标记考勤负责人一眼就能看到异常名单。论文实验部分我建议做三组对比第一组比较不同阈值的误识率与拒绝率给出你最终阈值的依据第二组比较 MTCNN 加 FaceNet 与传统的 LBPH 人脸识别在同一数据集上的准确率让深度学习路线的优势可视化第三组是不同光照条件下识别率的稳定性测试。这三组实验直接用你自己的系统跑就能完成数据真实可复现比任何文字描述都有说服力。我自己的血泪教训是在毕设验收前两周才意识到阈值没有做系统调优临时把采集照片重新翻拍了一轮差点赶不上提交。现在每次搭识别系统都会第一天就先把验证集和阈值标定脚本写好先把阈值基线定下来再做界面和数据库。希望你起步时就把这一步排进计划别像我一样到最后才补。整个方案到这里已经能支撑你完成一个高分毕设项目剩下的就是把代码里的注释写得更专业、把论文里的图表做得更干净。希望帮到你。本文还有配套的精品资源点击获取
返回列表