feat(sc): run the distillation teacher in the SingleController train pump - #3846
feat(sc): run the distillation teacher in the SingleController train pump#3846tianyi-zhang-02 wants to merge 9 commits into
Conversation
95fdc27 to
c649f5f
Compare
…troller
Distillation is the last algorithm with a rollout loop that SingleController
cannot run. Its teacher is a Policy, not a separate model class, so the
SingleController side needs no new driver -- only the missing top-k
entrypoint on the two layers every other forward already has:
- TQWorkerMixin.get_topk_logits_presharded: per-rank fetch -> forward ->
write-back. Unlike its siblings it writes back two tensors, and both
carry a third axis ([B, S, k]); the write-back validates only the batch
dimension, so that axis passes through unchanged.
- TQPolicy.get_topk_logits_from_meta: the 1-hop dispatch, reusing
LP_SEED_FIELDS because a teacher forward needs exactly what a logprob
forward needs.
Nothing calls these yet -- wiring the teacher into the train pump is a
follow-up. Splitting it out keeps this piece independently testable.
Signed-off-by: Tianyi Zhang <zhangtianyi975@gmail.com>
Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
…pump Builds on the teacher top-k forward and makes SingleController able to run a distillation step end to end. Distillation turns out to fit the existing shape almost exactly. Its teacher is a Policy, so it is a second TQPolicy rather than a class of its own, built and parked exactly the way the PPO critic is -- serially, with the trainer offloaded, because both worker groups sit on the training GPUs. What is different is the batch. DistillationLossFn reads only the sequence columns and the teacher's top-k: no importance ratio, no reference KL, no advantages. So the train pump skips both logprob forwards and the whole advantage stage, and the fetched column set is narrowed accordingly -- DP_TRAIN_FIELDS names advantages and the logprob columns, and fetching a column nobody wrote errors out rather than reading zeros. That narrowing needs train_microbatches_from_meta to take the train_fields argument train_from_meta already had. MasterConfig now admits a third algorithm block, still exactly one at a time, and a `teacher` block is required by distillation and rejected on every other path -- a teacher nobody reads is the same silent no-op this path already rejects unsupported algorithm knobs for. The recipe and the functional test are a follow-up. Signed-off-by: Tianyi Zhang <zhangtianyi975@gmail.com> Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
Two things a distillation run hits before any model loads. The legacy-async check read `config.grpo.async_grpo` on everything that was not PPO. `grpo` is None on a distillation run, so the launcher died with an AttributeError. DistillationConfig has no legacy async block at all -- distillation never had a v1 async path -- so there is nothing to reject. And the teacher's vocabulary is now checked against the student's. The teacher writes top-k *indices*, which the loss reads back as student vocabulary ids: a teacher on a different vocabulary produces indices that are silently wrong rather than an error. distillation.py already runs this check; SC skipped it. Same helper, same NRL_SKIP_DISTILLATION_TOKENIZER_CHECK opt-out, and it runs before the first model load so a mismatch costs seconds instead of a full spin-up. Signed-off-by: Tianyi Zhang <zhangtianyi975@gmail.com> Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
c649f5f to
f9a50cf
Compare
|
Heads-up on merge order, with the resolution written out. Four of my open PRs edit the same twelve-line block — the
I checked every pair by actually merging them. #3786 × #3787 is clean now — they only ever conflicted on the comment wording and on both adding a test before the same anchor, and both are fixed. The remaining four pairs are a genuine textual conflict on that block, and no restructuring avoids it: extracting or reordering the list conflicts just as hard. So rather than force a stack, here is the resolution. Whichever order they land in, this is the merged form: # An enabled one here describes shaping this run does not do. An entry
# leaves this list when the SC path starts implementing it -- rejecting a
# knob is only right while nobody honours it.
#
# DistillationConfig defines none of them: there is no reward to shape or
# filter on, so the list cannot even be evaluated on that path.
unsupported = (
[]
if is_distillation_run(master_config)
else [
name
for name, enabled in (
("use_dynamic_sampling", algo_cfg.use_dynamic_sampling),
("reward_scaling", algo_cfg.reward_scaling.enabled),
("reward_shaping", algo_cfg.reward_shaping.enabled),
)
if enabled
]
)I built that merge locally and ran the SC and config suites against it — 1084 passed. Happy to rebase whichever ones are left once the first lands; just say which order you want. |
18bd079 to
5305a62
Compare
The guard used to wrap that list in a conditional, which put this PR on the same twelve lines as NVIDIA-NeMo#3786 and NVIDIA-NeMo#3787 -- each of those removes an entry from it. Four pairwise conflicts on nothing but placement. Returns early instead. DistillationConfig defines none of the knobs the list names, so the check does not apply and the comprehension could not be evaluated anyway; everything after it is PPO-specific and returns early on this path already. The colocated requirement is the one thing that does apply, so it moves into a helper and the guard calls it directly rather than falling through. The list itself is now untouched by this PR, so all four conflicts go away and none of the three needs to land before the others. Signed-off-by: Tianyi Zhang <zhangtianyi975@gmail.com> Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
5305a62 to
364f5c4
Compare
# Conflicts: # nemo_rl/algorithms/single_controller.py # nemo_rl/algorithms/single_controller_utils/setup.py
validate_single_controller_config routed distillation around
_validate_algo_settings entirely, to dodge one check inside it -- the
reward shaping and filtering knobs, which DistillationConfig does not
declare, so reading them raises AttributeError.
That dropped three checks that are not about GRPO at all and whose own
comments say so: the max_num_epochs<=0 guard (the rollout pump gates on
it whatever the algorithm), the warmup_lookahead_versions capacity guard
('Capacity is sized from the peak window whatever the algorithm'), and
the value-without-ppo guard. A distillation config setting any of them
was silently accepted where GRPO raises -- the same silently-accepted
no-op this file's shaping check exists to prevent.
Read the four shaping knobs defensively instead, so distillation stays
on the one code path and only the check that cannot apply is skipped.
Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
Signed-off-by: Tianyi Zhang <zhangtianyi975@gmail.com> # Conflicts: # nemo_rl/algorithms/single_controller.py
Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
Signed-off-by: Tianyi Zhang <123608656+tianyi-zhang-02@users.noreply.github.com>
What does this PR do?
Wires distillation into the SingleController train pump. It builds and parks a teacher
TQPolicy, scores each chunk, writes the teacher top-k tensors to TransferQueue, narrows the student train fields, and skips the reward/advantage work that distillation does not use.The same change also closes the setup gaps exposed by the first real run: teacher schema registration and shutdown, teacher
train_iters, tokenizer compatibility, distillation-safe algorithm validation, and the missing setup timing.This is the middle PR in the #3843 → #3846 → #3849 stack. The recipe and checkpoint/restore functional test stay in #3849.
Ownership
The stack is on hold because its core teacher top-k entry point overlaps the still-open #2580. I am keeping this branch tested and current, but not extending it until that ownership question is resolved.
Validation
b8073a3d894bbad0f7c5a6c67c4faa92dfbe795eis aligned with upstreammainatccbcd4cc5442784f6af2288dd99021560480b8f2.907606048aa8c950027dbc199bff1495035e862cpassed 765 cases with 6 GPU-only skips.NCCL_NVLS_ENABLE=0was set for this Runpod H100 topology, matching the workaround already used by H100 recipes in this repository.