☰
Python+OpenCV人脸识别门禁系统实战:从LBPH到部署避坑
2026/10/7 5:03:58 网站建设 项目流程

简介:这是一份基于OpenCV的人脸识别门禁系统完整项目,覆盖人脸检测、特征提取、身份识别、权限认证、门禁控制及日志记录等核心环节,并包含配套文档说明,适合计算机视觉课程设计、毕业设计以及希望快速上手实际门禁项目的开发者。资源包共205个文件,压缩包大小31.4MB,文件类型以C++源码(cpp/h)、Qt界面文件(ui)、编译过程生成文件(tlog/obj/o)为主体,同时提供exe可执行程序、工程配置文件(sln/vcxproj)以及源代码阅读辅助的txt说明,整体目录结构清晰,既能查看完整实现,也可直接运行体验。内容预览显示项目内含人脸识别控制终端、串口通信类、主控窗口等模块,形象刻画了从摄像头采集、OpenCV算法处理到终端控制的完整链路,方便读者对照描述中的十步开发流程逐步理解。目前已有644人学习下载,对于需要掌握OpenCV人脸识别落地方法、了解门禁系统数据流转或准备相关项目答辩的人来说,是一份务实且可直接参考的素材。

1. 人脸识别门禁,不是摄像头加个比对就算完事

做门禁系统和做人脸考勤最大的差别,不在算法精度,而在“误放行”的代价。考勤认错人,最多是补卡纠纷;门禁认错人,就是物理安全的缺口。所以 Python 基于 OpenCV 人脸识别门禁系统这套方案,看起来是“一个摄像头 + 一个人脸比对”,真正落地时要把样本采集、模型训练、实时识别、开锁控制、日志留痕串成一条完整链路。这个项目适合两类人:一类是有 Python 基础、想拿 OpenCV 练手并交付真实小项目的开发者;另一类是实验室、小型办公室、仓库等场景的运维人员,想用几百块的成本替换掉刷卡门禁。它的优点是完全离线、可控性高,短板是它管的是“常规光线下的门”,不是“对抗攻击下的门”。

2. 选 OpenCV 而不是直接上深度学习:先算清楚你的算力账

很多人一上来就问“为什么不用 Facenet 或者 ArcFace”,这其实是没分清场景。门禁系统的算法选型,核心不是比谁准确率更高,而是比谁能在你的硬件上稳定跑起来。工业级人脸识别门禁机里的算法确实用到了深度特征,但那是定制硬件 + 专用 NPU 的产物。你在普通 PC 或者树莓派上做入门级门禁,OpenCV 自带的 Haar Cascade 检测 + LBPH 识别器是最稳妥而且能跑通全流程的方案。

2.1 Haar Cascade 和 LBPH 各自管什么

这个项目里 OpenCV 承担两件事:人脸检测和人脸识别。人脸检测用 Haar Cascade 分类器,它的原理是滑动窗口 + 积分图 + AdaBoost 级联,在 CPU 上跑得非常快,能实时框出“脸在哪里”。人脸识别用 LBPH(Local Binary Pattern Histogram),它的原理是把人脸划成小块,提取局部纹理特征生成直方图,再用直方图做相似度比对。LBPH 的优势在于它是 OpenCV 内置的,不需要额外装深度学习框架,也不吃 GPU,训练一张新人脸只需要几十张样本图片,算力开销远小于 CNN 特征提取。

2.2 什么情况下才需要升级到深度学习方案

如果你做的是几十个人甚至上百人的门禁,或者现场有逆光、侧脸、口罩遮挡等复杂情况,LBPH 就撑不住了。常见做法是保留 OpenCV 做人脸检测,把识别部分换成 OpenCV DNN 模块加载预训练的深度学习模型(比如 OpenFace 或 ArcFace 导出的 ONNX 模型)。但我建议你先把 LBPH 的完整链路跑通,因为它能让你把“门禁系统”的工程问题全部暴露一遍——样本采集、阈值调参、误识别控制、日志模块,这些问题换任何识别算法都得解决。直接上深度学习只会让你分不清到底是算法问题还是工程问题。

2.3 识别器选型对比:LBPH 还是 FisherFace

