读见

你的量化验收门缺少的那个数字

原题The Number Your Quantization Gate Doesn't Have

约 24 分钟约 5,795 字原文英文

读前速览

5 个要点

对十个量化验收门的审计发现,没有一个使用依赖阈值的指标;一项人脸验证实验表明,沿用继承的相似度阈值会导致 int8 量化后误接受率飙升 3.5 倍,而准确率和 rank-1 几乎不变。

  1. 量化验收门在结构上对依赖阈值的失败视而不见,因为它们完全依赖无阈值指标。

    在调查的十份指南中,每个验收门都使用准确率、top-1、top-5、输出方差、延迟或内存。没有一个报告在实际决策阈值下的 FAR 或 FRR,因此通过验收门的构建仍可能拥有严重失准的工作点。

  2. 在一项人脸验证实验中,int8 量化使继承阈值下的误接受率增至三倍,而排序指标几乎未动。

    在 τ=0.2592 时,误接受从每 10,000 对冒名对中的 30 例升至 104 例(FAR 0.0104 对 0.0030),误拒绝从每 10,000 对真实对中的 582 例升至 745 例。precision@10 和 rank-1 的变化小于 0.01,因此验收门本会放行该构建。

  3. 两种错误率同时恶化,意味着量化产生了不同的 DET 曲线,而非沿同一曲线移动。

    fp32 和 int8 的 DET 曲线在观测窗口内并不重合,因此没有任何单一阈值选择能恢复 fp32 的工作点。这推翻了「量化沿固定曲线以宽松换严格」的常见假设。

  4. 对敏感层进行选择性 16 位保护并未修复阈值偏移。

    保护嵌入头、头加批归一化、输入主干或首尾层,都使误接受保持在 102–104,与完全 int8 无法区分。该启发式方法解决的是准确率问题,而非余弦阈值所测量的尺度问题。

  5. 修复方案是将 τ 下的 FAR 和 FRR 加入验收门,在量化模型上重新推导 τ,并将 τ 版本化后随模型产物一同发布。

    在继承的 τ 下进行诊断读数可显示损害;阻断标准则在留出对上分别以各自重新推导的 τ 比较 fp32 和 int8。重新推导将误接受从 104 降至 44,但将误拒绝从 745 升至 959,因此修复是必要的,但并非没有代价。

完整摘要

文章审计了十份量化指南,发现每个验收门都由无阈值指标构成,如准确率、top-1、top-5、输出方差、延迟和内存。没有一个报告在实际决策阈值下的误接受率(FAR)或误拒绝率(FRR)。作者使用 Hailo 工具链将 ArcFace 人脸验证编码器量化为 int8,结果显示 precision@10 和 rank-1 几乎不变(0.978→0.972 和 0.990→0.985),但在继承的阈值 τ=0.2592 下,误接受从 10,000 对冒名对中的 30 例升至 104 例,误拒绝从 10,000 对真实对中的 582 例升至 745 例。两种错误率同时恶化,表明这是不同的 DET 曲线,而非沿同一曲线移动。作者认为这一失败是结构性的:阈值是拟合到 fp32 模型的常数,量化改变了分数分布,而验收门没有重新检查它。对首尾层进行选择性 16 位保护也无济于事。提出的修复方案是将 τ 下的 FAR 和 FRR 加入验收门,在量化模型上重新推导 τ,并将 τ 版本化后随模型产物一同发布。局限性已被详尽记录。

目录 · 24 节
Homage to Hamlet: an engineer asks a skull in the churchyard of the Elsinore castle whether he ought "to ship or not to ship" the quantized model build.

Somewhere in your quantization runbook there is one line that decides whether the int8 build ships. In the ten documents I surveyed, wherever that line is written down at all, it is a version of the same sentence. EdgeGate's gate table writes it as "INT8 accuracy ≥ FP32 accuracy − 1%". The oneuptime checklist reaches for QAT when PTQ "loses more than 1% accuracy". A practical PTQ guide says compare top-1 and top-5 against the FP32 baseline. One metric, one comparison, one verdict.

This article is an audit of that line, and it ends by asking you to add to it a false-accept and a false-reject rate, measured at the threshold your config file actually holds. Neither is in the line today. That absence is not an oversight.

All images, unless otherwise noted, are by the author.

Table of contents

  1. An audit in the form of a count
  2. The seam this article is about
  3. What the gate sees
  4. What it cannot see
  5. Looking harder in the wrong places
  6. Where else the seam runs
  7. Adding FAR and FRR at τ to the gate
  8. Back to the count
  9. Appendix 1: methods
  10. Appendix 2: limitations
    1. The absolute error rates are the weakest numbers in the piece.
    2. The target itself is my choice.
    3. The threshold shift is an illustration, not evidence.
    4. One drop of one model on one dataset with one toolchain.
    5. LFW is a saturated benchmark, and that cuts both ways.
    6. The calibration data was friendly.
    7. The green panels come from a different protocol.
    8. The 16-bit null is a null about this drop.
    9. The repair is measured, and it is neither sufficient nor free.
    10. The cost of the repair sits on the other side of the decision.
    11. The 377 extra rejections split, and the split is the price of the repair.
  11. Appendix 3: guide survey (acceptance gate and "threshold")
  12. References

Learn this step by step with the interactive Machine Learning roadmap.

An audit in the form of a count

Go through your acceptance gate and tally the numbers whose value depends on where your decision threshold sits. In the ten documents I surveyed, the answer is zero — every gate is built from accuracy, top-1, top-5, output variance, latency, memory.

The ten are not ten gates, and the split matters. Two of them name a numeric bar — EdgeGate's "INT8 accuracy ≥ FP32 − 1%" and oneuptime's 1% trigger for switching to QAT — and both bars are an accuracy delta. Five more tell you to compare accuracy against fp32 without naming a number. The last three are the remediation path you follow when that comparison comes out badly. Three genres, one count: zero. The number that decides is an accuracy number, the number you reach for when it fails is an accuracy number, and nowhere along that chain does a threshold-dependent one appear.

That zero is not an accident of which documents I picked. Those metrics grade the model, and the thing that decides an accept from a reject is not the model: it is a constant wrapped around it — a similarity threshold, a confidence cutoff, a reject gate — fitted against the fp32 scores. Quantization is not a neutral export; it changes how scores are produced. A gate made entirely of threshold-free numbers cannot ask whether that constant still lands where you put it, no matter how many such numbers you add.

The claim, then, is not that the industry's gate is wrong. It is that the gate is structurally incapable of seeing a specific, measurable failure, and that seeing it costs two numbers and an afternoon of measurement. The rest of this piece is the evidence: that the failure is real, that nothing already in the gate would have caught it, and that the repair is affordable but not free.

The seam this article is about

I measured that failure in one place — face verification, a pretrained ArcFace encoder, an int8 drop built through a Hailo toolchain — because that is where I could get real numbers rather than a slide-deck claim. The size of the effect below belongs to that drop and travels no further; the limitations appendix is the full list of what this measurement does not establish. The count does travel: it is a property of what the gate is made of, not of my encoder. Where else the seam runs — closed-set classification, detection, plain retrieval — is a map at the end of this piece, after the numbers have earned it.

A hole this cheap to close is worth one question: why is it still open? Not because the underlying fact is obscure. That a threshold belongs to a specific model on specific data, and dies when either changes, is a standard playbook item in RAG and vector search (Threshold by feel; the RAG threshold pinned to an absolute score), where teams pin cutoffs to a model revision and re-derive them in CI. In biometrics it is foundational, since false-match and false-non-match rates are defined against a threshold. Both halves of the knowledge exist and are written down.

What does not exist is the overlap. Read what you are actually handed while shipping an int8 build — the compiler user guide, the vendor's accuracy-debugging page, the edge deployment checklist — and "threshold" appears as an NMS parameter, a compiler split, or harness consistency, never as "re-derive the operating point after quantization" (see the survey table below).

Some teams close that gap anyway: an organisation under biometric certification re-derives its operating point on a schedule of its own, and this article is not addressed to them. It is addressed to everyone whose acceptance criterion comes from the toolchain documentation — where the constant that turns a score into a decision lives in a config file the build process never produces and the gate never reads. Nobody decides to ship an inherited threshold; the threshold is simply not one of the things a build is understood to produce. In that setting the people who own the threshold are not in the room when the build is accepted, and the document that accepts the build has no line for them. That is the seam this article is about, and it is why the count came out at zero.

