Skip to content

refactor(train): drive SFT/RL training through AutoModel's DeepSpeed-style Engine - #92

Draft
HuiyingLi wants to merge 58 commits into
mainfrom
huiyingl/feat/sft-automodel-engine-integration
Draft

refactor(train): drive SFT/RL training through AutoModel's DeepSpeed-style Engine#92
HuiyingLi wants to merge 58 commits into
mainfrom
huiyingl/feat/sft-automodel-engine-integration

Conversation

@HuiyingLi

@HuiyingLi HuiyingLi commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Molt stops maintaining its own training-execution loop and drives SFT and RL through AutoModel's DeepSpeed-style eager Engine, following the OpenRLHF↔DeepSpeed boundary:

model_output = actor(sequences, action_mask, ...)
loss = policy_loss(model_output.action_log_probs, old_log_probs, advantages, ...)
actor.model.backward(loss)
actor.model.step()

The Engine is a thin nn.Module wrapper (forward / backward(loss) / step() / zero_grad() / get_global_grad_norm()) around an already-distributed model. There is no Datum, loss callback, output envelope, or packing protocol at this boundary — AutoModel stays RL-oblivious.

Net diff: +2594 / −2936 across 49 files.

Main changes

  • Training execution delegated to Engine. FsdpStrategy loses its duplicate backward / gradient-sync / clip / optimizer / accumulation loop; SFT, policy, and critic trainers call engine.backward(loss) + engine.step() directly, with the RL formula staying in the trainer.
  • Model layer split by responsibility. models/base.py (918 → ~330 lines) keeps only the runtime forward contract; checkpoint-loading policy moves to models/loading.py, THD/VLM packing to models/packing.py, and the critic value head to models/critic.py.
  • Vocab-parallel primitives move to AutoModel. trainer/fsdp/packing.py is deleted; under TP the lm-head output is a vocab-sharded DTensor and log-prob/entropy come from AutoModel's token_log_probs / token_entropy, which handle dense and vocab-sharded logits without gathering the vocabulary.
  • HybridEP unequal-token handling deleted. Token-count equalization is fixed inside AutoModel's dispatcher (fix(moe): equalize and align per-rank token counts for HybridEP dispatch Automodel#3641, included in the pin), so molt's caller-side padding (~90 lines) is gone.
  • Legacy paths removed. HF-fallback MoE training and the FA2 varlen THD adapter are intentionally unsupported (fail fast); MoE requires an AutoModel-native implementation.

Ownership boundary

Molt owns:

  • logical batch → model inputs (padding, THD packing, CP/R3 contexts, VLM prep) and restoring dense token outputs
  • PPO/GSPO/CISPO, KL, entropy, value objectives; global token normalization
  • the critic value head: a plain fp32 module installed after AutoModel wraps the backbone, replicated across ranks (rank-0 broadcast, sync_replicated_grads before the step)
  • CPU optimizer offload (CpuOptimizerOffloader: AdamW step on CPU, params stay on GPU; --fsdp.offload none|optimizer)
  • optimizer/scheduler construction, checkpoint policy, vLLM refit, Ray orchestration, metrics

AutoModel owns:

  • distributed model construction and execution (FSDP2/TP/CP/EP), including HybridEP token equalization
  • Engine: backward, gradient finalization, clipping, optimizer update, gradient clearing
  • vocab-parallel token log-probability / entropy primitives; routing-replay adapter

Details: docs/automodel-engine-integration.md.

Validation

  • Unit: tests/unit green (234) in the 26.06 molt container; ruff + compileall clean.
  • SFT: 2-GPU FSDP2 parity (loss/grads/updates vs reference) and 8-GPU smoke.
  • RL: MoE RL smoke with CPU optimizer offload (Qwen3-30B-A3B class, 65.5 GB peak, nonzero grads on hard-math data); GAE critic smoke with the post-wrap value head (value_loss 2.79 → 2.27).
  • Parity on AutoModel side of the boundary: DSV4 PP2/EP2, CP+TP and dense packed THD runs match main.

Known gaps

  • PP > 1 is not wired into the molt trainers yet.
  • RL LoRA blocked by vLLM refit (ignores lora_A/B); TE GroupedLinear EP experts cannot refit to vLLM.
  • FP16 fails fast (bf16/fp32 contract only).
  • Intentional fail-fasts: mRoPE + packed THD with CP>1, HF-fallback MoE/THD packing, R3 + partial rollout/MTP.
  • PyPI nemo-automodel lacks the Engine API; requirements.txt pins the AutoModel source revision (78cac7159).

🤖 Generated with Claude Code

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
@HuiyingLi
HuiyingLi requested a review from hijkzzz August 23, 2026 07:52
…mments

