基于深度学习的人脸识别签到系统:从原理到工程落地
简介这是一份面向计算机、人工智能及相关专业毕业设计的人脸识别签到系统完整项目资料尤其适合需要快速搭建可演示原型、撰写设计文档或完成课程设计的学生。资源覆盖从人脸数据采集、模型训练、特征提取到签到记录管理的全流程由Python后端逻辑、前端页面模板、SQLite数据库、模型权重及配置文件共同构成共38个文件主要类型为py脚本、html页面、dat权重、master配置和readme说明压缩包约203MB目录结构清晰便于按模块理解与二次开发。目前已有109人学习下载。借助该资料可直接运行基础版本观察人脸注册、识别比对、签到数据持久化等核心环节的具体实现通过阅读py源码与html模板还能了解模型加载、请求处理、页面交互与数据库表设计等工程细节。requirements依赖清单与README可帮助快速搭建运行环境同时可作为毕业设计说明书的功能模块划分参考对理清深度学习项目从数据到落地的工程化流程很有价值。1. 教室门口的点名靠人喊、公司考勤靠刷工卡基于深度学习的人脸识别签到系统就是把这事自动化教室点名靠人喊、递纸条公司考勤靠刷工卡慢不说还挡不住代签。基于深度学习的人脸识别签到系统的思路很直接摄像头拍到人脸模型把这张脸压缩成特征向量再和注册库里的人脸特征做比对比对通过就自动写入一条签到记录。这个课题每年都有大量毕设参考项目在网上流传可一旦自己动手就会发现最花时间的根本不是训练模型而是把环境配通、把识别阈值调准、把签到判重做对。换句话说这套方案的核心价值不在“深度学习”四个字本身而在怎么把识别结果稳定地变成一条可信的考勤记录。如果你是正在做毕设的学生或者第一次接触人脸识别的开发者这篇笔记按“选型 → 注册 → 识别 → 排错 → 交付”的顺序讲一条能在两周内跑通的落地路径。2. 技术选型ArcFace、FaceNet 与 OpenCV 传统方法毕设到底选哪个2.1 从摄像头到签到记录人脸识别系统的完整数据链路一个完整的人脸识别签到系统数据链路是固定的摄像头拿到视频帧先做人脸检测找到画面里哪几个位置有人脸再做人脸对齐用关键点把歪头、侧脸校正到标准姿态然后做人脸特征提取把一张人脸压缩成一个固定长度的向量接下来拿这个向量和注册库里所有向量做比对找到最像的那个最后把比对结果和签到业务接起来写入考勤记录。这条链路里检测、对齐、特征提取三个环节在现在的开源生态里都已经是“拿来即用”不需要你自己设计网络、采集训练数据。毕业设计真正要写的不是模型而是调度逻辑和签到业务。所以先别急着搭 PyTorch 训练代码把这条链路的每个环节都跑通比什么都重要。特别要注意对齐这一步。很多人只听说过检测和识别忽略对齐结果侧脸照片识别率一塌糊涂。常见做法是让推理引擎输出 5 个关键点左眼、右眼、鼻尖、左嘴角、右嘴角再做仿射变换把眼睛拉到同一水平线。InsightFace 的 FaceAnalysis 在内部已经做了这件事这也是我推荐选它的原因之一。2.2 模型选型对比ArcFace、FaceNet 与 OpenCV 传统方法结论放前面毕设做这个题首选 ArcFace也就是 InsightFace 项目里那套基于 margin loss 的识别模型。维度ArcFaceInsightFaceFaceNetOpenCV LBPH/Eigenfaces特征维度512128无固定向量预训练权重官方一键下载社区权重质量不齐无需权重光照鲁棒性好较好差换光就崩工程生态自带检测对齐识别需另配检测器和对齐OpenCV 内置最简单答辩友好度高能讲 margin loss中低追问就露馅ArcFace 的过人之处在于角度间隔损失它在归一化特征上给正确类别加一个角度余量让同类特征在超球面上聚得更紧异类分得更开。实际效果就是512 维特征做余弦比对正常光照下同一个人的相似度轻松到 0.6 以上不同人通常在 0.3 以下阈值很好选。FaceNet 是 triplet loss 路线的代表特征只有 128 维速度不错但官方权重多年没有大更新社区流传的模型质量参差不齐而且它本身不带检测器你还得再找一个人脸检测和对齐方案链路长了一截。真要用也一定要把对齐做好否则效果比 ArcFace 差不少。OpenCV 自带的 LBPH、Eigenfaces 属于传统方法不用下载任何模型就能跑新手很容易图省事选它。但它的特征表达对光照极其敏感教室窗边、走廊灯下完全两种效果老师多问两句“深度学习体现在哪”就答不上来。做毕设选题别在这上面省事。市面上还有一些封装得特别彻底的现成人脸识别工具安装即用能刷脸能签到但内部是个黑匣子答辩讲不出深度也不适合当核心实现。顺带说一句编程语言的选择Python 在深度学习生态里最顺手模型调用、图像处理、界面骨架都有现成库毕设阶段就用 Python别用 C 折磨自己。以后要嵌入式部署再考虑把推理迁移到 C 端。2.3 Ubuntu 20.04 与 Windows 下的深度学习环境配置PyTorch 与 ONNX Runtime 的搭配装环境是第一个劝退点很多人在 conda 里把包装乱了最后全部推到重来。我一般会建一个全新环境Python 固定用 3.10不要用系统默认 Python。conda create -n face_sign python3.10 -y conda activate face_sign pip install torch torchvision pip install insightface onnxruntime-gpu opencv-python numpy PyQt5先解释一下为什么这样装。insightface 负责人脸检测、关键点和特征提取onnxruntime 负责跑 ONNX 格式的推理。这里其实不需要 PyTorch 参与推理纯 ONNX 跑更快更稳装 torch 只是为了有些调试代码和别的依赖会用到。如果你的电脑没有 NVIDIA 显卡就把 onnxruntime-gpu 换成 onnxruntimetorch 装 CPU 版识别照常能跑只是速度慢一些。GPU 版的一个重要注意点onnxruntime-gpu 对 CUDA 和 cuDNN 版本有对应关系要求装完必须检查推理引擎是否真正加载了 GPU。很多人的毕设翻车点就在这里——错装了不匹配的 onnxruntime-gpu运行时报 CUDA 加载失败程序又没报错静默回退到 CPU性能反而比纯 CPU 更差。安装后马上执行一行检查代码import onnxruntime as ort print(ort.get_available_providers())看到CUDAExecutionProvider在列表里才算成功。这个检查动作应该写进环境搭建清单里别嫌麻烦。模型权重方面InsightFace 首次运行时会自动下载预训练模型包如果网络不稳定下到一半失败重跑命令即可它支持断点续传。下载完成后本地会有一个模型目录后续离线也能用。这个模型包自带检测网络和识别网络等于把链路的前半段一次性配齐了。3. 注册阶段用 InsightFace 把人脸照片变成 512 维特征库3.1 注册照片采集脚本命名规范与多角度原则签到系统要先知道“谁是谁”。注册阶段干两件事采集人脸照片提取特征入库存。这个流程一般离线进行找一台带摄像头的笔记本就能完成。我建议每个参与者采集 3 张照片正面一张头向左偏约 20 度一张向右偏约 20 度一张。为什么要多角度因为现场摄像头不会恰好拍到完美正面多存几张再取平均特征更稳。采集时保证顺光别让窗户在身后背景里不要有其他人脸出没。import cv2 import os STUDENT_ID 2021001_zhangsan # 改成 学号_姓名中间用下划线 SAVE_DIR dataset/register os.makedirs(SAVE_DIR, exist_okTrue) cap cv2.VideoCapture(0) count 0 while True: ret, frame cap.read() if not ret: break cv2.imshow(face capture: press s to save, q to quit, frame) key cv2.waitKey(1) 0xFF if key ord(s): filename os.path.join(SAVE_DIR, f{STUDENT_ID}_{count}.jpg) cv2.imwrite(filename, frame) count 1 print(saved:, filename) elif key ord(q): break cap.release() cv2.destroyAllWindows()这个脚本把学号姓名写成了固定常量而不是每按一次就输入一次好处是批量采集多人时分批执行命令不用反复改。按s存一张按q退出。窗口里能看到取景画面方便人把脸对准。提示命名规则直接决定后面的特征归组学号_姓名之间用下划线分隔文件名里不要再出现空格和中文符号。3.2 用 InsightFace 批量提取人脸特征核心代码与参数说明照片采完以后批量提取特征并保存成一个特征库文件。这个环节是整个系统里“深度学习”浓度最高的一段代码但写起来反而不长import os import numpy as np import cv2 import insightface from insightface.app import FaceAnalysis # 注册阶段用 CPU 即可离线批量处理不需要 GPU app FaceAnalysis(namebuffalo_l, providers[CPUExecutionProvider]) app.prepare(ctx_id0, det_size(640, 640)) register_dir dataset/register feature_dict {} for file in os.listdir(register_dir): path os.path.join(register_dir, file) if not file.lower().endswith((.jpg, .jpeg, .png)): continue img cv2.imread(path) faces app.get(img) if len(faces) 0: print(file, no face, skip) continue # 多人同框时按框面积取最大的人脸避免把路人当主角 if len(faces) 1: faces sorted( faces, keylambda f: (f.bbox[2] - f.bbox[0]) * (f.bbox[3] - f.bbox[1]), reverseTrue, ) # normed_embedding 是 L2 归一化后的 512 维向量 emb faces[0].normed_embedding # 文件名形如 2021001_zhangsan_0.jpg按学号_姓名归组 key _.join(file.split(_)[:2]) feature_dict.setdefault(key, []).append(emb) print(key, embedding dim:, emb.shape) # 每人多张照片取平均再归一化一次特征更稳 names, embs [], [] for key, vecs in feature_dict.items(): mean_emb np.mean(np.vstack(vecs), axis0) mean_emb mean_emb / np.linalg.norm(mean_emb) names.append(key) embs.append(mean_emb) np.savez_compressed(face_db.npz, namesnp.array(names), embsnp.vstack(embs)) print(saved face_db.npz, total:, len(names))脚本逻辑分四步遍历注册目录对每张图调用app.get做检测取最大人脸框的特征按学号_姓名归组并平均。这里的参数值得多说几句。det_size(640, 640)是送入检测网络的输入分辨率分辨率越大越能检出小脸、远脸耗时也随之增加。注册阶段没有实时要求用 640 稳住现场识别如果嫌慢可以降到 (320, 320)。namebuffalo_l是 InsightFace 的预训练模型包名包含检测和识别两部分首次运行会自动下载权重。多人同框时按 bbox 面积排序选最大的人脸这条逻辑很关键。注册照片背景里走过一个人或者同学凑过来看屏幕都可能造成同框不排序就会把特征存到路人脸上。每人多张照片取平均是新手里值得做但容易忽略的一步平均后再归一化一次现场识别对姿态变化的容忍度会明显提高。3.3 特征库存储选型npz、SQLite 与 Faiss 怎么选特征提取完要落盘。最短路径是存成 npz 压缩文件里面就两个数组names学号_姓名字符串列表和embsN×512 特征矩阵。加载时np.load一下直接和摄像头实时特征做矩阵乘法代码最少、加载最快。10 到 20 个人的毕设演示用 npz 完全够。想显得更工程化可以把学生信息放进 SQLite 表特征向量以 BLOB 形式存储在列里签到写入也走 SQLite整体更统一。缺点是每次匹配要把全部特征从库里读出来再反序列化人少没问题人多就需要索引了。再往上可以提 Faiss 这类向量检索库百万向量毫秒级返回。但毕设不需要真接答辩老师问“如果全校一万人都注册了你还这么比对吗”你能答出“可以接 Faiss 建立索引做 ANN 检索”就已经拿到分了。我的默认组合是特征库用 npz学生名单放 SQLite。演示稳、代码少、答辩也能讲清楚。如果时间充裕再把特征也挪进 SQLite工作量多几十行但系统完整性会更强。4. 签到阶段实时识别、相似度阈值与 SQLite 考勤记录4.1 实时摄像头识别循环检测、提特征、算相似度的最小代码注册库建好之后进入核心环节连续从摄像头取帧对人脸做检测、特征提取、比对在视频上画出名字。这一步是可运行的完整脚本import cv2 import numpy as np import insightface from insightface.app import FaceAnalysis # 现场识别优先尝试 GPU失败会自动回退 CPU app FaceAnalysis( namebuffalo_l, providers[CUDAExecutionProvider, CPUExecutionProvider], ) app.prepare(ctx_id0, det_size(640, 640)) data np.load(face_db.npz, allow_pickleTrue) names data[names] embs data[embs] # 形状 (N, 512)已经归一化 def match(embedding, thresh0.5): # 归一化后做点积就是余弦相似度 scores embs embedding idx int(np.argmax(scores)) if scores[idx] thresh: return names[idx], float(scores[idx]) return unknown, float(scores[idx]) cap cv2.VideoCapture(0) frame_count 0 while True: ret, frame cap.read() if not ret: break frame_count 1 # 跳帧每 3 帧只识别一次保证画面流畅 if frame_count % 3 ! 0: cv2.imshow(attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break continue faces app.get(frame) for face in faces: name, score match(face.normed_embedding) if name unknown: continue bbox face.bbox.astype(int) cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2) cv2.putText(frame, f{name} {score:.3f}, (bbox[0], bbox[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的核心是match函数。embs embedding是矩阵乘法因为特征全部做过 L2 归一化点积就等于余弦相似度。分数最高的注册人就是最像的人分数达不到阈值就一律算 unknown。0.5 只是一个初始值不同光线、不同摄像头下需要按下一章的方法校准。跳帧逻辑看似不起眼却是流畅度的关键。每 3 帧做一次识别另外两帧直接显示CPU 占用明显下降画面观感反而更顺。人脸不会在两三帧之间变成另一个人识别频率不需要太高。这里要提醒app.get(frame)每调用一次会跑完整的检测识别网络默认配置下普通 CPU 耗时约 200 到 500 毫秒GPU 上几十毫秒。如果发现程序一卡一卡先别怀疑电脑检查是不是没有跳帧、没有缩检测分辨率。4.2 签到判重与迟到判定SQLite 表结构与插入逻辑识别出是谁之后不能直接往数据库里插记录。教室里一个人会在画面里出现几十秒到几分钟不判重的话一条签到会被写成好几条考勤统计直接没法看。常见做法是用时间窗口去重同一人在同一门课最近 5 分钟内已签到就不再重复写。import sqlite3 from datetime import datetime, timedelta conn sqlite3.connect(attendance.db) cur conn.cursor() cur.execute( CREATE TABLE IF NOT EXISTS students ( id TEXT PRIMARY KEY, name TEXT NOT NULL ) ) cur.execute( CREATE TABLE IF NOT EXISTS attendance ( record_id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL, course_id TEXT NOT NULL, check_time TEXT NOT NULL, status TEXT NOT NULL ) ) conn.commit() def do_checkin(student_id, course_id, within_minutes5): # 先查最近几分钟内是否已经签过 threshold_time datetime.now() - timedelta(minuteswithin_minutes) cur.execute( SELECT record_id FROM attendance WHERE student_id ? AND course_id ? AND check_time ? LIMIT 1 , (student_id, course_id, threshold_time.strftime(%Y-%m-%d %H:%M:%S))) if cur.fetchone(): return False, already checked in # 迟到判定拿签到时间与课程开始时间比较 now datetime.now() current now.strftime(%H:%M) course_start 08:30 status on_time if current course_start else late cur.execute( INSERT INTO attendance(student_id, course_id, check_time, status) VALUES (?, ?, ?, ?) , (student_id, course_id, now.strftime(%Y-%m-%d %H:%M:%S), status)) conn.commit() return True, status表结构只有两张最核心的students存学号和姓名attendance存每次签到记录。第一次运行就执行CREATE TABLE IF NOT EXISTS应用崩溃重启不会丢表。do_checkin的两个细节值得说明。时间比较用 Python 的 datetime 而不是 SQLite 的时间函数这样不同操作系统上行为一致。迟到判定拿签到时间和课程开始时间做字符串比较前提是两者都是 HH:MM 格式课程跨天或者有多个时段时要先把开始时间拼上日期这里从简。课程 id 和签到学生 id 在完整系统里应该由界面传入而不是识别出来就硬编码。但毕设第一个版本先把写入链路打通最重要。到课率统计也简单一条 SQL 就能出结果按课程分组、统计去重学生数。4.3 用 PyQt5 搭最小演示界面摄像头画面与签到状态一屏显示命令行里跑识别脚本老师看不到“系统”的样子。PyQt5 是给这套系统补一张皮的最快方式界面只需要三个元素摄像头画面、识别结果文本、签到按钮。import cv2 import numpy as np from PyQt5 import QtGui, QtWidgets, QtCore class CheckinUI(QtWidgets.QWidget): def __init__(self, app, names, embs): super().__init__() self.app app # FaceAnalysis 实例 self.names names self.embs embs self.video QtWidgets.QLabel(camera preview) self.state QtWidgets.QLabel(waiting...) self.btn QtWidgets.QPushButton(check in) layout QtWidgets.QVBoxLayout(self) layout.addWidget(self.video) layout.addWidget(self.state) layout.addWidget(self.btn) self.cap cv2.VideoCapture(0) self.timer QtCore.QTimer(self) self.timer.timeout.connect(self.on_frame) self.timer.start(30) def on_frame(self): ret, frame self.cap.read() if not ret: return # 简化版直接复用 4.1 的 match 逻辑每帧都识别 results [] for face in self.app.get(frame): scores self.embs face.normed_embedding idx int(np.argmax(scores)) if scores[idx] 0.5: results.append(f{self.names[idx]} {scores[idx]:.3f}) self.state.setText(; .join(results) if results else no one) rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qt_img QtGui.QImage(rgb.data, w, h, ch * w, QtGui.QImage.Format_RGB888) self.video.setPixmap(QtGui.QPixmap.fromImage(qt_img))这段代码把识别逻辑直接放进QTimer回调每 30 毫秒触发一次。人脸少的时候没问题人脸多了 UI 会卡因为识别是同步的。专业做法是开QThread在工作线程里跑识别主线程只负责显示最新一帧毕设演示若只面对一个人用这个简化版足够。签到按钮的回调里调用do_checkin识别出的学号姓名作为参数传进去返回成功后把界面状态改成类似“张三 已签到 08:31”。按钮和识别逻辑通过实例变量连接比在回调里重新读摄像头要干净。界面不需要漂亮演示时让老师一眼看到“摄像头画面 识别结果 签到成功状态”这三件事就够。5. 最容易翻车的 5 个坑环境、阈值、卡顿、光线与答辩现场排查这一章是血泪经验。很多毕设程序在我这里跑了一遍翻车点几乎都集中在下面这五类。每一条都按现象、原因、解决的顺序写可以直接当排查手册用。5.1 GPU 版 PyTorch 装了却还在用 CPU 推理现象环境里明明装了 GPU 版 PyTorchtorch.cuda.is_available()也返回 True但识别程序跑起来风扇不转帧率很低看起来像在裸奔。原因通常有两个。一是 onnxruntime 装的是 CPU 版而推理路径主要走 ONNX二是 onnxruntime-gpu 装上了但 CUDA/cuDNN 版本不匹配初始化失败后静默回退。后一种更隐蔽代码不报错就是慢。解决先打印可用 provider 列表确认CUDAExecutionProvider在里面只有 CPU 就卸载重装 onnxruntime-gpu如果列表里有 CUDA 也有 CPU要把 CUDAExecutionProvider 放在 providers 列表第一位让推理引擎优先选 GPU。import onnxruntime as ort print(ort.get_available_providers())重装后还是没有 CUDAExecutionProvider就去查 onnxruntime-gpu 的版本说明找到对应 CUDA 版本的安装包。这个顺序能解决九成“装了 GPU 却用 CPU”的问题。5.2 相似度阈值方向搞反同一个人也提示签到失败现象同一个人反复识别时成功时失败陌生人的脸偶尔也会被签进去或者干脆全部返回 unknown。原因最常见的还不是阈值设得不好而是把相似度和距离搞反了。InsightFace 返回的是 L2 归一化向量点积即相似度越大越像而很多文档里的 cosine distance 是 1 减去相似度越小越像。拿余弦距离的阈值去卡相似度结果必然全乱。解决把阈值当成参数而不是常量用一段校准脚本打印注册人自身的相似度分布和与其他人的最大相似度分布。for name, emb in zip(names, embs): all_scores embs emb self_score float(all_scores.max()) other_score float(np.delete( all_scores, int(all_scores.argmax())).max()) print(name, self:, round(self_score, 3), other_max:, round(other_score, 3))看输出里 self 和 other 两组数。如果 self 全部在 0.6 往上other 全部在 0.4 以下阈值取中间 0.5 很稳如果两分布有交叠说明注册照片质量太差或者现场光源和注册光源差异太大先去改进采集不要硬调阈值。玄学调参会越调越乱有分布数据才好找交界。5.3 现场识别一卡一卡摄像头画面变成幻灯片现象摄像头画面一顿一顿人一移动就拖影识别结果半天才刷新一次。原因每一帧都调app.get而且用默认 640 分辨率检测。CPU 机器上处理一帧要几百毫秒一秒钟只来得及处理两三帧观感就是幻灯片。解决三步走。第一识别和显示解耦每 3 帧只识别 1 帧第二把检测输入改小app.prepare(det_size(320, 320))或者先把画面缩到宽 640 再识别第三把识别放到后台线程UI 只负责显示。三招都用了还卡就改成按键签到模式——人对着摄像头按一下才识别一次帧率不再是瓶颈。对毕设演示来说按键模式反而更稳定老师不需要看实时高帧率只需要看到“识别成功”这个结果。5.4 逆光、侧脸、戴眼镜注册照和现场照差太多现象白天教室窗边逆光或者同学侧着坐系统直接认不出注册的时候明明好好的。原因识别模型的鲁棒性不是无限的。模型能容忍一定姿态和光照变化但注册阶段只拍了一张正面照片现场角度偏移超过 30 度或者脸上有大片阴影特征就跑到阈值边界外了。解决注册时一人多角度 3 张取平均特征现场识别前检查face.det_score低于 0.5 就提示“请正对摄像头”不要硬匹配戴眼镜的同学注册时戴眼镜、现场摘了或者反过来建议注册时多采一组因为眼镜对特征的影响比想象中大。特别提醒口罩会把下半张脸的关键特征挡住毕设系统不要承诺“戴口罩也能签”。老师问起来老实说这是已知限制比现场翻车好。5.5 答辩现场模型加载慢、摄像头被占、演示黑屏现象答辩那天双击启动程序后转圈十几秒才出画面或者摄像头指示灯亮着但窗口黑屏或者直接报错找不到文件。原因模型首次加载要初始化 ONNX Runtime 和分配内存本来就慢摄像头被其他软件占用后VideoCapture(0)打开失败程序不报错但画面黑屏代码里用了绝对路径换个电脑就崩。这类问题跟算法无关纯粹是现场工程准备不足。解决程序启动时先做一次空图预热把模型加载时间前置到界面出现之前后面调用会明显变快视频源路径全部改成相对路径用os.path.dirname(os.path.abspath(__file__))拼路径答辩前录一段 20 秒的演示视频代码里把VideoCapture(0)换成VideoCapture(demo.mp4)摄像头出问题时立刻切换视频源。这是我的后悔药方案救过不止一次。提示答辩前一晚把“删掉模型缓存后冷启动”完整跑一遍能过滤掉一半以上的现场故障。6. 交付之前离线部署、活体检测和识别成功率自测6.1 离线部署与活体检测让系统能在一台普通电脑上跑起来毕设交付时程序要能在没有 GPU 的笔记本上跑。推理全部走 ONNX Runtime模型文件放在程序目录下启动时不依赖 PyTorch这个取舍会让部署体积和启动速度都变得可控。老师问“怎么部署到教室那台旧电脑上”也能答得清楚。活体检测是这个题目绕不开的追问点拿一张打印照片能不能签到最小成本方案是引入静默活体模型输入一张 RGB 图输出真脸概率再简单一点可以要求先完成一次眨眼动作再放行用关键点估计眼睛开合度。如果时间不够答辩时主动说一句“当前版本未包含活体检测这是已知限制”也好过被当场戳穿。这一层做完系统才算从“人脸识别 demo”变成“签到产品原型”后续还能往课堂状态检测、门禁机终端这类方向延伸。6.2 用一张测试表把“能跑”变成“鲁棒”识别成功率自测方法答辩开始前给系统做一次量化测试。邀请 10 位同学每人注册 3 张照片然后按场景各测 20 次记录成功率结果填成类似下面的表测试场景测试人数成功次数成功率正对摄像头距离约 1 米1020/20100%头部偏转约 30 度1018/2090%逆光环境1016/2080%连续 10 分钟不重复签到1020/20100%未注册人脸误识率50/200%这张表的价值在于它把“系统还行”变成“正对摄像头成功率 100%逆光 80%未注册人脸误识为 0”这样可被评估的事实。老师问鲁棒性直接把表递过去比说任何漂亮话都有说服力。我第一次做同类课题最后悔的就是把时间全耗在调模型上没留一天做测试答辩时被问“你这系统到底多可靠”只能含糊过去。后来补了这张表演示也从“能跑”变成了“可信”。如果你也正在做这个方向希望这篇笔记能帮你少踩几个坑。希望帮到你。本文还有配套的精品资源点击获取