Skip to content

[DMD2 3/10] feat(ray): hand earlier trainer groups' outputs to later groups as external_data - #330

Open
zhihengy wants to merge 2 commits into
mainfrom
dmd2/02-external-data
Open

zhihengy wants to merge 2 commits into
mainfrom
dmd2/02-external-data

Conversation

@zhihengy

@zhihengy zhihengy commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Third of the DMD2 stacked series, on top of #329. A trainer group can now hand a per-rank result to the groups that train after it on the same rollout. For DMD2, the critic group uses this to pass the teacher's and the critic's predictions to the actor.

What

  • New --custom-train-output-function-path: fn(ctx, train_pairs) -> Any, run without grad after a rollout's train steps. FSDPTrainRayActor.train returns its value (None without the hook).
  • train_diffusion.py keeps each group's per-rank train refs for the current rollout and passes the earlier groups' refs to the next group.
  • RayTrainGroup.async_train(..., external_data): rank i receives rank i's ref from each earlier group, the pairing upstream miles uses for its critic values. Both groups share the world size and read the same rollout shard, so the paired ranks hold the same train pairs.
  • The receiving rank resolves the refs and exposes them to its prepare/loss hooks as ctx.external_data[<role>]. Without earlier groups this is {}.
  • --teacher-load also counts the new hook as a consumer of ctx.teacher_models.

Why

Upstream miles hands the critic's values to the actor the same way: critic.train returns one output per worker, and actor.train(..., external_data=values) gives worker i the output of critic worker i. Here the hand-off goes through Ray object refs, so the payload never passes through the driver and is freed once the rollout's refs go out of scope. The producer side is a hook, like the existing prepare/loss hooks, so the trainer stays role-agnostic.

Tests

  • tests/fast/rollout/test_placement_group.py: each rank receives the same-rank output of every earlier group, and an empty dict without them.
  • tests/fast/test_train_diffusion.py: the second group receives the first group's refs from the same rollout, and the first group receives nothing.
  • tests/fast/utils/test_reference_arguments.py: the train-output hook alone counts as a consumer of --teacher-load.
  • tests/fast/rollout/test_async_training.py, tests/fast/backends/fsdp_utils/test_ema_optimizer.py: updated for the new train signature.

Checklist

  • pre-commit hooks (black, isort, ruff at the pinned versions) pass on the changed files
  • Added/updated tests for new behaviour (rank pairing, driver hand-off)
  • pytest tests/fast is green (520 passed, 6 skipped; 518 on [DMD2 2/10] feat(ray): train several colocated trainer groups in the sync driver #329)
  • If launch flags changed, python3 train_diffusion.py --help still parses (the new flag is listed)
  • If a public flag was added, it appears in the CLI reference docs (--custom-train-output-function-path in cli-reference.md and customization.md)
  • If an example was added, it has a real walkthrough (no examples added)

Stack

DMD2: distill a flow-matching teacher into a few-step student with distribution matching. Merge in order:

  1. [DMD2 1/10] [DMD2 1/10] refactor(ray): build each trainer's args from a trainer config list #335: per-trainer args from a trainer config list
  2. [DMD2 2/10] [DMD2 2/10] feat(ray): train several colocated trainer groups in the sync driver #329: several colocated trainer groups in the sync driver
  3. [DMD2 3/10] this PR: hand earlier trainer groups' outputs to later groups as external_data
  4. [DMD2 4/10] [DMD2 4/10] feat(fsdp): add a train-input hook that preprocesses train pairs before micro-batching #338: a train-input hook that preprocesses a trainer group's train pairs before micro-batching
  5. [DMD2 5/10] [DMD2 5/10] feat(rollout): stop the rollout at a random step for DMD2 backward simulation #331: few-step rollout and the DMD2 step strategy (needs [Feat] diffusion-rl: stop a rollout at a requested step sgl-project/sglang#45230)
  6. [DMD2 6/10] [DMD2 6/10] feat(dmd2): train a fake-score critic group from the teacher and score the student's samples #333: DMD2 critic group: critic training and teacher/critic scoring of its last step
  7. [DMD2 7/10] [DMD2 7/10] feat(dmd2): train the student with the distribution matching gradient from the critic group's scores #334: --loss-type dmd2 on the actor
  8. [DMD2 8/10] [DMD2 8/10] perf(ray): sleep each trainer group right after its train #342: sleep each trainer group right after its train
  9. [DMD2 9/10] [DMD2 9/10] feat(dmd2): add the MiniMax H3 4-step DMD2 recipe with eval media and DMD2 metrics #343: MiniMax H3 4-step DMD2 recipe, eval media and DMD2 metrics
  10. [DMD2 10/10] [DMD2 10/10] feat(dmd2): add --dmd2-critic-scoring original, the reference's separate scoring before the critic's last step #351: --dmd2-critic-scoring original, the reference's separate scoring before the critic's last step

A GAN head and other follow-ups come after this series.

@zhihengy
zhihengy added this pull request to stack #332 October 19, 2026 20:42
@zhihengy
zhihengy marked this pull request as ready for review October 19, 2026 22:13
@zhihengy zhihengy added the run-ci-basic Authorize and run only basic unlabeled CI tests label Oct 19, 2026
@zhihengy
zhihengy removed this pull request from stack #332 October 21, 2026 06:08
@zhihengy
zhihengy force-pushed the dmd2/02-external-data branch from 7a190d5 to d81a40e Compare October 21, 2026 06:08
@zhihengy
zhihengy added this pull request to stack #336 October 21, 2026 06:08
@zhihengy zhihengy changed the title [DMD2 2/7] feat(ray): hand earlier trainer groups' outputs to later groups as external_data [DMD2 3/8] feat(ray): hand earlier trainer groups' outputs to later groups as external_data Oct 21, 2026
@zhihengy
zhihengy removed this pull request from stack #336 October 24, 2026 00:08
@zhihengy
zhihengy added this pull request to stack #339 October 24, 2026 00:09
@zhihengy zhihengy changed the title [DMD2 3/8] feat(ray): hand earlier trainer groups' outputs to later groups as external_data [DMD2 3/9] feat(ray): hand earlier trainer groups' outputs to later groups as external_data Oct 26, 2026
@zhihengy zhihengy changed the title [DMD2 3/9] feat(ray): hand earlier trainer groups' outputs to later groups as external_data [DMD2 3/10] feat(ray): hand earlier trainer groups' outputs to later groups as external_data Nov 2, 2026
@Rockdu
Rockdu force-pushed the dmd2/02-external-data branch from d81a40e to ac2c065 Compare November 4, 2026 19:41
Comment thread train_diffusion.py
logger.info(f"train: rollout {rollout_id} {role} train start")
ray.get(train_group.async_train(rollout_id, rollout_data_ref))
train_outputs[role] = train_group.async_train(rollout_id, rollout_data_ref, train_outputs)
ray.get(train_outputs[role])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation can pull the whole training output into our controller, we should return ray.put(train_output) in train in this case

@Rockdu Rockdu left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another from agent:
[Nit] docs/fsdp_model_roles.md:40 is stale after this PR: it still says --teacher-load requires a custom prepare/loss hook, but the validator now also accepts --custom-train-output-function-path alone (arguments.py:1735-1743), as cli-reference.md already says. Suggest requires a custom prepare/loss/train-output hook. (The file is outside this diff, so no inline suggestion.)

zhihengy added a commit that referenced this pull request Nov 9, 2026
`compute` took the old raw-ref hand-off: it ray.get the external_data
values directly and returned its output unboxed. With train's output now
boxed (#330), unbox `box.inner` here too and return `Box(ray.put(output))`,
so the driver's ray.get on a compute phase only waits as well.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
@zhihengy zhihengy added the run-ci-e2e Run e2e metric-regression tests on this PR label Nov 9, 2026
Base automatically changed from dmd2/01-trainer-groups to main November 10, 2026 03:59
`train` returned its train output directly, so the driver's ray.get on the
train refs pulled every rank's output into the controller before the next
group read it again from the object store. Return `Box(ray.put(output))`
like the rollout shards: the driver gets a small box, the payload stays in
the store until the paired rank of a later group unboxes it.

Also note the train-output hook as a `--teacher-load` consumer in
docs/fsdp_model_roles.md, which cli-reference.md already did.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
@zhihengy
zhihengy force-pushed the dmd2/02-external-data branch from e67f3de to 4a513a8 Compare November 10, 2026 03:59

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci-basic Authorize and run only basic unlabeled CI tests run-ci-e2e Run e2e metric-regression tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants