如何对 InsightFace 私有人脸识别模型进行测试
面向客户的私有模型评测指南:覆盖 MOU 前置条件、图像准备、Python 客户端示例与隐私要求,便于在自有数据上完成对开源/私有模型的准确率评估。
你将完成什么
InsightFace 通过托管的特征提取 API 向客户开放开源/私有识别模型,供其在签订商用合同前在自有验证数据上完成准确率评测。
本指南完整介绍评测流程:如何获取 API 端点、如何准备图像、如何使用最简 Python 客户端调用、我们的隐私与数据处理规则,以及免费评测窗口的合同范围。
开始之前
- 已签署涵盖合作意向、评测周期,以及共享模型标识保密范围的 MOU。端点 URL 仅在 MOU 签署后下发。
- Python 3.9 及以上环境,已安装 numpy、requests 与 OpenCV (opencv-python)。
- 覆盖生产分布的代表性验证集,并附带 RetinaFace、SCRFD 等检测器输出的人脸框。
- 明确的评测数据集和标签,以及科学的评测方法。
1. 签署 MOU 并申请访问
所有私有模型测试都从签署 MOU 开始。MOU 约定合作意向、评测周期,以及与贵司共享的模型标识的保密范围。
API 端点 URL、贵司在合同中获授权的模型名(MOU 中允许的开源或私有模型)以及账户级速率限制,都会通过邮件发送给 MOU 中列明的技术联系人。端点不会公开,请勿向授权联系人之外的任何方共享。
- 评测窗口自端点下发之日起最长 2 周,超出需签署商用合同。
- 每份 MOU 仅解锁约定的模型名;服务端会拒绝其他模型名的请求。
- 如端点需要轮换(例如评测窗口结束),新的 URL 会发送给同一份联系人列表。
2. 准备具有代表性的验证集
私有模型是识别 backbone,并不包含检测器。每次请求接受一张紧密裁剪的单人脸大头照,返回一个 embedding。请先用贵司检测器跑出框,再逐张发送裁剪结果。
但是服务端也会在大头照基础上进行二次检测与对齐,再提取 embedding,因此对小幅度的取景偏差是有一定容忍度的。
- 一张图一张人脸。源图含多人时需先把每张脸切成独立文件再发送。
- 按 MOU 数据条款,记录所用图像的同意与合法依据。
3. 裁剪并编码每张人脸图像
在检测框周围加一点扩边再上传。0.2 左右的 expansion factor(左右上下各 10%)能保留发际线、下颌和耳朵等对识别有用的上下文,又不会变成大半身像。可选地把最长边限制在 256 像素左右,使上传更小。
务必使用 PNG 等无损格式发送。JPEG、低质量 WebP 等有损格式会在眼睛和嘴部周围引入压缩噪声,对人脸识别相似度有可测量的负面影响。下面的 helper 仅作起点,请按贵司检测器的框约定进行调整。
- 在检测框外加扩边(expansion factor);紧贴框的裁剪会拉低准确率。
- 上传前务必保存为 PNG。在调用 API 前再次 JPEG 编码是异常低分最常见的原因。
import cv2
def crop_face_with_expansion(image, bbox, expansion_factor=0.2, max_side=256):
"""
Crop a single-person headshot with a small margin around the detected
face box, and optionally downscale so the longest side is <= max_side.
image: input image as a numpy array (BGR or RGB).
bbox: face bounding box [x, y, width, height] from your
detector of choice (RetinaFace, SCRFD, etc.).
expansion_factor: total margin ratio (0.2 means +10% on each side).
max_side: if set, the longest side of the result is bounded.
"""
img_h, img_w = image.shape[:2]
x, y, w, h = bbox
# 1. Margin distributed evenly to both sides.
dw = int(w * expansion_factor / 2)
dh = int(h * expansion_factor / 2)
# 2. Expanded box, clamped to image bounds.
x1 = max(0, x - dw)
y1 = max(0, y - dh)
x2 = min(img_w, x + w + dw)
y2 = min(img_h, y + h + dh)
face_img = image[y1:y2, x1:x2]
# 3. Optional bounded downscale with a high-quality filter.
if max_side is not None:
curr_h, curr_w = face_img.shape[:2]
longest = max(curr_h, curr_w)
if longest > max_side:
scale = max_side / longest
new_w = int(curr_w * scale)
new_h = int(curr_h * scale)
face_img = cv2.resize(
face_img, (new_w, new_h), interpolation=cv2.INTER_AREA,
)
return face_img
# Save the crop with a lossless format (PNG) before sending it to the API.
# Lossy formats (JPEG, WebP at low quality) introduce compression artifacts
# that can measurably reduce face recognition accuracy.
# cv2.imwrite("probe.png", crop)
4. 调用特征提取 API
API 接受 multipart/form-data POST:image 字段为单张图片,model_name 为字符串。返回 JSON 中包含 model_name 和 feature(该人脸的 L2 正规化 embedding)。建议把每个 embedding 保存为 numpy 的 .npy 文件,以便离线计算 cosine similarity。
下方示例客户端与生产协议完全一致,可以直接运行,也可以将 extract_feature 集成到贵司评测流水线中。请通过 --server-url 传入邮件中下发的端点 URL,不要硬编码到将提交至共享仓库的脚本里。
- model_name 必须为 MOU 中列出的值,未列出的名字会被服务端拒绝。
- feature 为空数组表示裁剪图中未检测到人脸,请检查框与扩边是否真正包含人脸。
- 用 L2 正规化向量上的 cosine similarity 进行比较,客户端无需再做归一化。
"""Example client for the private InsightFace feature extraction service.
Uploads a single-person face image to the test endpoint, reads the returned
embedding, and saves it locally with np.save for offline evaluation.
"""
import argparse
import os.path as osp
import numpy as np
import requests
def extract_feature(image_path, output_path, url, model_name, timeout=120.0):
filename = osp.basename(image_path)
data = {"model_name": model_name}
with open(image_path, "rb") as infile:
files = {
"image": (filename, infile, "application/octet-stream"),
}
try:
resp = requests.post(
url=url, files=files, data=data, timeout=timeout,
)
resp.raise_for_status()
except requests.RequestException as ex:
raise SystemExit(
"request failed: %s: %s" % (type(ex).__name__, str(ex))
)
payload = resp.json()
feature = payload.get("feature")
if not isinstance(feature, list):
raise SystemExit("server response feature must be a list")
if len(feature) == 0:
raise SystemExit(
"server returned no features; verify the image contains a "
"detectable face"
)
feat = np.asarray(feature, dtype=np.float32)
np.save(output_path, feat)
print("saved feature:", output_path, feat.shape, payload.get("model_name"))
return feat
def main():
parser = argparse.ArgumentParser(
description=(
"Upload a face image to the private InsightFace test server and "
"save the embedding with np.save."
),
)
# The --server-url value is delivered to evaluators by email.
parser.add_argument(
"--server-url", required=True,
help="Feature extraction API URL (sent privately by InsightFace)",
)
# The set of choices below is illustrative; the models actually unlocked
# for your account are listed in your MOU and may evolve over time.
parser.add_argument(
"--model-name", required=True,
choices=["buffalo", "dingo", "eagle"],
help="Recognition model to evaluate (must match an entry in your MOU)",
)
parser.add_argument("--image", required=True, help="Input face image path (PNG recommended)")
parser.add_argument("--output", required=True, help="Output .npy file path")
parser.add_argument("--timeout", type=float, default=120.0, help="HTTP timeout in seconds")
args = parser.parse_args()
extract_feature(
args.image, args.output, args.server_url, args.model_name,
timeout=args.timeout,
)
if __name__ == "__main__":
main()
5. 隐私与数据处理
上传的图像仅在请求处理期间用于计算并返回 embedding。响应发出后不保留原图,且评测流量不会用于任何模型的训练或微调。
- 不得向 MOU 列表之外共享端点 URL、模型名或返回分数。
- 如怀疑端点或凭据泄露,须在 24 小时内通知 InsightFace 联系人。
6. 评测范围与后续步骤
免费评测窗口自端点下发之日起最长 2 周。窗口内可在速率限制范围内随意调用,建议在申请端点前就把测试集、预处理与指标定义准备好,把窗口时间用在出数据上。
在测试结束后,请尽快评估结果并给出商业合作的结论。