Skip to content

Commit 7bf4fbd

Browse files
authored
[MLPerf 6.1][GRPO] Offline evaluation for Qwen3.5-397B GRPO (#906)
* [MLPerf 6.1][GRPO] Update Qwen3.5 reference assets Signed-off-by: Jeremi Piotrowski <jpiotrowski@nvidia.com> * llm_post_training: pin NeMo-RL mlperf-training-qwen35-next-v2 Advance the NeMo-RL pin to f987c0596 (mlperf-training-qwen35-next-v2), which adds deferred (offline) checkpoint evaluation to the reference launcher. * llm_post_training: document the offline evaluation requirement The rules require offline evaluation for qwen35_397b_grpo: checkpoints every step from the first-evaluation step through the end of training, evaluated post-run until one reaches the target; run_stop is backdated to the passing checkpoint's weight-update timestamp. Describe how the pinned reference implements this, and correct the submodule path in the checkout instructions. --------- Signed-off-by: Jeremi Piotrowski <jpiotrowski@nvidia.com>
1 parent f242fea commit 7bf4fbd

4 files changed

Lines changed: 181 additions & 85 deletions

File tree

‎llm_post_training/README.md‎

Lines changed: 145 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,19 @@ through Ray:
3333
in per-task Apptainer/Singularity containers.
3434

3535
The benchmark implementation is provided by
36-
[NVIDIA NeMo-RL](https://github.com/NVIDIA-NeMo/RL/tree/mlperf-training-qwen35-main).
36+
[NVIDIA NeMo-RL](https://github.com/NVIDIA-NeMo/RL/tree/mlperf-training-qwen35-next-v2).
3737
This repository checks out NeMo-RL under the local submodule path
38-
`llm_moe_grpo/RL`, pinned to commit
39-
`3fca04c9b313d313302923a5bb6b0c8dc0340ed6`. The source commit, recursive
38+
`llm_post_training/RL`, pinned to commit
39+
`f987c0596af2bd6cfb9958e768deb8df951d7504`. The source commit, recursive
4040
submodule revisions, Python dependency lock, and downstream build-time patches
4141
are recorded in the benchmark container.
4242

43-
The principal entrypoints are:
43+
The principal entrypoints and reference sections are:
4444

45-
| Purpose | Path |
45+
| Purpose | Path or section |
4646
|---|---|
47-
| Dataset download and preprocessing | `download_dataset.sh` |
47+
| Qualified dataset download | [Training and validation JSONL](#training-and-validation-jsonl) |
48+
| Dataset production from source | `download_dataset.sh` |
4849
| Dataset identity verification | `verify_dataset.sh` |
4950
| Pretrained-checkpoint download | `download_checkpoint.sh` |
5051
| Pretrained-checkpoint conversion | `RL/docker/mlperf/data_scripts/convert_ckpt.sh` |
@@ -83,11 +84,11 @@ scheduler nodes.
8384
From the root of this repository:
8485

8586
```bash
86-
git submodule update --init --recursive llm_moe_grpo/RL
87-
cd llm_moe_grpo/RL
87+
git submodule update --init --recursive llm_post_training/RL
88+
cd llm_post_training/RL
8889

8990
test "$(git rev-parse HEAD)" = \
90-
"3fca04c9b313d313302923a5bb6b0c8dc0340ed6"
91+
"f987c0596af2bd6cfb9958e768deb8df951d7504"
9192
git submodule update --init --recursive
9293
```
9394

@@ -103,7 +104,7 @@ submodules, installs the locked dependencies, applies the benchmark's guarded
103104
patches, builds the Arm HybridEP dependency, installs Apptainer, and prefetches
104105
the Ray and NeMo Gym virtual environments.
105106

106-
From the `llm_moe_grpo/RL` directory:
107+
From the `llm_post_training/RL` directory:
107108

108109
```bash
109110
source_commit="$(git rev-parse HEAD)"
@@ -152,12 +153,38 @@ The run requires five external artifacts:
152153
|---|---|---|
153154
| Qwen3.5 policy checkpoint | `HF_CKPT_PATH` | `Qwen/Qwen3.5-397B-A17B` snapshot `8472618112abcbd45acbcdc58436aff4233c23f7` |
154155
| Megatron checkpoint cache | `NRL_MEGATRON_CHECKPOINT_DIR` | Writable directory; an empty directory is converted from the HF checkpoint on first use |
155-
| Curriculum training JSONL | `QWEN35_CURRICULUM_DATA_PATH` | Exact 685-row curriculum-v2 artifact described below |
156-
| Validation JSONL | `NEMO_GYM_SWE_VALIDATION_DATA_PATH` | 256 held-out R2E-Gym tasks |
157-
| Per-task SIF images | `NEMO_GYM_SWE_SIF_DIR` | All 941 environments referenced by the qualified train/validation lists; the checked-in helper builds a compatible 977-image superset |
156+
| Curriculum training JSONL | `QWEN35_CURRICULUM_DATA_PATH` | Exact 700-row artifact described below |
157+
| Validation JSONL | `NEMO_GYM_SWE_VALIDATION_DATA_PATH` | 251 held-out R2E-Gym tasks |
158+
| Per-task SIF images | `NEMO_GYM_SWE_SIF_DIR` | All 951 environments referenced by the qualified train/validation files |
158159

159160
### Policy checkpoint
160161

162+
Download the qualified Hugging Face checkpoint from
163+
[MLCommons storage](https://training.mlcommons-storage.org/index.html#qwen3-5-397b-benchmark):
164+
165+
```bash
166+
cd llm_moe_grpo
167+
168+
checkpoint_dir="<shared-path>/qwen35-397b-a17b"
169+
170+
bash <(curl -s https://raw.githubusercontent.com/mlcommons/r2-downloader/refs/heads/main/mlc-r2-downloader.sh) \
171+
-d "${checkpoint_dir}" \
172+
https://training.mlcommons-storage.org/metadata/qwen35-397b-a17b.uri
173+
174+
export HF_CKPT_PATH="${checkpoint_dir}"
175+
mkdir -p "<shared-path>/qwen35-mcore-cache"
176+
export NRL_MEGATRON_CHECKPOINT_DIR="<shared-path>/qwen35-mcore-cache"
177+
```
178+
179+
The downloader verifies the package's MD5 manifest. `HF_CKPT_PATH` must name
180+
the directory containing `config.json` and the model weight shards. The
181+
MLCommons package mirrors Hugging Face revision
182+
`98d1a504ba52e88924b3a3a008447cf2fdbd518c`; its model, configuration, and
183+
tokenizer files match revision `8472618112abcbd45acbcdc58436aff4233c23f7`
184+
pinned by the helper below. Those revisions differ only in `README.md`.
185+
186+
#### Optionally, download the checkpoint from Hugging Face
187+
161188
The checked-in download helper pins the exact Hugging Face model revision and
162189
uses the standard Hugging Face cache layout:
163190

@@ -190,22 +217,16 @@ incompatible NeMo-RL/Megatron Bridge/Megatron Core stack is not supported.
190217
The benchmark training input is:
191218

192219
```text
193-
benchmark_r2e_gym_easy_train.filtered.curriculum-v2-classic-cycles2-seed20260710.jsonl
220+
benchmark_r2e_gym_easy_train.jsonl
194221
```
195222

196223
Its known identity is:
197224

198225
| Property | Value |
199226
|---|---:|
200-
| Rows | 685 |
201-
| Size | 444,421,669 bytes |
202-
| SHA-256 | `c07bcd64ed1c558e28d091239104e38295a5e696c1d21bb0b61f0346c7eaa0f7` |
203-
204-
The checked-in converter and instance-ID lists reproduce this filtered,
205-
curriculum-ordered input. Commit
206-
`9e64cc37197c9d00954ee8285c623fd7e53595e2`, which is included in the pinned
207-
NeMo-RL revision, removes 36 unusable environments from the former 721-row
208-
training list and orders the remaining 685 rows deterministically.
227+
| Rows | 700 |
228+
| Size | 446,364,412 bytes |
229+
| SHA-256 | `e772af09599270ab16a04ccbbcef395bcb2929607f050478033a84b087a606fc` |
209230

210231
The held-out validation input is:
211232

@@ -217,17 +238,38 @@ Its reference identity is:
217238

218239
| Property | Value |
219240
|---|---:|
220-
| Rows | 256 |
221-
| Size | 173,801,096 bytes |
222-
| SHA-256 | `452d0e6b3c1973669334062dc24931355de51749df1ab51fc9bb71a129f7bb5c` |
241+
| Rows | 251 |
242+
| Size | 172,078,629 bytes |
243+
| SHA-256 | `2d5bddf717afb4389510a7d197f26ce5eb7f610aab4b20d6af6e0150de6f2c79` |
223244

224-
The package pins source dataset revision
225-
`e8b9fcbce43eaca0dc2c0d4798ee6f3e965f590a` of
226-
[`R2E-Gym/R2E-Gym-Subset`](https://huggingface.co/datasets/R2E-Gym/R2E-Gym-Subset).
227-
The download script retrieves that revision, runs the pinned converter, gives
228-
the curriculum output its canonical name, and invokes the independent
229-
verification script. The preprocessing host requires `uv`/`uvx` and Git
230-
network access for repository metadata not already present in `cache_dir`.
245+
Download both qualified files from the
246+
[Qwen3.5 397B benchmark page](https://training.mlcommons-storage.org/index.html#qwen3-5-397b-benchmark)
247+
with the MLCommons R2 Downloader:
248+
249+
```bash
250+
cd llm_moe_grpo
251+
252+
dataset_dir="<shared-path>/benchmark-r2e-gym-easy"
253+
254+
bash <(curl -s https://raw.githubusercontent.com/mlcommons/r2-downloader/refs/heads/main/mlc-r2-downloader.sh) \
255+
-d "${dataset_dir}" \
256+
https://training.mlcommons-storage.org/metadata/benchmark-r2e-gym-easy.uri
257+
258+
export QWEN35_CURRICULUM_DATA_PATH="${dataset_dir}/benchmark_r2e_gym_easy_train.jsonl"
259+
export NEMO_GYM_SWE_VALIDATION_DATA_PATH="${dataset_dir}/benchmark_r2e_gym_easy_val.jsonl"
260+
```
261+
262+
The downloader verifies the package's MD5 manifest. Verify the SHA-256 values
263+
above before running because `data.shuffle: false` makes training row order
264+
part of the benchmark input.
265+
266+
#### Optionally, produce the dataset from source
267+
268+
The checked-in converter and instance-ID lists reproduce the qualified,
269+
curriculum-ordered input from the pinned
270+
[`R2E-Gym/R2E-Gym-Subset`](https://huggingface.co/datasets/R2E-Gym/R2E-Gym-Subset)
271+
source revision. The preprocessing host requires `uv`/`uvx` and Git network
272+
access for repository metadata not already present in `cache_dir`.
231273

232274
```bash
233275
cd llm_moe_grpo
@@ -246,33 +288,43 @@ cache_dir="<work-path>/r2e-repository-cache"
246288

247289
`download_dataset.sh` uses
248290
`RL/tools/create_r2e_gym_easy_subset_jsonl.py`,
249-
`RL/tools/train-instance-ids.txt`, and `RL/tools/val-instance-ids.txt`.
250-
The similarly named converter under `RL/docker/mlperf/data_scripts/` retains
251-
the earlier 721-row, non-curriculum training split and is not the qualified
252-
data generator.
253-
254-
The curriculum algorithm is fixed in the script: seed `20260710`, 16 examples
255-
per curriculum batch, and two easy-to-hard cycles. It ranks difficulty using
256-
75% inverse classic pass rate and 25% changed-line count capped at 20, divides
257-
the training set into 25% easy, 50% medium, and 25% hard buckets, and changes
258-
the per-batch mixture through warmup, core, and hardening phases. The converter
259-
also validates row structure, verifies that all requested IDs were found, and
260-
rejects overlapping train and validation ID sets. The hash checks above are
261-
the final identity gate because `data.shuffle: false` makes row order part of
262-
the benchmark input.
291+
`RL/tools/train-instance-ids.txt`, and `RL/tools/val-instance-ids.txt`. The
292+
converter validates row structure, verifies that all requested IDs were
293+
found, and rejects overlapping train and validation ID sets. The verification
294+
script checks the resulting files against the row counts, sizes, and SHA-256
295+
identities above.
263296

264297
### R2E-Gym task containers
265298

266-
The qualified lists under `RL/tools` contain 685 training IDs and 256
267-
validation IDs, for 941 task environments used by the benchmark. The SIF
268-
builder's committed manifest is a 977-environment superset: it also contains
269-
the 36 training environments filtered out by the qualified training list.
270-
Those extra images are never selected by the generated JSONL. The helper below
271-
builds all 977 Arm Docker images, pushes them to a user-supplied registry, and
272-
converts them to SIF:
299+
The qualified dataset contains 700 training tasks and 251 validation tasks,
300+
for 951 task environments used by the benchmark. The SIF builder's committed
301+
manifest contains those 951 environments.
302+
303+
Download the SIF bundle for the target host architecture from
304+
[MLCommons storage](https://training.mlcommons-storage.org/index.html#qwen3-5-397b-benchmark).
305+
The qualified Arm reference uses the `aarch64` bundle:
273306

274307
```bash
275-
cd llm_moe_grpo/RL
308+
sif_package_dir="<shared-path>/r2egym-sifs-aarch64"
309+
310+
bash <(curl -s https://raw.githubusercontent.com/mlcommons/r2-downloader/refs/heads/main/mlc-r2-downloader.sh) \
311+
-d "${sif_package_dir}" \
312+
https://training.mlcommons-storage.org/metadata/r2egym-sifs-aarch64.uri
313+
314+
export NEMO_GYM_SWE_SIF_DIR="${sif_package_dir}/images"
315+
```
316+
317+
For an x86_64 host, use
318+
`https://training.mlcommons-storage.org/metadata/r2egym-sifs-x86_64.uri`
319+
instead. Both packages store the SIF files under `images/`.
320+
321+
#### Optionally, build the Arm SIF files from source
322+
323+
The helper below builds all 951 Arm Docker images, pushes them to a
324+
user-supplied registry, and converts them to SIF:
325+
326+
```bash
327+
cd llm_post_training/RL
276328

277329
export DOCKER_REGISTRY="<registry>"
278330
export DOCKER_USER="<registry-user>"
@@ -326,7 +378,7 @@ installation requires them.
326378

327379
## Run and time the benchmark
328380

329-
Run the submission wrapper from the `llm_moe_grpo/RL` directory. Supply the
381+
Run the submission wrapper from the `llm_post_training/RL` directory. Supply the
330382
container, external data configuration, shared result root, and Slurm routing:
331383

332384
```bash
@@ -436,11 +488,11 @@ The task data originates from
436488
[`R2E-Gym/R2E-Gym-Subset`](https://huggingface.co/datasets/R2E-Gym/R2E-Gym-Subset).
437489
The fixed easy split retains tasks whose environments can be built for the
438490
target architecture and whose source changes satisfy the easy-subset
439-
selection. The qualified instance-ID files under `RL/tools` define disjoint
440-
685-task training and 256-task validation sets.
491+
selection. The qualified MLCommons package defines disjoint 700-task training
492+
and 251-task validation sets.
441493

442-
The benchmark trains on the fixed 685-row curriculum-v2 artifact identified in
443-
Section 2. It validates on all 256 held-out tasks. Runtime data shuffling is
494+
The benchmark trains on the fixed 700-row artifact identified in Section 2. It
495+
validates on all 251 held-out tasks. Runtime data shuffling is
444496
disabled, so training row order is preserved.
445497

446498
## NeMo Gym and OpenHands configuration
@@ -487,8 +539,10 @@ Relevant environment settings are:
487539
| Validation concurrency | 256 |
488540
| Training agent timeout | 1,800 seconds |
489541
| Validation agent timeout | 1,800 seconds |
490-
| Training test timeout | 300 seconds |
491-
| Validation test timeout | 180 seconds |
542+
| Training test timeout | 60 seconds |
543+
| Validation test timeout | 60 seconds |
544+
| Training command timeout | 60 seconds |
545+
| Validation command timeout | 60 seconds |
492546
| Task runtime | Apptainer/Singularity SIF |
493547
| Model reasoning parser | `qwen3` |
494548
| Tool parser | `qwen3_xml` |
@@ -566,7 +620,7 @@ The pinned runtime identities are:
566620

567621
| Component | Version | Revision or source |
568622
|---|---|---|
569-
| NeMo-RL | source reports `0.6.0` | `3fca04c9b313d313302923a5bb6b0c8dc0340ed6` |
623+
| NeMo-RL | source reports `0.6.0` | `f987c0596af2bd6cfb9958e768deb8df951d7504` |
570624
| NeMo Gym | `0.4.0rc0` | `610a08ab5fe9f8f5fb5fff36b170429ea67f0f92` |
571625
| Megatron Bridge | `0.6.0` | `554c7b9324225aa863eee52e8b8fdde7abced2b1` |
572626
| Megatron Core | `0.19.0` | `002255075c3728fded9a2e435677840b08560d55` |
@@ -662,15 +716,15 @@ measured run.
662716

663717
## Quality metric
664718

665-
The quality metric is observed grouped `pass@4` on the 256-task validation
719+
The quality metric is observed grouped `pass@4` on the 251-task validation
666720
set. Each task is evaluated with four independently sampled trajectories. A
667721
task passes when at least one of its four rewards is positive:
668722

669723
```text
670724
pass@4 = mean_over_tasks(any(reward[task, generation] > 0))
671725
```
672726

673-
One complete validation therefore executes 1,024 agent trajectories. NeMo-RL
727+
One complete validation therefore executes 1,004 agent trajectories. NeMo-RL
674728
also reports all-four-pass `pass^4` and average pass@1 across the four
675729
trajectories as diagnostics, but the MLPerf `eval_accuracy` and convergence
676730
decision use `pass@4`.
@@ -683,8 +737,8 @@ The quality target is:
683737
pass@4 >= 0.69
684738
```
685739

686-
On a 256-task validation set, the first representable score meeting this
687-
threshold is `177 / 256 = 0.69140625`: at least 177 held-out tasks must be
740+
On a 251-task validation set, the first representable score meeting this
741+
threshold is `174 / 251 = 0.6932270916`: at least 174 held-out tasks must be
688742
solved by one or more of their four attempts.
689743

690744
The checked-in RCP logs provide six independent seeds at each qualified GBS:
@@ -695,10 +749,9 @@ The checked-in RCP logs provide six independent seeds at each qualified GBS:
695749
| 512 | `rcp_logs/512/seed_*.out` | 6 / 6 | Step 10 | 0.6953125 - 0.73046875 |
696750
| 1024 | `rcp_logs/1024/seed_*.out` | 6 / 6 | Step 7 | 0.70703125 - 0.75000000 |
697751

698-
All 18 runs therefore reach the target at the first scheduled validation.
699-
This is stable observed convergence at the configured evaluation schedule; it
700-
does not imply zero latent crossing-step variance before the first
701-
observation.
752+
All 18 runs reach the target at the first scheduled validation. This is stable
753+
observed convergence at the configured evaluation schedule; it does not imply
754+
zero latent crossing-step variance before the first observation.
702755

703756
When an evaluation reaches the target, the MLPerf logger emits a successful
704757
`run_stop` and terminates training. The submission wrapper must receive
@@ -713,16 +766,35 @@ qualified time-to-target run.
713766
| 512 | No | 10 | Every step | No | 20 |
714767
| 1024 | No | 7 | Every step | No | 10 |
715768

716-
The validation dataloader uses the entire 256-task validation JSONL at every
769+
The validation dataloader uses the entire 251-task validation JSONL at every
717770
evaluation. The four trajectories for each task use validation temperature
718771
`0.1` and top-p `0.95`.
719772

720-
Each validation executes 1,024 complete agent trajectories, so evaluation is
773+
Each validation executes 1,004 complete agent trajectories, so evaluation is
721774
substantially more expensive than a conventional forward-only validation
722775
pass. The schedule delays the first evaluation to the GBS-specific RCP
723776
convergence window, then evaluates every step so a later crossing is detected
724777
within one additional training step.
725778

779+
## Offline evaluation
780+
781+
The benchmark rules require offline evaluation for this benchmark:
782+
`qwen35_397b_grpo` does not validate inline. Training must save a checkpoint
783+
after every step from the table's first-evaluation step until training stops,
784+
each checkpoint must record the timestamp of its latest weight update, and the
785+
saved checkpoints are evaluated after the run until one reaches the target
786+
accuracy. The run's `run_stop` event is emitted with the passing checkpoint's
787+
weight-update timestamp, so checkpoint writing and evaluation time are
788+
excluded from the measured time.
789+
790+
With `DEFERRED_OFFLINE_EVAL=1` (see `RL/docker/mlperf/README.md`), the
791+
reference implements this directly: training runs without inline validation
792+
and saves a weights-only checkpoint after every step from the table's
793+
first-evaluation step through the final step, recording each checkpoint's
794+
weight-update end time in its metadata. After training stops, the launcher
795+
evaluates the saved checkpoints in the same allocation, in step order,
796+
stopping at the first checkpoint that reaches the target.
797+
726798
# 6. Approximate runtime
727799

728800
The reference results were produced on NVIDIA GB300 NVL72 racks, using 64

0 commit comments

Comments
 (0)