公司动态
基于FaceNet与PyQT5的人脸识别考勤系统设计与实现
简介这是一套面向计算机、人工智能及相关专业本科生的毕业设计级学生人脸识别考勤系统实现方案基于PyQt5构建图形界面结合FaceNet卷积神经网络完成人脸特征提取与比对解决传统考勤中人工登记效率低、代签漏洞等问题。资源包共55个文件含23个Python核心模块如face_recognition.py、facenet.py、Sqlite_UI.py、12个UI界面文件.ui及对应py转换脚本、3个SQLite数据库文件含学生信息与考勤记录、以及模型参数.npy、说明文档README.md和图标资源.png整体仅1.98MB轻量易部署。已有294人学习下载代码经严格调试支持Windows 10与Ubuntu 20.04双平台兼容TensorFlow 1.15 CPU/GPU版本。使用者可直接运行SetUpMainWindow.py启动主界面完成用户录入、人脸采集、模型训练与实时检测全流程亦可基于模块化结构如tools/、src/、DB/分层目录快速理解系统架构适合作为课程设计、毕设原型或AI视觉入门实践项目。1. 项目从0到1毕设做一个人脸识别考勤系统我的完整思路与踩坑记录最近后台收到不少学生党留言都在问人脸识别考勤系统怎么做。刚好我手头有个非常典型的毕设项目——基于PyQTFaceNet卷积神经网络实现的学生人脸识别考勤系统源码加说明文档一起放出来了。这篇就把整个项目的设计思路、核心实现、以及那些文档里不会写清楚的坑一次性讲透。先说这系统是干什么的学生走进教室摄像头抓拍一张人脸系统自动识别出是谁然后记录考勤状态出勤、迟到、缺勤。老师端能查看和管理所有记录。就这么一个场景覆盖了图像采集、人脸检测、特征提取、特征比对、界面交互、数据库存储这几大块技术点。适合谁参考计算机、软件工程、电子信息这几个专业做毕设的同学或者想入门人脸识别落地开发的工程师。我翻过很多类似的开源项目多数是“能跑起来但换台机器就废”的状态。这个项目做得比较完整的地方在于它把训练好的FaceNet模型直接拿来做推理不需要你自己训练PyQT负责界面和业务逻辑SQLite存考勤数据整个链路是通的。你拿到源码之后真正要花时间理解的是“流程怎么串起来的”而不是“模型怎么调的”。1.1 为什么选PyQT而不是Web方案考勤系统这种项目很多人的第一反应是做成Web页面。但我个人建议毕设阶段用桌面应用理由很现实第一Web方案要多写一套后端 API还要处理浏览器摄像头权限问题。你用 Flask 或者 Django 搞个上传接口前端再调 getUserMedia光联调就能耗掉一礼拜。PyQT 有 QtWebEngine 和 QCamera 模块直接调本地摄像头省掉中间一层网络传输调试链路短。第二桌面应用在答辩演示的时候更稳。你带着笔记本到教室双击 exe 就能跑不用开着 MySQL、不用部署 Node 服务、不用担心演示现场 Wi-Fi 断了接口超时。PyQT 封装出来的单机程序所有逻辑都在本地出错概率低得多。第三PyQT 的信号槽机制天生适合做这种“触发—响应”的交互逻辑。摄像头检测到人脸、识别结果出来、写数据库、刷新表格每一步都是事件驱动的用信号槽串起来代码比写回调函数清晰得多。所以选 PyQT 不是因为它多高大上纯粹是“最省事、最稳、最贴近实际考勤场景”的选择。1.2 FaceNet在2024年的定位训练太重推理真香FaceNet 是 Google 在 2015 年提出的模型核心思想是把人脸图像映射到 128 维欧氏空间让同一张人脸的向量距离近、不同人脸的向量距离远。在深度学习人脸识别领域它属于“老前辈”了但到现在依然没有过时特别是在毕设和个人项目里。为什么不选更新的模型比如 ArcFace、CosFace说实话2024 年这些新模型确实精度更高但它们的问题是预训练权重不好找、训练资源要求高、部署依赖重。而 FaceNet 有一大堆现成的预训练模型keras-facenet这个库甚至把权重打包好了pip 装完就能用。对于毕设来说你的工作量应该花在“系统集成”而不是“训练模型”上FaceNet 恰好是那个能让你把精力放在系统上的选择。这里要特别说明的是项目标题里写着“FaceNet卷积神经网络”但很多同学容易把卷积神经网络CNN和 FaceNet 混为一谈。FaceNet 的骨干网络实际上是 GoogLeNet 的改进版Inception 结构本质就是 CNN。所以这个标题的意思是基于 CNN 架构的 FaceNet 模型。你用 CNN 做的人脸检测也一样叫卷积神经网络不必被字面绕晕。2. 系统整体架构与三大核心模块这个考勤系统的架构不算复杂但麻雀虽小五脏俱全。整个系统可以画成三个模块界面与交互层、识别引擎层、数据管理层。下面逐个拆解。2.1 界面层从登录到考勤结果展示的完整交互链界面层用 PyQT5 实现主窗口分四个区域登录区教师输入账号密码进入系统这个不是摆设数据库里有一张 admin 表。有人觉得考勤系统做登录多此一举但毕设答辩时评委经常问“这些记录谁能查看有没有权限控制”有这个模块就多一个可讲的点。摄像头预览区调用本机摄像头实时显示画面这是整个系统最有视觉冲击力的部分。原理是用 QCamera 和 QCameraViewfinder 搭起来底层通过 VideoProbe 抓取每一帧画面做识别。识别结果区把抓到的学生姓名、学号、识别时间、考勤状态显示在界面上。为什么识别结果和考勤状态要分开显示因为识别正确不代表考勤记录一定写对分开显示方便排查问题。考勤记录表用 QTableView 展示数据库中的历史记录支持按日期、班级筛选。界面交互的核心逻辑是摄像头预览视频流 — 按下“开始考勤”按钮 — 系统对当前帧做人脸识别 — 识别成功则更新考勤记录并短暂锁定10秒防止同一人连续刷脸 — 识别失败则显示“未注册学生”的提示。注意PyQT5 里 QCamera 的 viewfinder 和 VideoProbe 的关系很多人搞不清楚。Viewfinder 是显示用的VideoProbe 是抓帧用的两者互不干扰但共用同一个摄像头资源。很多新手在这里直接卡住明明画面正常显示但无法抓帧分析就是忘了加 VideoProbe。2.2 识别引擎预训练模型余弦相似度简单有效的人脸比对方案识别引擎是整个系统的技术核心工作流拆开看就四步人脸检测用 OpenCV 的 Haar Cascade 分类器检测画面中的人脸框。这个方案虽老但胜在轻量、CPU 就能跑、速度极快。你项目里写的 CNN 是为了提取特征检测这一步用传统算法完全够用不需要杀鸡用牛刀。如果想在答辩时表现得更高级把检测换成 MTCNN多任务卷积神经网络也行代码改动不大但可视化效果更炫。人脸对齐检测到人脸框后FaceNet 要求输入 160x160 的标准化图像。OpenCV 检测到的框可能包含背景直接缩放训练模型没见过的尺寸会掉精度。所以需要一个预处理步骤用 OpenCV 的眼睛检测器找到两眼位置然后通过仿射变换把眼睛对齐到固定坐标。这一步很多人直接跳过但实测下来对齐前后识别准确率能差 10% 以上。特征提取把对齐后的人脸图像160x160x3输入 FaceNet 预训练模型得到 128 维特征向量。这一步是整个系统最依赖深度学习的部分模型把高维像素信息压缩成紧凑的语义向量像给每张人脸做“指纹”。特征比对拿当前人脸的特征向量和数据库中所有已注册学生的向量算余弦相似度。相似度最大的那个超过阈值通常取 0.85就判定为对应学生。否则判定为陌生人。这里给一个余弦相似度的计算示例import numpy as np def cosine_similarity(vec1, vec2): 计算两个128维特征向量的余弦相似度 dot np.dot(vec1, vec2) # 这里加一个保护的逻辑如果范数为0就返回0防止除零 norm_a np.linalg.norm(vec1) norm_b np.linalg.norm(vec2) if norm_a 0 or norm_b 0: return 0.0 return dot / (norm_a * norm_b)为什么用余弦相似度而不是欧氏距离FaceNet 的训练目标是让同一个人的特征向量在欧氏空间中“几乎重合”所以严格来说欧氏距离更符合 FaceNet 的原生语义。但实际工程里余弦相似度对特征向量的模长不敏感在预训练模型没有针对数据集微调的情况下余弦相似度的稳定性更好。这个取舍在答辩时值得展开讲评委很吃这种“我不仅做了还明白为什么这么做”的回答。2.3 数据层SQLite轻量存储让毕设无需繁琐的数据库配置数据库用 SQLite理由跟不用 Web 一样省事。用一个.db文件就能搞定不需要安装 MySQL 服务不需要配账号密码代码里几行就能连上。整个系统三张表表名字段用途adminid, username, password教师登录账号studentsid, student_id, name, class_name, face_embedding学生基本信息128维特征向量attendanceid, student_id, date, status, time每日考勤记录students 表里直接把 face_embedding 存成 BLOB 类型二进制大对象这样省掉了单独建特征索引表的麻烦。读取的时候用 np.frombuffer 还原成 128 维 float32 数组。有个细节采集人脸样本的时候建议一个人采集 3~5 张不同角度的照片然后把特征向量取平均存起来。这样做的好处是能抵消角度、光照带来的偏差比单张照片的特征稳定得多。这个“平均特征”的操作在毕设里是一个很自然的加分项一定要写进项目报告里。这里要提醒一点SQLite 的并发写能力弱如果两个线程同时写数据库会报 database is locked。PyQT 界面线程和识别线程都在跑写着写着突然报错多半就是这个原因。解决方法是把数据库操作放在主线程识别线程只负责把识别结果通过信号发回来由主线程执行写入。如果你用多线程写数据库再多的数据也会被锁问题劝退。3. 实操过程从环境搭建到产出一套完整系统光讲理论没用接下来把你从空环境开始一步一步把项目跑起来的全过程过一遍。这部分我参考了项目源码的实际结构和常见实践按“环境→数据准备→核心代码→打包部署”四个阶段来讲。3.1 环境搭建Python版本、依赖库、模型文件一个都不能少先交代环境这不是废话。很多同学拿到源码跑不起来90% 是环境不一致导致的版本冲突。我的建议环境如下操作系统Windows 10/11项目打包 exe 最方便Python3.8 或 3.9不要用 3.11PyQT5 和老版本 TensorFlow 的兼容性会出问题关键依赖PyQt55.15.4opencv-python4.5.5.64tensorflow2.6.0 或 tensorflow-cpu2.6.0numpy1.19.5keras-facenet直接 pip 安装自带预训练权重mysql-connector 不需要我们用的是 SQLite为什么特意把版本号写出来因为 keras-facenet 内部依赖 TensorFlow 2.x 的特定 API用新版的 TensorFlow 2.10 以上经常会报各种不兼容的警告虽然能跑但功耗大且容易出奇怪问题。实际项目中有个典型的坑TensorFlow 2.7 及以上版本依赖libcudnn的版本号变了如果你装的是 CPU 版没这个问题但 GPU 版会突然全部报错。所以 2.6.0 是比较稳的选择。安装依赖的命令pip install pyqt55.15.4 opencv-python4.5.5.64 tensorflow-cpu2.6.0 numpy1.19.5 keras-facenet注意tensorflow-cpu 和 tensorflow 不能同时装否则会有冲突。GPU 版需要额外装 CUDA 和 cuDNN但毕设项目用 CPU 推理足矣FaceNet 在 CPU 上跑一次识别只要 100ms 左右考勤场景完全够用。另外要单独放置模型文件。keras-facenet 会在第一次使用时自动下载权重但如果网络不稳定下载失败后几乎无法恢复。我的建议是挂代理下载后手动放到~/.keras/models目录mac/Linux或C:\Users\用户名\.keras\modelsWindows下面文件名一般是facenet_keras_weights.h5。这一步很多同学忽略然后在运行时报OSError: Unable to open file这种错误卡半天。3.2 学生人脸注册录入流程与特征入库考勤系统在识别之前必须先完成“注册”环节——把每个学生的照片特征存进数据库。这一块的实现逻辑比大多数人想的多几个弯。注册界面长这样输入学号、姓名、班级然后摄像头对准学生正面点击“采集”按钮系统会连续抓取5帧图像每帧检测人脸→对齐→提取特征最后把5个特征向量平均存库。关键代码如下def register_student(self, student_id, name, class_name): 采集多张人脸图像并提取平均特征 embeddings [] for i in range(5): # 从视频流抓一帧 frame self.get_current_frame() # 检测人脸 faces detect_faces(frame) if len(faces) 0: continue # 这一帧没有脸跳过继续 # 提取特征这里以封装后的FaceNet接口为例 emb extract_embedding(faces[0]) embeddings.append(emb) # 界面提示“请缓慢转头”方便采集不同角度 QTimer.singleShot(200, lambda: self.status_label.setText(f采集第{i1}/5帧...)) if len(embeddings) 0: self.show_warning(未检测到人脸请调整光线和角度重试) return avg_emb np.mean(np.array(embeddings), axis0) save_embedding_to_db(student_id, name, class_name, avg_emb) self.status_label.setText(注册成功)这段代码里我故意留了个细节——采集时提示“请缓慢转头”。实际测试中如果学生盯着一处不动5 帧图像几乎一样平均特征和单张特征没什么区别。只有轻微转脸才能让特征覆盖更多角度提高后续识别鲁棒性。注册这个环节还有个细节容易被忽略姓名中的生僻字。有些学生的名字里带生僻字在数据录入时如果编码不对特别是 PyQT5 的默认编码和 Windows 控制台编码不一致会导致存库后显示乱码。建议所有字符串字段统一用 UTF-8 编码连接 SQLite 时加上encodingutf-8。3.3 考勤逻辑识别、去重、状态判定完整流程识别考勤的主流程是系统最核心的部分。我把核心逻辑简化后大概长这样def on_attendance_button_click(self): 点击‘开始考勤’按钮后的处理逻辑 self.is_attending True self.last_record_time {} # 保存每个学生最后一次考勤时间用于去重 def process_frame(self, frame): 对视频流中的每一帧进行识别处理在VideoProbe回调中调用 if not self.is_attending: return # 没开始考勤就不处理 # 第一步检测并提取特征 faces detect_faces(frame) if len(faces) 0: return # 取画面中最大的人脸做识别 largest_face max(faces, keylambda f: f[2] * f[3]) embedding extract_embedding(largest_face) # 第二步和数据库所有已注册学生比对 best_match, best_score self.find_best_match(embedding) # 第三步判定结果 if best_score THRESHOLD: student best_match # 去重逻辑同一个学生5秒内不能重复打卡 current_time time.time() last_time self.last_record_time.get(student.id, 0) if current_time - last_time 5: self.record_attendance(student.id, self.classify_status()) self.last_record_time[student.id] current_time self.ui.result_label.setText(f{student.name} {self.classify_status()}) else: self.ui.result_label.setText(f{student.name} 已打卡请勿重复操作) else: self.ui.result_label.setText(未注册学生)这里有几个值得展开的逻辑点。第一个是“去重逻辑”没有吃透系统的同学容易漏掉甚至理解反了。实际场景中摄像头一直在工作同一张脸连续出现在视频流里如果不做去重系统会把同一个人识别成 20 次出勤记录。去重的方式很多按时间窗口比如 5 秒内不重复或者按状态已出勤的不再写入都可以我在代码里用时间窗口方案简单直接。第二个是考勤状态判定。刚上课 10 分钟内到 迟到超过 30 分钟还没到 缺勤这是根据当前时间动态算的。这里注意系统在 9 点上课时老师点击“开始考勤”但学生在 9 点 31 分进教室此时系统应该怎么处理项目里的方案是时间超过应到时间 30 分钟后识别成功但状态标为“缺勤”但会同时弹一个提示“该学生已迟到是否修改为出勤”。这个交互细节非常拉好感做完就能在答辩时展示“我考虑了边界情况”。第三个是“取最大人脸”的策略。考勤场景往往是学生一个接一个刷脸画面里可能同时出现多张脸。取最大的那张作为“正在考勤的人”这个 naive 的策略在教室门口的场景其实够用且不易出错。如果有人同时出现在镜头里系统会优先识别离镜头近的人正好符合考勤排队进教室的场景。3.4 界面美化让毕设看起来像产品的小技巧很多毕设源码功能都实现了但界面丑得让人没眼看。这里分享三个低成本提升界面质感的技巧技巧一用 QSS 统一样式。PyQT 支持类似 CSS 的 QSS 语法你只需要在代码里加载一份样式表就能把默认的灰色控件换成有圆角、有渐变色的现代风格。比如给考勤状态标签加颜色QLabel#status_ok { color: #2e7d32; background-color: #e8f5e9; border-radius: 8px; padding: 8px; font-weight: bold; } QLabel#status_fail { color: #c62828; background-color: #ffebee; border-radius: 8px; padding: 8px; font-weight: bold; }技巧二加一个启动画面。PyQT 有 QSplashScreen加载模型需要几秒钟与其让用户对着白屏等着不如显示一张带 logo 和进度条的启动画面。虽然技术上不复杂但答辩演示时前 5 秒的观感差别巨大。技巧三用 Thread 加载模型。FaceNet 模型加载大概要 5-8 秒如果放在主线程启动时窗口会无响应。把模型加载放到 QThread 里加载完发一个信号通知界面用户以为程序启动很快实现也不复杂class ModelLoader(QThread): finished_signal pyqtSignal() def run(self): global model model load_facenet_model() self.finished_signal.emit()这三个技巧能显著提升项目的完成度。很多同学问“我的功能和别人一样为什么分比别人低”——差异往往就在这种产品化的细节里。3.5 打包exe从源码到可交付的完整闭环毕设交上去老师多半会让现场演示也会问“你这个怎么部署能不能给我发一个可执行文件”。这时候就需要把项目打包成 exe。PyQT 项目打包的常用工具是 PyInstaller但直接打包经常会遇到各种坑。先说命令pyinstaller --windowed --name AttendanceSystem --iconicon.ico --add-data facenet_keras_weights.h5;models main.py这里有几个关键点--windowed表示不显示命令行窗口打包出来是纯 GUI 应用。如果用了--windowed又用print()调试到时候看不到输出。所以在打包前把调试逻辑全删掉有问题宁愿加日志文件也不要依赖控制台。--add-data后面用分号Windows或冒号macOS分隔源文件和目标文件夹。模型文件必须打进包里否则用户双击 exe 会因为找不到模型而闪退。还有一个大坑是 OpenCV 和 TensorFlow 的 dll 文件。PyInstaller 在打包 TensorFlow 时经常漏掉一些动态链接库导致生成的 exe 在其他电脑上运行时报DLL load failed。解决方案是在 spec 文件里手动添加 missing modules或者用官方推荐的--collect-all tensorflow参数pyinstaller --windowed --collect-all tensorflow --name AttendanceSystem main.py注意--collect-all tensorflow会让 exe 体积直接膨胀到 500MB因为把所有 TensorFlow 组件都塞进去了但换来的是一键可运行。实际项目中我发现一个更精细的做法先正常打包运行测试报错时根据缺失的模块名一个个补进去。这可能要多花两小时但体积能控制在 200MB 以内。毕设就图个稳用--collect-all保平安也行。4. 常见问题与排查技巧实录这部分是我在复现和调试这类项目时最常遇到的问题整理成速查表方便直接按图索骥。每个问题都附上排查思路和解决方式。4.1 问题速查表与解决方案对照问题现象可能原因解决方法摄像头画面黑屏无内容摄像头权限没打开或索引号错了检查系统隐私设置允许摄像头访问尝试cv2.VideoCapture(0)改为(1)程序启动后闪退模型文件找不到确认facenet_keras_weights.h5在正确路径用绝对路径加载开发阶段识别结果全部是“未注册学生”特征比对阈值设置偏高把余弦相似度阈值从 0.9 降到 0.8观察效果也可能学生注册时照片质量太差每次识别耗时 2 秒以上使用了 GPU 版本的额外开销或未做帧间隔降低处理帧率每 5 帧才跑一次识别确认使用 tensorflow-cpu数据库写入报 locked 错误多线程同时写 SQLite所有数据库写操作集中到主线程或给连接加timeout10打包 exe 后运行报 DLL 加载错误PyInstaller 漏掉动态链接库用--collect-all tensorflow重打包或把错误信息里缺的 DLL 手动从 Python 环境 copying 到 dist 目录中文姓名显示乱码PyQT 字符串编码问题在代码文件头部声明# -*- coding: utf-8 -*-数据库连接encodingutf-84.2 我自己踩过最深的坑摄像头占用冲突调试过程中最头疼的问题之一PyQT 的 QCamera 和 OpenCV 的 VideoCapture 同时抢占摄像头。项目里启动界面时 QCamera 把摄像头占了然后你又要用 cv2.VideoCapture(0) 去抓帧结果后者的画面永远是空的。这个问题的原因很简单摄像头设备只允许一个进程打开同一个进程里也不能同时用两套 API 打开同一设备。解决办法要么统一用 QCamera 的全部链路通过 QAbstractVideoSurface 抓帧要么放弃 QCamera 直接用 OpenCV 做出预览。我的经验是项目和考勤这种简单交互场景直接用 OpenCV 的VideoCapture加QLabel刷新做预览比 QCamera 省心得多。QCamera 虽然集成度高但它的视频流处理接口学习曲线陡反而把简单问题复杂化了。代码框架大概是class CameraThread(QThread): change_pixmap_signal pyqtSignal(np.ndarray) def run(self): cap cv2.VideoCapture(0) while self.running: ret, frame cap.read() if ret: self.change_pixmap_signal.emit(frame) time.sleep(0.03) # 30 FPS cap.release()然后主线程里把frame转成 QImage 显示在 QLabel 上识别的时候直接拿最后一帧用。思路简单问题也少。4.3 准确率不够的排查方向如果你的系统在测试时识别准确率低先别怀疑模型按这个顺序排查注册照片质量注册时摄像头是不是对着一张模糊的、逆光的脸试试换一个光线均匀的正脸环境重新注册。识别距离FaceNet 训练数据大多是人脸占画面主体的情况如果你站在 3 米外人脸框太小特征提取会出问题。考勤系统应该设计成“学生走到镜头前刷脸”而不是远距离自动识别。遮挡问题口罩、刘海、眼镜都会影响特征。特别是口罩FaceNet 没针对遮挡优化戴上口罩识别率会直线下降。解决方式就是注册时要求不戴口罩识别时排队摘口罩或者换一个对遮挡鲁棒的模型但那是另一个项目了。阈值选择在系统里加一个调试模式把相似度分数打到界面上观察不同人的分数分布。比如同一个人最低相似度可能是 0.87不同人最高相似度可能是 0.82那阈值取 0.85 左右就合适。每个班的分布不同最好做成可配置项在配置文件里随时调。5. 项目扩展与答辩加分建议我把这个项目从“能用”做到“好讲”的过程中总结了一些扩展方向。毕设答辩时间有限评委很难在一个不熟悉的项目里深挖细节但如果你能主动展示系统设计的亮点就比较容易拿到高分。5.1 从单机到演示三个扩展方向方向一加入动态检测与出席状态统计。目前系统是一帧一帧识别考勤记录每天一条。扩展成“按课程”考勤一个 Excel 导出功能就能展示全班出勤率。用pandas或openpyxl生成统计报表放在“下课后一键导出”按钮后面。这个功能不复杂但视觉冲击力强。方向二用 MTCNN 替换 Haar Cascade。答辩现场评委问“你用的传统人脸检测有什么不足”你答“Haar 在侧脸和遮挡场景下容易漏检”然后顺带说“我调研过 MTCNN它通过三个级联 CNN 检测人脸精度更高下一步可以替换”这比背完整段人脸识别发展史更有力。方向三界面上加一个实时视频监控画面中的人脸框。很多人以为识别就是对着视频流闷头算没有把检测到的人脸在画面上画框框出来。实际上 OpenCV 画框很容易x, y, w, h largest_face cv2.rectangle(frame, (x, y), (xw, yh), (0, 255, 0), 2) cv2.putText(frame, student_name, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)把这个加到识别线程里每帧回传的时候把画好框的画面显示出来。视觉效果从“后台在跑”升级成“实时跟踪”在演示时非常加分。5.2 答辩时必问的三个问题与回答框架“FaceNet 的损失函数是什么”答三元组损失Triplet Loss通过拉近同一人特征向量的距离、推远不同人特征向量的距离来训练模型。你不需要手推公式但至少能把 Anchor、Positive、Negative 三个样本的关系讲清楚。博主多提醒一嘴讲损失函数时不要背公式要用直白的话解释“这是一个让同类聚拢、异类散开的训练方式”。“为什么不用 ArcFace”答ArcFace 在 margin 处加了角度惩罚分类精度更高但预训练权重体积大、部署复杂在毕设场景下选 FaceNet 是因为“模型加载快、权重公开、CPU 可实时推理”在精度要求够用的情况下优先保证系统流畅度。不要贬低新模型说“我在毕设中做了权衡”比“新模型没用过”好得多。“如果老师换一个摄像头你的系统还能用吗”答能。模型输入是 160x160 的标准化图像和摄像头分辨率无关。只要保证注册时和识别时的人脸质量相近识别结果不会因为摄像头更换而产生本质差异但像素太低如 320x240会影响小脸检测。这是一个能展示你理解“模型输入输出”的好问题可以主动展开。5.3 最后的经验总结做项目这件事我从一开始就坚持一个原则毕设项目最重要的不是模型有多新、功能有多全而是整个链路真正跑通、你真正理解每一行代码在干什么。这个考勤系统在技术深度上不算顶尖但每一环都是补全一个“完整产品”的必要拼图。我见过太多人在模型上花了两周调参结果演示时因为数据库连接超时当场翻车的。如果你打算直接拿这套源码去改我建议第一个动手点放在“去重逻辑”上——试着把 5 秒窗口改成“每个学生每次课程只记录一次”这会让代码结构发生连锁变化而你恰好可以通过这次重构理解整个系统的数据流。我个人实际用下来的体会是考勤系统这种项目特别适合做毕设是因为它的“演示效果”天然有故事性——你可以现场刷脸签到跟评委互动。你只需要把其中一条主线比如识别→入库→统计讲清楚就已经是合格的毕设了。别贪多别想着塞一堆花哨功能进去专注把一条链路做到可靠就是最好的设计。本文还有配套的精品资源点击获取