A flowchart: a quantized build is evaluated on the reference set, then a gate asks whether every metric is within the toolchain guide's tolerance and ships or rejects the build. The checklist the gate reads - accuracy, top-1, precision@k, latency, memory - enters from one side. From the other, on a dashed line the gate does not consult, enter the constant from the config file and the false accepts and false rejects at it.

What the gate sees

So: the table the gate reads. Same encoder in fp32 and int8, the same evaluation photographs, embeddings and metrics side by side. The drop looks almost free:

  • precision@10: 0.978 → 0.972

  • rank-1: 0.990 → 0.985

If you stopped here — and the gate says you may — you would ship.

Now notice what those two numbers have in common. Precision@10 and rank-1 both ask who came back first, not how high the score was. They are threshold-free by construction: whatever happened to the scale the scores live on, these are the last metrics it would show up in — and they are what the acceptance gate is made of. The same pattern — a large share of individual predictions changing while top-1 barely moves — is documented for quantized image classifiers [1]; it is not a quirk of this drop.

What it cannot see

Here are the two numbers from the opening — the ones that never made it onto the dashboard. Fix the threshold — leave in the config exactly the number fp32 was calibrated to on one identity pool — and score held-out pairs from a disjoint identity pool. Already at fp32 the false-accept count is 30 out of 10 000 impostor pairs. Apply the same inherited cutoff to the int8 drop and the count jumps to 104 (FAR 0.0104): about 3.5× the held-out fp32 rate, FAR delta +0.0074 on the same impostor pairs (76 reject→accept flips against 2 the other way). False rejects move with them: 582 → 745 out of 10 000 genuine pairs (FRR 0.0745).

And notice what did not happen. You did not trade strictness for laxity. Both error rates got worse at once. That is not one operating point sliding along the same curve — it is a different curve.

Two DET curves on log-log axes, false accept rate against false reject rate. The blue fp32 curve runs below the orange int8 curve across the whole window; they never meet. A marker on each curve shows where the same inherited threshold lands: on fp32 at 30 false accepts and 582 false rejects, on int8 at 104 and 745. A dashed hairline joins the two markers.

One inherited threshold, two curves. DET for the fp32 reference and the full-int8 build on the same held-out TEST pairs, with the inherited τ = 0.2592 marked on each. The curves do not coincide, and within the window shown no choice of threshold puts the int8 build back on the fp32 one. Rates on one fixed sample of 31 identities, not absolute field FAR/FRR; the figure's footnote carries the denominators and the window's left edge.

Looking harder in the wrong places

The obvious defence is that I measured it wrong. I scored at a fixed cutoff — the literal number derived on fp32 DEV impostors and then held on TEST. That is not how the field reports it. The convention is TAR at FAR = 1e-4, a number that quietly re-derives the cutoff on the evaluation set before printing the result. NIST FRVT Part 3 (NISTIR 8280) [2] is blunt about what that convention hides: most academic studies, it says, report false negative rates "at fixed false positive rates rather than at fixed thresholds, thereby hiding excursions in false positive rates and misstating false negative rates." NIST is tracking excursions across demographic groups rather than across builds, so the transposition is mine — but the premise it rests on is exactly mine too: "the vast majority of biometric systems are configured with a fixed threshold against which all comparisons are made." A deployed system re-derives nothing. It holds a constant in a config file until a human edits it. Report this drop the standard way and the failure leaves the table — the convention hides the same failure the gate does. That is the reason for the protocol, not a flaw in it.

The second defence is that I did not look at enough panels. I did look. A fifth of the neighbour list turned over (recall@10 against fp32 is 0.81) while the gate's number moved by six tenths of a point — the ranking table survived because the swapped photographs are usually the same person. The geometry checks stayed flat: intrinsic dimensionality 269 → 271, contrast 0.596 → 0.567. Not one of those numbers is a function of where the cutoff sits. Nor would a wider gate have rescued the situation. The two items in my survey table that compare int8 outputs against fp32 directly — EdgeGate's output variance, PyTorch's per-layer SQNR — could well have registered that something moved. Neither can say which side of the cutoff it moved to, or how far the constant now sits from the rate it was fitted for. That is what threshold-free means, and it is the whole content of the count. Four green panels on one drop — two ranking numbers, recall against fp32, the geometry checks — and the only red one was the panel nobody was watching.

