拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于人脸识别的考勤签到小程序设计:从选型到上线避坑指南

基于人脸识别的考勤签到小程序设计:从选型到上线避坑指南

简介:一份基于人脸识别的考勤签到小程序毕业设计论文PDF,面向高校学生、小程序开发者及教育信息化研究者,用于解决传统手动点名与刷卡签到效率低、易被替代的痛点。内容从引言、国内外研究现状、需求分析,到系统架构、技术实现、数据库管理与测试评估,完整展示了基于微信小程序(WXML+WXSS+JavaScript)与人脸识别CNN模型的设计方案,并给出教师端/学生端双端业务流程、云端人脸接口对接、数据库设计方法及系统功能测试结果。资源共1个PDF文件,约1.54MB,结构清晰、章节完整,已有473人学习。读者可参考其中人脸识别签到全流程的落地思路,包括卷积神经网络选型、后台服务器设置、学生人脸数据比对等细节,作为毕业设计、课程项目或实际考勤系统开发的直接蓝本,也可为后续结合5G、多生物识别技术的研究提供起点。

1. 基于人脸识别的考勤签到小程序设计:一份设计稿如何变成每天早晨的真实打卡

行政群里最常听到的一句话是“谁又忘打卡了”。如果换成小程序里的一个人脸识别签到流程,员工打开微信、对准摄像头、一秒完成打卡,管理员在后端能看到迟到、早退、缺卡,这套东西从设计文档到可运行系统,真正的难点并不在人脸识别算法本身,而在“采集图像是否合格、活体检测有没有被绕过、打卡记录是否可靠”这三件事上。基于人脸识别的考勤签到小程序设计,解决的正是企业或学校场景里“人、时间、地点”三要素的自动化核验问题。适合行政管理者、独立开发者和准备做企业内部工具的技术团队,下面从选型开始完整拆一遍落地路径。

2. 人脸识别链路选型:先定识别在哪跑,再谈打卡体验

2.1 人脸识别三种主流行法:端侧、云端 API 与端云协同

很多项目第一版喜欢把“人脸识别”四个字当成一个黑匣子,直接把 OpenCV 的 Haar 特征拿来做检测,再用一个简单特征比对就上线。考勤签到这样涉及员工权益的场景,这条路走不通。主流的可落地做法有三种,区别在于模型跑在哪里、数据从哪里来。

第一种是纯端侧方案,典型硬件是固定点位的人脸识别门禁机,或者像人脸识别 K10 行空板这类带摄像头和 NPU 的开发板。模型直接跑在设备上,不依赖办公网络,响应可以做到 200 毫秒以内。但小程序端很难复刻这种体验,小程序包体有限制,本地推理能力受手机性能影响很大,而且前端直接加载模型会让首次启动时间变得不可接受。

第二种是云端 API 方案,也就是把“注册人脸”和“人脸搜索”两个能力交给云服务。小程序端只负责拍照和上传,云端返回最相似的人以及相似度分数。这是考勤签到小程序最常见的做法,集成成本低,识别精度由服务方保证,适合员工规模在几十到几千人的场景。代价是每次比对都需要网络请求,而且会产生接口调用费用。

第三种是端云协同,端侧做人脸检测和活体动作引导,云端做人脸比对。这一种兼顾了体验和安全性,但开发量最大。对于大多数团队来说,第一步先用云端 API 把流程跑通,后续再根据成本逐步把活体检测或人脸质量判断挪到端侧,是性价比最高的路径。

我通常会在项目一开始就画一张选型表,把团队真实约束摆出来,而不是先选最酷的方案。

维度端侧门禁机云端 API端云协同
实时性毫秒级视网络约 0.5-2 秒0.3-1 秒
离线可用可以不可以部分可以
识别精度依赖硬件与模型服务方持续迭代,较高综合较优
实施成本硬件采购高按调用量计费开发成本高
数据隐私数据不出设备原图与特征都在云端原图抽样上云
适用场景固定闸机、单一出入口小程序签到、移动办公对安全要求高的企业

2.2 人脸注册与 1:N 搜索:小程序选型绕不开的基本功

考勤签到里常用的是 1:N 搜索,也就是在一个人脸库里找到“这个人是谁”。流程拆开只有两步:员工首次录入时,把一张正脸照片和员工编号绑定,注册到人脸库;每次签到时,拍一张照片,到同一个库里去搜,拿到返回的 personId 和匹配分数。

这里面有一个容易被忽略的点:人脸注册和人脸搜索是两个独立接口,但共用同一个人脸库。设计阶段就要想清楚人脸库的分组方式。小企业可以只建一个分组,按 employeeNo 作为 userId;大型企业按部门或办公地点建立多个分组,签到时先根据当前位置选定分组,再在分组里做 1:N 搜索,能明显减少搜索时间并降低误识别概率。

调用方式上,我一般不会在前端直接对接人脸服务,而是把调用封装成一个独立的云函数或后端服务。伪代码结构如下:

class FaceService { async register(userId, imageBase64, groupId) { // 调用人脸注册接口,返回 personId // 常见参数:userId、imageBase64、groupId } async search(imageBase64, groupId) { // 调用人脸搜索接口,返回匹配到的人与相似度分数 // 常见参数:imageBase64、groupId、qualityControl } }

这里最关键的两个参数,一个是图像质量控制,一个是相似度阈值。很多识别失败问题不是算法问题,而是上传的照片模糊、逆光或者人脸占比太小。人脸服务通常提供 qualityControl 参数,建议设置为高,宁可在录入阶段多让员工重拍几次,也不要让低质量照片进入人脸库。相似度阈值不要用服务默认值,默认值往往偏向易用性,对考勤来说偏松,具体阈值怎么定在第 6 章单独说。

2.3 活体检测是考勤签到的第一条红线

考勤签到最怕的不是识别不准,而是照片冒签。一张打印照片放摄像头前,很多基础人脸识别也能通过,所以活体检测必须作为独立模块设计,不能省略。

常见的活体内核有两种。动作活体会让用户按照屏幕提示完成眨眼、张嘴、左右摇头等动作,后端校验动作顺序和动作耗时;静默活体则是一次拍照,通过纹理、反光等特征判断是不是真人。小程序签到场景,我更建议用动作活体,用户拿手机操作并不排斥多花两三秒,而且动作活体对低端机的兼容性更好。静默活体虽然体验好,但需要较强的模型支撑,服务成本也会更高。

活体检测需要注意两个参数:动作超时时间和重试次数。默认给 5 秒一个动作比较合适,超过 5 秒判为超时;一个动作最多重试两次,连续失败后要求用户重拍。如果完全不设重试限制,用户在光线不好的地方很容易卡在活体检测,体验会很难看。

2.4 隐私合规与用户授权:上线前绕不开的审查项

人脸信息属于敏感个人信息,小程序在调用摄像头和人脸服务之前,必须做明确授权。不能只在用户点击“签到”的时候弹一个系统授权框,而是要在首次进入时展示单独的说明页,写明“采集人脸信息用于考勤签到身份核验,不用于其他用途”。

录入阶段要区分两个授权:一个是微信的摄像头授权,一个是业务层面的人脸信息使用授权。业务授权需要用户主动勾选并点击同意,这行记录要存到数据库里,包括授权时间、版本号和用户 openid。后续如果用户离职,管理员要能一键删除该用户的人脸特征,并且在前端界面提供“注销人脸信息”入口。

这里还要注意原图保存策略。人脸搜索完成后,原图不应该长期保留在云存储里。常见做法是签到记录里只保存一个缩略图用于人工复核,而且缩略图只保留固定周期,比如 30 天,到期后自动清理。人脸库里的特征数据则保留到员工离职当天。

2.5 离线兜底与网络质量差的备案

云端识别依赖网络,办公室地下层、电梯间、会议室深部经常出现请求超时。签到这个动作又偏偏不允许“等会再试”。我在实际项目中会设计一个本地待提交队列:用户点击签到后,先把签到请求写入本地缓存,状态为“待提交”,然后立即提示用户“签到请求已记录,正在确认身份”;网络恢复后自动补提交,并对比服务端的最终识别结果。

如果人脸比对始终失败,方案里还要有一个兜底动作:允许员工通过输入工号加密码的方式临时签到,但这条记录标记为“人工核验”,管理员事后比对现场照片。全自动固然好,但考勤产品最重要的是不让人“无路可走”。

3. 从云开发到人脸识别录入:最小可跑通的微信小程序签到闭环

3.1 载体选型:微信小程序、uni-app 还是 H5

基于人脸识别的考勤签到小程序,载体上最常见的选择是微信小程序。微信生态自带身份体系,用户不需要注册账号,获取 openid 后就能对应到员工编号,免登录体验对考勤签到这种高频小动作非常重要。如果你的团队后续还要做 Android、iOS、鸿蒙版本,那么就需要考虑 uni-app 开发微信小程序这一条路径。uni-app 能一套代码编译到多个端,但人脸识别相关能力需要做平台适配,摄像头调用和文件上传的差异要单独处理。纯 H5 方案最不建议,浏览器对人脸识别权限的控制不一致,部分安卓浏览器甚至拿不到清晰的前置摄像头画面。

3.2 用云开发初始化项目:环境与集合

云开发最大优势是省掉自建服务器,云函数、云数据库、云存储三件套对中小规模考勤系统足够用。新项目我一般这样初始化:

// app.js App({ onLaunch() { if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else { wx.cloud.init({ env: 'your-env-id', traceUser: true }) } } })

env 填你自己的云环境 ID,开通云开发后在控制台能看到。traceUser 设为 true 后,云函数里通过 cloud.getWXContext() 可以直接拿到用户的 openid,不需要前端手动传。

接下来在云开发控制台创建三个集合:users、face_registry、attendance_records。users 存员工基础信息,face_registry 存人脸库关联关系,attendance_records 存每天签到记录。集合权限建议全部设为“仅管理员可读写”,所有业务操作都通过云函数完成,避免前端越权。

3.3 人脸录入流程:拍照、上传与云端注册

员工首次使用需要先录入人脸。前端流程是调用摄像头拍一张正脸照片,上传到云存储,再触发云函数完成注册。

async function registerFace() { const res = await wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['camera'], camera: 'front', sizeType: ['compressed'] }) const filePath = res.tempFiles[0].tempFilePath const openid = wx.getStorageSync('openid') const uploadRes = await wx.cloud.uploadFile({ cloudPath: `faces/${openid}_${Date.now()}.jpg`, filePath }) await wx.cloud.callFunction({ name: 'registerFace', data: { fileID: uploadRes.fileID, employeeNo: 'E10001', name: '张伟' } }) }

这里必须使用 sourceType: ['camera'],禁止从相册选择。相册里的照片可能是美化过的、翻拍的,甚至不是本人,会直接污染人脸库。sizeType 选 compressed 可以显著减少上传时间,但压缩过度会影响云端识别精度,如果后端返回图像质量分过低,要在前端提示重新拍摄。

云函数 registerFace 负责真正的人脸注册和落库:

const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event) => { const { OPENID } = cloud.getWXContext() const { fileID, employeeNo, name } = event const res = await cloud.downloadFile({ fileID }) const imageBase64 = res.fileContent.toString('base64') // 调用人脸注册服务,groupId 对应企业 ID const faceResult = await faceService.register({ userId: employeeNo, imageBase64, groupId: 'company_main' }) const db = cloud.database() await db.collection('users').add({ data: { openid: OPENID, employeeNo, name, faceStatus: 'registered', createTime: db.serverDate() } }) return { code: 0, personId: faceResult.personId } }

OPENID 直接从云端上下文获取,而不是前端传入,这样防止有人伪造员工绑定关系。人脸注册的凭证绝不能写在小程序前端代码里,否则等于公开了调用额度。云函数里调用云存储下载原图再转 base64,比前端直接传 base64 更安全,也不容易超出云函数请求体大小限制。

3.4 每日签到识别:照片、人脸搜索与防重复写入

签到流程与录入流程类似,但多了两个关键判断:这个人是谁、今天是否已经打过卡。

async function signIn() { const res = await wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['camera'], camera: 'front', sizeType: ['compressed'] }) const filePath = res.tempFiles[0].tempFilePath const uploadRes = await wx.cloud.uploadFile({ cloudPath: `signin/${Date.now()}.jpg`, filePath }) const result = await wx.cloud.callFunction({ name: 'signIn', data: { fileID: uploadRes.fileID } }) if (result.result.code === 0) { wx.showToast({ title: '签到成功' }) } else { wx.showModal({ title: '签到失败', content: result.result.message }) } }

