refactor(metrics): unify the percentile convention; address #417 round-3 review#423
refactor(metrics): unify the percentile convention; address #417 round-3 review#423nvzhihanj wants to merge 5 commits into
Conversation
- HIGH: Report.from_snapshot._series_dict short-circuited count==0 before _series_to_metric_dict could preserve the all-null ES map — the empty-series self-description was dead code on the real consumer path. Now delegates unconditionally; pinned through Report.from_snapshot, not the helper. - ES is best-effort on the finalize path: _es_estimates wraps the computation and degrades to map-omitted on any exception (the publisher finalized guard would otherwise turn one ES error into a lost final report). - find_min_passing boundary now strict (odds < 1-c), matching loadgen/early_stopping.cc:70 exactly; docstring notes it. - _betacf cap comment: removed the wrong 3e-8 figure; safety comes from the odds~0.01 decision boundary, midpoint accuracy is explicitly not claimed (and now pinned by a cap-regime test). - _discard_count docstring off-by-one wording fixed ((t+1)-th -> t-th highest, t-1 discarded). - script: gap lines gate on empirical is not None (0.0 was silently dropped); percentage guarded separately against zero division. - EarlyStoppingResult is a frozen slots dataclass like its sibling spec. - tests: betai branch closed-forms + cap regime; t=1 no-discard boundary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…surface The codebase mixed 99.9 (grid, np.percentile, summary keys) and 0.999 (the ES surface), stitched together by two different rounding hacks (round(p/100, 6) and round(p*100, 4)) and a key-reconstruction helper. Now every product surface speaks the grid convention: - EarlyStoppingSpec.percentiles, es_percentile_estimate(percentile), EarlyStoppingResult.percentile, ES_MIN_PERCENTILE (50.0), es_targets_from_grid values, and the script --percentiles flag are all 0-100. - es_targets_from_grid does no conversion and no rounding at all: keys are str() of the original grid entries, values the entries themselves. - grid_percentile_key is deleted; explicit spec overrides key via str() like the grid path. - The LoadGen-parity kernel (find_min_passing/_odds/_discard_count) keeps LoadGen's own fraction domain; the single conversion lives inside es_percentile_estimate, unrounded — the same /100 np.percentile applies internally, so empirical/grid parity is exact by construction. Estimates are bit-identical before/after (interfaces only). This also resolves the round-3 review question about the 6-vs-4 rounding inconsistency: both roundings no longer exist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
MLCommons CLA bot All contributors have signed the MLCommons CLA ✍️ ✅ |
Floors computed with find_min_passing(1, p) + 1 at c=0.99; rule of thumb floor ~= 6.64 / (1 - p/100). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…drop stale comment Review round on #423 (Codex: clean; Claude: 3 findings, all verified): - the convention switch made old fraction-style invocations (--percentiles 0.99) silently compute sub-1% percentiles with plausible sufficient=True output; the script now hard-errors on values < 1 with a did-you-mean message, and es_percentile_estimate documents the footgun. - CLI fallback targets dedupe on the parsed value with canonical float-style keys (99 == 99.0 -> one computation, key 99.0). - snapshot.py comment still named the deleted grid_percentile_key helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…drop stale comment Review round on #423 (Codex: clean; Claude: 3 findings, all verified): - the convention switch made old fraction-style invocations (--percentiles 0.99) silently compute sub-1% percentiles with plausible sufficient=True output; the script now hard-errors on values < 1 with a did-you-mean message, and es_percentile_estimate documents the footgun. - CLI fallback targets dedupe on the parsed value with canonical float-style keys (99 == 99.0 -> one computation, key 99.0). - snapshot.py comment still named the deleted grid_percentile_key helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dc6edf4 to
50e02bf
Compare
… worked example) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review round (Codex + Claude, both on
|
Follow-up to #417 (early-stopping percentile estimates), two commits:
1.
06f3b2b— address the post-merge round-3 review of #417All eight findings from the round-3 review council on #417 (each verified against HEAD before fixing):
Report.from_snapshot._series_dictshort-circuitedcount==0to{}before_series_to_metric_dictcould keep the all-nullearly_stopping_percentilesmap. Now delegates unconditionally, and the regression test pinsReport.from_snapshotitself rather than the helper (the previous test's false-confidence trap)._es_estimatescatches, logs, and omits the map instead of letting an exception escapepublish_finalafter the finalized guard is set (which would cost the entire final report)._betacfiteration-cap comment cited a fabricated3e-8accuracy figure; replaced with the correct rationale (safety comes from the odds ≈ 0.01 decision boundary — midpoint accuracy in the cap regime is explicitly not claimed, and is now pinned by a test).find_min_passing's boundary is now strict (odds < 1 − c), byte-matchingloadgen/early_stopping.cc:70._discard_countdocstring off-by-one wording; script gap lines gated onis not None(a legitimate0.0empirical was silently dropped) with zero-division guarded separately;EarlyStoppingResultconverted to a frozen slots dataclass like its sibling spec._betaiboth branches pinned against closed forms (I_x(a,1) = x^a,I_x(1,b) = 1−(1−x)^b), the iteration-cap regime pinned sane, and thet=1no-discard floor boundary pinned (estimate == max,discarded == 0).2.
9c88b33— one percentile convention: grid (0–100) at every surfaceThe codebase mixed
99.9(grid,np.percentile, summary keys) and0.999(the whole ES surface), stitched together by two different rounding hacks (round(p/100, 6)andround(p*100, 4)) and a key-reconstruction helper. Now:EarlyStoppingSpec.percentiles,es_percentile_estimate(percentile=…),EarlyStoppingResult.percentile,ES_MIN_PERCENTILE(= 50.0),es_targets_from_gridvalues, and the script's--percentilesall speak 0–100.es_targets_from_griddoes no conversion and no rounding at all — keys arestr()of the original grid entries, values the entries themselves.grid_percentile_keyis deleted; both rounding constants are gone.find_min_passing/_odds/_discard_count) keeps LoadGen's own fraction domain; the single conversion lives insidees_percentile_estimate, unrounded — the same/100thatnp.percentileapplies internally, soempirical-vs-grid parity is exact by construction.Gate: 1340 unit tests, 13/13 pre-commit hooks, LoadGen reference anchors (459/44/661) unchanged.
🤖 Generated with Claude Code