The reflex, once you accept those numbers, is to reach for the compiler. The knob that looks made for this is protecting the first and last layers with extra bits — a heuristic my vendor repeats, and which traces back to a single related-work line in NVIDIA's integer-quantization paper reporting that other people do it [3] at sub-8-bit widths. That paper's own int8 recommendation is the opposite: quantize everything, then find the sensitive layers by measuring them. I ran it at four scopes — the embedding head (fc1), the head plus its preceding batch-norm, the input stem (conv1) alone, and first-and-last together — and under the same held-out protocol all four left the false-accept count at 102–104 against fp32's 30, indistinguishable from full int8 (a null about this drop, not about selective 16-bit in general — see the limitations appendix). Which should have been the tell. The heuristic never promised anything about the scale a cosine threshold is measured on: every claim behind it, thinly sourced or not, is a claim about accuracy. I reached for an accuracy tool because those are the tools lying around — the same reflex that put an accuracy number in the gate.

Where else the seam runs

Faces were the case I could measure. The magnitude is theirs alone, but the seam is structural: wherever an encoder emits a continuous score and something downstream cuts it into a decision, that cut is a constant fitted to one version of the encoder, and nothing in the gate re-checks it. Vehicle gallery matching is nearly isomorphic — same cosine, same accept-or-reject. Closed-set classifiers have no similarity cutoff, but they have a confidence at which a human gets pulled in, and confidence calibration is what quantization has been shown to move, in quantized classifiers [1] and in VLMs [4] — in either direction, depending on whether the base model was under- or overconfident to begin with. That the move can be an improvement is not a reprieve: a cutoff fitted to the old scale is wrong either way, and the accuracy panel will not tell you which way it went. Detectors have a box-score threshold with a precision-recall curve behind it. Whether any of these moves as far as mine did is an open question in each case — worth a measurement, not an assumption. The one place this article does not reach is pure retrieval with nothing to accept or reject: there the pain is reshuffled neighbours, and no threshold dies because there was never one to die.

Which brings us back to the count. Every panel that came out green in this experiment was threshold-free, and every panel that would have caught the failure was absent from the gate — the same absence the ten documents share. Adding a fifth green panel would not have helped, and neither would the compiler. The only thing that helps is putting FAR and FRR at the threshold τ into the gate. Here is what it takes to read them honestly.

Adding FAR and FRR at τ to the gate

Four lines: one names the operating point, two read the pair — once at the threshold you inherited, once at the threshold the new build earns — and one ships the result. Only the second reading blocks. None of it replaces anything in your existing gate; it sits next to the accuracy delta.

  1. Name the operating point the build is promising — a target false-accept rate, a confidence at which a human is pulled in, whatever your decision rule is actually for. Derive it from what each error direction costs; mine is FAR = 1e-3.

  2. Report FAR and FRR at the inherited τ — as a diagnostic, not a blocker. This is the pair the count came up short by: it shows 104 false accepts against fp32's 30, and 745 false rejects against 582, on a build the accuracy delta waves through. Read both halves — NIST demands the same [2], for the same reason: systems run at a fixed threshold, so both rates must be reported at that threshold. "This is rarely done — most reports are concerned only with false negatives." Do not block on it, though. The old constant is not obliged to survive a new build, and blocking on its death forbids quantization by definition.

  3. Re-derive τ on the quantized model, then gate on FAR and FRR at the new τ — this one blocks. That is the promise the build is actually making, and the criterion is a comparison, not an absolute: fp32 standing at its own τ against the quantized build standing at its own τ, on the same held-out pairs, and the build blocks when either rate has moved further than the cost of that error direction allows — the same reckoning that produced step 1's target. An absolute rate cannot be the criterion on a pair budget like mine; the methods appendix says why. Both halves again, and here they move in opposite directions: re-deriving cut my false accepts 104 → 44 while raising my false rejects 745 → 959. Standing each build at its own τ, that is 30 against 44 on one side (thin evidence) and 582 against 959 on the other, a far larger margin. The repair is necessary, not sufficient, and not free; the limitations appendix carries the evidence under each of those three, including how much of the second margin rests on one identity.

  4. Ship τ inside the model artifact, versioned with it — never as a global constant in a config file that outlives the model it was fitted to. "Recalibrate after quantization" is too narrow a rule to survive your next pipeline change. The one worth keeping is that a threshold is an output of a specific build. Any process that produces a new build without producing a new threshold is shipping a number that refers to a model which no longer exists.

