ARTICLE DETAIL

资讯详情

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

基于TensorFlow的人脸识别神经网络毕业设计全流程实战

基于TensorFlow的人脸识别神经网络毕业设计全流程实战 简介这是一份基于TensorFlow构建的人脸识别神经网络毕业设计完整教程面向需要完成相关课题或入门卷积神经网络的开发者和学生。资源以zip压缩包形式提供共6个文件包含4个Python脚本、1个Markdown说明文档和1个License文件压缩包整体仅14KB代码精炼且便于阅读。脚本覆盖了人脸数据采集、他人参照样本处理、模型训练与应用识别等核心环节其中训练部分基于卷积神经网络实现README文档则说明了Python 3.x与TensorFlow的依赖环境及Windows/Linux下的运行方式。整个项目围绕“让神经网络认出目标人物”展开完整包含数据准备、模型构建、训练与测试的思路从收集个人照片和他人照片作为数据集到最终识别是否为预设目标步骤清晰可作为课程设计、毕业设计或TensorFlow实战练习的参考框架。整个教程以一个有趣的小项目入手适合想通过动手实践理解CNN原理的初学者。目前已有625人学习是快速上手人脸识别项目的实用参考资料。1. 毕业设计选人脸识别不要从“跑通Demo”开始“基于TensorFlow训练的人脸识别神经网络”这个选题每年都有大量计算机、电子、自动化专业的毕业生在做但我见过太多人把它做成“下载一个人家训练好的模型跑一个摄像头Demo就交差”的版本。答辩时被问一句“网络结构哪里来的”“损失函数为什么这么选”“预处理做了什么”基本答不上来。这篇教程要讲的是一条能让你从零走通、能被导师追问、能写进论文的完整链路环境搭建、数据集选型、网络结构设计、训练与调参、评估指标、部署验证每一步都给你能直接落地的代码和参数。适合有Python和一点深度学习基础的学生也适合想把“人脸识别门禁系统”这类题目做扎实的从业者。看完你会发现难点根本不在“训练”这两个字上而在数据清洗和全流程设计。2. 动手前先立骨架环境、数据集与硬件怎么选2.1 TensorFlow环境搭建版本搭配比安装本身更容易翻车碰到最多的问题不是装不上TensorFlow而是版本搭配错了。TensorFlow 2.10及之前的版本对GPU支持比较直接2.11之后Windows上不再提供官方GPU支持需要走WSL2。所以我的建议是毕设项目直接用TensorFlow 2.10.0搭配Python 3.9或3.10CUDA 11.2cuDNN 8.1。这套组合我是踩过无数次坑之后固定下来的稳定、教程多、报错一搜就有答案。# 建议用conda创建独立环境不要把全局Python搞乱 conda create -n face tf python3.9 conda activate facenet # 安装tensorflow 2.10.0 CPU版本 pip install tensorflow-cpu2.10.0 # 如果机器有NVIDIA显卡装GPU版没有就跳过CPU也能跑通全流程 # pip install tensorflow2.10.0装完之后第一件事不是急着写网络而是先验证环境。用下面这段代码检查TensorFlow能不能正常调用设备这会省掉后面一堆莫名其妙的报错。import tensorflow as tf # 输出版本号确认安装的是2.10.0 print(tf.__version__) # 如果装了GPU版这里会列出可用的GPU设备 print(tf.config.list_physical_devices(GPU))逻辑说明这段代码同时干了三件事——确认TensorFlow导入成功、确认版本号、确认GPU是否被识别。如果你的机器只有CPU第二行会输出空列表这不影响后续训练只是速度慢。参数说明conda创建环境时指定Python 3.9是为了避开TensorFlow 2.10与Python 3.11的不兼容问题。如果你用Python 3.8也没问题但不要用3.11及以上。2.2 数据集从哪来公开数据集、自建小数据集和扩充策略毕设人脸识别用什么数据取决于你论文里怎么写。最省事的路线是用公开数据集。LFWLabeled Faces in the Wild有13233张人脸图标注了5749个人适合做验证集和评估。CASIA-WebFace大约50万张、1万多人是训练用的经典数据但官方链接经常失效网盘搬运的资源质量参差不齐下载后一定要检查图片能不能正常解码。如果你做的是“人脸识别门禁系统”这类偏工程应用的题目我建议用自建数据找20到30个同学每人采集30到50张不同角度的照片。这个数量级训练的模型在答辩现场做演示完全够用。# 自建数据集目录结构按这个格式组织 # dataset/ # train/ # person_01/ # img_001.jpg # img_002.jpg # person_02/ # ... # val/ # person_01/ # person_02/import os import tensorflow as tf train_dir dataset/train val_dir dataset/val # 用tf.keras自带的image_dataset_from_directory读目录 # 它会自动按子文件夹名生成标签不需要你手动写标注文件 train_ds tf.keras.preprocessing.image_dataset_from_directory( train_dir, validation_split0.2, subsettraining, seed42, image_size(160, 160), batch_size32 ) val_ds tf.keras.preprocessing.image_dataset_from_directory( val_dir, image_size(160, 160), batch_size32 ) # 打印类别名确认标签顺序没有错位 print(train_ds.class_names)逻辑说明image_dataset_from_directory根据文件夹名自动生成整数标签按文件名排序。因此文件夹命名不要用中文也不要带空格否则标签会对不上。参数说明validation_split0.2表示从train目录里再切20%作为内部验证集image_size(160, 160)统一输入尺寸人脸识别领域160的输入分辨率是FaceNet之后的事实标准训练性价比最高。batch_size设32和后续学习率的设定直接挂钩不要随意改。2.3 硬件不是越贵越好CPU、GPU与显存选型很多同学纠结实验室没有好显卡能不能做答案是可以。你这套自建小数据集用CPU训练ResNet50一个epoch大约需要10到15分钟训练20个epoch就是4到5小时挂机一晚上能跑完。如果你有GPUGTX 1660 Super 6GB显存就够用batch_size开到32没问题8GB以上显存可以上ResNet5012GB可以试试ResNet101。显存不够最常见的方法是调小batch_size但注意batch_size变了学习率也要按比例调整。一个直观的折算batch_size从32降到16学习率从0.001降到0.0005左右否则loss曲线会抖得厉害。不要一上来就追求大batch人脸识别毕设的数据量本来就不大泛化性主要靠数据增强而不是大batch。3. 网络结构怎么定从卷积神经网络到人脸特征嵌入3.1 为什么基础分类模型够用卷积神经网络与预训练权重怎么选人脸识别不是上来就写一个网络而是先立住一个观点人脸识别本质上是“先学身份特征再做相似度比较”。完成这个目标主流做法是用卷积神经网络CNN做特征提取器比如ResNet50、MobileNetV2、EfficientNet。很多参考文献提到用BP神经网络做人脸识别老实说BP网络处理原始像素级特征能力有限没有卷积的局部感受野和参数共享机制对光照、姿态变化极敏感论文里很难自圆其说。除非你标题就叫“基于BP神经网络的人脸识别”否则不要选它。Transformer在处理序列数据上很强2024年人脸识别方向也有一些基于ViT的研究但训练数据需求量大、调参更玄学不适合毕业设计这个时间预算。我的选择是用ResNet50做骨干网络它在ImageNet上预训练过迁移到你的人脸数据上收敛速度快、精度有保障。import tensorflow as tf from tensorflow.keras import layers, Model def build_face_model(input_shape(160, 160, 3), num_classes30, embedding_dim128): # 加载预训练ResNet50不包含顶部分类层 base_model tf.keras.applications.ResNet50( include_topFalse, weightsimagenet, input_shapeinput_shape, poolingavg # 全局平均池化 ) # 冻结前100层保留浅层通用特征微调深层语义特征 base_model.trainable True for layer in base_model.layers[:100]: layer.trainable False inputs tf.keras.Input(shapeinput_shape) x base_model(inputs, trainingTrue) # BatchNorm防止深层网络梯度消失同时加快收敛 x layers.BatchNormalization()(x) # Dropout减少过拟合训练集人少的时候非常有效 x layers.Dropout(0.5)(x) # Embedding层输出128维特征向量 embedding layers.Dense(embedding_dim, activationrelu, nameembedding)(x) # 分类头用于训练阶段计算Softmax损失 outputs layers.Dense(num_classes, activationsoftmax, nameclassifier)(embedding) model Model(inputs, outputs) return model逻辑说明这里设计的是“Embedding 分类头”结构。训练阶段用输出层做身份分类推理阶段取model.get_layer(embedding)的输出作为人脸特征向量。这样既享受了Softmax分类训练稳定的优点又不影响后续做人脸比对。参数说明poolingavg让ResNet50最后输出的特征图变成一维向量embedding_dim128是人脸识别领域最常用的向量维度太小判别力不足太大会降低比对速度冻结前100层是迁移学习里的经验值你可以根据自己的数据量调整数据少于1万张就冻结前120层更稳。3.2 损失函数Softmax分类和ArcFace怎么选损失函数是整个模型的“方向盘”。最常见的选型有两个Softmax交叉熵损失和以ArcFace为代表的角度间隔损失。Softmax实现简单直接用model.compile(losssparse_categorical_crossentropy)就行适合小数据量和毕设快速验证。但Softmax学到的特征分布是“扇形”的类间边界不够紧凑在开集识别场景来了一个陌生人容易误判。ArcFace在Softmax的logit上加了角度间隔让同类特征更聚拢、异类特征更分散是人脸识别工业界的标准方案。缺点是自定义损失函数实现略复杂而且对数据量和清洗质量要求更高小数据集上容易训练不收敛。我给的建议是训练第一阶段用Softmax跑通整个流程如果验证集准确率已经到95%以上就保留当前模型用于毕业设计完全够了。如果你想让论文有增量创新点可以在Softmax基础上加入三元组损失做联合微调这样既好解释代码量也在可控范围。# 第一阶段Softmax分类训练 model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-4), losssparse_categorical_crossentropy, metrics[accuracy] ) # 训练结束后把embedding层输出作为特征向量 embedding_model tf.keras.Model( inputsmodel.input, outputsmodel.get_layer(embedding).output )逻辑说明这里用Adam优化器学习率1e-4是预训练模型微调的安全起点。直接把分类模型的中间层输出抽出来组成新的特征提取模型不需要重新训练这是毕设里性价比最高的做法。参数说明如果你的显卡显存小batch_size降到16学习率也要跟着降到5e-5不要抱着一个学习率跑到底。3.3 人脸识别完整链路检测、对齐、特征提取与比对训练模型只是人脸识别的一半。完整的推理链路是用OpenCV读取摄像头帧做人脸检测框出人脸位置对人脸区域做对齐缩放然后送入你的Embedding模型得到128维向量最后和库里的人脸向量算余弦相似度超过阈值就判定为同一个人。import cv2 import numpy as np # 使用OpenCV自带的DNN人脸检测器比Haar级联稳定 net cv2.dnn.readNetFromCaffe( deploy.prototxt, res10_300x300_ssd_iter_140000.caffemodel ) def detect_face(image): h, w image.shape[:2] blob cv2.dnn.blobFromImage(image, 1.0, (300, 300), (104.0, 177.0, 123.0)) net.setInput(blob) detections net.forward() # 取置信度最高的人脸框 max_conf 0 box None for i in range(detections.shape[2]): conf detections[0, 0, i, 2] if conf max_conf: max_conf conf x int(detections[0, 0, i, 3] * w) y int(detections[0, 0, i, 4] * h) x2 int(detections[0, 0, i, 5] * w) y2 int(detections[0, 0, i, 6] * h) box (x, y, x2, y2) return box逻辑说明blobFromImage把图像缩放并减均值这是Caffe模型的输入格式要求。检测结果是一个[1, 1, N, 7]的张量每行前两个是背景类索引第三个是置信度后四个是归一化后的框坐标。参数说明(104.0, 177.0, 123.0)是SSD模型训练时的均值不能随便改不然检测精度会下降。检测到人脸后需要把框出的区域裁出来缩放到160x160再喂给识别模型这一步就是人脸对齐的简化版。如果角度偏得厉害还要做关键点对齐毕设一般不做也不会被扣分。4. 训练全流程数据预处理、超参调优和模型评估4.1 数据预处理三件套清洗、增强与归一化人脸识别翻车最多的地方就在数据预处理。第一个坑是训练集里混了非人脸图片模型学到的是背景特征。第二个坑是图片尺寸不统一有的脸只占图片的10%有的占80%模型学到的不是“这个人是谁”而是“脸大的是谁”。第三个坑是忘了归一化直接把0到255的像素值喂给预训练模型。def preprocess_image(image, label): # 像素值归一化到[0, 1]和ImageNet预训练输入分布一致 image tf.cast(image, tf.float32) / 255.0 return image, label def augment_image(image, label): # 随机翻转注意人脸识别不能用上下翻转 image tf.image.random_flip_left_right(image) # 随机调整亮度模拟不同光照条件 image tf.image.random_brightness(image, max_delta0.2) # 随机调整对比度让人脸特征对光照变化更鲁棒 image tf.image.random_contrast(image, lower0.8, upper1.2) return image, label # 训练集做增强验证集只做归一化 train_ds train_ds.map(augment_image).map(preprocess_image).prefetch(tf.data.AUTOTUNE) val_ds val_ds.map(preprocess_image).prefetch(tf.data.AUTOTUNE)逻辑说明数据增强是在训练时随机修改图片变相增加样本量。random_flip_left_right左右翻转不会改变人脸身份但上下翻转会让模型学到错误特征所以不要加flip_up_down。参数说明max_delta0.2表示亮度在正负20%范围内随机变化太小起不到泛化作用太大脸部细节会丢失prefetch(tf.data.AUTOTUNE)让数据加载和模型训练并行训练速度能提升不少。random_contrast的lower0.8, upper1.2是对比度缩放范围这两个值我调过多次当前设置对光照敏感的数据集效果最稳定。4.2 关键超参数batch size、学习率、训练轮数与优化器超参数设计这块我的经验值可以抄batch size 32初始学习率1e-4优化器Adam训练20到30个epoch每5个epoch手动衰减学习率。如果你的数据集只有几百张加一个早停机制验证集准确率连续5个epoch不涨就停防止过拟合。学习率衰减是训练里最不能省的操作。Adam虽然自带自适应学习率但后期仍需要手动衰减才能收敛到更平稳的最优点。callbacks [ # 学习率衰减每5个epoch下降为原来的1/2 tf.keras.callbacks.LearningRateScheduler( lambda epoch: 1e-4 * (0.5 ** (epoch // 5)) ), # 早停验证集loss连续5轮不下降就停止 tf.keras.callbacks.EarlyStopping( monitorval_loss, patience5, restore_best_weightsTrue ), # 自动保存最优模型 tf.keras.callbacks.ModelCheckpoint( best_face_model.h5, monitorval_accuracy, save_best_onlyTrue ) ] model.fit( train_ds, validation_dataval_ds, epochs30, callbackscallbacks )逻辑说明LearningRateScheduler接收一个函数传入当前epoch序号返回该epoch使用的学习率这里用的是指数衰减策略。EarlyStopping监控验证集loss连续5轮没有改善就恢复最优权重这是防止训练后期loss震荡的后悔药。参数说明restore_best_weightsTrue会把你模型权重回滚到验证集表现最好的状态如果不设模型保存的是最后一轮的权重往往不是最优。save_best_onlyTrue只在验证集准确率提升时才覆盖保存文件避免磁盘写满。4.3 模型评估准确率、混淆矩阵与FAR/FRR指标毕业设计答辩时导师最爱问的一句是“你怎么证明你的系统是好的”。只说“准确率95%”不够人脸识别场景还有两个关键指标误识率FARFalse Accept Rate和拒识率FRRFalse Reject Rate。FAR是陌生人被当成熟人的概率FRR是熟人被拒之门外的概率两者此消彼长。import numpy as np from sklearn.metrics import confusion_matrix, accuracy_score def extract_embeddings(dataset, embedding_model): embeddings [] labels [] for images, label in dataset: emb embedding_model.predict(images, verbose0) embeddings.append(emb) labels.append(label.numpy()) return np.vstack(embeddings), np.hstack(labels) # 计算两组特征的余弦相似度 def cosine_distance(a, b): a_norm a / np.linalg.norm(a, axis1, keepdimsTrue) b_norm b / np.linalg.norm(b, axis1, keepdimsTrue) return a_norm b_norm.T # 对每个测试样本和所有库中人脸算相似度取最大值作为识别结果 threshold 0.5 pred_labels [] for emb in embeddings_test: sims cosine_distance(emb.reshape(1, -1), embeddings_gallery) max_sim np.max(sims) pred_labels.append(np.argmax(sims) if max_sim threshold else -1)逻辑说明这套评估流程模拟了门禁系统的真实工作方式先注册一批人脸特征进库再来一个人算他和库里所有人的相似度超过阈值才放行。参数说明threshold0.5是初始值之后要画ROC曲线来选最优阈值。cosine_distance先对特征做L2归一化再点乘等价于余弦相似度数值范围在-1到1之间阈值一般取0.3到0.6之间。如果识别效果不好不要急着调网络先把阈值打印出来看同类相似度和异类相似度的分布是否可分。4.4 断点续训与模型导出训练中止了不用从头再来训练中途断电、显存溢出、系统崩溃都是家常便饭。如果没有断点续训机制前面十几个小时就白费了。有两种做法第一种是ModelCheckpoint里设置save_freqepoch每个epoch结束保存一次权重第二种是保存整个模型包括优化器状态和当前epoch。# 保存完整可续训模型包括优化器和epoch状态 model.save(face_model_last.keras) # 断点续训 from tensorflow.keras.models import load_model model load_model(face_model_last.keras) model.fit(train_ds, validation_dataval_ds, epochs30, initial_epoch15) # 导出推理用的Embedding模型 embedding_model tf.keras.Model( inputsmodel.input, outputsmodel.get_layer(embedding).output ) embedding_model.save(face_embedding_model.h5)逻辑说明model.save后加载直接接着initial_epoch15继续训练学习率调度器会从第16个epoch开始计算所以前面的学习率衰减不会重复。参数说明initial_epoch必须大于已经训练过的轮数否则会重复训练且学习率被重置。最后导出的embedding_model.h5只保留输入到embedding的部分不含分类头推理时占用的显存更小加载更快。5. 训练和部署中躲不开的 5 个坑现象、原因与解决方法5.1 损失函数在下降验证集准确率却一动不动现象训练集loss从2.0降到0.5训练集准确率接近100%但验证集准确率始终卡在60%左右。原因这是典型的过拟合。人脸数据太少模型把训练集里每个人的背景、衣服、拍照角度都记住了而不是学到人脸本身。另一个隐性原因是数据泄漏——同一个人的照片被同时分进了训练集和验证集验证集看起来在涨实际模型什么都没学到。解决第一检查数据划分确保同一个人所有照片都在同一个集合里按人划分而不是按图片划分。第二提高Dropout比例到0.5以上增强随机亮度对比度。第三如果数据量少于每类20张考虑用所有照片做数据增强扩到每类100张再训练。5.2 训练时OOM显存溢出程序直接被系统杀掉现象训练到第5个epoch报错ResourceExhaustedError程序退出。原因显存被打满。常见元凶是batch_size太大、输入图片分辨率太高、或者数据加载阶段extra的中间变量过多。解决先把batch_size从32降到16同时学习率从1e-4降到5e-5。如果还爆把输入尺寸从160降到112这是人脸识别领域另一个常用分辨率精度损失有限。再不行就用tf.config.experimental.set_memory_growth设置显存按需增长不要让TensorFlow一开始就申请全部显存。gpus tf.config.experimental.list_physical_devices(GPU) if gpus: for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True)5.3 人脸检测框忽大忽小训练数据里全是半张脸现象检测结果时好时坏有的框只框住下巴有的框把整个额头切掉。模型训练时学的是这些残缺图片识别当然不准。原因OpenCV默认的SSD检测器对于侧脸、小脸、暗光场景本身召回率就有限而你的检测代码里取了置信度最高的一帧没有做时序平滑。侧脸时置信度低检测框不稳定。解决摄像头场景做检测帧的滑动平均连续三帧的检测框取交叠区域。如果单张图片检测失败不要硬送进识别模型直接跳过该帧。建议用宽动态摄像头别用笔记本自带的暗光摄像头这是门禁系统设计里最容易忽略的硬件因素。5.4 保存的模型推理时报错输入输出对不上现象训练时好好的model.save之后load_model推理时要求输入的shape对不对或者输出特征向量的维度和预期不一致。原因保存和加载的模型对象不一致。有人保存的是face_embedding_model加载时却往里面传了原始图像的完整预处理流程或者直接把归一化前后的数据搞混了。还有的情况是定义了Functional API模型但用Sequential方式加载。解决把预处理逻辑和模型绑定成一个完整的推理函数。把归一化、resize、模型推理封装成一个函数测试时直接调用。def inference_pipeline(image_path, embedding_model): img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (160, 160)) img img.astype(float32) / 255.0 img np.expand_dims(img, axis0) emb embedding_model.predict(img, verbose0) return emb[0]5.5 OpenCV读摄像头画面颜色偏蓝或颜色失真现象摄像头预览颜色正常但保存的图片发蓝、识别效果变差。原因OpenCV读摄像头默认返回BGR格式训练时用的是RGB格式两者混用了。人脸识别模型训练时用的是RGB推理时拿到BGR就直接喂进去颜色通道错位。解决所有推理代码统一用cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转换一次。这条规则简单到容易忽略但让我见过不少人在答辩前夜突然翻车。你的数据增强里如果随机调了亮度对比度也要保证在RGB空间做和训练时保持一致。6. 把毕设做成完整闭环部署Demo、结果展示和答辩加分项模型训练好之后最后一个核心环节是把它变成能演示的系统。这不是让你做一个产品级门禁而是让答辩现场的老师看到你的模型真的能实时工作。一个完整的Demo至少包含三部分实时人脸检测、特征提取与比对、历史识别记录。video_capture cv2.VideoCapture(0) while True: ret, frame video_capture.read() if not ret: break box detect_face(frame) if box is not None: x, y, x2, y2 box face frame[y:y2, x:x2] face cv2.resize(face, (160, 160)) face face.astype(float32) / 255.0 emb embedding_model.predict(np.expand_dims(face, axis0), verbose0)[0] # 和注册库里所有人脸算余弦相似度超过阈值就标注姓名 name recognize_person(emb, face_database) cv2.rectangle(frame, (x, y), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(Face Recognition Demo, frame) if cv2.waitKey(1) 0xFF ord(q): break这段代码跑通之后把三类数据记录下来写进论文模型参数量和尺寸、单张人脸特征提取耗时、FAR与FRR曲线。打开任务管理器看GPU占用率把它作为工程性指标写进毕业设计说明书这是“基于TensorFlow训练的人脸识别神经网络”这类题目最能体现工作量、也最容易被评委会认可的部分。如果你把32G内存、CPU训练每个epoch的耗时和最终准确率做成一张表在答辩现场展示效果比念十页原理都好。以前我做过一个树莓派人脸识别门禁方案用MobileNetV2替换ResNet50推理速度从200毫秒降到50毫秒代价是准确率掉了两个百分点。这种性能与精度的权衡分析是老师最喜欢的讨论素材。你可以把这次训练过程中记录的损失曲线、学习率曲线、不同阈值下的FAR/FRR对比全保留下来它们会成为你论文里最扎实的原创图表。我自己的习惯是每调一轮参数就截一张损失曲线图训练结束后用这些图反推最优参数范围永远不要让训练过程变成一锤子买卖。希望帮到你。本文还有配套的精品资源点击获取
返回列表