云函数 signIn 的完整逻辑应该包含四步:下载图片、人脸搜索、判断重复签到、写入记录。

const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async (event) => { const { OPENID } = cloud.getWXContext() const db = cloud.database() const { fileID } = event const fileRes = await cloud.downloadFile({ fileID }) const imageBase64 = fileRes.fileContent.toString('base64') // 第一步:人脸搜索 const searchRes = await faceService.search({ imageBase64, groupId: 'company_main' }) if (searchRes.score < 80) { return { code: 1001, message: '人脸匹配不通过,请重试' } } const employeeNo = searchRes.userId // 第二步:查今天是否已签到 const today = db.serverDate() const existRes = await db.collection('attendance_records') .where({ employeeNo, date: today, type: 'signin' }) .count() if (existRes.total > 0) { return { code: 1002, message: '今日已签到,请勿重复操作' } } // 第三步:写入签到记录 await db.collection('attendance_records').add({ data: { employeeNo, date: today, type: 'signin', score: searchRes.score, photoFileID: fileID, createTime: db.serverDate() } }) return { code: 0, employeeNo, score: searchRes.score } }

这里的 date 字段建议按本地时区格式化成年月日字符串,比如“2026-05-20”,不要直接存时间戳。云函数运行在云端,默认时区是 UTC,直接用 new Date().toISOString() 可能差 8 小时,导致凌晨签到的记录被算到前一天。这个坑我踩过不止一次。

3.5 小程序动态设置标题:让签到页带上部门信息

用户打开小程序后,顶部标题如果只是“考勤签到”四个字,管理员分不清每个页面在哪个环节。我一般会在页面加载后动态设置标题,顺便把当前员工的部门或班次信息带进去:

wx.setNavigationBarTitle({ title: '考勤签到 - 研发部' })

这里不只是为了好看。小程序的头部标题同时也决定了分享卡片和最近使用列表中的文案,动态设置成“考勤签到 - 研发部”后,员工从最近使用里进入时能确认自己没进错入口。多个班级或多个办公点的场景,这个细节能减少大量误操作。

在 uni-app 开发微信小程序的场景下,动态标题用小程序的 wx.setNavigationBarTitle 仍然有效,但要注意页面 onShow 里设置,否则从一个页面跳转返回时标题会被重置成全局配置。

4. 考勤签到业务设计:防代签、防重复与关键参数

4.1 从“打卡”到“考勤”的业务闭环

人脸识别解决了“是谁”的问题,但考勤系统还要回答“几点来、几点走、迟到多久、缺卡怎么补”。很多基于人脸识别的考勤签到小程序设计文档把重心全放在识别流程上,上线后才发现连最简单的“上午 9 点上班”都没法配置。

业务上要拆成三个层级:打卡事件、考勤规则、考勤结果。打卡事件是员工的一次签到或签退记录;考勤规则定义上班时间、下班时间、迟到判定边界、午休时长;考勤结果每天凌晨由定时任务把原始记录对照规则计算出来,生成正常、迟到、早退、缺卡、外勤等状态。

这一层如果不用定时任务,每天让管理员手工看记录,就没有达到“考勤系统”的目的。云开发里可以设置定时触发器,每天凌晨 1 点跑一次云函数,统计前一天的打卡记录。

4.2 防代签三件套:定位、Wi-Fi 与 openid 配合

人脸识别本身已经很强,但人脸是可以被授权他人代签的。比如员工 A 委托员工 B 拿手机对着自己脸拍一张,系统无法区分。为了降低这类风险,需要叠加位置和网络环境信息。

小程序里通过 wx.getLocation 可以拿到经纬度,参数设置如下:

wx.getLocation({ type: 'gcj02', isHighAccuracy: true, highAccuracyExpireTime: 4000, success(res) { console.log(res.latitude, res.longitude) } })

isHighAccuracy 开启后会调用高精度定位,速度会慢一些,但考勤签到可以接受 1 到 3 秒的等待。拿到定位后,在云端计算与公司坐标的距离,超过 300 米就标记为“位置异常”,需要管理员人工确认。

定位可以被模拟器伪造,所以还要加入 Wi-Fi 信息。小程序里的 wx.getConnectedWifi 能拿到当前连接的 Wi-Fi 的 BSSID,办公场景下固定几个 BSSID 可以作为强校验条件。如果员工连接的是公司 Wi-Fi,即使定位数据异常,也可以放行;如果两者都异常,直接拦截。

openid 只是身份标识,防不了“人借手机给同事”的问题,但能防止手工篡改请求参数。三道校验合在一起,代签成本会明显提高,日常使用中也能起到威慑作用。

4.3 防重复签到与补卡规则

重复签到是另一个高频问题。员工可能点了一次没反应,又点了一次,结果生成两条记录。数据库层要做唯一约束,云开发数据库支持给字段创建唯一索引。建议对 attendance_records 集合建立 employeeNo + date + type 的复合唯一索引,type 区分 signin 和 signout。

万一业务上允许补卡,补卡记录不能和正常记录走同一个写入通道。补卡应该走申请审批流程,员工提交补卡申请,管理员审批通过后,在后台以特殊类型写入一条记录,状态标记为“补卡”,原签到时间不参与迟到判定。这样可以避免员工为了掩盖迟到,先签退再补卡。

还有一种情况是员工早上打卡成功,下午外出忘打卡。很多系统只判断“今天是否打过卡”,这会导致签退通道被拦截。所以查询条件必须带上 type 字段,signin 和 signout 分别判断,而不是只查 date。

4.4 数据库表设计:员工、打卡记录与图像留痕

云开发数据库是文档型数据库,但设计时仍然要遵循一定的关系约束。三个核心集合的结构建议如下:

users 集合:

{ "_id": "auto", "openid": "oXXXX", "employeeNo": "E10001", "name": "张伟", "department": "研发部", "faceStatus": "registered", "faceGroupId": "company_main", "authorization": { "agreed": true, "agreedAt": "2026-05-20 10:00:00", "version": "v1" }, "status": "active" }

attendance_records 集合:

{ "_id": "auto", "employeeNo": "E10001", "date": "2026-05-20", "type": "signin", "signTime": "09:01:23", "score": 92.5, "latitude": 31.2304, "longitude": 121.4737, "wifiBSSID": "d8:xx:xx:xx:xx:xx", "photoFileID": "cloud://env/signin/xxx.jpg", "status": "normal", "source": "face" }

face_registry 集合:

{ "_id": "auto", "employeeNo": "E10001", "personId": "人脸服务返回的唯一 ID", "groupId": "company_main", "createTime": "2026-05-20 10:00:00" }

photoFileID 必须保存,因为识别分数再高,最终也要有人工抽检余地,万一闹纠纷,一张现场照片比任何日志都有效。但照片保留时间要设上限,定期清理,降低隐私风险。

4.5 考勤规则参数表:上线前和业务方对齐

考勤规则不要写成死代码,要配置化。最直观方式是建一张参数表,由管理员修改,云函数读取。核心参数如下:

参数名示例值说明
work_start09:00上班时间
work_end18:00下班时间
late_grace_minutes10迟到宽容分钟数
early_leave_minutes10早退宽容分钟数
signin_timezoneAsia/Shanghai签到时间时区
location_radius300允许签到半径,单位米
wifi_requiredtrue是否必须连接指定 Wi-Fi
face_score_threshold80人脸搜索最低相似度
liveliness_threshold80活体检测最低分数
photo_retention_days30签到原图保留天数

这个表格必须在开发前找业务方签字确认,否则开发完再改规则,牵涉到历史数据回溯,会非常痛苦。

5. 避坑:光线、遮挡、相似脸与过期授权,5 条踩坑记录

5.1 逆光打卡识别率暴跌:图像质量检查不能只靠前端判断

现象:员工站在窗户边,人脸背光严重,活体检测通过后,人脸搜索却返回相似度极低,系统提示签到失败。同一个员工换个角度就能成功。

原因:人脸搜索前整个人脸区域处于阴影中,特征提取后关键点信息不完整,不是算法的错。

解决:在云函数里调人脸搜索前,先调一次人脸质量检查。常见质量维度包括人脸角度偏转、遮挡比例、亮度、清晰度。质量分低于阈值就直接返回“请正对光源重新拍摄”,而不是继续走搜索浪费时间和费用。前端文案可以写成“请正对光线,避免逆光,再拍一次”。

5.2 相似脸误识别:相似度阈值设置得太随意

现象:一个部门两名同事长相相似,A 打卡时系统识别成了 B,生成了错误的考勤记录。

原因:人脸服务默认相似度阈值通常在 70 到 75 之间,这个值对安防场景够用,但考勤场景里一旦阈值过低,相似脸群体就会频繁互串。

解决:上线第一天不要用默认值。先用第 6 章的验证方法,拿真实员工照片扫一遍阈值,把阈值定到 80 以上。同时,在云函数里增加二次校验:人脸搜索返回 personId 后,再取该员工的最近一张打卡照片做 1:1 比对,分数同时超过阈值才放行。双次校验对双胞胎场景尤为有效。

5.3 戴口罩后识别率断崖下跌:不要只依赖下半张脸

现象:新冠疫情后员工习惯戴口罩,识别率从 99% 掉到 60%,大量员工卡在活体检测环节。

原因:人脸搜索模型依赖嘴巴、鼻子等区域的特征,下半张脸被口罩遮住,特征缺失。

解决:分两步。第一步,在签到页加“是否佩戴口罩”的选项,小程序端调整引导文案,提示摘下口罩;如果确实不能摘下,后端切换到口罩识别模式,口罩模式只比对眉骨、眼睛、额头区域,相似度阈值可以适当放宽 3 到 5 分。第二步,长期方案是做人脸底库更新,要求员工在无口罩状态下录入两到三张不同角度的照片,保证特征完整。

5.4 打印照片绕过活体检测:动作活体指令过于固定

现象:员工把一张清晰打印的照片放在摄像头前,活体检测仍然通过,系统被盗签。

原因:有些低端活体检测只做了“张嘴”“眨眼”动作识别,没有校验动作顺序,也没有限制动作完成时间,录一段视频就能依次触发。

解决:动作活体必须使用随机动作序列,每次签到从动作池里随机抽 2 到 3 个动作,并且每个动作都有独立的时间戳校验。后端记录动作开始时间、结束时间、人脸框移动轨迹,动作总耗时超过 15 秒直接失败。另外,不要把活体检测分数作为唯一标准,还要对人脸框区域做背景一致性检查,识别到屏幕边缘反光或照片纸张纹理就直接拒绝。

5.5 授权过期与版本更新后的兼容问题

现象:小程序更新版本后,老用户首次打卡时摄像头无法唤起,页面白屏或停留在“授权中”。管理员后台看日志,发现很多老员工连续几天没有人脸授权记录。

原因:微信基础库升级后,部分授权行为需要重新触发;或云开发环境配置被更换,前端请求的 env 还是旧环境 ID,导致云函数调用失败。

解决:在 App 的 onLaunch 里校验云环境可用性,并在签到页首次渲染时调用 wx.getSetting 检查摄像头授权状态。如果授权被拒绝,不要只在回调里提示,要主动弹出一个引导页,说明人脸采集的目的。授权状态和云环境 ID 都要做版本检查,新版本发布后强制清理本地缓存并重新初始化。

5.6 云函数冷启动导致签到超时

现象:早上 8 点 50 分,几百人同时打开小程序签到,云函数大量返回超时,前端提示“网络异常”。

原因:云函数在并发高峰期会冷启动,冷启动耗时可能到 3 到 5 秒,而小程序端默认请求超时时间只有 5 到 15 秒。

解决:把超时时间调到 20 秒,同时在云函数控制台配置内存为 512 MB 或更高,冷启动概率会明显下降。更稳妥的做法是设置定时触发器,在每天签到高峰前自动调用一次云函数做预热。前端也要做重试逻辑,一次失败后隔 1 秒重试,最多重试 3 次。

6. 上线前用留出集校准阈值:FRR 与 FAR 怎么选

要设人脸识别阈值,必须先定义两个指标。FRR 是把一个本人识别成别人,表现为员工本人屡次打卡失败;FAR 是把一个他人识别成本人,表现为代签或相似脸蒙混过关。考勤场景里 FAR 的代价远高于 FRR,一次误识别就是一次考勤事故,宁可让 FRR 高一点,也不能让 FAR 失控。

建议上线前从真实场景收集两组照片:一组是 50 个员工各拍 3 张正脸作为“本人组”,另一组是这些员工互相拍照形成的 30 张“冒签组”。然后把照片灌到人脸搜索接口里,记录每组照片的相似度分数,用 Python 快速扫一遍阈值:

import json with open('eval_scores.json', 'r') as f: data = json.load(f) # data 结构示例: # {"genuine": [{"score": 92}, {"score": 88}], # "imposter": [{"score": 70}, {"score": 75}]} for threshold in range(65, 96): frr_count = sum(1 for x in data['genuine'] if x['score'] < threshold) far_count = sum(1 for x in data['imposter'] if x['score'] >= threshold) frr = frr_count / len(data['genuine']) far = far_count / len(data['imposter']) print(f"threshold={threshold}, FRR={frr:.3f}, FAR={far:.3f}")

扫描结果会出现一个交叉点。如果 FAR 在 80 分左右已经降到 0,FRR 在 2% 到 3% 之间,就可以把生产阈值定在 80。如果 FAR 直到 90 分才归零,但 FRR 已经到了 8%,说明人脸库照片质量不过关,这时候改阈值没有意义,应该回头提升录入照片质量。

上线后不要一次性放开全员,先选一个 20 人到 50 人的部门灰度跑两周。这两周里,每一次识别失败都由管理员人工复核现场照片,并把失败原因记录下来。如果失败以逆光、遮挡为主,调整引导文案;如果失败集中在某两三个长相相似的同事身上,单独提高他们的比对阈值。我自己的习惯是,灰度期结束后保留最近 30 天的签到现场照片,每次有考勤争议都先看图再查日志,别只盯着分数判断对错。

希望帮到你。

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

返回列表