Two bar panels side by side, one per error direction, three columns each: fp32 at the inherited threshold, int8 at the inherited threshold, int8 at its own re-derived threshold. Columns are coloured by build, blue for fp32 and orange for int8, the same key as the DET figure. False accepts go 30, 104, 44; false rejects go 582, 745, 959. Arrows between adjacent columns carry the signed change. The panels are on different scales, one ticked to 100 and the other to 1000.

Two error directions across three operating points. Held-out TEST counts on 10 000 genuine and 10 000 impostor pairs from an identity-disjoint seed-0 split. Under the inherited cutoff both directions move the wrong way at once; re-deriving the cutoff on the int8 build's own DEV impostors buys back 60 false accepts by spending 214 further false rejects — the prescribed repair trades one direction for the other, it does not restore the fp32 operating point. The panels are on different scales. Counts on one fixed pair sample of 31 identities, not absolute field FAR/FRR.

Two further moves belong to other people's playbooks rather than my measurement: calibrating scores to probabilities so a cutoff degrades gracefully across model swaps, and keeping thresholds in a checked-in file pinned to a model revision with an alarm on gate-rate drift — standard practice in the retrieval playbooks cited earlier. And if you are measuring FAR and FRR for the first time, the methods appendix carries the three traps that cost me the most: identity-disjoint splits, a pair budget counted in identities rather than pairs, and paired tests instead of differences of absolute rates.

The one part nobody handed me is the anti-checklist — what does not count as evidence the decision rule survived:

  • TAR at a fixed FAR, taken from a report. The number re-derives the threshold before printing it; a deployed system does not (see NIST above).

  • Top-1, rank-1, precision@k. Threshold-free by construction. Wherever the cutoff went, these do not look there.

  • Intrinsic dimensionality, contrast, or any global geometry check. Threshold-free too, which is why all of mine stayed green.

  • "We re-derived τ on DEV." The act is not the result. The result is FAR and FRR on held-out pairs at that τ.

  • "FAR dropped after we re-derived τ." Against the inherited number, yes. The same act raised false rejects. Check both sides.

  • "We hit 1e-3." Unanswerable on 31 identities — see the pair budget in the methods appendix. Report counts and ratios instead.

Back to the count

Ten documents, one count: not one number in any of their gates depends on where the decision threshold sits. Adding more accuracy panels does not change that, and neither does reaching for the compiler. What changes it is the pair the gate is missing — false accepts and false rejects at the threshold your config actually holds, read once at the τ you inherited and once at the τ the new build earns. The line that decides whether the int8 build ships stays one line. It just has to carry two numbers an accuracy delta cannot stand in for.

Appendix 1: methods

The body numbers come from one encoder, one dataset split, and an identity-disjoint pair protocol. Dataset: LFW via fetch_lfw_people(min_faces_per_person=20, color=True) — 3023 faces, 62 identities (verified against that call). A seed-0 permutation holds out 1024 images as the quantization calibration pool; the remaining 1999 are evaluation. Model: pretrained ArcFace w600k_r50.onnx, embeddings L2-normalised. Toolchain: Hailo DFC, in-net normalization1, calibset_size=256; the int8 figures are branch A (full int8, no precision-mode overrides). Selective 16-bit scopes: B = fc1; C = fc1+batch_norm25; D = conv1; E = conv1+fc1.

Operating point (held-out): identity-disjoint seed-0 split of the 62 evaluation identities — 31 identities / 806 faces DEV, 31 / 1193 TEST; 10 000 genuine and 10 000 impostor pairs drawn independently in each half. Threshold τ = 0.259203 is the cutoff that leaves exactly 10 of the 10 000 fp32 DEV impostor pairs above it — the 1−1e-3 quantile of those scores, which is a rank on DEV and not a rate this data could verify. FAR/FRR and raw counts are reported on TEST, whose identities DEV never saw. Paired discordance is read off the same TEST impostor pairs across sets. Per-set DEV τ is reported only as an illustration next to a resampling baseline (±0.0167 at 95% half-width on the same pair budget); it is not the primary drift claim.

