
简介基于卷积神经网络CNN的人脸在线识别系统毕业设计资源包面向计算机、人工智能等专业需要完成毕业设计、期末大作业或课程设计的学生尤其适合需要完整可运行项目参考的初学者。代码已获导师指导并通过属于高分项目可直接用于项目展示与二次开发。压缩包共10个文件大小约195.48MB核心是4个Python脚本模型训练、人脸识别、辅助功能等并配套TensorFlow模型文件.meta、.index、.data-00000-of-00001、checkpoint、lfw.tgz人脸数据集以及训练日志结构清晰便于对照学习。目前已有421人学习。下载后可使用lfw标准数据集通过faces_train.py、face_recognition.py等脚本完成从训练到在线识别的完整流程模型检查点支持断点续训或直接加载配合文件说明可快速复现实验作为毕业设计演示或人工智能课程项目都很有价值。1. 在本地用摄像头实时识别人脸难点从来不在算法理论而在训练和推理能不能连贯这套基于 TensorFlow 的卷积神经网络人脸识别系统把 LFW 数据集、CNN 模型训练、checkpoint 保存和人脸在线识别串在了一个压缩包里。它不是一个只有前端演示的半成品而是包含数据加载、模型构建、训练保存、推理比对完整链路的代码。对于课程设计、期末大作业以及想拿真实数据集跑通人脸识别流程的同学这份项目能直接作为骨架省去从零搭环境的痛苦。读完你会理解为什么 LFW 数据集适合做人脸识别实验训练代码里每一步在做什么以及在线识别和离线分类到底差在哪。2. 先从数据集和文件结构说起LFW 与这套代码的关系2.1 解压 LFW 数据集搞清目录结构LFW全称 Labeled Faces in the Wild是真实场景下的人脸数据集包含超过 1.3 万张网络图片覆盖 1680 个人。每张图片大小固定为 250x250 像素目录结构是“每个人名一个子文件夹”子文件夹里放该人的多张照片。LFW 的典型用途是人脸验证与人脸识别算法评估因为它的图片来自真实网络背景杂乱、光照不均比实验室自拍数据集更有说服力。项目用lfw.tgz作为训练输入解压后的目录结构需要保持原样tar xzf lfw.tgz mv lfw data/ # 查看前20个人名目录确认解压正确 find data/lfw -maxdepth 1 -type d | head -20find命令列出前 20 个子目录能确认数据是否解压完整。如果看到的是FOLDER_0这类编号目录而不是人名说明压缩包被二次打包过需要先修正结构。接下来生成训练标签时脚本会基于这些目录名自动映射 label所以目录名不能改动否则标签会整体错乱。注意LFW 原始图片含较多背景噪声训练前通常需要裁剪或归一化。这个项目一般直接用 160x160 居中裁剪后续识别阶段也需要使用同样尺寸。2.2 数据预处理为什么要统一像素与人名标签CNN 输入要求固定尺寸张量而 LFW 图片是 250x250 且人脸不总在中心。常见做法是先用 OpenCV 或 MTCNN 检测人脸再对齐到 160x160。对于初版毕业设计中心裁剪加缩放是最省事的方案虽然会损失部分头部区域但能让训练先跑通后续再升级检测逻辑。下面这段是典型的数据读取脚本思路是把文件夹名映射成整型标签再统一缩放import os import cv2 import numpy as np def load_data(root, img_size160): X, y [], [] for label, name in enumerate(sorted(os.listdir(root))): person_dir os.path.join(root, name) if not os.path.isdir(person_dir): continue for fname in os.listdir(person_dir): if not fname.endswith(.jpg): continue img cv2.imread(os.path.join(person_dir, fname)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (img_size, img_size)) X.append(img) y.append(label) return np.array(X), np.array(y)这段代码按目录名排序生成 label保证同一个人的所有照片对应同一个整型标签。cv2.resize把图片统一为 160x160。注意这里没有做人脸对齐如果数据集里存在多人合影或误检图片准确率会受影响。后面的img_size参数必须和faces_train.py里的输入维度一致否则模型训练时张量维度对不上。还有一个容易忽略的点训练前要按人的标签做分层划分避免某些人只出现在训练集或只出现在验证集。常见做法是使用train_test_split的stratify参数from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split( X, y, test_size0.2, stratifyy, random_state42)stratifyy确保每个类别在训练集和验证集中的比例相近。random_state固定随机种子保证实验可复现。很多同学忽略这一步同一个项目跑两次结果差异很大问题往往就出在这里。2.3 文件依赖关系与参数配置解压后的项目里几个文件的分工很明确文件作用faces_train.py训练主脚本负责加载数据、构建 CNN、保存 checkpointfaces_my.py自定义模型或数据调试脚本通常是训练脚本的可修改副本face_recognition.py在线识别脚本加载模型并对单张图或摄像头帧进行识别faces_other.py辅助脚本用于测试不同预处理或对比不同模型train_faces.model-900.meta等TensorFlow 训练到第 900 步保存的模型文件events.out.tfevents.*TensorBoard 日志文件checkpoint记录最新模型路径这套结构的核心是训练与识别分离。faces_train.py只负责输出模型face_recognition.py只负责读取模型中间通过train_faces.model-900三个文件衔接。如果解压后有一个master文件夹通常模型文件会被输出到里面识别脚本里的路径也需要同步改成master/train_faces.model-900。很多人会在这一步踩坑把.meta、.data-00000-of-00001和.index三个文件分开存放导致 saver 找不到模型。正确做法是保持同一目录且使用同一命名前缀缺一个都无法还原计算图。3. 卷积神经网络模型搭建与训练3.1 网络结构选型层数不是越深越好从文件命名和 checkpoint 格式来看这个项目运行在 TensorFlow 1.x 环境下模型采用卷积神经网络提取人脸特征。在毕设场景下常见选择是 LeNet-5 的改进版或像 VGG 一样堆叠卷积层。LeNet-5 只有两层卷积用于灰度手写数字可以但处理 RGB 人脸时特征表达能力不足。VGG 结构规整、容易理解但参数太多CPU 上训不动。更合理的是三层卷积加两层全连接的轻量结构配合 BatchNorm 和 Dropout。import tensorflow as tf def inference(images, num_classes1680): # images: [batch, 160, 160, 3] with tf.variable_scope(conv1): net tf.layers.conv2d(images, 32, 3, activationtf.nn.relu, paddingsame) net tf.layers.batch_normalization(net, trainingTrue) net tf.layers.max_pooling2d(net, 2, 2) with tf.variable_scope(conv2): net tf.layers.conv2d(net, 64, 3, activationtf.nn.relu, paddingsame) net tf.layers.batch_normalization(net, trainingTrue) net tf.layers.max_pooling2d(net, 2, 2) with tf.variable_scope(conv3): net tf.layers.conv2d(net, 128, 3, activationtf.nn.relu, paddingsame) net tf.layers.batch_normalization(net, trainingTrue) net tf.layers.max_pooling2d(net, 2, 2) with tf.variable_scope(fc): net tf.layers.flatten(net) embedding tf.layers.dense(net, 128, activationtf.nn.relu, nameembedding) dropout tf.layers.dropout(embedding, rate0.5, trainingTrue) logits tf.layers.dense(dropout, num_classes, namelogits) return logits, embedding这里的embedding层是 128 维特征向量训练时走最后的 softmax 分类测试时直接取embedding输出做比对。batch_normalization能让训练更稳定max_pooling降低特征图尺寸同时保留主要位置信息。注意num_classes要等于数据集里面的人数而不是图片总数。如果填错损失计算和准确率统计都会报错。3.2 训练脚本的核心逻辑faces_train.py通常按四步走加载数据、构建模型、定义优化器、启动 Session 训练。TensorFlow 1.x 里训练主循环是显式session.run模型保存使用saver.save。参考代码如下x tf.placeholder(tf.float32, [None, 160, 160, 3], nameinput) y tf.placeholder(tf.int64, [None], namelabel) logits, _ inference(x, num_classes1680) loss tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits( logitslogits, labelsy)) train_op tf.train.AdamOptimizer(0.001).minimize(loss) saver tf.train.Saver(max_to_keep5) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for step in range(900): batch_x, batch_y get_batch(batch_size128) _, loss_value sess.run([train_op, loss], feed_dict{x: batch_x, y: batch_y}) if step % 100 0: print(step %d, loss %.4f % (step, loss_value)) saver.save(sess, train_faces.model, global_stepstep)tf.sparse_softmax_cross_entropy_with_logits需要label是整型序列对应我们前面生成的y。AdamOptimizer(0.001)在多数小数据集上都能直接收敛不需要手动调学习率衰减。每 100 步保存一次模型所以最终文件名会有-900。如果训练中断checkpoint文件会记录最后一次保存位置tf.train.latest_checkpoint能自动找到它。3.3 训练参数默认值能跑通但需要理解下面这组参数是这类项目常见的初始值也适用于本项目的faces_train.py参数推荐值说明图像尺寸160x160太小丢失五官细节太大显存占用高batch_size64128CPU 环境建议降到 32否则一次迭代很慢学习率0.001Adam 默认值一般不手动衰减训练步数900小数据集上能收敛但不一定达到最好精度dropout0.5放在全连接层后防止过拟合optimizerAdam对学习率不敏感适合初学者训练步数 900 在 LFW 子集上可能只覆盖几个 epoch。如果损失曲线明显锯齿状抖动优先把学习率降到 0.0001再考虑调 batch。反过来如果验证 loss 一直不降通常不是参数问题而是数据预处理没对齐或标签顺序乱了。3.4 用 TensorBoard 观察训练过程项目解压后的events.out.tfevents.*就是 TensorBoard 日志。训练脚本会在每个 step 写入 loss 和 accuracy。启动命令tensorboard --logdir./ --port6006然后浏览器打开http://localhost:6006。观察 loss 曲线是否平滑下降也可以看直方图了解权重分布。如果损失在几百步后变成 NaN第一检查点就是输入图像是否归一化到 0~1。很多人直接传 0~255 的像素值梯度瞬间爆炸这是最高频的坑。4. 在线识别从模型推理到摄像头实时框人4.1 加载 checkpoint 并提取特征向量在线识别的核心是加载训练好的train_faces.model-900三个文件提取输入图片的 embedding。TensorFlow 1.x 里可以使用import_meta_graph直接恢复计算图import tensorflow as tf import numpy as np import cv2 tf.reset_default_graph() saver tf.train.import_meta_graph(train_faces.model-900.meta) sess tf.Session() saver.restore(sess, tf.train.latest_checkpoint(./)) graph tf.get_default_graph() x graph.get_tensor_by_name(input:0) embedding graph.get_tensor_by_name(fc/embedding/Relu:0) def get_embedding(img): img cv2.resize(img, (160, 160)) img img.astype(np.float32) / 255.0 vec sess.run(embedding, feed_dict{x: [img]}) return vec[0]latest_checkpoint读取根目录下的checkpoint文件自动找到最后一次保存的模型。get_tensor_by_name需要知道计算图里的 tensor 名称这里的fc/embedding/Relu:0对应我们前面模型定义里的embedding层输出。如果实际项目里层名不同会报 KeyError此时用graph.get_operations()打印所有节点名来确认。拿到 128 维向量后后续所有识别都只在这组浮点数上运算不再依赖原始图片。4.2 人脸检测与对齐在线识别里最容易被忽略训练时如果直接用整张图识别阶段也必须用相同方式取出人脸区域。最稳妥的方案是 MTCNN它本身也是一个小型 CNN能同时输出人脸框和五个关键点。参考代码from mtcnn import MTCNN detector MTCNN() img cv2.cvtColor(cv2.imread(test.jpg), cv2.COLOR_BGR2RGB) face detector.detect_faces(img) if face: x, y, w, h face[0][box] crop img[y:yh, x:xw] vec get_embedding(crop) else: print(no face detected)face[0]取置信度最高的人脸框。直接裁剪会把背景也带进来框太紧会切掉额头框太松又引入无关区域。更精细的做法是使用face[0][keypoints]做仿射变换把双眼对准固定坐标。没有这一步输入图片角度稍微变化提取出的向量就会明显漂移相似度阈值再准也认不出。4.3 相似度计算与阈值判定人脸识别最后一步是比较两个向量的相似度。余弦相似度比欧氏距离更常用因为它对向量长度不敏感受光照和对比度影响小。def cosine_similarity(vec1, vec2): return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2)) threshold 0.65 if cosine_similarity(vec_a, vec_b) threshold: print(same person) else: print(different person)阈值 0.65 是经验值不同数据集上分布差异很大。更可靠的方法是在验证集上计算所有正负样本对的相似度画 ROC 曲线取等错误率对应的阈值。项目初始代码里写死阈值是为了方便演示但答辩或实际应用前一定要重新标定。4.4 在线识别的三种运行模式对比这套系统的在线识别可以落在三种形式上对应face_recognition.py的几种调用路径模式输入源延迟适用场景单张图片文件路径瞬时验证一张图的身份摄像头逐帧USB / 笔记本摄像头100300ms门禁、签到演示视频文件mp4 / avi取决于帧率离线回放、调试摄像头模式需要每帧都跑一次人脸检测和特征提取检测耗时超过帧间隔就会卡顿。常见做法是每隔 3 帧检测一次中间帧用上一次的人脸框跟踪。也可以把送入网络的图片从 160x160 压缩到 112x112识别速度提升近一倍精度损失可控。5. 排错与改进这套系统还能怎么压榨5.1 训练 loss 不降或震荡三个高频原因学习率过大、标签错乱、输入未归一化。先把 Adam 学习率从 0.001 降到 0.0001看前 200 步 loss 是否平稳下降。标签错乱大多是os.listdir的排序在训练和验证脚本中不一致导致的建议统一用sorted并且固定 random seed。输入图像必须除以 255使像素范围落在 0~1否则梯度数值容易溢出。5.2 checkpoint 加载报错如果报 “Cannot find checkpoint” 或 “No variables to save”先确认三个.model-*文件是否在同一目录加载时是否使用了绝对路径。再看tf.reset_default_graph()是否在import_meta_graph之前调用否则之前残留的计算图节点会造成 tensor 名冲突。如果项目代码里的模型输出到了master/子目录路径要对应改成master/train_faces.model-900。5.3 提高识别准确度的具体技巧不要一开始就上 triplet loss小数据集上收敛极慢毕设阶段容易失控。更实际的做法是对同一人的多张照片分别提取 embedding求平均后作为该人的注册向量。修改face_recognition.py就能做到。注册照片至少要覆盖正面、左右偏转和不同光照只拍一张正面照会导致光线变化时相似度暴跌。还可以检查faces_my.py里是否开启了数据增强。随机水平翻转、亮度抖动能明显提升鲁棒性但注意验证集不能做增强。如果是纯 CPU 环境把 batch_size 降到 16图像尺寸从 160 降到 128训练速度会快一倍精度损失不大。调参前后各记录一组验证集准确率答辩时直接拿出对比数据比口头解释更有说服力。本文还有配套的精品资源点击获取