简介:这份资源是面向高校学生与Python初学者的机器学习人脸识别系统完整项目包,可直接用于期末大作业、课程设计或自学练手。压缩包共4个文件,包含2个py源码文件、1份pdf报告文档和1份md说明文件,整体约631KB,体积轻便易于部署。其中主程序与分类模块分工明确,代码附有注释,新手也能看懂运行逻辑;pdf报告则完整呈现了人脸性别识别大作业的设计思路与实现过程,可直接参考撰写实验报告。目前已有283人学习下载,说明其在同类作业资源中具有一定认可度。读者拿到后既能获得可运行的识别系统源码,也能借助报告文档理解机器学习在人脸识别中的落地流程,适合需要快速完成课程任务或积累项目经验的同学参考使用。
1. 从一份期末大作业说起:机器学习人脸识别系统到底交付了什么
每年到了期末季,总有一批同学在深夜对着「人脸识别系统」这几个字发愁。标题里这份「Python期末大作业基于机器学习的人脸识别系统源码+报告文档」,本质上是一个完整的课程设计交付物:一套能跑起来的 Python 代码,加上一份讲清楚原理和实验过程的报告。它要解决的问题很具体——给定若干人的照片,训练一个模型,让它能认出镜头前是谁。适合谁?正在做机器学习、模式识别、计算机视觉课程设计的学生,以及想用一个小项目把「数据预处理→特征提取→模型训练→推理部署」这条链路走通的新手。人脸识别门禁机这类产品听起来高大上,但拆开看,核心流程和一份期末作业没有本质区别,区别只在数据规模和工程细节。这一章先把这套系统讲清楚,后面几章带你从零复现,再讲清楚哪里最容易翻车。
2. 人脸识别系统的技术选型:为什么是机器学习而不是深度学习
2.1 课程作业场景下的三条技术路线对比
在动手之前,先想清楚用哪条路线。人脸识别从技术演进看,大致分三代:早期基于几何特征和模板匹配,中期基于传统机器学习(如 PCA、LDA、SVM),近期基于深度卷积网络(如 FaceNet、ArcFace)。期末大作业这个场景,选型不能只看精度,还要看算力、数据量、可解释性和报告好不好写。
| 路线 | 代表方法 | 数据需求 | 训练算力 | 报告可写性 | 适合场景 |
|---|---|---|---|---|---|
| 传统机器学习 | PCA+SVM、LDA | 每人 10~20 张即可 | CPU 可跑 | 原理清晰,公式好推导 | 课程作业、小样本 |
| 轻量深度学习 | 小型 CNN | 每人 50 张以上 | 需要 GPU 或较长时间 | 网络结构可画图 | 有显卡的进阶作业 |
| 预训练模型微调 | ArcFace、FaceNet | 每人 5 张即可 | 推理需 GPU 更佳 | 偏工程,原理难自证 | 追求高精度的项目 |
我一般会建议:如果报告要求你推导原理、画特征脸、讲清楚数学过程,选 PCA+LDA+SVM 这条传统机器学习路线;如果只是要求系统能跑、精度够看,可以用预训练模型做特征提取再套一个分类器。标题里写的是「机器学习」,不是「深度学习」,所以本文主线走传统机器学习路线,这也是最稳妥、最容易复现的选择。
2.2 最小可运行环境搭建
环境是第一个坑。Python 版本、库版本不匹配,能让你在第一步就卡住。下面这套组合是我在多个课程作业里验证过能跑通的。
# 建议 Python 3.8 ~ 3.10,太新的版本部分库轮子不全 python -m venv face_env # Windows 激活 face_env\Scripts\activate # Linux / macOS 激活 source face_env/bin/activate # 核心依赖,指定版本避免踩坑 pip install numpy==1.24.3 pip install opencv-python==4.8.1.78 pip install scikit-learn==1.3.0 pip install matplotlib==3.7.2 pip install pillow==10.0.0逻辑说明:用虚拟环境隔离依赖,避免污染系统 Python。numpy 是数值计算底座,opencv 负责图像读取和人脸检测,scikit-learn 提供 PCA、LDA、SVM 等算法,matplotlib 用来画图写报告。参数说明:numpy 不要装 2.x,部分老代码会报np.float已移除;opencv 用opencv-python而不是opencv-contrib-python,除非你要用额外的人脸模块。安装 numpy 库的方法如果报错,优先检查 pip 是否升级到最新:python -m pip install --upgrade pip。
提示:如果下载慢,换国内镜像源,命令末尾加
-i https://pypi.tuna.tsinghua.edu.cn/simple,不要用来源不明的第三方包。
2.3 数据集的组织方式
数据组织决定了后面代码好不好写。常见做法是按人分文件夹,每个文件夹放这个人的多张照片,文件夹名就是标签。
dataset/ ├── person_01/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... ├── person_02/ │ ├── 001.jpg │ └── ... └── person_03/ └── ...每人类别建议不少于 10 张,总数不少于 100 张,否则 PCA 降维后特征不稳定。照片要求:正脸、光照均匀、分辨率不低于 100x100。如果用人脸识别门禁机的真实场景数据,还要考虑侧脸和遮挡,但课程作业阶段先用规整数据把流程跑通。
3. 从原始照片到特征向量:数据预处理与 PCA 降维实操
3.1 人脸检测与对齐的最小代码
原始照片往往背景杂乱,直接送进模型会引入大量噪声。第一步是用 OpenCV 的 Haar 级联检测器把人脸框出来,再统一缩放到固定尺寸。
import cv2 import os import numpy as np # 加载 OpenCV 自带的人脸检测器 face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' ) def detect_and_crop(img_path, size=(100, 100)): img = cv2.imread(img_path) if img is None: return None gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # scaleFactor 和 minNeighbors 是两个关键参数 faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60) ) if len(faces) == 0: return None # 取面积最大的那张脸,避免误检 x, y, w, h = max(faces, key=lambda f: f[2] * f[3]) face = gray[y:y+h, x:x+w] face = cv2.resize(face, size) # 直方图均衡化,缓解光照影响 face = cv2.equalizeHist(face) return face逻辑说明:detectMultiScale返回所有人脸框,取面积最大的一个作为主脸。scaleFactor=1.1表示每次缩放比例,越小检测越细但越慢;minNeighbors=5控制误检,值越大越严格但可能漏检。equalizeHist做直方图均衡化,这一步对光照不均的照片提升明显。参数说明:minSize设成 60x60 可以过滤掉远处的小脸误检;如果照片里人脸很大,可以适当调大。
3.2 构建特征矩阵与标签向量
把每张照片处理成人脸图像后,拉平成向量,堆成矩阵。这一步是传统机器学习人脸识别的核心数据结构。
def build_dataset(root_dir, size=(100, 100)): X, y = [], [] label_map = {} current_label = 0 for person_name in sorted(os.listdir(root_dir)): person_dir = os.path.join(root_dir, person_name) if not os.path.isdir(person_dir): continue label_map[current_label] = person_name for fname in os.listdir(person_dir): if not fname.lower().endswith(('.jpg', '.png', '.jpeg')): continue face = detect_and_crop(os.path.join(person_dir, fname), size) if face is None: continue # 拉平成 10000 维向量 X.append(face.flatten().astype(np.float32)) y.append(current_label) current_label += 1 return np.array(X), np.array(y), label_map逻辑说明:flatten()把 100x100 的图像变成 10000 维向量,这是 PCA 的输入。label_map保存标签到人名的映射,推理时用来输出名字。参数说明:如果检测失败的照片较多,说明数据质量差,需要人工筛一遍;size不要设太小,50x50 以下会丢失辨识特征。
3.3 PCA 降维:把 10000 维压到 100 维
10000 维直接训练,样本数往往只有几百,维度灾难会让模型完全失效。PCA 的作用是找到方差最大的方向,把维度压下来。
from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler # X 是 build_dataset 返回的特征矩阵 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # n_components 可以设成整数(保留多少维)或小数(保留多少方差) pca = PCA(n_components=0.95, whiten=True) X_pca = pca.fit_transform(X_scaled) print(f"原始维度: {X.shape[1]}, 降维后: {X_pca.shape[1]}")逻辑说明:先标准化再 PCA,避免某些像素值域差异影响主成分方向。n_components=0.95表示保留 95% 的方差,具体降到多少维由数据决定,通常几百张图会降到 50~150 维。whiten=True让各主成分方差归一,对后续 SVM 有帮助。参数说明:如果报告要求固定维度,把n_components改成整数如 100;如果降维后精度反而下降,检查是否漏了标准化。
注意:PCA 必须在训练集上 fit,再 transform 测试集。如果全量数据一起 fit,会造成数据泄露,报告里的精度虚高,答辩时容易被问穿。
4. 分类器训练与推理:SVM、LDA 怎么选怎么调
4.1 用 SVM 做多分类的完整流程
PCA 降维后,接一个分类器。SVM 在小样本高维数据上表现稳定,是课程作业的常见选择。
from sklearn.svm import SVC from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.metrics import classification_report, accuracy_score # 划分训练集和测试集,stratify 保证每类比例一致 X_train, X_test, y_train, y_test = train_test_split( X_pca, y, test_size=0.25, random_state=42, stratify=y ) # 网格搜索找最优参数 param_grid = { 'C': [0.1, 1, 10, 100], 'gamma': ['scale', 0.001, 0.01, 0.1], 'kernel': ['rbf', 'linear'] } grid = GridSearchCV(SVC(probability=True), param_grid, cv=5, n_jobs=-1) grid.fit(X_train, y_train) print("最优参数:", grid.best_params_) best_svm = grid.best_estimator_ y_pred = best_svm.predict(X_test) print("准确率:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))逻辑说明:stratify=y保证训练测试集里每类样本比例一致,避免某类全进训练集。GridSearchCV用 5 折交叉验证遍历参数组合,n_jobs=-1用满 CPU。参数说明:C越大对误分类惩罚越重,容易过拟合;gamma控制 RBF 核的影响范围,越大越容易过拟合。kernel='linear'在样本数远小于维度时可能更稳。如果准确率低于 80%,先检查数据预处理,再调参数。
4.2 LDA 做特征降维加分类
LDA 和 PCA 的区别:PCA 找方差最大方向,无监督;LDA 找类间距离大、类内距离小的方向,有监督。人脸识别里 LDA 往往比 PCA 更适合分类。
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # LDA 降维的维度上限是 类别数 - 1 n_classes = len(np.unique(y)) lda = LinearDiscriminantAnalysis(n_components=min(n_classes - 1, 50)) X_lda = lda.fit_transform(X_scaled, y) # 再用简单分类器 X_train, X_test, y_train, y_test = train_test_split( X_lda, y, test_size=0.25, random_state=42, stratify=y ) clf = SVC(kernel='linear', C=1.0) clf.fit(X_train, y_train) print("LDA+SVM 准确率:", clf.score(X_test, y_test))逻辑说明:LDA 降维必须传入标签y,这是它和 PCA 的本质区别。n_components最大只能是类别数减一,比如 10 个人最多降到 9 维。参数说明:如果类别数很少(比如 5 个人),LDA 降到 4 维可能损失信息,可以先用 PCA 降到 100 维再用 LDA。常见做法是 PCA+LDA 串联,先无监督降噪再有监督降维。
4.3 推理阶段:从摄像头到识别结果
训练完模型要能实际用。下面是一个从单张图片推理的完整函数。
import joblib # 保存模型 joblib.dump({'pca': pca, 'scaler': scaler, 'svm': best_svm, 'label_map': label_map}, 'face_model.pkl') def predict_face(img_path, model_path='face_model.pkl'): bundle = joblib.load(model_path) face = detect_and_crop(img_path) if face is None: return "未检测到人脸" vec = face.flatten().astype(np.float32).reshape(1, -1) vec = bundle['scaler'].transform(vec) vec = bundle['pca'].transform(vec) pred = bundle['svm'].predict(vec)[0] prob = bundle['svm'].predict_proba(vec).max() name = bundle['label_map'][pred] return f"{name} (置信度 {prob:.2f})"逻辑说明:推理时的预处理必须和训练时完全一致,顺序是检测→缩放→均衡化→拉平→标准化→PCA→分类。任何一步不一致,结果都会错得离谱。参数说明:predict_proba给出置信度,低于 0.6 建议判为未知,避免强行归类。如果部署到人脸识别门禁机场景,还要加活体检测,但课程作业阶段可以省略。
5. 避坑与排查:人脸识别作业最容易翻车的五个地方
5.1 准确率虚高到 99% 但实际不能用
现象:测试集准确率 99%,但换一张新照片就认错。原因:训练集和测试集划分时没有按人划分,同一个人既在训练集又在测试集,模型只是记住了这张脸。解决:用GroupShuffleSplit按人划分,或者确保测试集里的人不在训练集出现。课程作业里如果要求「识别已知的人」,可以按样本划分,但报告里要写清楚。
5.2 检测不到人脸或框错位置
现象:detectMultiScale返回空列表,或者框到背景。原因:Haar 检测器对侧脸、暗光、遮挡很敏感,参数不合适。解决:调小scaleFactor到 1.05,调大minNeighbors到 6,加minSize限制。如果还是不行,换 DNN 人脸检测器,OpenCV 自带cv2.dnn模块,精度高很多但需要下载模型文件。
5.3 PCA 降维后维度还是很高
现象:n_components=0.95降完还有 800 维。原因:图像像素间相关性低,方差分散,95% 方差需要很多主成分。解决:先把图像缩小到 50x50,或者改用n_components=100固定维度。也可以先做 PCA 再 LDA,LDA 能把维度压到类别数减一。
5.4 内存溢出或训练极慢
现象:几千张图训练时内存爆掉。原因:10000 维浮点矩阵,几千张就是几百 MB,PCA 计算协方差矩阵更吃内存。解决:用PCA(svd_solver='randomized')加速,或者分批读取数据。图像尺寸降到 80x80 能省近一半内存。
5.5 报告里的公式和代码对不上
现象:报告写 PCA 用协方差矩阵特征分解,代码用 sklearn 的svd_solver。原因:sklearn 默认用 SVD 实现 PCA,和教科书上的特征分解数学等价但数值更稳。解决:报告里说明「采用 SVD 实现,与特征分解等价」,或者手动用np.linalg.eigh实现一遍对照。答辩时老师问起,能讲清楚两者关系就是加分项。
6. 让作业再上一个台阶:交叉验证与混淆矩阵的写法
前面把系统跑通了,但如果想让报告更有说服力,光有一个准确率数字不够。我一般会补两样东西:交叉验证的稳定性分析和混淆矩阵的可视化。交叉验证能说明模型不是靠某一次划分走运,混淆矩阵能暴露哪些人容易被认错。
from sklearn.model_selection import cross_val_score, StratifiedKFold from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt import seaborn as sns # 5 折交叉验证,看准确率波动 cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(best_svm, X_pca, y, cv=cv, scoring='accuracy') print(f"交叉验证准确率: {scores.mean():.3f} ± {scores.std():.3f}") # 混淆矩阵 cm = confusion_matrix(y_test, y_pred) plt.figure(figsize=(8, 6)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=[label_map[i] for i in range(len(label_map))], yticklabels=[label_map[i] for i in range(len(label_map))]) plt.xlabel('预测标签') plt.ylabel('真实标签') plt.title('人脸识别混淆矩阵') plt.tight_layout() plt.savefig('confusion_matrix.png', dpi=150)逻辑说明:cross_val_score返回 5 次划分的准确率,均值反映整体水平,标准差反映稳定性。标准差大于 0.05 说明数据划分影响大,需要检查样本是否均衡。混淆矩阵对角线是正确分类,非对角线是错分,如果某两列数值特别高,说明这两个人特征相似,可以考虑增加这两人的训练样本。参数说明:StratifiedKFold保证每折里类别比例一致,比普通 KFold 更适合分类任务。
还有一个技巧:把 PCA 降维后的前两个主成分画散点图,能直观看到类别是否可分。如果散点图里各类混在一起,说明 PCA 降维丢了判别信息,应该换 LDA 或者增加数据。这个图放进报告,比干巴巴的数字有说服力得多。
最后说个血泪经验:我见过太多人把代码跑通就交差,报告里只贴结果不写过程。其实老师看的是你有没有理解每一步在干什么。把预处理前后的对比图、PCA 保留方差曲线、不同参数的准确率对比表放进去,工作量看起来扎实,分数自然高。这套流程走下来,从数据到模型到评估,完整闭环,换成真实的人脸识别门禁机场景,要补的只是活体检测和工程部署,核心逻辑一模一样。希望帮到你。
本文还有配套的精品资源,点击获取