Three traps sit between "I measured FAR" and "I measured FAR credibly", and they are the ones that cost me the most rework. Split by identity, not by image: a threshold fitted and scored on the same pairs is an in-sample number, and mine moved by ±0.017 under nothing but resampling. Count your pair budget in identities: with K identities there are C(K,2) distinct couples, so even a run with zero false accepts supports only a 95% upper bound of 3/C(K,2) — my 31 identities cap that at 0.0065, which is why no absolute FAR claim in this article stands on its own. Claim gaps with a paired test on the same pairs — discordant counts, χ², and, where pairs are independent enough to carry one, a bootstrap interval on the delta — never as the difference of two absolute rates. That interval is the one instrument here I do not use: resampling pairs treats 10 000 draws from 31 identities as 10 000 observations, and would report a precision this budget has not got. The counts and χ² beside it are untouched by the same clustering, because they claim nothing beyond the fixed pair set they were measured on.

χ² here is McNemar's: it is read off the discordant pairs alone — the ones where the two builds disagree, since a pair both builds call the same way says nothing about the difference between them — against a null in which a flip is equally likely either way, so the expectation is an even split and the continuity-corrected statistic is roughly the squared number of standard errors from it. The false-accept result's 76-against-2 sits about eight of those out. That third trap is what separates my two surviving results: the residual FAR gap after recalibration rests on 32 discordant pairs (23/9, χ² 5.3, a bit over two standard errors) — a margin of 14 pairs out of 10 000, which seven flipping back would erase — while the FRR cost rests on 415 (396/19, χ² ≈ 341), a margin of 377. Same bar, nothing like the same footing.

Green ranking / geometry figures (precision@10, rank-1, intrinsic dim, contrast, recall@10 vs fp32) still come from the earlier single-draw protocol on the full evaluation set; they are threshold-free by construction and are not mixed into the false-accept result.

The code behind every number above is the companion repository, maxeffectus/quantization-threshold-drift, MIT-licensed. The three held-out operating points can be rechecked without a Hailo licence: the derived embeddings ship as a checksummed Release asset, and check_eval_release.py verifies the SHA-256 of each array before it reprints the counts this appendix reports; flip_identity_breakdown.py reproduces the genuine-pair flips identity by identity, and refuses to print a breakdown that does not come to 396 against 19. What does not ship is the face crops, which the repository does not redistribute, or the int8 rebuild, which needs the Hailo Dataflow Compiler. The released embeddings are not MIT either — they are biometric templates derived from the encoder weights, and inherit those weights' non-commercial research terms. The repository's README states the protocol above and draws those lines in full; this appendix does not restate them.

Appendix 2: limitations

What follows is the list of things this measurement does not establish. It is longer than the list of things it does, which is the normal ratio for a single-drop experiment, and most of the entries bound the numbers in the body rather than threaten them.

The absolute error rates are the weakest numbers in the piece.

Ten thousand impostor pairs drawn from 31 identities are not ten thousand independent observations: pairs sharing an identity share its idiosyncrasies, so the effective sample is closer to the identity count than to the pair count. The consequence is concrete. With 31 identities there are 465 distinct identity couples, so an operating point that produced zero false accepts across all of them would still only support a 95% upper bound of 3/465 ≈ 0.0065 — above the 1e-3 the cutoff was fitted to. Any sentence of the form "this build misses 1e-3 in absolute terms" is therefore unsupported on this dataset, mine included. What survives is what is conditioned on the fixed pair set: the raw counts, the relative ratios, and the paired discordance (76 pairs flipping reject→accept against 2 the other way). Read the false-accept result as "×3.5 on the same pairs", never as "FAR is 0.0104 in the field".

The target itself is my choice.

FAR = 1e-3 is hardcoded in my evaluation scripts because I had to pick something; it is not an industry standard, not a product SLA, and not a number anybody handed me. A real operating point is derived from the cost of each error direction. Everything in the body is a statement about how a cutoff fitted to one target behaves under quantization, not an argument for that target.

The threshold shift is an illustration, not evidence.