Merge the duplicated packing/HF-fallback/aux-loss/routing-replay validation
from the two BaseModel construction branches into one shared
_finalize_model_setup, drop the dead get_flat_mesh import fallback, make the
cross-module debug helper public, and cut war-story comments down to concise
why-only notes per AGENTS.md.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
…itic docs

Experience's two hand-rolled recursive tensor walkers collapse into a single
_map_tensors helper, and the Critic/value-head docstrings shrink to the
why-only form AGENTS.md asks for.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
… consumer

pack_padded_batch/unpack_to_padded serve BaseModel's input preparation, not the
FSDP strategy layer; molt/trainer/fsdp/packing.py becomes molt/models/packing.py.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
BaseModel now owns only the runtime forward contract (padded/packed/VLM/CP/R3
input preparation, backbone forward, dense output restore). Everything about
turning a path into a distributed AutoModel module — registry probing, MoE
detection, kernel-backend selection, MTP disabling, fail-fast validation, and
post-load configuration — moves to molt/models/loading.py behind two entry
points: load_automodel() and configure_loaded_model().

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Actor/Critic now return an ordinary dict of tensors and every consumer uses
subscript access, removing the dual attribute/mapping magic. _forward_backbone
returns the logits tensor directly (nothing consumed the rest of the backbone
output, including the dead output_hidden_states knob), and the VLM
placeholder-id probe is inlined where it runs.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
- promote agents.base._first_scalar to first_scalar; drop the cross-module
  private import alias in samples_generator
- inline the single-call _iter_nemo_moe_gates and critic hidden-size probe
- freeze_moe_router imports Gate directly: nemo_automodel is a hard dependency,
  and the ImportError fallback silently skipped a requested freeze
- drop SFTTrainer.save_logs_and_checkpoints's unused step_bar parameter

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
_get_automodel_group folds into _get_dp_group, and _get_automodel_mesh drops
its dead try/except: 'required' was always true past the device_mesh early
return, so the except branch could only re-raise. Pin nemo-automodel to the
matching Engine commit.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
The AutoModel HybridEP dispatcher now equalizes and aligns per-rank token
counts itself (padded rows route to no expert and are sliced off after
combine), so BaseModel no longer probes dispatcher internals or pads packed,
padded, and VLM inputs to an EP-group width; pack_padded_batch loses its
pad_to_tokens escape hatch. Pin nemo-automodel to the dispatcher-fix commit.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
pack_vlm_batch is the VLM counterpart of pack_padded_batch (dense RL batch ->
model-native packed layout + restore indices), so it belongs in
molt/models/packing.py; BaseModel passes its model-derived state (token ids,
layout, cp_size, module) explicitly.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
--fsdp.attn_implementation gains choices=, so loading.py's duplicate validator
and its three choice-set constants go away (only the transformer-engine
importability check remains, which argparse cannot do). Also drop
_will_use_hf_model's never-passed default parameter and a single-use dtype
variable.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
@HuiyingLi
HuiyingLi force-pushed the huiyingl/feat/sft-automodel-engine-integration branch from f451772 to e25ce7e Compare August 24, 2026 09:39
HuiyingLi and others added 7 commits August 24, 2026 13:33
…load

Restore molt/trainer/fsdp/optimizer_offload.py: CpuOptimizerOffloader runs the
AdamW step on CPU with params resident on GPU, now wrapping the optimizer with
the step()/zero_grad() surface the AutoModel Engine drives (checkpointing and
the LR scheduler keep the wrapped optimizer). --fsdp.offload choices become
none/optimizer; the AM FSDP2 CPUOffloadPolicy 'full' mode, its gloo backend,
the refit CPU-staging hunk, and its tests are removed. Checkpoint resume pages
Adam moments back to CPU as before. Pin nemo-automodel to the matching revert
commit.

8-GPU SFT smoke with --fsdp.offload optimizer converges identically to the
GPU-step run (numerically equivalent, not bit-identical).

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Restore the pre-integration design: the value head is a plain replicated
_ValueHead module installed after AutoModel's FSDP wrap (rank-0 broadcast for
identical replicas, tie_word_embeddings cleared), and the critic trainer
DP-all-reduces its gradient before each optimizer step via the restored
FsdpStrategy.sync_replicated_grads. The pre_fsdp_hook plumbing through
load_automodel is removed. Pin nemo-automodel to the matching revert commit.

Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
@HuiyingLi HuiyingLi changed the title refactor(engine): delegate SFT and RL execution to AutoModel refactor(train): drive SFT/RL training through AutoModel's DeepSpeed-style Engine Aug 25, 2026
HuiyingLi and others added 6 commits August 24, 2026 20:54
…el pin

The AutoModel branch dropped its unused datasets.vlm package re-exports;
import pack_vlm_samples, merge_media_values, and the collaters from
their defining modules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant