Repository navigation
Conversation
This was referenced Oct 19, 2026
Open
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
October 19, 2026 23:26
b9b818c to
79e0d90
Compare
4 tasks done
zhihengy
marked this pull request as ready for review
October 21, 2026 05:56
6 tasks done
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
October 21, 2026 06:08
79e0d90 to
4b58472
Compare
zhihengy
commented
Oct 21, 2026
Comment on lines
+14
to
+15
| if args.loss_type != "dmd2": | ||
| return [TrainerConfig(role="actor")] |
Collaborator
Author
There was a problem hiding this comment.
Not very elegant to leave the whole logic of training config re-ride of dmd2 here, but since dmd2 is the only multi-trainer algo supported I will just leave it here. Need a brief refactor in the future, won't be a major blocker.
zhihengy
force-pushed
the
dmd2/04-critic
branch
2 times, most recently
from
October 23, 2026 23:47
b7cb51e to
d3a03db
Compare
6 tasks done
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
October 24, 2026 00:09
d3a03db to
7b72bd6
Compare
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
October 24, 2026 00:17
7b72bd6 to
9f074b8
Compare
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
October 26, 2026 06:19
9f074b8 to
7a83c73
Compare
5 of 6 tasks
Draft
5 of 6 tasks
5 of 6 tasks
Rockdu
force-pushed
the
dmd2/04-critic
branch
from
November 4, 2026 19:41
7a83c73 to
3d42d0e
Compare
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
November 9, 2026 22:16
3d42d0e to
5c36a60
Compare
Rockdu
reviewed
Nov 10, 2026
| teacher_models: dict[str, torch.nn.Module] = field(default_factory=dict) | ||
| external_data: dict[str, Any] = field(default_factory=dict) | ||
| # Filled by loss hooks during one train call and read by the train-output hook. | ||
| train_output_inputs: list[Any] = field(default_factory=list) |
Collaborator
There was a problem hiding this comment.
optim_step_idx and train_output_inputs change during training; better to pass them as hook arguments / return values instead of mutating the ctx, which should stay rollout-level and read-only.
…e the student's samples
zhihengy
force-pushed
the
dmd2/04-critic
branch
from
November 10, 2026 03:59
5c36a60 to
4e5688e
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sixth of the DMD2 stacked series, on top of #331.
--loss-type dmd2adds acritictrainer group that trains before the actor on every rollout. The critic starts from the teacher by default, learns the student's samples, and scores its last step's samples with the frozen teacher and itself, before that step's update. The actor's generator loss arrives in [DMD2 7/10]; until then--loss-type dmd2takes the actor's prepare and loss hooks from the user.What
--loss-type dmd2selectsdata_conversion_hub.dmd2.expand_samples_to_train_pairs. Each sample comes from a rollout thatstop_at_random_step([DMD2 5/10] feat(rollout): stop the rollout at a random step for DMD2 backward simulation #331) stopped at stepi, and becomes one pair:latent = x_i,x0 = x̂0_i,timestep,sigma = σ_i,step_index = i, the cond and the sample index. Rewards are ignored.Under
dmd2,compute_trainer_configs(miles/utils/trainer_config.py, [DMD2 1/10] refactor(ray): build each trainer's args from a trainer config list #335) lists acritictrainer before the actor; other loss types still get only the actor. The critic's overrides:--hf-checkpointis the new--critic-load, which defaults to--teacher-load;--lris the new--critic-lr, which defaults to--lr;--load/--savepoint to sibling<dir>_criticdirs, the upstream--critic-savedefault;num_steps_per_rolloutis--dmd2-critic-steps-per-rollout(K, default 5), over the same DP shard as the actor;loss_hub.dmd2.The actor's only override drops
--teacher-load, so only the critic group holds the teacher.Critic steps (
prepare_critic_batch,critic_loss_formula) use the SFT flow-matching loss on the student'sx̂0_iwith the rollout's positive cond. The prepare hook builds SFT-shaped pairs and callsprepare_sft_batch, and the loss issft_loss_formula, so the critic's inputs, targets, seeds and loss are exactly SFT's. The one exception is the last of the K steps: its σ is drawn only from the grid points whose unshifted σ lies in--dmd2-score-sigma-range(default 0.02–0.98, the reference scoring range), through a new optionalgrid_index_rangeofprepare_sft_batch. Unset, SFT is unchanged.--dmd2-critic-loss-space(defaultvelocity) picks the space of the critic's loss. Underx0,critic_loss_formulahandssft_loss_formulathe x0 predictionx_s − σ·vand the x0 target instead of the velocity pair, so the loss is the x0 MSE, i.e. σ² times the velocity MSE.Scoring reuses the last step's training forward.
critic_loss_formulakeeps each pair's noisy inputx_s, its σ and timestep, andx0_fake = x_s − σ·v_fake, all detached. This is the critic after K−1 steps, before the step that trains on these samples.score_last_step_samples, the train-output hook from [DMD2 3/10] feat(ray): hand earlier trainer groups' outputs to later groups as external_data #330, runs after the K steps, when no critic graph is alive. The teacher predicts at the keptx_swith CFG--dmd2-teacher-guidance-scaleover the rollout's negative cond, givingx0_real.ctx.external_data["critic"]. Only the last step's samples are scored.EMAOptimizer.use_weightsdoes, so sleep moves shards.DiffusionLossContextfields carry this:optim_step_idx, set by the train loop, andtrain_output_inputs, which loss hooks fill for the train-output hook and which starts empty on each train call. Both are documented incustomization.md.Train metrics of a non-actor group carry its role:
train/critic-loss,train/critic-grad_norm,train/critic-lr. Upstream miles'log_train_stepdoes the same. The actor's keys don't change.validate_dmd2_argsinarguments.py:--teacher-load,--dmd2-teacher-guidance-scale >= 1, a visual--fsdp-flow-shiftand0 <= MIN < MAX <= 1in--dmd2-score-sigma-range;It only rejects; defaults stay in
set_default_diffusion_args.--critic-loadand--critic-lrwithout--loss-type dmd2are rejected because nothing would read them.Docs:
cli-reference.md,customization.md,fsdp_model_roles.md.Why
x0_faketoward the sample itself and biasg, so the scored batch is held out until the scoring forward.FSDPTrainRayActorwith SFT hooks and the teacher as its frozen model, so the only change to the class is the metric names. Upstream miles builds its PPO critic the same way._compute_critic_overridesoverrides load, save and lr; the critic trains first, and the actor receives its outputs asexternal_data.--guidance_lrnext to--generator_lr. FastVideo's DMD2 gives each of student, teacher and critic its owninit_from, and the critic its ownfake_score_learning_rate(8e-6 against the student's 2e-6 in its Wan recipe). Upstream miles' PPO critic has--critic-load/--critic-lr, defaulting to--load/--lr. Here the student, teacher and critic start from--hf-checkpoint,--teacher-loadand--critic-load.gagainst its own recomputed x0 in [DMD2 7/10], so g's normalizer uses the trainer's x0, as in the reference.--min_step_percent/--max_step_percent), the default here. FastGen draws its noising time insample_t_cfg.min_t..max_tbefore the shift, the convention--dmd2-score-sigma-rangefollows, and picks the critic's loss withfake_score_pred_type(floworx0). FastVideo's MiniMax H3 4-step recipe uses 0.001–0.999 and an x0-space critic (fake_score_loss_space: x0).Known gap
When guidance is 1 the engine doesn't encode the negative prompt, so
denoising_env.neg_cond_kwargsis empty. Teacher CFG above 1 then raises an explicit error. Returning the negative cond without running CFG needs a small sglang-miles change, which will be a follow-up. Until then the teacher can only run unguided.Tests
tests/fast/backends/fsdp_utils/test_loss_hub_dmd2.py:prepare_sft_batchon (x̂0_i, positive cond). On the last step it equals the same draw over grid points 2–98%. A custom--dmd2-score-sigma-rangebounds the unshifted σ, checked with an asymmetric range.sft_loss_formulaon every step. Under--dmd2-critic-loss-space x0the loss equals the x0 MSE and σ² times the velocity MSE, and the prepared batch is left as is.x_s, which is the reference computation at the shared draw.tests/fast/rollout/test_dmd2_convert.py: one pair per stopped sample. Full trajectories and trajectories that run past their first returned step are rejected.tests/fast/utils/test_trainer_config.py:--critic-loadat--critic-lr, with sibling checkpoint dirs and K steps.tests/fast/utils/test_dmd2_arguments.py: valid args pass unchanged, and each unsupported combination is rejected, including an empty, reversed or out-of-[0, 1]--dmd2-score-sigma-range.--critic-load/--critic-lrdefault to--teacher-load/--lrunless set, and are rejected withoutdmd2.Checklist
pre-commithooks (black, isort, ruff at the pinned versions) pass on the changed filespytest tests/fast: 550 passed, 7 skipped; 4 tests fail in this environment and fail the same way without this change (test_hybrid_shard_mesh::test_mesh_axesandtest_metric_buffer_dist::test_metric_buffer_reduction, whose torchrun children import another checkout, and twotest_sft_h3_ref2va_encodercases)python3 train_diffusion.py --helpstill parses (dmd2and the six new flags are listed)--dmd2-critic-steps-per-rollout,--dmd2-teacher-guidance-scale,--dmd2-score-sigma-range,--dmd2-critic-loss-space,--critic-load,--critic-lr)Stack
DMD2: distill a flow-matching teacher into a few-step student with distribution matching. Merge in order:
external_data--loss-type dmd2on the actor--dmd2-critic-scoring original, the reference's separate scoring before the critic's last stepA GAN head and other follow-ups come after this series.