识别库里只有我一个人,它还是把我室友认成了'我'

库里只录了我自己,系统看了我室友一眼,说这是我,置信度还不低。往回挖,是三个各自没毛病的决定凑到了一起:糊掉的录入样本、取最高分的匹配、把单帧错误锁死的粘性身份。

· 4 min read

识别库里只有我一个人,它还是把我室友认成了”我”

最近在给桌面助手加人脸识别。全在本地跑,不上云,库里只存经过本人同意录入的照片。

管线跑通那天晚上,我正对着摄像头调东西,室友回来了,从我身后走过。

屏幕上他的脸被框出来,标签写着我的名字。

库里只录了我一个人。这个系统认识的全部人类加起来,就是我。然后它看着我室友,说:这是我。

置信度还不低。


它是怎么工作的

链路本身不复杂。

MediaPipe 负责盯着摄像头,发现”有张脸”就叫一声。这层很宽容,眨眼、侧脸都不影响——它只管有没有人,不管是谁。然后把脸裁下来交给 OpenCV:YuNet 对齐,SFace 抽一个 128 维向量,拿去和库里的样本算余弦相似度。

另外我自己加了一层”粘性身份”。因为置信度在你眨眼或转头的瞬间会自然往下掉,要是每一帧都重新判断你是谁,名字就在屏幕上闪来闪去。所以认出你之后,只要画面里一直有脸,就一直是你;人离开五秒以上才忘掉。低头看手机不会被忘,真走了才会。

当时觉得这个设计还挺优雅的。记住它,后面它要负主要责任。


往回挖

说实话,第一反应是怀疑我俩长得像。

我们不像。

挖了一阵之后,我怀疑是三件事凑到了一起。

第一,录样本的时候我没做任何质量检查。 有几张是我一边动一边拍的,糊的。糊照片抽出来的向量是什么?是”一张糊掉的脸”的平均长相,五官细节全被抹掉了。这种向量跟谁都有点像。

第二,匹配逻辑取的是所有样本里的最高分。 当前帧跟库里每一张都比一遍,谁分高听谁的。也就是说库里只要混进一张烂样本,整个库等于废了——室友跟我那些清楚的照片比,分数很低,但没用,他只需要跟那张糊的对上。

第三,就是那个我觉得很优雅的粘性身份。 前两条凑出来的,其实只是某一帧的误判,按理说下一帧可能就纠正了。但粘性逻辑不知道这一帧是错的,它只知道”认出来了”,然后忠实地执行自己的职责:画面里有脸,就保持这个身份。

一帧的错,被它锁成了持续的错。


两个改动。

录入时加质量过滤,糊的照片直接拒收。源头断掉,那种”跟谁都像”的向量就不会再产生了。

身份不再由单帧决定。连续好几帧都认成同一个人,才真正锁进粘性逻辑。偶然对上一帧的,没机会转正。

本来还考虑过在录入时弹个提示让人”请保持不动”,后来觉得不如直接拒收糊的——用户什么都不用配合,系统自己变严格就行了。


导师的补刀

我把这事讲给导师听。他觉得修得对,比弹提示强,然后顺手把问题往深处推了一把:

比较用的基准照片本身就有很多值得琢磨的;再往前走一步,应该设一个最低相似度门槛,而且这个门槛不是拍脑袋定的——等库里的人多起来,按人和人之间的正常差异去算。

这句话戳到我了。我一直把阈值当成调参问题,调到”感觉对了”为止。但它其实是个统计问题:阈值要区分的是”不同人之间的差异”和”同一个人自己的波动”,那它就应该从数据里长出来,而不是从我的手感里。

而库里只有一个人的时候,“人和人之间的差异”这个数据根本不存在。系统没见过第二个人,它连”不像”是什么都不知道。所以这个 bug 挑在只录了我自己的时候爆出来,不是巧合。


后记

要说学到什么,大概是:每一层单独看都挺合理,不代表拼起来合理。检测层宽容是对的,取最高分听着也没毛病,粘性身份确实让体验更顺——三个”没毛病”,叠出一个特别自信的错误答案。

还有一条:只有一个用户的测试环境,测不出任何跟”区分”有关的问题。识别系统的本职就是把人分开,而这件事在第二个人走进画面之前,根本没被测过。

阈值自动校准还在做,做完再写。