OpenCV 里内置了三种传统人脸识别器:LBPH、FisherFace、EigenFace。EigenFace 本质是 PCA 降维,对光照极端敏感,同一个人的两张照片在不同光线下投影系数可能差很远。FisherFace 依赖 LDA,对样本数量很挑剔,每个人少于 10 张样本就经常出问题。LBPH 对光照相对鲁棒,因为它提取的是局部二进制模式的纹理特征,而不是全局灰度分布。所以我的选择顺序是:LBPH 优先,因为它适合小样本、适合 CPU 实时推理、参数可解释性强。下面的实现部分也以 LBPH 为主线。

# recognizer 选择:LBPH 是 OpenCV 内置的三种识别器之一 # createLBPHFaceRecognizer() 在 OpenCV 4.x 里属于 face 模块 import cv2 # 初始化 LBPH 识别器 recognizer = cv2.face.LBPHFaceRecognizer_create() # 参数默认值是 radius=1, neighbors=8, grid_x=8, grid_y=8 # neighbors 越大纹理描述越细,但也越容易受噪声干扰 # grid_x/grid_y 把人脸切成多少个小格子,切得越细,位置变化影响越大

这段初始化代码看起来简单,但我见过很多人直接跑cv2.face.LBPHFaceRecognizer_create()报AttributeError: module 'cv2' has no attribute 'face'。这不是代码写错了,是 OpenCV 的 face 模块在opencv-contrib-python包里,只装opencv-python是没有的。安装命令要换成pip install opencv-contrib-python,这个问题在避坑章节再展开。

3. 把最小可运行版本跑通:从采集样本到开门动作

这个项目的可复现路径分四步:采集人脸样本、预处理、训练识别器、实时识别与控制门禁。每一步都有必须注意的参数和边界条件,我会把代码拆开写。

3.1 样本采集:不是拍够数量就能用

样本采集是整个系统里最容易被轻视的环节。常见错误是让人坐在摄像头前不动,连续拍几十张几乎一样的照片。这种样本集训练出来的模型遇到角度变化就直接翻车。正确做法是让人脸在画面里缓慢转动,左右各转 30 度,抬头低头各 15 度,同时让环境光线有明暗变化。每采集一张就把人脸区域裁剪出来,缩放到统一尺寸再保存。

import cv2 import os # 采集指定 ID 的人脸样本 # 这里以学号或工号作为 label,后续识别结果会返回这个 label person_id = input("请输入人员 ID:") save_dir = f"./dataset/{person_id}" os.makedirs(save_dir, exist_ok=True) # 0 表示默认摄像头,如果你有两个摄像头,就试 1 cap = cv2.VideoCapture(0) # 设置采集分辨率,640x480 足够用于 LBPH 训练 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 加载 Haar Cascade 人脸检测器 # 这个 xml 文件在 opencv-python 包的 data 目录下 face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) count = 0 while count < 50: # 每个人采集 50 张有效样本 ret, frame = cap.read() if not ret: print("摄像头读取失败,请检查索引或权限") break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, # 每次缩放图像的比例,越小精度越高但越慢 minNeighbors=5, # 每个候选框需要被多少个相邻框确认 minSize=(80, 80) # 小于 80x80 的框直接忽略,过滤远处目标 ) for (x, y, w, h) in faces: # 把检测到的人脸区域裁出来,并统一缩放到 200x200 face_roi = gray[y:y+h, x:x+w] face_resized = cv2.resize(face_roi, (200, 200)) img_path = f"{save_dir}/{count:03d}.jpg" cv2.imwrite(img_path, face_resized) count += 1 # 在画面上画框,给你一个实时反馈 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow("Collecting Faces", frame) # 按 q 可以提前退出 if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows() print(f"采集完成,共保存 {count} 张样本到 {save_dir}")

这里有一个很多人踩过的坑:cv2.imwrite写的是灰度图,因为训练时用灰度图能减少光照和色彩干扰。但如果你把同一个人的彩色图缩放到 200x200 存成灰度图,训练出来的模型对色彩信息完全无感,所以不要在彩色图上做识别再画框,识别和训练的数据类型要保持一致,都走灰度。另一个隐蔽问题是detectMultiScale的minSize设置太小,比如改成(30, 30),画面里的噪声区域会被当成脸,存进去一堆无效样本。我的经验值是室内场景(80, 80)起步,如果摄像头安装距离远再往上调。