Resampling the evaluation pairs moves the fp32 threshold by ±0.017 at 95% half-width. The cosine gap that would restore the fitted FAR on the int8 DEV pool is about +0.025 — barely 1.5× that noise (the in-sample shift of +0.032 I measured earlier fares no better, at ~1.9×). Under my own bar that is a flag, not a finding, which is why it does not appear in the body. The drift claim rests on held-out false-accept counts, and nothing else.

One drop of one model on one dataset with one toolchain.

No repeated identity splits, no second encoder, no second compiler, no second task. Both halves of the operating-point protocol come from a single seed-0 identity split; an identity-level bootstrap over splits is what would put a defensible interval on any of these numbers, and it is not something I ran. The map of neighbouring domains near the end of the article is explicitly a set of hypotheses, and the magnitudes in the body should not be carried into any of them.

LFW is a saturated benchmark, and that cuts both ways.

Anyone who works in face recognition will note that I measured on a dataset the field considers solved. The structural claim does not depend on the accuracy panels staying green — a threshold-free number tells you nothing about where the operating point went, whether it moves or not — but the demonstration does, and on a harder benchmark the accuracy delta might have been large enough to stop this build for the wrong reason. What saturation buys is the regime that matters: a verification encoder is chosen precisely because it sits near the ceiling on metrics of that kind, so "accuracy looks fine" is the normal reading there rather than a lucky one. What it does not buy is demographic coverage. The same NIST report I quote on fixed thresholds is the one that documents how far false-match rates vary across demographic groups; 31 LFW identities cannot speak to that, and nothing here claims the drift I measured is uniform across the people a deployed system would actually see.

The calibration data was friendly.

Those 1024 calibration images come from the same LFW pool as the evaluation images, so the int8 drop saw in-distribution data when the compiler set its activation ranges. Real pipelines calibrate on whatever images are convenient. My drift is a friendly case, not a worst case. Relatedly, how many images the compiler uses is a separate trap — error versus set size is U-shaped — and I fixed calibset_size rather than sweeping it. The failure mode here is a decision threshold left untuned, not an undersized calibration set.

The green panels come from a different protocol.

Precision@10, rank-1, recall@10 against fp32, intrinsic dimensionality and contrast were computed under the earlier single-draw protocol on the full evaluation set, before the identity-disjoint split existed. They are threshold-free by construction, which is exactly the point being made about them, so the looser protocol costs the argument nothing — but they are not drawn from the same pairs as the FAR figures and should not be arithmetically combined with them.

The 16-bit null is a null about this drop.

Four precision-mode scopes left the false-accept count where full int8 put it. That refutes "reach for the compiler knob and the operating point survives" on this encoder. It says nothing about whether selective 16-bit helps elsewhere, and answering that needs a factorial experiment I deliberately did not run.

The repair is measured, and it is neither sufficient nor free.

