简介:一套基于Python机器学习实现的人脸识别大作业源码包,面向需要完成机器学习课程设计、毕业设计或入门计算机视觉实战的Python学习者。压缩包约3.52MB,集中了照片与视频两大场景下的完整人脸识别流程,涵盖照片性别识别、视频人脸识别、人脸与眼睛检测、视频性别识别等多个可独立运行的功能模块,并配有神经网络模型文件、工程代码说明文档以及运用BP神经网络实现性别检测的工作报告。各模块分工明确,照片类任务可验证单张图像处理,视频类任务则覆盖实时帧读取与识别,便于按需调用和二次开发;工程代码说明文档详细梳理了项目结构、运行环境与调用方法,BP神经网络工作报告则从传统机器学习角度补充了算法原理,有助于读者对比不同技术路线。目前已有1449人浏览学习,整套资源体量虽小,但内容覆盖面广,既能作为课程大作业的直接参考,也能帮助初学者理解从数据准备、模型加载到结果输出的典型机器学习实践流程,还可在此基础上扩展其他面部分析任务。
1. 拿到“基于python机器学习实现的人脸识别大作业源码+课程报告+项目说明.zip”,你该怎么用
这类压缩包是机器学习课程设计里最常见的交付形态:一份能跑通的 Python 人脸识别程序、一份用来应付查重和答辩的课程报告、一份让别人能接手的环境说明。我的建议是别急着双击运行,先把它当成一条完整流水线来看:人脸检测、人脸对齐、特征提取、分类器识别,四个环节缺一不可。真正决定大作业能拿什么档次的,往往不是模型层数,而是数据预处理到不到位——预处理严格了,SVM 也能在班级数据集上跑到 95% 以上;预处理粗糙,再贵的网络也救不回来。适合三类人读:被课程设计 deadline 追着跑的学生、想让简历里多一个“能讲清楚”的计算机视觉项目的人、以及在做门禁考勤类产品预研的开发者。
2. 人脸识别大作业的路线选择:为什么课设该走“检测到分类”这条线
2.1 把人脸识别拆成四步:检测、对齐、特征、分类
很多人一上来就找“人脸识别算法”,然后陷入了计算机视觉和机器学习到底有什么区别的困惑里。实践中,一个能用机器学习理论解释的人脸识别系统,不是单一算法,而是四段流水线。
第一步是人脸检测,解决“脸在哪”的问题。常见工具是 OpenCV 的 Haar Cascade,或者更稳一点的 MediaPipe、RetinaFace,这一步输出的是人脸边界框坐标。第二步是人脸对齐,把检测到的脸通过仿射变换摆正,统一眼睛位置和面部尺度,这是整套系统里最容易被跳过的环节,也是识别率波动的最大来源。第三步是特征提取,把一张图像变成一串固定长度的数值向量,早期用 LBP、HOG,现在主流是预训练卷积网络输出 embedding。第四步是分类或检索,训练一个机器学习分类器,或者拿特征向量去库里比对相似度,最终回答“这张脸是谁”。
把流程拆开有个额外好处:课程报告每一章都能对应一个独立实验,每个环节都能单独出图表和指标,答辩时老师问到哪里你都能接上。很多同学把“人脸识别”理解成一个端到端模型,等到报告里要写原理公式时发现全是黑匣子,那才是真难受。
2.2 常见实现路线对比:LBPH、特征+分类器、端到端网络
我把市面上课设常用的三条路线做过一次对比,结论挺明确:题目标着“机器学习”的,最优解是“手工特征或预训练特征 + sklearn 分类器”,而不是堆一个深度学习网络。
| 实现路线 | 特征来源 | 分类/匹配方式 | 适用场景 | 课设适配度 |
|---|---|---|---|---|
| LBPH | 局部二值模式直方图 | OpenCV 内部近邻匹配 | 小规模人脸库、快速演示 | 中,代码最短,但公式少、没体现机器学习 |
| HOG/手工特征 + SVM | HOG 或 LBP 直方图 | sklearn 的 SVC、MLP | 班级考勤、门禁机原型 | 高,覆盖机器学习核心知识点 |
| 预训练 CNN + 分类器 | MobileNet/ResNet embedding | sklearn 的 SVM、随机森林 | 数据量小但要求高鲁棒性 | 高,兼顾效果和可解释性 |
| 端到端深度学习 | 网络自己学 | Softmax 分类 | 数据量大、有 GPU | 低,偏离课程主题,报告难写深 |
LBPH 看起来最省事,几行代码就能跑,但它在 OpenCV 内部做的是近邻距离比对,严格说跟你机器学习课上学的东西关系不大。端到端深度网络效果确实最好,但人脸数据集通常只有几百张,网络训练容易过拟合,而且答辩时被问到“反向传播推导”会很痛苦。中间两条路线把“特征工程”和“分类器设计”分开,正好落在机器学习课程的核心章节上,报告能写的内容也最充实。
2.3 选型的三个约束:可解释、能复现、能写进报告
给课程设计选技术路线,跟给生产项目选型不太一样。生产项目看效果和运维成本,课设更多看三点约束。
第一是可解释。SVM 的软间隔目标函数、RBF 核的 gamma 参数、交叉验证选参,这些在周志华的《机器学习》里都有完整章节,写进报告是名正言顺的。换成深度网络,报告里只能写“用了某预训练模型”,原理部分几乎凑不出字数。
第二是能复现。课设环境往往是室友的笔记本、实验室的公用台式机,没 GPU 是常态。我一般要求代码在纯 CPU 机器上十分钟内能跑完一轮训练,这个约束直接把大网络淘汰掉了。
第三是能写进报告。课程报告需要实验图表,sklearn 的混淆矩阵、GridSearchCV 的验证曲线都是现成的,截图就能用。如果选端到端方案,Loss 曲线之外就没什么好画的,报告厚度撑不起来。
所以见到这类标题时,先看一眼里面的分类器是 SVM、决策树还是 MLP——是这个,说明思路对路;如果是某个接好的深度学习框架,反而要警惕,因为那更像调包教程而不像机器学习大作业。下一章开始做数据,这是整个项目真正拉开差距的地方。
3. 数据准备与预处理:识别率 70% 还是 95%,差别在这一步
3.1 数据集从哪里来:公开人脸库与自采数据的取舍
数据集是“先有数据还是先有模型”这个问题里最现实的一环。课设常见三种来源:压缩包自带数据、公开人脸库、自己用摄像头采集。
如果压缩包里有现成的数据目录,直接沿用即可,但我会建议读懂它的目录组织方式——学号/姓名作为文件夹名、每个文件夹下是这个人不同姿态的照片,这种结构本身就是一种标签体系。没有自带数据的话,公开人脸库是最省事的选择。ORL 库 40 个人每人 10 张正面照,规模小、光照相对统一,非常适合做课程实验;想增加挑战性可以换 Yale 库,光照变化更大。注意,用公开数据集时报告里要写清楚来源和引用,这是学术规范问题,答辩时老师很在意。
自采数据更贴近门禁机场景,但有三件事要提前约定好:拍谁、拍多少、怎么拍。我一般建议至少 5 个人,每人 30 到 50 张,覆盖正面、左右偏转 15 度、戴不戴眼镜、室内灯光和背光各一组。数据采集前要让对方知情同意,采集完按“姓名_序号.jpg”的规则命名,不要创建“新建文件夹(3)”这种目录,否则后面遍历代码要改半天。
3.2 人脸检测与对齐:把视频帧里的脸裁成统一尺寸
拿到原始照片后,第一步不是训练,而是做检测和对齐。这里给出一段可用的预处理函数,核心思路是先检测人脸框,再裁剪缩放,最后归一化像素值。
import os import cv2 import numpy as np FACE_SIZE = 112 # 统一缩放到 112x112,兼顾细节与计算量 def load_cascade(): # OpenCV 自带的 frontalface 模型,适合正脸居多的课设数据 cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml" return cv2.CascadeClassifier(cascade_path) def preprocess_face(img, cascade, face_size=FACE_SIZE): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 直方图均衡化:把过暗或过亮的像素分布拉开,缓解光照差异 gray = cv2.equalizeHist(gray) faces = cascade.detectMultiScale( gray, scaleFactor=1.1, # 每轮检测图像缩小 1.1 倍,值越小越精细也越慢 minNeighbors=5, # 候选框至少经过 5 重验证才保留,越大误检越少 minSize=(80, 80) # 小于 80x80 的区域直接忽略 ) if len(faces) == 0: return None x, y, w, h = faces[0] # 多人场景可循环处理,单人课设先取最大框即可 roi = gray[y:y+h, x:x+w] face_resized = cv2.resize(roi, (face_size, face_size)) return face_resized / 255.0 # 归一化到 [0,1],让特征数值量纲一致这段代码有三个参数值得细说。scaleFactor=1.1 表示每次检测将图像缩小 10%,数值越小检测越耗时但有可能会找回偏小的脸;minNeighbors=5 控制误检与漏检的平衡,设大了容易漏脸,设小了背景区域会被当成脸;minSize 直接过滤掉远处的路人脸,在门禁和考勤场景非常实用。
需要说明,真正的“对齐”需要眼睛或鼻尖关键点。上面的退化方案只是把人脸框裁出来做缩放,当人脸左右偏转较大时,框的中心点并不等于面部中心。工程上更可靠的做法是先用关键点模型检出双眼坐标,再用仿射变换把双眼映射到固定位置。课设里如果没有现成关键点模型,至少保证裁剪框上方留一点额头区域,别把脸裁切掉一角,这已经是成本最低的有效对齐。
3.3 划分训练集和验证集:按人头分,不按帧分
预处理完的数据要划分训练集和验证集。这里有一个极常见却隐蔽的错误:用随机划分把同一个人的照片同时分到训练集和验证集。人脸照片之间相似度极高,模型实际上“见过”验证集里那个人的脸,结果就是验证准确率虚高,换个人就现原形。
正确做法是按人头分组划分,sklearn 里用 GroupShuffleSplit 可以一步到位。
from sklearn.model_selection import GroupShuffleSplit # X 是所有预处理后的特征矩阵,y 是姓名标签,persons 是每个人唯一的组编号 gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, val_idx = next(gss.split(X, y, groups=persons)) X_train, X_val = X[train_idx], X[val_idx] y_train, y_val = y[train_idx], y[val_idx]这段代码里的 groups 参数是防止数据泄漏的关键。persons 数组里同一个人的所有照片必须对应同一个 ID,而不是按文件名随机编号。划分之后最好检查一遍验证集里是否包含与训练集相同的人,检查方式很简单:打印 train_idx 和 val_idx 对应的人名集合,确认交集为空。这一步花不了两分钟,能避免整个实验结论失去意义。
数据集就绪后,下一个问题是特征怎么提取。如果你用的是 HOG 这类手工特征,特征维度取决于图像尺寸和分块参数;如果走预训练模型,特征维度可能是几百到一千多。两种方式写法差异很大,下一章分开讲。
4. 特征提取与分类器训练:把一张脸变成一串可比较的数字
4.1 手工特征路线:HOG 特征配合 SVM
机器学习课设里最正统的做法,是手工特征加分类器。我倾向于用 HOG 而不是 LBPH,因为 OpenCV 的 LBPH 直接封装好了匹配逻辑,绕过了 sklearn;HOG 特征则能正常走“提取特征 → 标准化 → 训练模型 → 调参”这个标准流程。
HOG(方向梯度直方图)的核心思想是把图像划分成小格子,统计每个格子里的梯度方向分布。对人脸来说,眼睛、鼻梁、嘴角这些位置的梯度方向有相对稳定的模式,正好可以作为身份特征。
import numpy as np from skimage.feature import hog from sklearn.svm import SVC from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.model_selection import GridSearchCV def extract_hog(face_gray): # face_gray 是上一章预处理输出的 112x112 灰度图,像素范围 [0,1] fd = hog( face_gray, orientations=9, # 梯度方向分成 9 个桶,类似直方图的 bin 数 pixels_per_cell=(8, 8), # 每个格子 8x8 像素 cells_per_block=(2, 2), # 每个块包含 2x2 个格子,用于局部归一化 block_norm="L2-Hys" # L2 归一化变体,对光照变化更稳 ) return fd # 假设 X_train 是 n 张 112x112 的灰度图数组 X_train_hog = np.array([extract_hog(x) for x in X_train]) X_val_hog = np.array([extract_hog(x) for x in X_val]) pipe = make_pipeline( StandardScaler(), # 特征数值范围差异大,先标准化再进 SVM SVC(kernel="rbf", class_weight="balanced") ) param_grid = { "svc__C": [1, 10, 100], # 正则化系数,越大对训练样本拟合越强 "svc__gamma": [0.001, 0.0001], # RBF 核作用半径,越小决策边界越平滑 } grid = GridSearchCV(pipe, param_grid, cv=3, scoring="f1_macro", n_jobs=-1) grid.fit(X_train_hog, y_train) print("best score:", grid.best_score_, "params:", grid.best_params_) print("validation accuracy:", grid.score(X_val_hog, y_val))三个参数务必理解后再改。orientations=9 表示每个格子统计 9 个方向的梯度强度,太小丢失细节,太大特征维度暴涨;pixels_per_cell=8 意味着 112x112 的图像会被切成 14x14 个格子;C 是 SVM 正则化强度的倒数,C=1 是默认起点,过度拟合时调小,欠拟合时调大。gamma 在 RBF 核里控制单一样本的影响半径,gamma 太大会把边界画得弯弯曲曲,现实中表现为“只认训练集里那张脸”。
这里有一个课设常见的误区:为了追求准确率,一上来就把 C 调到 1000。结果是小样本下训练集得分几乎满分,验证集表现一般,典型的过拟合。网格搜索的意义就在于让你看到这个趋势,而不是挑一个最高验证分就完事。把不同 C 和 gamma 组合的验证分数画成热力图,这张图放进报告里会非常加分。
4.2 预训练模型抽特征:在 sklearn 里接一个现成视觉编码器
如果想让识别更鲁棒,可以用预训练卷积网络提取特征,再接 sklearn 分类器。这个方案的本质是:用别人在海量图像上训练好的网络模型当特征提取器,自己只训练最后那个分类器。这也是计算机视觉和机器学习结合得最舒服的方式——模型部分交代清楚来源,分类器部分保留你的工作量。
import torch from torchvision import models, transforms # MobilenetV2 轻量,CPU 上跑得动;新版本 torchvision 用 weights 参数 model = models.mobilenet_v2(weights=models.MobileNet_V2_Weights.DEFAULT) model.eval() # 评估模式,关闭 Dropout 和 BatchNorm 的随机性 # 输入尺寸 224x224,均值方差使用 ImageNet 的统计值 transform = transforms.Compose([ transforms.ToPILImage(), transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) # 去掉最后的全连接分类层,只保留特征部分 feature_extractor = torch.nn.Sequential(*list(model.children())[:-1]) def extract_cnn_feature(face_gray): face_float = (face_gray * 255).astype("uint8") # 还原成 0-255 灰度图 face_rgb = cv2.cvtColor(face_float, cv2.COLOR_GRAY2BGR) # 转三通道 x = transform(face_rgb).unsqueeze(0) # 增加 batch 维度 with torch.no_grad(): feat = feature_extractor(x) # 全局平均池化:把 7x7x1280 压缩成一个 1280 维向量 feat = feat.mean(dim=(2, 3)).numpy() return feat.flatten()重点在最后一步全局平均池化。MobileNetV2 最后的特征图形状是 (batch, 1280, 7, 7),mean(dim=(2,3)) 把空间维度全部平均掉,得到不受输入尺寸影响的 1280 维向量,这个向量就是“人脸 embedding”。首次运行这段代码会自动下载预训练权重,耗时取决于网络状况,之后的实验直接读缓存。
14140 行说明:feature_extractor 里的参数被冻结了,反向传播不可能,也不需要;特征只算一次,本地存成 npy 文件后,后续调分类器参数不需要重新过网络。这一条能省下大量时间,我看很多人每次调参都把整批图重新过一遍网络,纯属浪费。
拿到 embedding 后,接的分类器我建议优先试 SVM 和 MLPClassifier。SVM 在几百维到一千多维的特征上表现稳定,MLPClassifier 能让你在报告里多写一段神经网络。如果追求快,随机森林也能用,但效果通常会差一点。特征维度相对图像原始像素已经低了很多,一般不需要再降维,强行 PCA 反而可能丢掉区分信息。
4.3 用摄像头跑通实时识别:检测、识别、画框三步循环
训练完成后,实时演示是整个大作业最有视觉冲击力的环节,也是答辩现场最容易出岔子的环节。一个稳定的实时识别循环就三段:读帧、识别每个脸、画框和标签。
import cv2 cap = cv2.VideoCapture(0) cap.set(3, 640) # 画面宽度 640 cap.set(4, 480) # 画面高度 480 while True: ok, frame = cap.read() if not ok: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.equalizeHist(gray) faces = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(80, 80)) for (x, y, w, h) in faces: roi = cv2.resize(gray[y:y+h, x:x+w], (112, 112)) / 255.0 # 用训练好的模型预测:注意 SVM 分类器是哪条路线就调用哪条 feat = extract_hog(roi) if use_hog else extract_cnn_feature(roi) label = grid.best_estimator_.predict([feat])[0] score = grid.best_estimator_.decision_function([feat])[0] cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(frame, f"{label}: {score:.2f}", (x, y - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow("face-recognition", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()decision_function 返回的是样本到决策超平面的距离,值越正代表越有把握。课设里把这个距离直接显示在框上方,既有技术感,又能直观看出分类器的确信程度。注意如果用的是 SVC 多分类,decision_function 默认返回一对一投票的得分矩阵,想省事可以在训练时设 decision_function_shape="ovr"。
视频帧率方面,预处理和特征提取都会耗时。如果发现画面明显卡顿,常见做法是每隔一帧做一次识别,但检测和画框可以每帧都做;或者把输入尺寸降到 320x240,帧率立刻上来。答辩演示时,分辨率低一点但帧率流畅,远比高分辨率卡成幻灯片体面。
到这里,一条“检测 → 对齐 → 特征 → 分类器 → 实时演示”的完整链路已经跑通。接下来聊聊我实际调这东西时踩过的坑,有些坑几乎每个做人脸识别课设的人都会遇到。
5. 人脸识别课设避坑记录:五个让人崩溃的经典翻车现场
5.1 训练集 98%,摄像头下一换角度就认错
现象:训练集上验证准确率 98%,网格搜索结果也很漂亮,但拿到摄像头前,稍微侧脸、仰头或者换个光线,名字就开始在框上乱跳。
原因:数据集太“干净”了。采集或公开数据集里的人脸基本全是正脸、均匀光照,预处理的直方图均衡化也压不住角度变化带来的特征偏移。模型实际上只学会了“正脸模板”,没有学到“人脸”这个类别的不变特征。
解决:回到数据层面加变化。自采数据时把左右偏转、俯仰、戴不戴眼镜、室内灯光和靠窗背光都录进去;如果只有公开数据集,用随机亮度调整、水平翻转、轻微旋转变换来扩充。记住一个原则:预处理里见过的变化,模型才可能学会;预处理剔除掉的变化,等于主动放弃鲁棒性。
5.2 特征矩阵维度对不上,fit 直接报错
现象:训练时报 “setting an array element with a sequence” 或者维度不一致的 ValueError,代码逻辑看着没问题,但就是跑不过去。
原因:绝大多数情况是某张图没检测到人脸,函数返回了 None 或者空数组,但外层代码没有过滤,直接把它 append 进特征列表,最后列表里混入了不同长度的对象,np.array 一转换就报错。
解决:在特征提取前统一过滤无效帧。检测不到脸的样本要么跳过并记录文件名,要么先用最大的人脸框兜底。更稳妥的做法是在预处理函数里约定“返回 None 表示无效”,外层代码遇到 None 就跳过,最后再统计一下被跳过的数量。这个统计值写到课程报告里还能侧面证明数据清洗的严谨性。
5.3 摄像头画面卡顿,识别像放幻灯片
现象:实时演示时画面一卡一卡,脸框和名字刷新跟不上人移动,答辩现场非常尴尬。
原因:性能瓶颈通常不在检测模型,而在你每帧都做了太重的事。比如把整帧缩放、每帧都跑一次完整特征提取、在 1920x1080 的分辨率上做检测。另一个隐蔽问题是 waitKey 参数设得过大,比如 waitKey(30) 等于每帧强制等 30 毫秒,帧率上限被锁死在 33。
解决:先量一下每一段的耗时,用 time.time() 分别统计 detectMultiScale、特征提取、分类预测的开销。定位到瓶颈后,把摄像头分辨率设成 640x480,识别循环改成每 2 帧识别一次,检测框依然每帧绘制。waitKey(1) 就够了,不要用大数值去“控制帧率”,界面卡顿大部分时候不是这个原因。
5.4 同一人换眼镜或刘海后识别率骤降
现象:训练时这个人识别得好好的,换一副眼镜、剪了刘海、或者戴个帽子,直接认成另一个人。
原因:特征向量里把“这个人长什么样”和“这个人当时的装扮”混到了一起。HOG 特征本身对局部遮挡不敏感,但裁剪对齐不严时,刘海变化会显著改变整个特征向量的分布;预训练 CNN 特征也有同样问题。
解决:核心还是数据增强和对齐。采集数据时跑完眼镜摘戴、刘海拨开、帽子摘戴几个变化,模型才能区分“身份特征”和“装扮特征”。另外一个工程技巧是,注册人脸库时不用单张图的特征,而是用同一个人多张图的平均特征,平均操作能抹掉一部分装扮带来的噪声。这个技巧放到第六章讲检索式识别时会显得特别自然。
5.5 验证准确率虚高,答辩演示时露馅
现象:本地代码里验证集准确率 99%,答辩现场换了个环境,识别率突然一半都不到,整个人站在台上懵掉。
原因:十有八九是数据划分泄漏。常见三种泄漏:一是随机划分训练集和验证集,同一个人的照片两边都有;二是先做全数据集上的预处理和特征提取,再划分,特征提取器“见过”验证集;三是公开数据集里本身就包含重复或相近的帧,没去重就划分。
解决:严格按人头分组划分,保证验证集里的人名和训练集零交集;特征提取必须在划分之后对训练集单独拟合,标准化器也要用训练集的统计量。这里介绍一个最终验证方法:把识别系统带到教室、实验室等一个完全没参与训练的数据环境里,拍 5 个人的新照片,看准确率还成不成立。这个“现场测试”的数据和结论写进报告,含金量比验证集数字高得多。
6. 把作业做成简历亮点:注册式识别、指标与界面
6.1 从分类器升级为人脸库检索:注册式识别的思路
分类器方案有个天然缺陷:来一个新同学,你得重新训练整个模型。真实门禁系统通常不是这么干的,它们用注册式识别:每个用户入库时只保存一条特征向量,识别时拿当前人脸特征去库里找最相似的那条,相似度超过阈值才算识别成功。
注册式识别对课设来说并不复杂,把上一章的训练环节替换成特征入库和比对即可。
import numpy as np # db: {姓名: 特征向量},特征向量取多张图的平均值,降低噪声 db = {} for name in ["zhang", "li", "wang"]: embs = [extract_cnn_feature(f) for f in load_faces_of_person(name)] db[name] = np.mean(embs, axis=0) def recognize(embedding, threshold=0.65): best_name, best_sim = None, -1 for name, emb in db.items(): sim = np.dot(embedding, emb) / ( np.linalg.norm(embedding) * np.linalg.norm(emb)) if sim > best_sim: best_name, best_sim = name, sim return best_name if best_sim > threshold else "unknown"这段代码里 threshold=0.65 不能拍脑袋定。用验证集里每个人的多张照片两两比对,算出“同一个人的相似度”和“不同人的相似度”两组分布,取两个分布的交点附近作为阈值,才是有说服力的做法。门禁机场景宁可不识别也不乱识别,阈值适当调高,这就是从“能用”到“好用”的差别。
6.2 阈值怎么定:用小批量验证样本找等错误率
等人脸库建好后,我有两个固定动作。第一,把验证集的每一张照片去库里检索,得到每个人所有样本的相似度,然后分别画出正确匹配和错误匹配的相似度分布图。第二,在这个分布图上找等错误率的点——也就是误识率和拒识率相等的阈值,再根据具体场景往高或往低偏一点。门禁系统更怕陌生人闯入,阈值就取得比等错误率更高;实验室门禁场景更怕自己人被挡在外面,阈值就略微放低。
6.3 课程报告里写清这些指标,答辩才能站稳
报告里不要只放一个准确率。实际答辩中老师经常追问的指标有三个:混淆矩阵能说清楚哪个人的容易被认错;每个类别的精确率与召回率能反映人头不均衡时模型的偏向;单次识别耗时常说明能否实时运行。如果做了注册式识别,把阈值与错误率的关系曲线也画进去,答辩时可讲的东西会多很多。
如果还有余力,给程序加一个简单的 Tkinter 界面,把摄像头画面和识别结果放在同一个窗口里,然后录一段演示视频放报告附录,整个交付物的完整度立刻上了一个台阶。我个人的习惯是先把三人小库跑通,再做十人规模扩展;先固定数据采集规则,再调模型参数,每一步都记录在案。已经完成了最重要的部分——思路理顺了,代码和数据只是时间问题。希望这套生产链路能帮你少走弯路,交出一份真正能讲清楚的人脸识别大作业。
本文还有配套的精品资源,点击获取