3.2 训练识别器:把样本文件夹变成可用的模型文件

采集完样本后,要把图片路径和标签读出来,交给train()方法。这里最关键的是标签的一致性。如果你给张三标 ID 1,给李四标 ID 2,那模型输出的就是 1 和 2,后续门禁逻辑要靠这个 ID 去数据库查询权限。

import cv2 import os import numpy as np dataset_dir = "./dataset" faces = [] labels = [] label_id = 0 label_map = {} # 记录 ID 和姓名的映射关系 # 遍历 dataset 下的每个子目录 # 子目录名就是人员 ID for person_dir in sorted(os.listdir(dataset_dir)): person_path = os.path.join(dataset_dir, person_dir) if not os.path.isdir(person_path): continue # 建立从文件名字符串到整数标签的映射 label_map[label_id] = person_dir print(f"正在加载 {person_dir} -> 标签 {label_id}") for img_name in os.listdir(person_path): img_path = os.path.join(person_path, img_name) # 读取灰度图 gray_img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) if gray_img is None: print(f"警告:{img_path} 读取失败,可能文件损坏") continue faces.append(gray_img) labels.append(label_id) label_id += 1 # 训练 LBPH 模型 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.train(faces, np.array(labels)) # 保存模型和标签映射 recognizer.write("./models/face_model.yml") print(f"训练完成,共加载 {len(faces)} 张样本") # 标签映射单独保存成文本,方便识别阶段读取 import json with open("./models/label_map.json", "w", encoding="utf-8") as f: json.dump(label_map, f, ensure_ascii=False, indent=2)

训练阶段我需要强调一个边界习惯:模型文件face_model.yml和标签映射文件要分开保存。因为模型文件只认识整数标签,不认识“张三”“李四”。有人训练完直接把recognizer.write的文件当黑匣子,部署到新机器上忘了带label_map.json,结果识别结果是数字 3,却不知道 3 是谁。这个 JSON 文件就是整个系统的“姓名对照表”,属于文档的一部分。另外,如果某个人的样本少于 10 张,训练时模型也能跑,但识别阶段大概率误报。LBPH 的直方图统计需要足够像素样本来形成稳定分布,少于 10 张基本是在碰运气。

3.3 实时识别与控制门禁:识别结果怎么变成开门信号

实时识别环节要同时处理两个需求:识别人脸和触发门禁动作。门禁动作在真实项目里通常是树莓派的 GPIO 输出一个高电平给电磁锁,或者通过串口指令给门禁控制器。在本项目中,我用模拟的方式演示——识别成功就在画面里显示名字,并通过命令行输出开门信号。

import cv2 import json import time # 加载训练好的模型和标签映射 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read("./models/face_model.yml") with open("./models/label_map.json", "r", encoding="utf-8") as f: label_map = json.load(f) # 把 JSON 的字符串键转成整数键 label_map = {int(k): v for k, v in label_map.items()} face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) cap = cv2.VideoCapture(0) # 识别阈值,LBPH 返回的 confidence 是距离值,越小越相似 # 这个值需要根据实际场景反复调,没有通用最优解 confidence_threshold = 65 # 防抖:记录最近一次开门的时间,避免识别成功一次就疯狂开门 last_unlock_time = 0 unlock_cooldown = 5 # 5 秒内不重复开门 # 识别跳帧计数器:每 2 帧做一次识别,降低 CPU 占用 frame_skip = 2 frame_count = 0 while True: ret, frame = cap.read() if not ret: print("摄像头读取失败,尝试释放并重连") break frame_count += 1 if frame_count % frame_skip != 0: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale( gray, scaleFactor=1.05, # 识别阶段比采集阶段更精细一点 minNeighbors=5, minSize=(100, 100) ) for (x, y, w, h) in faces: face_roi = gray[y:y+h, x:x+w] face_resized = cv2.resize(face_roi, (200, 200)) # predict 返回两个值:label 和 confidence label, confidence = recognizer.predict(face_resized) if confidence < confidence_threshold: name = label_map.get(label, "Unknown") now = time.time() # 冷却时间判断 if now - last_unlock_time > unlock_cooldown: print(f"DOOR_UNLOCK: 识别到 {name}, confidence={confidence:.2f}") # 真实项目中,这里调用 GPIO 输出高电平或者串口指令 # GPIO.output(lock_pin, GPIO.HIGH) last_unlock_time = now color = (0, 255, 0) display_text = f"{name} ({confidence:.0f})" else: color = (0, 0, 255) display_text = f"Unknown ({confidence:.0f})" cv2.rectangle(frame, (x, y), (x+w, y+h), color, 2) cv2.putText(frame, display_text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.imshow("Access Control", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

