Spaces:
Paused
Paused
|
Download sensorflow/seqeval/DESIGN.md from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 25.8 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/seqeval/DESIGN.md
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/sensorflow/seqeval/DESIGN.md
-
curl -L -o DESIGN.md https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/sensorflow/seqeval/DESIGN.md
25.8 kB
| # seqeval — Efficient Sequential Regression Detection: Technical Design | |
| **Goal.** Detect small model regressions (~2 percentage points on specific | |
| classes/conditions) on every model update WITHOUT evaluating the full corpus, | |
| with statistical validity under (a) sequential looks, (b) clustered | |
| (non-independent) frames, (c) stratified non-proportional sampling, and | |
| (d) many simultaneous hypotheses. | |
| This document records the design *and the judgment calls*: which candidate | |
| techniques were adopted, which were rejected, and why. The reference | |
| implementation lives in this package and is exercised by `tests/test_seqeval/`. | |
| --- | |
| ## 1. Problem formulation | |
| For each metric m (recall is the driver metric in v1) and each population | |
| stratum h, let Δ_h = m_candidate(h) − m_baseline(h) measured on the same data. | |
| With a **practical-significance margin** δ (default 0.005 = 0.5pp): | |
| ``` | |
| H0 (no material regression): Δ_h ≥ −δ | |
| H1 (material regression): Δ_h < −δ | |
| ``` | |
| plus the separate equivalence-style claim used for PASS: reject `Δ_h ≤ −δ` | |
| in favor of `Δ_h > −δ`. | |
| **Absolute vs relative deltas.** The default driver is **absolute percentage | |
| points**, for three reasons: | |
| 1. Safety cost is absolute: a 2pp recall drop means the same number of extra | |
| missed pedestrians whether baseline recall was 0.98 or 0.80. Relative | |
| deltas overstate changes on strong baselines (0.98→0.96 is "only −2% | |
| relative") and understate them on weak ones. | |
| 2. Absolute deltas keep Bernoulli variance algebra and margin semantics simple | |
| and uniform across strata; relative margins make the null hypothesis depend | |
| on the (estimated) baseline value, contaminating the test with baseline | |
| estimation error. | |
| 3. Absolute margins are what promotion policies elsewhere in this platform | |
| already use (`megaeval.analysis.DEFAULT_PROMOTION_POLICY` is expressed in | |
| absolute drops), so the gate composes with existing policy language. | |
| Relative deltas are still *reported* in every evidence record (`rel_delta`) | |
| because they are useful for human interpretation, especially for rare classes | |
| with weak baselines. They are never the decision variable. | |
| **Practical vs statistical significance.** With ~320k objects, a 0.05pp drop | |
| can be "statistically significant" yet operationally meaningless. Hence the | |
| margin δ: nothing is declared REGRESSION unless the evidence shows the drop | |
| exceeds δ, and nothing is declared PASS unless the evidence shows any drop is | |
| smaller than δ. Statistical significance against a point null of exactly zero | |
| is deliberately not used anywhere. | |
| ## 2. The statistical unit: clusters, not frames | |
| Frames/objects are not independent: objects in one container (scene/track) | |
| share weather, lighting, scenario, scene-level hardness and — critically — | |
| correlated model failures (one bad scene produces many misses). Treating | |
| objects as iid understates variance by the design effect | |
| ``` | |
| DEFF = 1 + (m̄ − 1) · ICC (m̄ = size-weighted mean cluster size) | |
| ``` | |
| and every "significant" result computed that way is anti-conservative. | |
| Adopted design: | |
| * **Inference unit = cluster**: all sampled objects of a stratum in one | |
| container. Per-cluster mean paired differences (bounded in [−1, 1]) are the | |
| observations fed to the confidence sequences and e-processes. This is valid | |
| under *arbitrary* within-cluster correlation — no correlation model is | |
| assumed — and whole clusters are sampled so the unit is never split | |
| (`planner.py` selects container groups, not objects). | |
| * **Diagnostics, not corrections**: ICC (one-way ANOVA estimator), DEFF and | |
| n_eff = n/DEFF are computed per node (`units.py`) and attached to every | |
| evidence record. They are reporting artifacts; validity does not depend on | |
| them because clustering is handled structurally. | |
| * An interesting empirical note the machinery surfaces: pairing itself removes | |
| most cluster correlation (the shared scene effect hits both models and | |
| cancels in d_i), so ICC on paired differences is small (~0.07 in the worked | |
| example, DEFF ≈ 1.2) even when ICC on raw outcomes is large. We keep the | |
| cluster unit anyway — the residual correlation is real and free to handle. | |
| * **Clustered bootstrap** (resampling clusters, not objects) is the prescribed | |
| fallback for metrics that don't decompose into per-object outcomes (§3). It | |
| is not needed for the v1 recall driver. | |
| Rejected: variance-inflation-only corrections (divide n by an assumed DEFF≈2 | |
| and pretend units are iid). Rejected because ICC varies by stratum and model | |
| pair; a fixed inflation is either wasteful or invalid, and it composes poorly | |
| with anytime-valid methods whose guarantees are per-observation. | |
| ## 3. Paired evaluation as the core design | |
| Both models are evaluated on the **same sampled units** (`paired.py`), and all | |
| inference is on the per-unit difference d_i = c_i − b_i ∈ {−1, 0, +1}. | |
| Why this is the single biggest efficiency lever: for two independent samples, | |
| Var(m̂_c − m̂_b) ≈ 2·p(1−p)/n (~0.18/n at p=0.9). For paired evaluation, | |
| Var(d) = ν − Δ², where ν = P(models disagree) — the discordance rate. Adjacent | |
| model versions agree on almost everything (measured ν ≈ 0.03–0.06 in the | |
| harness), so pairing cuts required samples by roughly 3–10×. The paired | |
| design also removes the between-unit difficulty variance *and* the shared | |
| scene effects (§2). | |
| **McNemar correspondence.** For binary per-object outcomes the information in | |
| the paired design is exactly the discordant counts: n01 (baseline right, | |
| candidate wrong) vs n10 (the reverse) — McNemar's setup. We do not use the | |
| classical McNemar *test* (it is fixed-n); instead the same discordant-pair | |
| information drives (a) the sequential e-process on d_i and (b) the | |
| Beta-Binomial posterior of §14, which is literally a Bayesian McNemar. The | |
| counts n01/n10 are reported in attribution because they are the most | |
| interpretable summary of "what changed". | |
| **Which metrics get which treatment:** | |
| | Metric | Per-object outcome? | Sequential treatment | | |
| |---|---|---| | |
| | recall (per-GT-object TP/FN) | yes, Bernoulli | confidence sequence / e-process on paired d_i ✔ (v1 driver) | | |
| | precision (per-prediction) | yes, but the unit set differs per model (each model's own predictions); pair at the cluster level | CS on cluster-level paired precision differences | | |
| | safety_recall, per-stratum recall | yes | same as recall ✔ | | |
| | mean IoU (matched pairs) | bounded continuous per object | CS on paired IoU differences (empirical-Bernstein handles [0,1] naturally) | | |
| | AP / mAP / AUC | **no** — rank statistics over the whole set; not a mean of per-object terms | **paired clustered bootstrap** at checkpoints (resample clusters, recompute both models' AP on the same resample, CI on the difference). No anytime-valid guarantee: schedule at *pre-registered* checkpoints (end of each budget stage) with alpha-spending across the (few, fixed) looks | | |
| This split is a deliberate honesty boundary: Bernoulli-style anytime-valid | |
| treatment is used exactly where a metric is a mean of bounded per-unit terms; | |
| composite rank metrics get bootstrap at a small number of pre-committed looks | |
| rather than pretending they support continuous monitoring. | |
| ## 4. Sampling design: hybrid Neyman + risk floors + safety minimums | |
| Candidates considered: | |
| * **Proportional allocation** — matches the population, but starves exactly | |
| the strata we care about (pedestrian|night is ~5% of objects; a proportional | |
| 10k sample gives ~500 units, hopeless for 2pp detection). Rejected as the | |
| primary rule. | |
| * **Equal allocation** — maximal per-stratum power for fixed budget, but | |
| wastes budget on huge easy strata and makes population-level estimates | |
| needlessly noisy. Rejected. | |
| * **Risk-weighted only** (as in `megaeval.sampling`) — right instinct, no | |
| optimality story; can be arbitrarily far from variance-optimal. Rejected as | |
| the *sole* rule, kept as a multiplier. | |
| * **Neyman allocation** n_h ∝ N_h·σ_h — variance-optimal for the *population* | |
| estimate, but per-stratum guarantees are incidental, and σ_h must not come | |
| from candidate outcomes (bias firewall, §13). Adopted as the baseline using | |
| a candidate-independent PRIOR variance profile (documented rates by | |
| class/condition; a pilot baseline-only estimate would also qualify). | |
| **Adopted hybrid** (`planner.py`), in order: Neyman shares from prior σ_h → | |
| multiplicative risk weights (VRU ×1.5, night ×1.3) → mandatory minimum per | |
| stratum (200) → pre-registered safety-critical strata get a floor sized for | |
| the target MDE (2500 by default, §10) → truncate at N_h, select whole | |
| clusters in a seeded shuffled order. | |
| **Unbiasedness under oversampling.** Every sampled object carries the | |
| Horvitz–Thompson weight w_h = N_h/n_h. Population and class-level estimators | |
| are weight-corrected (weighted cluster means feed the overall/class/difficulty | |
| nodes), so the classic stratified identity `E[Σ_h W_h p̂_h] = p` holds and | |
| oversampling safety strata does not bias headline estimates. Per-stratum | |
| (leaf) inference is self-weighting and needs no correction. | |
| ## 5. Multi-stage pipeline | |
| ``` | |
| sanity (≈500 units) → stratified screening (~15% of plan) | |
| → sequential confirmation (batched to 100% of plan, anytime-valid) | |
| → targeted escalation (reserve clusters, suspect/safety strata only) | |
| ``` | |
| * **Sanity**: fingerprints resolved, frozen plan hashed, smoke batch checks | |
| for degenerate outputs (rate ∉ (0.2, 1], discordance ≥ 0.5 ⇒ abort — a | |
| broken export should fail in seconds, not consume the budget). | |
| * **Screening** flags "suspect" strata early (e-value ≥ 1.5 — soft evidence, | |
| *never* a decision) to prioritize escalation later. Screening consumes the | |
| same frozen plan; it does not select units based on outcomes (§13). | |
| * **Sequential confirmation** processes the remaining plan in batches; all | |
| decisions (REGRESSION / PASS) can fire at any batch boundary because the | |
| underlying processes are anytime-valid. | |
| * **Escalation** pulls from each stratum's pre-frozen reserve (shuffled at | |
| plan time) for strata still undecided that are suspect or safety-primary, | |
| up to a hard cap. Escalation to *full* evaluation of a stratum is the | |
| terminal step of the same mechanism (the reserve is the rest of the | |
| stratum). | |
| ## 6. Sequential methodology: anytime-valid confidence sequences, not CUSUM | |
| **Why repeated fixed-sample CIs are wrong.** A 95% CI computed at n has 5% | |
| miscoverage *at that single n*. Recompute after every batch and stop on the | |
| first exclusion of the null and the type-I error inflates monotonically — | |
| by the law of the iterated logarithm the statistic crosses any fixed-n | |
| boundary infinitely often under the null, so "peek until significant" has | |
| error probability → 1. Evaluation gates that check after every batch are | |
| exactly this setting. | |
| **Adopted: confidence sequences / e-processes** (`sequential.py`): | |
| * Empirical-Bernstein confidence sequence (Waudby-Smith & Ramdas 2023, | |
| predictable plug-in): a CS valid *simultaneously over all n*, so the gate | |
| may look after every batch and stop whenever it likes. Empirical-Bernstein | |
| rather than Hoeffding because paired differences have tiny variance | |
| (ν ≪ 1/4) and EB width scales with the realized variance; a Hoeffding CS | |
| would be ~3–4× wider here (Hoeffding rejected for power, kept as a mental | |
| baseline). | |
| * One-sided **betting e-processes** for the two directed nulls. The wealth | |
| process K_t = Π(1 − λ_t(X_t − m)) is a nonnegative supermartingale under | |
| H0: μ ≥ m for any predictable λ_t in the admissible range; Ville's | |
| inequality gives P(∃t: K_t ≥ 1/α) ≤ α. Bet sizing = max(empirical-Bernstein | |
| recipe, aGRAPA/approximate-Kelly λ ≈ drift/(σ̂² + drift²)), clipped — | |
| sizing affects power only, never validity. E-values (not just CIs) are kept | |
| because they compose: they feed e-BH in the hierarchy (§8) and multiply | |
| across independent looks/runs if evidence is ever pooled. | |
| * Batch-incremental updates, sticky decisions, per-batch trajectory recording | |
| (`log e vs n` with the 1/α boundary) for the dashboard. | |
| **Where CUSUM legitimately fits — and why not here.** CUSUM/Page tests are | |
| designed for *change-point detection in an ongoing stream*: an unknown time | |
| at which a monitored process shifts, with guarantees phrased as average run | |
| length (ARL) to false alarm and detection delay. That is the right tool for | |
| **production drift monitoring** (a deployed model's live metric degrading at | |
| an unknown time — a reasonable future addition next to | |
| `sensorflow/evaluation/regression.py`). It is the wrong tool for **gated A/B | |
| evaluation**, where there is no change-point: the candidate is a fixed, | |
| already-different model, the hypothesis is about a fixed mean difference, ARL | |
| semantics don't map to per-release type-I error, and CUSUM provides neither | |
| confidence intervals for effect size nor e-values for hierarchical multiple | |
| testing. CUSUM is therefore explicitly rejected for the gate. | |
| (Group-sequential designs — O'Brien-Fleming etc. — were also considered: | |
| valid, but they require pre-committing to a fixed number of looks and a | |
| maximum n, which fights the budget-staged, stop-any-time controller; the | |
| anytime-valid framework strictly dominates for this use.) | |
| ## 7. Mandatory three-outcome decisions | |
| Non-detection is NEVER equivalence. Each node ends in exactly one of: | |
| * **REGRESSION** — the regression e-process crossed 1/α_allocated: confirmed | |
| drop beyond the margin. Gate: block. | |
| * **PASS** — the equivalence e-process crossed 1/α_pass: confirmed that any | |
| drop is smaller than the margin (this is a one-sided equivalence / | |
| non-inferiority claim, the sequential analogue of a TOST bound). Gate: | |
| allow, but only when the overall node AND every pre-registered safety | |
| primary individually PASS. | |
| * **INSUFFICIENT_EVIDENCE** — budget exhausted first. Gate: expand within | |
| budget if possible, else report with explicit language: *"not proven | |
| equivalent — do not treat as a pass."* (`controller.INSUFFICIENT_LANGUAGE` | |
| is exactly that sentence; the tiny-budget test asserts it.) | |
| ## 8. Hierarchical multiple testing | |
| Testing ~28 nodes (overall, 6 classes, 18 class×condition strata, 3 difficulty | |
| bands) at α each would give a family error of ~1−0.95²⁸ ≈ 76%. Naive | |
| Bonferroni over everything destroys power for the one node that matters. | |
| Adopted structure (`hierarchy.py`): | |
| ``` | |
| Level 1 overall α·0.30 single test | |
| Safety pre-registered primaries α·0.30 split equally, tested individually | |
| Level 2 class α·0.15 e-BH within level | |
| Level 3 class × condition strata α·0.15 e-BH within level (non-safety) | |
| Level 4 difficulty bands α·0.10 e-BH within level | |
| ``` | |
| Budgets sum to α, so the union bound gives family-wise control across levels; | |
| within a level, **e-BH** (BH on e-values) controls FDR *under arbitrary | |
| dependence* — mandatory here because levels re-aggregate the same units and | |
| strata share scenes. e-BH composes with optional stopping because an | |
| e-process value at any stopping time is a valid e-value (this is the reason | |
| the implementation carries e-values and not just CSs). | |
| **Gatekeeping and the masking trap.** Classic top-down gatekeeping ("test | |
| children only where the parent rejected") was **rejected** for the regression | |
| direction: a 2pp pedestrian-night drop inside a globally *improved* model is | |
| invisible at level 1, and a gate that only descends on level-1 rejection would | |
| never see it. Instead every level has its own reserved budget, and | |
| **safety-critical strata are pre-registered primary hypotheses** with | |
| dedicated alpha, tested individually and exempt from e-BH masking by | |
| better-behaved siblings. The power test in the suite is exactly this case: | |
| overall metric up ~0.3pp, pedestrian-night down 2pp — and the stratum node | |
| must fire. Alpha propagation is top-down only in the *reporting* sense | |
| (attribution descends into difficulty bands of regressed strata); decisions | |
| never require a parent rejection. | |
| ## 9. Rare-class policy | |
| * **Minimum counts**: every non-empty stratum gets ≥ 200 sampled objects | |
| (or all of N_h if smaller) — below that, even 10pp effects are undetectable | |
| and the honest answer is INSUFFICIENT_EVIDENCE, which the three-outcome | |
| logic produces naturally. | |
| * **Oversampling with weight correction**: rare + risky strata get risk | |
| multipliers and floors (§4); HT weights keep population estimates unbiased. | |
| * **Per-class thresholds**: the margin δ and alpha allocation are policy | |
| fields; safety primaries effectively run at tighter per-node alpha with far | |
| larger minimum samples. (A per-stratum δ_h map is a straightforward policy | |
| extension; v1 keeps a single δ for legibility.) | |
| * **Escalation**: rare strata that end INSUFFICIENT but suspect consume their | |
| reserve up to the whole stratum. If a stratum is exhausted and still | |
| undecided, the ledger says so explicitly (stopping_reason | |
| `escalation_exhausted`) — the decision is a visible budget/detectability | |
| statement, not silence. | |
| ## 10. Sample sizes and power (with the design effect) | |
| Fixed-n planning numbers for a Bernoulli recall metric at baseline p₀ = 0.9, | |
| one-sided α = 0.05, power 0.9 ((z₀.₉₅+z₀.₉)² ≈ 8.57). Paired variance uses | |
| Var(d) ≈ ν − Δ² with measured-typical discordance ν ≈ 0.04 + Δ: | |
| | detectable Δ | unpaired, per model arm | paired, iid units | paired, DEFF = 2 | paired, DEFF = 2, anytime (~×1.35) | | |
| |---:|---:|---:|---:|---:| | |
| | 1pp | 16,100 | 4,280 | 8,550 | ~11,500 | | |
| | 2pp | 4,150 | 1,280 | 2,550 | ~3,450 | | |
| | 5pp | 745 | 300 | 600 | ~810 | | |
| | 10pp | 214 | 111 | 223 | ~300 | | |
| Notes: (i) pairing beats doubling the corpus — the unpaired column is per | |
| *arm*; (ii) the anytime-valid overhead (~30–40% at these scales, shrinking | |
| with the e-process's adaptive bets) buys the right to stop at any time, which | |
| in expectation *saves* samples whenever the effect is larger than the MDE | |
| (the 10pp test stops ~15× before budget); (iii) measured DEFF on paired | |
| differences was ≈1.2, not 2 — the table's DEFF=2 column is the conservative | |
| planning figure requested, the ledger reports the realized one. The default | |
| safety floor (2,500) is the 2pp row with the realized design effect plus | |
| head-room. | |
| ## 11. Compute-budget staging | |
| Default policy on a 320k corpus: plan ≈ 25k objects, staged | |
| `[15%, 40%, 100%]` of plan (≈ 3.8k → 10k → 25k) + escalation reserve, i.e. a | |
| 10K→50K→250K→full ladder scaled to plan size. Advancement rules: advance | |
| while any node is undecided and budget remains; stop immediately on any | |
| confirmed REGRESSION (sticky, anytime-valid) or when overall + all safety | |
| primaries PASS with nothing undecided; escalate only suspect/safety strata; | |
| terminal state otherwise INSUFFICIENT_EVIDENCE with per-stratum sample | |
| accounting in the ledger. | |
| ## 12. Prediction caching | |
| Baseline predictions are computed once per (dataset fingerprint, model | |
| fingerprint) and reused across every candidate update | |
| (`paired.PredictionCache`, disk + memory). Fingerprints cover everything | |
| predictions depend on — dataset identity/seed/size, model version + effects | |
| config — so **invalidation is by construction**: any change produces a new | |
| key; nothing stale can ever be served. A compute counter proves in tests that | |
| the second candidate evaluation performs zero baseline recomputation. In a | |
| real deployment the fingerprint inputs become dataset content hash + model | |
| checkpoint digest + inference config (thresholds, NMS, quantization), and the | |
| cache stores per-unit predictions rather than a success vector. | |
| ## 13. Sampling-bias prevention | |
| * The plan is built by a function that **cannot see candidate outcomes** (no | |
| such parameter exists), from population structure + a prior variance | |
| profile only, then persisted with seed, config and a content hash over the | |
| selected and reserve unit ids. The hash is recorded in every run's lineage; | |
| two candidates with opposite outcomes provably receive byte-identical plans | |
| (asserted in tests). | |
| * Escalation draws from a reserve whose order was frozen at plan time, so | |
| even adaptive *budget* decisions never become adaptive *selection*. | |
| * **If outcome-adaptive sampling is ever added** (e.g. active sampling of | |
| high-disagreement regions), estimates must be corrected with | |
| inverse-propensity weights w_i = 1/π_i under recorded, everywhere-positive | |
| selection probabilities π_i, and CS validity must be re-established for the | |
| weighted observations (bounded w_i·d_i, e.g. via truncated propensities). | |
| Without that correction, adaptive selection biases Δ̂ toward whatever the | |
| selector chases. v1 deliberately does not do adaptive selection. | |
| ## 14. Bayesian complement | |
| Alongside every frequentist decision, `sequential.py` computes a Beta-Binomial | |
| posterior P(Δ < −δ | data) from the discordant pairs (θ = P(regression pair | | |
| discordant) with Beta(1,1) prior; Δ = ν(1−2θ)). Operational comparison: | |
| * *For* the posterior: a single monotone number ("99.99% probable this is a | |
| real regression") that program managers read correctly without training; | |
| natural for a decision-theoretic cost tradeoff. | |
| * *Against* (as the gate): posteriors have no frequentist stopping guarantee | |
| under continuous monitoring without committing to a prior + loss and | |
| accepting their audit burden; e-values give the same "accumulating | |
| evidence" narrative *with* an error guarantee any auditor can check | |
| (e ≥ 1/α), and they compose across strata (e-BH) and across looks by | |
| multiplication. | |
| Verdict: **e-values gate, posterior explains.** Both are in the ledger. | |
| ## 15. Failure modes and mitigations | |
| | Failure mode | Mitigation in this design | | |
| |---|---| | |
| | Peeking inflates type-I | everything anytime-valid; fixed-n intervals never gate (§6) | | |
| | Correlated frames overstate n | cluster inference units; DEFF/n_eff in every record (§2) | | |
| | Masking: overall improves, stratum regresses | reserved per-level alpha + pre-registered safety primaries (§8) | | |
| | Multiplicity across strata | hierarchical budgets + e-BH within level (§8) | | |
| | "No signal" read as "safe" | three-outcome logic; INSUFFICIENT carries "not proven equivalent" (§7) | | |
| | Outcome-dependent sampling bias | frozen hashed plans, frozen reserves, no adaptive selection (§13) | | |
| | Rare strata silently undecidable | floors, oversampling+weights, escalation, explicit exhaustion reason (§9) | | |
| | Stale baseline predictions | fingerprint-keyed cache, invalidation by construction (§12) | | |
| | Broken candidate export burns budget | sanity smoke stage aborts in ~500 units (§5) | | |
| | Trivial-magnitude "significance" | practical margin δ in both null hypotheses (§1) | | |
| | Irreproducible decisions | seeds + fingerprints + plan hash + config in lineage; end-to-end determinism asserted in tests | | |
| | Metric doesn't decompose per object (mAP) | pre-registered checkpoint paired clustered bootstrap, not fake sequential (§3) | | |
| ## 16. Final recommendation (defaults shipped in `controller.DEFAULT_POLICY`) | |
| Paired, cluster-unit, stratified evaluation against a frozen hybrid | |
| Neyman/risk-floor plan; empirical-Bernstein confidence sequences + betting | |
| e-processes with a 0.5pp practical margin; three-outcome decisions; hierarchical | |
| alpha budget (30% overall / 30% safety primaries / 15% class / 15% stratum / | |
| 10% difficulty) with e-BH within levels; staged budget 15%→40%→100% of a ~25k | |
| plan with targeted escalation; fingerprint-keyed baseline cache; Beta-Binomial | |
| posterior as explanatory complement. CUSUM reserved for future production | |
| drift monitoring, not release gating. | |
| ## 17. Worked example: planted 2pp pedestrian-night regression (real run) | |
| Setup: population `320k` objects / 26,666 containers (megaeval synthetic, | |
| seed 42). Baseline `model-v41`. Candidate `model-v42` with planted effects | |
| {pedestrian|night: −2pp, global: +0.4pp} — the masking case. Default policy | |
| (δ = 0.5pp, α = 0.05, plan target 24k). Ground truth measured on the full | |
| corpus afterwards: overall Δ = **+0.28pp** (candidate looks *better* | |
| headline-wise), pedestrian|night Δ = **−1.63pp** realized (N = 17,561, | |
| baseline recall 0.801). | |
| Run (deterministic, reproducible from lineage): | |
| * Plan: 25,699 objects frozen across 18 strata; pedestrian|night allocated | |
| 2,501 (floor-driven; weight w = 7.02) + 15,060-object reserve. | |
| * Decision: **REGRESSION** at 26,705 objects total = **8.4% of the corpus** | |
| (~12× cheaper than a full paired evaluation), stopping reason | |
| `regression_confirmed`, during escalation of the suspect safety primary. | |
| * Pedestrian|night node at stop: n = 3,507 objects in 1,195 clusters | |
| (ICC 0.065, DEFF 1.20, n_eff 2,923); Δ̂ = −1.94pp, | |
| 95% CS [−3.97pp, −0.48pp]; discordant pairs n01 = 110 vs n10 = 42; | |
| log e-value **7.50** vs boundary log(1/α_safety) = **4.89** | |
| (e ≈ 1,808 ≥ 133); Beta-Binomial P(Δ < −δ | data) = 0.99999. | |
| * Evidence trajectory (n, log e): (129, 1.5) → (1002, 1.6) → (2501, 2.8) → | |
| (3006, 4.9) → (3507, 7.5) — the dashboard plots exactly this against the | |
| 4.89 boundary. | |
| * Overall node: Δ̂ = +0.03pp, CS [−0.51pp, +0.38pp] — correctly *not* | |
| regressed and *not yet* provably equivalent; the hierarchy is what surfaced | |
| the stratum despite the healthy headline. | |
| * Ledger: 28 node records, each with the full §15-proof field set; lineage | |
| carries plan hash, fingerprints, seed and the entire statistical config. | |
| The same engine with a null candidate (no planted effect) across 24 Monte | |
| Carlo repetitions produced **zero** false REGRESSION verdicts (α = 0.05), and | |
| with a planted 10pp effect stopped at ~2.8k samples — 11% of plan, <1% of the | |
| corpus (both asserted in `tests/test_seqeval/`). | |