Re-deriving the cutoff on the quantized model — the fix the article prescribes — cuts false accepts roughly in half against the inherited number, so the compatibility gate really is blind to work the repair already does. But the residual gap to fp32 is still there: 44 against 30 on the same pairs, and while a paired test does resolve it, it barely does (χ² 5.3 against the false-accept result's 68). Treat the residual as detectable on thin evidence — an order weaker in support than the compatibility failure, which is a statement about how well the gap is established and not about how large it is. It is certainly not "half again worse".

The cost of the repair sits on the other side of the decision.

At the same promised operating point the int8 build rejects genuine pairs 1.6× more often (396 flips ok→error against 19 back, χ² ≈ 341), and that carries a warning for anyone who implements the fix — a gate watching only false accepts will call the recalibration a success and never notice that legitimate users started getting turned away. The identity clustering above applies to this side too, and more heavily: the identity contributing 79% of the genuine pairs holds 80% of the flips, so four fifths of that χ² is one person, and the count is a property of this pair set, not a field rate. It does survive the person, though. On the 2085 genuine pairs from the other 30 identities the flips run 79 against 11 (χ² ≈ 50): the same direction, still nowhere near an even split. Nor is the cost a handful of unlucky faces: the largest identity flips at 4.01% against a pooled 3.96%, exactly its share, and thirteen of the 31 test identities produced no flip at all, so the rate is not uniform either. What the breakdown rules out is the reading that one or two faces carry the cost.

The 377 extra rejections split, and the split is the price of the repair.

At the inherited τ the int8 build already rejected 745 genuine pairs against fp32's 582, so 163 of the extra rejections are the quantized scores alone, and the other 214 are what raising τ far enough to cut false accepts costs. Recalibration buys back 60 false accepts at that price.

Appendix 3: guide survey (acceptance gate and "threshold")

Bounded claim for the opener and the scoping paragraph: ten documents from nine sources, the ones a practitioner is likely to open while shipping an int8 drop — the Hailo DFC User Guide counted at 3.27 and at 3.30, because the gate line survived the version bump unchanged, plus the eight further sources below. That is one shelf, and deliberately so: toolchain and edge-deployment documentation. Product-QA manuals and biometric certification protocols are a different shelf, not sampled here, which is the same distinction the body draws when it says who the article is not addressed to.

Within the shelf the ten split three ways. Two name a numeric accuracy bar (EdgeGate, oneuptime). Five say to compare accuracy against fp32 without naming a number (both DFC sections, the Hailo accuracy-degradation thread, TensorRT, the PTQ guide). Three are remediation only (Hailo Quantization Scheme, the YOLO recall thread, PyTorch accuracy debugging). None of the three genres treats re-deriving a verification operating point as part of the quantization acceptance gate. If a counterexample appears, it belongs in this table — it would sharpen the piece, not collapse it.

Source

Stated acceptance / quality gate

Where "threshold" (or equivalent) appears

Hailo DFC User Guide 3.27 and 3.30

Accuracy of the quantized model vs the original; layer analysis and optimization levels as the remediation path

nms_score_threshold, split_threshold, dead-channel zeroing, compiler utilization — not operating-point recalibration

Hailo Quantization Scheme

Fix under-representation / degradation via 16-bit on sensitive layers, richer calibration, etc.

Layer precision advice; no verification τ

Hailo community: accuracy degradation

Compare original and compressed outputs to ground truth with task-specific postprocessing and metric

Metric left unspecified; silent on fixed-τ vs TAR@FAR

Hailo community: YOLO recall after HEF

Restore recall after compile

nms_score_threshold / iou_threshold as harness consistency with the PyTorch run — not drift of a deployed cutoff

PyTorch Quantization Accuracy Debugging

Restore accuracy after PTQ (SQNR / per-layer sensitivity)

No decision-rule / operating-point gate

TensorRT Working with Quantized Types

Correct quantized inference; accuracy vs FP32 as the practical check

No verification operating-point recalibration

EdgeGate: FP32 vs INT8 on real hardware

Explicit gate table: latency, memory, accuracy delta INT8 ≥ FP32 − 1%, output variance

"Threshold" = gate threshold on those metrics; not a similarity cutoff

oneuptime quantization checklist

Prefer QAT if PTQ loses more than ~1% accuracy

No operating-point recalibration step

Practical Guide to PTQ for Edge AI

Compare top-1 / top-5 to the FP32 baseline

No operating-point recalibration step

References

[1] G. Xia, S. Ha, T. Azevedo, P. Maji, An Underexplored Dilemma between Confidence and Calibration in Quantized Neural Networks (2021), arXiv:2111.08163

[2] P. Grother, M. Ngan, K. Hanaoka, Face Recognition Vendor Test (FRVT) Part 3: Demographic Effects (2019), NISTIR 8280, National Institute of Standards and Technology

[3] H. Wu, P. Judd, X. Zhang, M. Isaev, P. Micikevicius, Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation (2020), arXiv:2004.09602

[4] A. Bouguerra, D. Montoya, A. Gomez-Villa, C. Mraidha, F. Arnez, Less Precise Can Be More Reliable: A Systematic Evaluation of Quantization's Impact on VLMs Beyond Accuracy (2025), arXiv:2509.21173


Written By

Maxim Podstrechnyy

AI Video Engineer at Network Optix. I quantize, optimize, and deploy vision models, vision-language models, and others across CPUs, GPUs, NPUs, and edge devices. I am a member

值得记下

那个零并非我挑选文档的偶然。那些指标评估的是模型,而决定接受还是拒绝的东西不是模型:它是包裹在模型外面的一个常数。
你并没有以严格换宽松。两种错误率同时恶化了。这不是一个工作点沿同一曲线滑动——而是一条不同的曲线。
唯一有帮助的是把阈值 τ 下的 FAR 和 FRR 放进验收门。
修复是必要的,但不充分,也并非没有代价。