Changing a Face Recognition Model Also Changes Your Threshold A developer's follow-up to a six-model face recognition comparison shows that swapping recognizers in UniFace — which exposes AdaFace, ArcFace, EdgeFace, MobileFace and SphereFace behind a common interface — changes the meaning of stored embeddings and similarity scores even when the Python code is unchanged. The writeup argues that replacing a recognizer requires regenerating gallery embeddings and rebuilding the search index, and that a similarity score is a measurement rather than a match probability, with deployment thresholds needing a larger labeled evaluation than the ten pairs used in the original test. Replacing a face recognition model can leave the Python code almost unchanged while changing the meaning of every stored embedding and similarity score. UniFace exposes AdaFace, ArcFace, EdgeFace, MobileFace, and SphereFace through a common recognizer interface. That makes it convenient to compare implementations. It does not make their embedding spaces or decision thresholds interchangeable. This article develops that distinction from my original recognition model comparison https://yakhyo.github.io/blog/2026/09/face-recognition-adaface-arcface-edgeface/ . The focus here is what needs to change in a pipeline when the recognizer changes. The recognition path starts with an image and five alignment landmarks. The landmarks define a standardized face crop, and the recognizer maps that crop to an embedding. UniFace can return an L2-normalized vector for cosine similarity. Two models can both produce 512 values without assigning the same meaning to those coordinates. Comparing an AdaFace vector with an ArcFace vector is not a supported identity comparison. Even switching weights within a model family should be treated as a representation change unless compatibility has been established. For a stored gallery, replacing the recognizer therefore requires regenerating its embeddings and rebuilding the corresponding search index. A migration that changes only the model constructor can produce plausible-looking scores from incompatible vectors. The following example uses an explicit ArcFace variant and requires one face in each input. It prints a similarity score without attaching an uncalibrated match label. Install the CPU runtime with pip install "uniface cpu " , then supply two portrait images: python import cv2 from uniface import ArcFace, RetinaFace, compute similarity from uniface.constants import ArcFaceWeights detector = RetinaFace recognizer = ArcFace model name=ArcFaceWeights.MNET def embed path : image = cv2.imread path if image is None: raise FileNotFoundError f'Could not read {path}' faces = detector.detect image if len faces = 1: raise ValueError f'Expected one face in {path}, found {len faces }' return recognizer.get normalized embedding image, faces 0 .landmarks score = compute similarity embed 'portrait a.jpg' , embed 'portrait b.jpg' , normalized=True, print f'Cosine similarity: {score:.3f}' The score is a similarity measurement, not the probability that the images show the same person. A decision threshold is a separate part of the application. Selecting the highest-confidence detection would not resolve an ambiguous multi-person image: detector confidence measures detection confidence, not which subject the caller intended. Requiring exactly one face makes this example's assumption explicit. The original post compared six model configurations on five archival portraits, producing two same-person pairs and eight different-person pairs. That exercise showed how the scores changed between models while the input images stayed fixed. It did not establish a general accuracy ranking, a false-match rate for deployment, or a recommendation for a particular device. Ten pairs are too few to characterize errors across lighting, pose, age, camera quality, and population differences. The illustration above comes from a separate demo run and should not be treated as the numerical record of the comparison. A small test can check that a pipeline runs and reveal obvious score changes. Choosing a deployment threshold requires a larger, representative evaluation. Collect labeled same-person and different-person examples that reflect the intended inputs. Separate threshold selection from final evaluation so the reported result is not simply a fit to the calibration examples. For a rule that accepts a pair when its score is at least a threshold, increasing that threshold generally reduces false matches while increasing missed matches. Evaluate both errors and choose an operating point based on the application's requirements. A value copied from a demo or another model has no such justification. The detector and preprocessing belong in that evaluation. Different landmarks change alignment; crop geometry, color conventions, and image quality can also affect embeddings. Keep the detector, alignment method, recognizer weights, and preprocessing fixed when comparing scores. For one-to-many search, evaluate the search workflow with a gallery of representative size as well. A pairwise threshold study alone does not describe the chance of an incorrect identification across many candidates. Record the recognizer variant, weight checksum, and preprocessing configuration alongside the embedding collection. Build the replacement gallery separately, recalibrate its decision rule, and validate it before switching the application to the new index. Runtime measurements need the same care. Weight-file size describes storage, not latency. Measure preprocessing, alignment, inference, and search on the intended hardware and execution provider. The laptop timings in the original post are observations from that setup, not performance promises for another device. A shared API reduces the code needed to compare recognizers. The data migration and evaluation remain part of the change. See UniFace's recognition documentation https://yakhyo.github.io/uniface/modules/recognition/ , threshold calibration guide https://yakhyo.github.io/uniface/concepts/thresholds-calibration/ , and model licence terms https://yakhyo.github.io/uniface/license-attribution/ before selecting a configuration for deployment.