第 20 / 30 课。 前置:上一课。本篇的运行范围以实验说明和验证记录为准,构造样例不代表真实任务精度或板端性能。
同一张脸为什么没有固定分数
对齐、模糊、光照和模型权重会影响特征。特征归一化后,余弦相似度在 [-1,1],不是正确率。ArcFace 是训练损失的一种方法,不是所有模型都适用的“相似度大于某值就通过”规则。
一对一核验判断两份输入是否属于同一身份;一对多检索从注册库找候选。库越大,偶然出现高相似候选的机会也会变化,所以两者不能不加验证地复用同一阈值。
用错误类型定义阈值
FAR 是异人对被接受的比例,FRR 是同人对被拒绝的比例。本课规则为 score >= threshold 接受。提高阈值通常降低 FAR,却增加 FRR。分母分别是异人对和同人对,不能统一除以总样本数。
验证对按身份或采集来源独立组织,避免同一近重复照片同时参与训练与验收。阈值选好后冻结,再使用独立测试对报告结果。
运行实验
代码使用明确标注为构造的同人/异人分数,找满足零验证误接受条件下 FRR 最低的阈值。零误接受只针对这几个样本,不是实际 FAR 为零的证据。测试对中故意放入一个较高异人分数,用来观察泛化失败。
练习
增加一个高分异人对,重新选择阈值;再固定旧阈值只修改测试集,解释为什么不能悄悄重调后继续称其为独立测试。
工程输出应含候选 ID、分数、是否接受和拒绝原因。若没有任何候选满足要求,必须允许“未知”,不能总把最高分身份当作识别成功。人脸身份相似度本身也不提供活体结论。
配套实验与运行记录
实验:特征识别:人脸向量、相似度与阈值
查看完整代码:main.py
"""在独立验证对上选择阈值,区分一对一核验、检索与未知身份拒绝。"""
import numpy as np
def rates(genuine, impostor, threshold):
return float(np.mean(impostor >= threshold)), float(np.mean(genuine < threshold))
def main():
genuine = np.array([0.62, 0.71, 0.80, 0.91])
impostor = np.array([0.1, 0.3, 0.5, 0.65])
candidates = np.r_[
-1.0,
np.unique(np.r_[genuine, impostor]),
np.nextafter(np.unique(np.r_[genuine, impostor]), np.inf),
1.01,
]
feasible = [
(rates(genuine, impostor, t)[1], t)
for t in candidates
if rates(genuine, impostor, t)[0] == 0
]
frr, threshold = min(feasible)
far, frr = rates(genuine, impostor, threshold)
assert far == 0 and frr == 0.25
print("constructed validation threshold", threshold, "FAR", far, "FRR", frr)
test_rates = rates(np.array([0.6, 0.85]), np.array([0.2, 0.72]), threshold)
print("frozen-threshold constructed test FAR/FRR", test_rates)
assert test_rates == (0.5, 0.5)
if __name__ == "__main__":
main()
在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。
cd 'content/posts/AI-深度学习/嵌入式视觉模型进阶/20-特征识别/code' && python3 main.py
在本篇代码目录执行 python3 main.py。依赖安装、可选入口见包内 README.md;实际运行结果见 verification.md。下载包包含本课使用的辅助模块与样例。
参考与进一步阅读
原始论文或官方文档用于核对结构、接口和适用范围。本篇代码是围绕教学目标编写的最小实验;除明确注明的完整入口外,不视为对应论文模型的完整复现。