人脸识别
人脸识别就是把 embed 任务用在人脸上。检测器定位并对齐每张人脸,识别 head 为每张人脸返回一个 L2 归一化向量,身份由与已注册参考向量的余弦相似度决定,而不是由一份固定的类别列表决定。
定义
人脸识别为每张人脸返回一个向量,而不是一个标签。预测分两个阶段:人脸检测器 定位每张人脸及其五个关键点,裁剪出来的图像被变换到标准的 112x112 对齐姿态, 识别 head 输出一个 L2 归一化的嵌入向量。
result.embeddings 是一个形状为 (N, D) 的 Embeddings 负载,与
result.boxes 按行对齐,因此第 i 行描述的就是第 i 个检测框里的人脸。由于
每一行都是单位向量,余弦相似度就是点积,embeddings.similarity() 一次调用就能
对另一个 Embeddings 或整个矩阵算出结果。
给人脸命名是单独的一步。Gallery 保存带名字的参考向量;给 predict() 传入
gallery= 会附加 result.identities,它与嵌入向量按行对齐,为每张人脸给出一个
名字和对应的最佳余弦分数。低于匹配阈值的人脸,名字保持为 None,绝不会用最接近
但仍低于阈值的名字顶替。
库里规范的任务键是 embed。face-recognition、facial-recognition、reid 和
face 都会规范化到它,所以 task="face-recognition" 和 task="embed" 选中的是
同一件事。人脸是这个更宽泛任务的区域形态;嵌入向量
讲的是整图形态和文本形态、共用的 Embeddings、Identities 与 Gallery API,
以及那些不做任何检测就产出向量的模型。
模型
LibreFaceRec 是这个任务的模型家族。它是藏在一次调用
背后的两个 ONNX 产物:librefacerec-l.onnx,一个输出 512 维嵌入向量的 iResNet100
识别 head;以及 librefacerec-det.onnx,带五个关键点的默认人脸检测器,取自
OpenCV zoo。两者都在首次使用时从 LibreYOLO 的 Hugging Face 组织下载。任何其他遵循
ArcFace 约定的 ONNX 文件(输入对齐后的 112x112,输出 (N, D))都可以替换识别
head,只要传它的路径而不是 librefacerec-* 名称。
embed 任务键比人脸更宽。CLIP、
SigLIP2 和 DINOv2 同样支持
task="embed",返回一个整图向量,那是图像检索而不是人脸身份。它们共用 Gallery
和 Embeddings API,所以下面的注册与匹配流程可以照搬,但它们不检测人脸,也不做
人脸对齐。
识别 head 通过 onnxruntime 运行,而基础安装并不带它:
pip install "libreyolo[onnx]"预测
from libreyolo import LibreYOLO, SAMPLE_IMAGE # librefacerec-* 名称一律路由到人脸嵌入向量家族,与文件后缀无关,# 并在首次使用时连同默认人脸检测器一起从 LibreYOLO 的 Hugging Face# 组织下载model = LibreYOLO("librefacerec-l.onnx")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy) # (N, 4) 人脸检测框print(result.embeddings.data.shape) # (N, D),每张人脸一行print(result.embeddings.dim)libreyolo predict model=librefacerec-l.onnx source=photo.jpgfrom libreyolo import LibreYOLO model = LibreYOLO("librefacerec-l.onnx") # 对两张图都跑检测和嵌入向量,并比较各自置信度最高的人脸,# 余弦相似度取值范围为 [-1, 1]outcome = model.verify("person_a.jpg", "person_b.jpg", threshold=0.4)print(outcome["similarity"], outcome["same_person"])from libreyolo import Gallery, LibreYOLO model = LibreYOLO("librefacerec-l.onnx") gallery = Gallery(model)gallery.enroll("ada", ["people/ada/1.jpg", "people/ada/2.jpg"])gallery.enroll("grace", "people/grace/1.jpg")gallery.save("faces.npz") result = model("group_photo.jpg", gallery=gallery, threshold=0.4)for name, score in result.identities.data: print(name, score) # 低于阈值时 name 为 Nonelibreyolo enroll model=librefacerec-l.onnx source=people/ gallery=faces.npzlibreyolo predict model=librefacerec-l.onnx source=group_photo.jpg gallery=faces.npzfrom libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("librefacerec-l.onnx") # face_boxes 会完全跳过检测;face_detector 接受可调用对象、# LibreYOLO 检测模型或 FaceDetector 实例result = model(SAMPLE_IMAGE, face_boxes=[[34, 12, 90, 80]])print(result.embeddings.data.shape)不去动它的话,predict() 会下载并配上默认检测器。face_detector 用可调用对象、
LibreYOLO 检测模型或 FaceDetector 实例覆盖它,既可以在构造函数上设置,也可以
按次调用设置。face_boxes 用你手上已有的检测框绕过检测。在 CLI 上,
face_detector= 接受人脸检测器的 .onnx 路径或 LibreYOLO 检测器名称。
model.verify(image_a, image_b) 是两张图的快捷方式:它对每张图里置信度最高的
人脸求嵌入向量,返回 {"similarity", "same_person", "threshold"}。
model.embed(sources) 把一张或多张图像里的所有人脸行堆叠成单个 (N_total, D)
张量返回。关于输入源、流式处理和结果处理,见预测。
数据集格式
注册按每个身份一个文件夹来读取。文件夹名就是身份,里面的每张图像都为这个名字 贡献参考:
people/
ada/
1.jpg
2.jpg
grace/
1.jpglibreyolo enroll 会遍历这棵目录树,写出一个 .npz 人脸库。已存在的库文件是
就地扩展而不是被替换,所以身份可以随着时间陆续加入。人脸库通过嵌入向量维度和
文件指纹绑定到产出它的权重上;用另一个模型来匹配会直接抛出异常,而不是去比较
不兼容的向量空间。
默认情况下每张源图像只贡献一行参考,也就是置信度最高的那张人脸,所以一张背景里
有路人的肖像只会注册它的主体。给 Gallery.enroll 传 select="all" 可以把返回的
每一行都存下来。
训练
这个任务里没有任何家族能在 LibreYOLO 内部训练。LibreFaceEmbedder.train() 会
抛出异常:在上游训练一个识别 head,按 ArcFace 约定导出为 ONNX,再按路径加载
文件。
验证
这个任务没有数据集验证器,val() 会抛出异常,而不是假装有。验证精度用
model.verify() 在带标注的图像对上测量,扫一遍 threshold 挑出你想要的工作点。
识别精度则通过注册一个人脸库、在留出图像上读取 result.identities.name 和
result.identities.score 来测量,把 None 名字算作拒识。
导出
识别 head 本身已经是 ONNX 计算图,没有什么可转换的:LibreFaceEmbedder.export()
会抛出异常。直接部署这个 .onnx 文件,或者把 LibreYOLO 指向它,让这个家族来处理
检测、对齐和归一化。