feat(metrics): consolidate gpt-oss/deepseek accuracy, dedup reporting, surface TPS#400
Conversation
|
MLCommons CLA bot All contributors have signed the MLCommons CLA ✍️ ✅ |
There was a problem hiding this comment.
Code Review
This pull request introduces support for consolidating multiple accuracy datasets into a single combined score using a consolidation group tag, which is particularly useful for rolling up subsets like gpt-oss (e.g., AIME25, GPQA, and LiveCodeBench) into a sample-weighted overall score. It extracts shared accuracy breakdown helpers into a new module, refactors compliance and plotting utilities to use them, and updates the benchmark finalization process to attach consolidated accuracy breakdowns to the report. The feedback suggests a cleaner and safer approach to retrieving the dataset sample count during per-subset audit entry generation by avoiding fragile assert statements and direct internal attribute access in favor of the public num_samples() API.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
213b8c5 to
1ecb9e8
Compare
Squashes the feat-consolidate-accuracy-reporting branch into one commit: - Consolidate gpt-oss/deepseek accuracy into per-dataset scoring; dedup reporting; surface TPS. - Add composite MLPerf gpt-oss-120b accuracy dataset + scorer: one results.json entry with a headline score plus a per-subset breakdown (aime25 / gpqa / livecodebench), mirroring the DeepSeek-R1 model. - Score each accuracy dataset into its own entry; drop the group dispatch/config field so examples emit one entry per dataset. - Delete the superseded GptOssAccuracyScorer orchestrator. - Enable per-dataset max-osl; cover the performance num_samples branch. - Bake the MLCommons-standard per-subset repeats (aime25 8 / gpqa 5 / livecodebench 3) into the gpt-oss-120b composite dataset: load() replicates each subset's rows in-memory by its factor, and the composite scorer divides the replication back out so the headline stays weighted by unique problem count. A config-level num_repeats multiplies on top. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8eb098f to
d29c111
Compare
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… accuracy list shape Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…zation The base Scorer.score() returned np.mean(...) (numpy.float64). After the per-dataset-list refactor this raw score flows into Report.accuracy and results.json, and both msgspec (result_summary.json) and json.dump (results.json) reject numpy scalars — Report.to_json aborted the entire report write with "Encoding objects of type numpy.float64 is unsupported" (caught on a live gpt-oss-120b run). Fix at the source (return a native float) and defensively at the entry boundary in _score_accuracy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two CI failures on PR #400, both from the accuracy dict→list refactor: - mypy: the hasattr-based numpy coercion in _score_accuracy tripped union-attr on the float|None score. numpy.float64 is a float subclass, so isinstance(score, float) + float(score) is the mypy-clean equivalent. - Tests: the integration accuracy pipeline test still indexed results["accuracy_scores"] as a dict keyed by dataset name; index the per-dataset list by dataset_name instead. (Integration tests are deselected from the default `pytest` run, so this was missed earlier.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each accuracy entry now carries duration_s — the wall-clock of that dataset's issue phase, matched by phase name (== dataset_name; the inline-scored "performance" entry keys on the perf phase). Rendered in the report summary as `... total=T, duration=D.DDs)`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…folder
After scoring, emit <report_dir>/accuracy/accuracy_results.json carrying
the same {"accuracy_scores": [...]} list shape as results.json, so
downstream accuracy consumers read one focused artifact. Written only
when scoring produced entries (perf-only runs leave no accuracy/ folder).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the change. Can you help add some examples of the accuracy report (e.g. for gpt-oss) after the change? |
Emit the perf-metrics report at <report_dir>/performance/result_summary.json (paired with the accuracy/ folder from a117e9f). Consumers updated: metrics/results_plots.py (load_run), scripts/publish_submission.py (reads it under performance/, still copies flat into the MLPerf performance/run_1 layout), and plot_results.py help text. results.json stays at the report_dir root — it is NOT performance-only: it carries accuracy_scores, per-sample responses, and errors, and compliance reads it as the accuracy artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the two summary artifacts to the names the endpoints submission checker expects: - performance/result_summary.json -> performance/results_summary.json - audit/audit_result.json -> audit/audit_<test_id>.json (e.g. audit_output_caching_test.json; auto-adapts per audit test) verify_<TEST>.txt (MLPerf validator marker) is unchanged. Consumers updated: results_plots.py, publish_submission.py (still copies flat into the MLPerf performance/run_1 layout), plot_results.py, compliance/result.py, commands/audit.py, the tests, and the audit/report docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Revert results_summary.json -> result_summary.json (benign; reduces churn vs the pre-existing name). The audit_<test_id>.json rename is kept. - result_summary.json is now purely performance: Report.to_json drops the accuracy field. Accuracy lives only in the dedicated accuracy report (accuracy/accuracy_results.json, plus results.json for compliance). The accuracy field stays on the Report struct so report.txt / the console summary still render it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New AccuracyConfig.scale (default 1.0) multiplies the scalar accuracy score in _score_accuracy — e.g. scale: 100 reports a [0,1] pass@1 as a percentage, matching the 0-100 scale DeepSeek-R1 already uses. Applies only to the scalar score, not to any per-subset breakdown. The gpt-oss-120b example configs set scale: 100 so their aime25/gpqa/livecodebench scores render as percentages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…down fields Add an average accuracy across components and remove two now-redundant fields from the DeepSeek-R1 breakdown. - average_accuracy: plain mean of the per-dataset scalar scores (3 components for gpt-oss, 1 for DeepSeek-R1; equals the single score when there's one). The inline "performance" entry and non-numeric scores are excluded. Surfaced on Report.display (report.txt + console), accuracy/accuracy_results.json, and results.json; not in result_summary.json (perf-only by design). - DeepSeek breakdown no longer stores overall_accuracy (it's the entry's scalar score) or per_subset_status. build_breakdown drops the overall/**extra params; the block is now subset_scores + total_samples + complete. - Recognizer keys on subset_scores (shared by BFCL + DeepSeek) instead of overall_accuracy; new find_accuracy_entry exposes the owning entry so plotting sources the headline from score. BFCL breakdowns are unchanged (they build their own dicts and keep the compliance gate metrics). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review Council — architecture review (Concern-driven)Reviewed by Claude + Codex (independent), thorough. Two maintainer concerns; both confirmed by both reviewers. Proposing the concrete change for each. 🏗️ Concern 1 —
|
nvzhihanj
left a comment
There was a problem hiding this comment.
Review Council — net-new findings
Inline findings not already covered by the open threads (architecture proposal is in the top-level summary). Reviewed by Claude + Codex.
…ry.json + accuracy_results.json results.json duplicated data now owned by the consolidated artifacts: perf rollups (qps/tps/latency) live in performance/result_summary.json and accuracy scores in accuracy/accuracy_results.json. Its response/error blocks only mirrored events.jsonl. Removing it drops a redundant primary artifact. Writers: - execute.py finalize no longer emits results.json (nor the now-dead config/tps/responses/errors locals); only accuracy/accuracy_results.json. - bfcl_v4_multi_turn_cli.py writes accuracy/accuracy_results.json. Readers repointed to accuracy/accuracy_results.json: - compliance/checker.py accuracy gate - metrics/results_plots.py accuracy plot - scripts/publish_submission.py: split perf/accuracy artifact sets; _verify_performance reads qps from result_summary.json. Tests migrated to the consolidated artifacts; obsolete test_results_json_output dropped (result_summary self-completeness already covered). BREAKING: the upstream MLCommons submission_checker endpoints parser reads results.json; its migration to accuracy_results.json must be coordinated separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…=="performance" checks AccuracyConfiguration gains a dataset_type field (DatasetType.PERFORMANCE for the inline perf-scored entry, DatasetType.ACCURACY for real accuracy datasets). All four "is this the perf entry?" branches in _score_accuracy and accuracy- phase building now key on dataset_type instead of matching dataset_name == "performance", so a dataset legitimately named "performance" is never misclassified. dataset_name stays as the phase-name / report key. Resolves PR #400 review threads on execute.py:1352 and :1417. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ults.json load_run now reads the accuracy breakdown from accuracy/accuracy_results.json (the results.json removal), but two test_results_plots fixtures still wrote it to results.json, so RunArtifacts.accuracy came back None and CI failed. Write the fixture to the migrated path. Missed in the original sweep because the write line also calls _accuracy_results(), which an over-broad grep exclusion hid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e849f24 to
135df5e
Compare
…lit scorer outputs Closes PR #400 threads #2/#3/#7 and the deferred #6 memory bound. token_metrics: extract load_reference_tokenizer / load_reference_backend (a single from_pretrained(trust_remote_code=True) construction point) and public encode_lengths; _init_worker and _load_tokenizer route through them. scoring: split get_outputs into _iter_complete + get_raw_outputs (what the model generated) + get_scoring_outputs (text to score; base == raw). BFCLv4Scorer overrides get_scoring_outputs only, reusing the shared reader. execute: delete _load_osl_tokenizer and the model_max_length=1e12 hack; _phase_osl_stats counts via the shared backend; _score_accuracy builds uuid_to_text from get_raw_outputs (correct raw-text OSL for tool-call scorers), bounded to the accuracy population via a one-shot sample_idx_map.json union read. Accuracy OSL now uses the same reference tokenizer as the perf side, so DeepSeek/custom tokenizers no longer silently disable OSL (missing trust_remote_code), with zero hot-path changes. Tool-call OSL exactness is tracked in #410. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
viraatc
left a comment
There was a problem hiding this comment.
Review Council — local pass (Codex + Grok + manual verification)
Scope: integration + the report-layout / accuracy-list migration. The core migration is sound — I verified the BFCL and DeepSeek-R1 compliance/breakdown paths and that the list-shape consumers (checker.py, results_plots.py, publish_submission.py, bfcl_v4_multi_turn_cli.py) are all migrated. Most issues below are user-facing artifacts the diff didn't touch. (BFCL/dataset coupling inside the accuracy module is intentionally out of scope — tracked separately.)
Inline comments: gpt-oss plots empty · average_accuracy scale + dataset_type · numpy coercion · the accuracy-write site (stale artifacts / dead collector.responses / non-atomic).
🔴 Not inline (files unchanged by this PR) — broken examples
The layout change breaks committed examples users follow verbatim:
examples/11_Edge_Agentic_Example/README.md:244,252— readsresults.json+ dictaccuracy_scores['bfcl_v4::function_calling']; it's nowaccuracy/accuracy_results.json+ a list →FileNotFoundError/TypeError.examples/03_BenchmarkComparison/compare_with_vllm.py:352— readsreport_dir / 'result_summary.json'; it's now underperformance/.
Fix: update both to the new paths + list shape (mirror bfcl_v4_multi_turn_cli.py:229-247).
Notes (low)
- Accuracy-write failure is swallowed (
except Exception: logger.error) → exit-0 with no accuracy artifact; consider hard-failing when scoring produced entries. find_accuracy_breakdownreturns the firstsubset_scoresentry — fine for BFCL-only / DSR1-only; a hypothetical mixed run could grab the wrong block by ordering.
Gemini errored out; Opus didn't return in time — this is Codex + Grok + manual verification.
…ion, dataset_type, atomic write From viraatc's review of the accuracy-list / report-layout migration: - examples/11 README + examples/03 compare_with_vllm.py: migrate to accuracy/accuracy_results.json (list shape) + performance/result_summary.json (were results.json + dict access -> FileNotFoundError/TypeError). - results_plots: extract_accuracy falls back to per-dataset scalar scores when no scorer emits a breakdown, so gpt-oss accuracy plots are no longer empty. - execute: coerce numpy scalar scores via numbers.Real (isinstance float missed np.float32/ints); persist dataset_type on each accuracy entry. - accuracy_results: average_accuracy excludes the inline perf entry by dataset_type (not dataset_name), and omits the headline (warns) when components span mixed scales rather than emitting a meaningless mean. - accuracy_results.json now written atomically (utils/atomic_write.py) and write failures raise ExecutionError instead of exiting 0 with no artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-3447 setuptools 82.0.1 (transitive via pbr/torch) carries PYSEC-2026-3447, failing the pip-audit CI job. Add a constraint floor mirroring the existing click floor; uv lock bumps only setuptools 82.0.1 -> 83.0.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves the review thread on report.py's series_metric_dict duplicating the aggregator's token-series construction. Adds registry.build_token_series_dict + TOKEN_HDR_LOW/HIGH/SIG_FIGS/HISTOGRAM_BUCKETS as the single source; the aggregator's OSL/ISL registration and report.py's accuracy OSL both use it. report.py drops the duplicated _TOKEN_HDR_LOW/HIGH constants, the aggregator- private imports (SeriesSampler / _metric_to_dict / _DEFAULT_PERCENTILES), and the confusing HDR comment nvzhihanj flagged. Behavior-preserving — identical values and output shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ntry score scoring.py (PR #400 thread, medium data-integrity): base Scorer.score() took np.mean over only the samples that produced a COMPLETE event, so a run that lost samples to a drain-timeout/crash reported accuracy inflated over the survivors, and an all-missing run produced np.mean([]) == NaN (not valid JSON). Divide the summed per-sample scores by the *issued* count (len(sample_index_map), written at issue time) so missing samples count as failures; the empty/all-missing path returns 0.0 and issued==0 returns None; n_repeats derives from issued. checker.py (PR #400 thread, testing): check_accuracy read overall_accuracy only from the breakdown block, but only BFCL stores it there — DeepSeek-R1/gpt-oss keep the headline on the entry's scalar score, so a genuine DeepSeek submission gated on overall_accuracy failed 'metric missing'. Fall back to entry['score'] for the overall metric (parity with the plot path). Fixture updated to the realistic DeepSeek shape (no overall in the block) + a bounded-fallback test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…etrics.py Resolves the review thread on ACCURACY_METRIC_KEYS living in the generic accuracy_results.py breakdown helper. The dict maps ruleset golden-metric names (bfcl_*) to the BFCL scorer's breakdown keys — both halves are BFCL-specific, so it doesn't belong in a scorer-agnostic module. Move it to a new dependency-light evaluation/bfcl_v4_metrics.py (no bfcl-eval import) so compliance/checker.py and metrics/results_plots.py import it without pulling in the heavy BFCL scorer or the optional bfcl extra. Behavior-preserving; the metric-keys test moves to test_bfcl_v4_metrics.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review-council (Codex) found the base Scorer.score() denominator fix (82531a4) didn't reach BFCLv4Scorer, which overrides score() and averaged over only the samples that produced a COMPLETE event. BFCL is the compliance-gated scorer, so a partial run (some issued UUIDs missing, but still above min_samples) reported an inflated overall_accuracy / normalized_single_turn_score that could wrongly PASS the gate. Fix the accuracy by padding each issued-but-missing sample as 0.0 under its subset before the existing means run, so overall, per-subset, per-category, and the sample-weighted category weights all divide by the issued count — no rewrite of the aggregation math. Also drop_duplicates(sample_uuid) so a duplicate COMPLETE can't double-weight a sample (the point Claude + Grok independently raised on the base scorer). Track completed and issued counts separately (a follow-up review found padding had silently flipped total_samples from completed to issued): total_samples keeps its pre-PR meaning — the number that actually completed, which is what the min_sample_count gate / plot / rollup read — and a new issued_samples records the accuracy denominator. An all-missing run reads as "0 of N completed" rather than "0 of 0". Complete runs are unchanged (completed == issued). Adds partial-run, duplicate-COMPLETE, and all-missing regression tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-code-quality flagged the BFCL scorer tests importing inference_endpoint.evaluation.bfcl_v4_scorer both ways — `from ... import BFCLv4Scorer` at module scope and `import ... as mod` inside four test methods (to monkeypatch module-level Language / ast_checker). Replace the in-method module imports with monkeypatch.setattr on a dotted-string target (_SCORER) and hoist pandas to the top, so the scorer module is imported one way only and the lazy imports (discouraged by the repo guidelines) are gone. Test behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tale results.json (#2631) The endpoints accuracy artifact was renamed from results.json (with a responses field) to accuracy/accuracy_results.json in mlcommons/endpoints#400, which also dropped the responses field entirely (per-sample text now lives in events.jsonl, not part of the submission). truncate_accuracy_log.py still looked for the old results.json/responses shape and would only log "missing" for every endpoints submission. Detect the endpoints layout via the scenario-root config.yaml and skip truncation, and remove the now-dead PERFORMANCE_ENDPOINTS_DIR/ACCURACY_ENDPOINTS_DIR constants left over from the loader.py refactor to ENDPOINTS_SCENARIO_DIR. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
Reworks accuracy reporting into a uniform per-dataset list and aligns the run's report directory with the MLPerf endpoints submission layout (mlcommons/inference#2628).
Per-dataset accuracy list
Report.accuracyandresults.json'saccuracy_scoresare now an always-present list (empty for perf-only), one entry per accuracy dataset. Each entry:dataset_name,extractor,ground_truth_column,score,unit_samples,num_repeats,total_samples(= unit × repeats),duration_s(the dataset's issue-phase wall-clock),complete, and — for multi-subset scorers (DeepSeek-R1, BFCL) — a BFCL-shapedbreakdown.find_accuracy_breakdowniterates the list;compliance/checker.py+metrics/results_plots.pyare unchanged behind it.report.txt/results_summary.json:<name>: <score> (unit=U, repeats=R, total=T, duration=Ds).Removed the composite gpt-oss path
gptoss_120b_accuracypredefined dataset,GptOss120bAccuracyScorer, and_weighted_breakdown; the per-subsetaime25::gptoss/gpqa::gptoss/livecodebench::gptossdatasets are the sole path. DeepSeek-R1 keeps its combined-subset breakdown as one list entry.Report directory conforms to #2628
scripts/publish_submission.pystill copies the artifact trio flat into the MLPerfperformance/run_1/submission layout.Fixes surfaced during validation
float: the baseScorer.score()returnednp.mean(...)(numpy.float64), which crashedReport.to_json(msgspec) andresults.json(json) serialization — caught on a live gpt-oss run.scripts/publish_submission.pyandevaluation/bfcl_v4_multi_turn_cli.pyto the list shape.End-to-end validation (GB200 NVL72)