这里confidence_threshold是整个系统里最需要经验值的参数。LBPH 的 confidence 不是 0 到 1 之间的概率,而是直方图距离,理论上最小值是 0,表示完全匹配。实际项目中,同一个人的正常识别距离通常在 40 到 60 之间,陌生人通常在 80 到 120 以上。阈值设成 65 是一个中间值,但如果你把阈值设到 80,陌生人有概率被放进来;设到 30,自己人经常被拒之门外。我的做法是先用 20 个人的样本做一轮遍历测试,把每个正样本的识别距离画成曲线,取正样本最大距离和负样本最小距离的中位数作为初始阈值。

unlock_cooldown这个变量也是工程细节。如果去掉防抖,识别器在连续帧中看到同一张脸会多次触发开门信号,轻则日志刷屏,重则电磁锁频繁通断烧毁。冷却时间一般根据门禁场景设定,正常单人通行 3 到 5 秒就够。

4. 文档说明:别人接手时最需要的东西是什么

标题里特意写了“项目+文档说明”,说明这份交付物不只是代码,还包括能让人快速接手项目的文档。我见过很多小项目,代码写得能跑,但过了三个月连作者自己都看不懂dataset目录下的数字文件夹是什么。好的文档说明不是写论文,而是让下一个工程师能在半小时内完成环境搭建和模型重训。

4.1 项目目录结构与各模块职责

一个可维护的门禁项目,代码不应该只有两个文件。我建议按职责拆分:

face_access/ ├── README.md # 项目简介、环境要求、最简运行命令 ├── requirements.txt # 依赖清单,注明版本范围 ├── collect_faces.py # 样本采集脚本 ├── train_model.py # 训练脚本,输出 yml 和 json ├── recognize.py # 实时识别脚本 ├── models/ # 存放训练产物 │ ├── face_model.yml │ └── label_map.json ├── dataset/ # 原始样本,按人员 ID 分目录 │ ├── 1/ │ └── 2/ └── docs/ ├── 环境搭建.md └── 参数调优.md

requirements.txt里最核心的依赖就两个:opencv-contrib-python和numpy。注意是opencv-contrib-python而不是opencv-python,因为 LBPH 识别器在 contrib 模块里。文档里要明确写:pip install opencv-contrib-python==4.8.0.76这类带版本号的命令,避免新版本 API 变动导致代码失效。

README.md至少要包含三个部分:环境要求(Python 版本、OpenCV 版本、摄像头支持)、运行顺序(先采集、再训练、后识别)、常见目录说明。很多人写 README 只写“怎么安装”,不写“数据放哪里”,这其实是最关键的。新人拿到项目,第一件事就是把采集脚本跑通,没有清晰的样本目录说明,他大概率会自己改代码里的路径。

4.2 文档里必须记录的参数调优表格

文档另一项核心内容是把调参经验沉淀成表格。这个表格能给接手的人一个起点:

参数默认值调优方向翻车现象
scaleFactor1.05值越小检测越慢,但越不容易漏检1.3 以上人脸小一点就检测不到
minNeighbors5值越大概率误报越少,但可能漏检设为 1 时墙上的插座也会被框出来
minSize(80, 80)根据摄像头安装距离调整设太小人脸区域太小送入识别器无效
confidence_threshold65用样本遍历测试取中位数设太高陌生人被放行,设太低自己被拒
grid_x / grid_y8格子越多对局部变化越敏感超过 12 后轻微歪头就识别失败

这份表格在文档里比代码值钱。因为它记录了参数之间的耦合关系:比如scaleFactor调小的同时,如果minNeighbors不跟着调,你会发现自己人的脸被分成多个框,识别器拿到的是半张脸,confidence 直线上升。

4.3 从采集到部署的完整操作步骤

文档里的操作步骤要按顺序写死,不能给用户选择余地。我一般这样列:

  1. 连接摄像头,运行collect_faces.py,为每个需要通行的人员录入 50 张样本。
  2. 检查dataset/{people_id}目录下的样本,删除模糊、重复、背景占比过大的图片。这一步不可省,宁可删到只剩 30 张干净样本,也不要保留 50 张噪声样本。
  3. 运行train_model.py,确认最终打印的样本总数和人员数。如果某个人是 0 样本,训练脚本会直接报错,这在避坑章节里细说。
  4. 运行recognize.py,先让已录入的人正对摄像头,观察 confidence 数值。把 confidence_threshold 调成比正常数值高 15 到 20 左右的点。
  5. 找未录入的人员做负样本测试,确保他们的 confidence 高于阈值。
  6. 部署到值班机器上,设置开机自启脚本,接入门禁控制器信号。

这套顺序的核心逻辑是“先验证数据,再验证模型,最后验证硬件”。很多人都喜欢先调代码再采集数据,最后发现样本不够又回来重采,来回折腾。

5. 避坑与排查:五个让项目停摆的高频问题

这个项目里遇到的大部分问题都不是算法问题,而是环境问题、路径问题和数据问题。我按实际踩坑的频率整理了五个:

5.1 ModuleNotFoundError: No module named 'cv2'

现象:运行采集脚本时提示缺少 cv2 模块。原因有几种:第一是虚拟环境里没装 OpenCV;第二是装了opencv-python但代码里调用了cv2.face,同样报错;第三是电脑里有多个 Python 环境,pip 装到了 A 环境,但脚本用 B 环境运行。解决:先pip list确认当前环境里有没有opencv-contrib-python。如果没有,用pip install opencv-contrib-python --user安装。最关键的是搞清楚你运行脚本的解释器路径:在 IDE 里看当前解释器,在终端里用which python确认。我自己的血泪经验是,用 VS Code 时默认解释器经常被切换成全局环境,导致 pip 和运行时不一致。

5.2 cv2.error: OpenCV(4.4.0) ... 摄像头打不开

现象:cap.read()一直返回False,控制台打印“摄像头读取失败”。原因通常不是代码,而是摄像头被其他程序占用,或者VideoCapture的索引号不对。解决:先关闭所有占用摄像头的软件(包括浏览器、微信、会议软件)。然后依次测试索引:

for i in range(3): cap = cv2.VideoCapture(i) ret, frame = cap.read() print(f"索引 {i}: {'成功' if ret else '失败'}") cap.release()

如果你用的是 USB 摄像头,有时候插拔之后索引从 0 变成 1,这种“玄学”问题用上面的循环就能排查。另外树莓派上要注意摄像头接口使能,sudo raspi-config里如果没打开 Camera Interface,VideoCapture(0)永远读不到数据。

5.3 训练时报错 Train dataset empty

现象:运行recognizer.train()时抛出Train dataset empty错误。原因有两个:一是dataset目录下没有任何图片;二是读取图片时cv2.imread()返回None,但代码里没有跳过 None 对象。解决:训练脚本里要统计并跳过读取失败的图片,同时在训练前打印每个人员的样本数。空数据集还有一个隐蔽来源——OpenCV 的imread不支持中文路径,如果你的dataset目录在陈小明的文件夹下,图片会全部读取失败。解决方法是把项目放到纯英文路径下,或者在读取时用cv2.imdecode(np.fromfile(...), ...)替代。

# 解决中文路径下无法读取的问题 def load_image_cn(path): import numpy as np data = np.fromfile(path, dtype=np.uint8) return cv2.imdecode(data, cv2.IMREAD_GRAYSCALE)

这个函数是处理中文路径问题的标准写法,我用它替换掉项目里所有cv2.imread调用,再也没有出现过空数据集。

5.4 识别结果乱跳,同一个人的 confidence 忽高忽低

现象:张三站在摄像头前,一会儿识别成张三,一会儿识别成 Unknown。原因通常是检测框的位置不稳定,导致送入识别器的人脸区域包含了不同比例的背景。解决:一是把送入识别器的人脸区域适当放大,不要只裁剪检测框给的矩形,往外扩 10% 到 15%,让 LBPH 拿到更多稳定特征;二是对多帧识别结果做投票,比如连续 5 帧中有 4 帧识别为同一个 ID 才算有效:

if label == last_label: vote_count += 1 else: vote_count = 1 last_label = label if vote_count >= 5: # 执行开门动作

帧投票机制能明显降低闪烁误判。它引入了一个副作用——识别延迟会增加,所以投票帧数不要超过 10,否则人站在门前要等一秒多才开门,很影响体验。

5.5 陌生人 confidence 比熟人还低

现象:明明没录入过的人,识别结果却给了小于阈值的 confidence,导致误放行。这通常是因为训练样本太单一,模型把人脸的背景纹理当成了特征。比如所有样本都是在同一个位置的同一面白墙前采集的,模型可能学习到了“这个位置 = 这个人”,换一个人站到同样位置也匹配。解决:采集样本时强制要求人物移动位置、改变背景、调整光线,至少让样本中的背景有 3 种以上变化。其次,负样本测试要作为训练后的固定步骤,每个新模型都要让至少 5 个未录入人员做一次实测,记录置信度分布。这一条也是门禁项目“文档说明”里最有价值的内容——记录负样本测试结果,防止模型过拟合到场景。

6. 进阶调试:把 confidence 曲线可视化,找出最佳阈值

很多人调confidence_threshold全靠试,错了再改。这里有一个更高效的办法:把每个人员的样本跑一遍识别,保存所有 confidence 的分布,再画成直方图。这样你一眼就能看出阈值该往哪边挪。常见做法是在训练好的模型上,对每个样本执行predict(),收集负样本和正样本的 confidence 列表,然后用 Matplotlib 画图。

import cv2 import numpy as np import matplotlib.pyplot as plt # 加载模型 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read("./models/face_model.yml") # 收集正样本的 confidence positive_scores = [] for person_id in [1, 2, 3]: for img_name in os.listdir(f"./dataset/{person_id}"): img = cv2.imread(f"./dataset/{person_id}/{img_name}", cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (200, 200)) label, confidence = recognizer.predict(img) positive_scores.append(confidence) # 用未录入人员样本作为负样本收集 confidence negative_scores = [] for img_name in os.listdir("./negative_samples"): img = cv2.imread(f"./negative_samples/{img_name}", cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (200, 200)) label, confidence = recognizer.predict(img) negative_scores.append(confidence) plt.hist(positive_scores, bins=20, alpha=0.7, label="Known Person") plt.hist(negative_scores, bins=20, alpha=0.7, label="Unknown Person") plt.axvline(x=65, color="red", linestyle="--", label="Current Threshold") plt.xlabel("LBPH Confidence") plt.ylabel("Count") plt.legend() plt.show()

这个可视化脚本的价值在于,它把玄学调参变成了看数据分布。正样本和负样本的曲线如果完全重叠,说明阈值怎么调都没用,问题出在样本采集或模型选择上;如果两条曲线有明显的间距,阈值就取两峰之间的谷底。我在做一个办公室门禁项目时,用这个方法把误放行率从 15% 降到 2% 左右,主要是发现有一个员工的样本在逆光场景下和其他人高度混淆,重新采集后才解决。

快要收尾了,最后说一个我自己的习惯:门禁系统的“开放性”测试一定要做,而且要保留记录。每训练完一个新模型,我至少找三个没有录入过的同事去摄像头前站三秒,把他们的 confidence 数值抄下来。如果陌生人里有人低于阈值,这说明模型过拟合到了办公室环境,要重新增加样本多样性。宁可多花半天采样本,也不要上线后被人从门口试出来隐患。这个项目的核心永远不是算法有多强,而是你知道你的模型在什么条件下不可靠。希望帮到你,回去跑通一遍再改,你会有自己的调参手感。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询