Repository navigation
[megatron][vllm] Qwen3.8-Flash-Next (qwen4_exp) support: Megatron bridge, QSA sparse attention, GSM8K + DAPO recipes - #2513
Draft
xinyuangui2 wants to merge 41 commits into
Draft
xinyuangui2 wants to merge 41 commits into
xinyuangui2 wants to merge 41 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…r type; test health timeout Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…ider Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…output gate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
Qwen4Exp's ngram_context_offsets lives in the weights pool and came back as zeros after a level-2 sleep, corrupting PLE n-gram contexts after every weight sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…LE in recipe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…memory Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…memory_utilization 0.6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
NVLS buffers stay resident through offload/sleep (~24 -> ~12.5 GiB/GPU trainer residual at EP16), which blocked the adapter load at step 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp5LxobgMKPQJkiDxEXcef
…rity row on 4xH100 (EP4, vLLM TP4) make_slice.py replaces the ~51B-parameter PLE n-gram table with a small random one (ngram_vocab_size_base 2048, head sizes/offsets recomputed with the HF prime formula), so the 4-layer slice is 23 GB and fits the 4xH100 CI shape. Parity on 4xH100: forward |dlogp| 0.051, post-sync common prefix 0.021. The gsm8k recipe is renamed to the _lora_2node variant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
Zero adapter 0.052, stale sampler 0.239, updated adapter 0.056 (threshold 0.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…, FlexAttention fast path Megatron previously ran the QSA indexer only in its exactly-dense regime (<= indexer_budget + compress_ratio - 1 = 2051 tokens) and raised beyond. Qwen4ExpQSAIndexer now mirrors HF's selection: per query, the top indexer_budget/r of the complete r-token blocks (fp32-mean pooled keys, k_layernorm, RoPE at the block start, sum_h relu(q_h.k)/sqrt(d)) plus the incomplete tail, per packed sequence. qsa_sparse_attention attends over the selection: FlexAttention on CUDA (~3x faster than SDPA's math kernel, the only one it has for head_dim 256 with a bool mask), chunked SDPA otherwise. Short sequences keep the TE dense kernel. Tests: CPU selection/attention/grad parity vs HF (26 pass); GPU vLLM-vs-Megatron logprobs on ~7.7K-token prompts (test_qwen38_qsa_long_context.py): 0.056 mean |dlogp| (short dense row: 0.051). 8K core attention fwd+bwd on one H100: TE dense 6.7 ms, flex 44 ms, SDPA math 138 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…he host With optimizer_cpu_offload and offload_fraction 1.0, megatron-core still builds FP32 main-param shards on the GPU (DistributedOptimizer._build_model_and_main_param_groups) and the HybridDeviceOptimizer only copies them to the host; at expert-DP 1 (Qwen3.8-Flash-Next, EP16) that is ~29 GiB/GPU and init OOMed. patch_hybrid_optimizer_cpu_masters routes full-FT runs to megatron-core's precision-aware CPU-offload path (no GPU masters, BF16 decoupled grads), builds each master as one pinned host tensor, and skips the throwaway HDO over the full model. LoRA keeps the default (the adapter store reads the FP32 shards). Also: QSA entries in the patches README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…sync_lora_targets_only) merge_lora=true re-sent the whole model each sync. With megatron_config.lora_config.sync_lora_targets_only=true, MegatronWeightSource gets a param_filter keeping only the blocks that hold a LoRA-wrapped (or trainable) parameter, as whole blocks because vLLM's layerwise reload rebuilds a module from all of its tensors. vLLM's copies of the frozen rest must stay the checkpoint's, so vLLM sleeps at level 1 for weights in this mode. Rejected with dummy load format, FP8 weight sync, and the sharded_rdt/delta backends. Off by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
chunked_mlp (mlp_chunks_for_training on hyper-connection layers: qwen4_exp, glm5_next) calls the MoE router once per sequence chunk, but megatron-core's RouterReplay holds one [tokens, topk] tensor per call: replay handed every chunk the whole micro-batch's indices (Qwen3.8 DAPO + R3: 'expected index [10634, 10] to be no larger than self [1330, 512]'), and recording kept only the last chunk. Each chunk now gets its slice of the forward target and of the recompute FIFO entry, and a recording is reassembled from the chunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
chunked_mlp ran min(num_chunks, tokens) MoE calls via torch.chunk, which also returns fewer chunks than asked for short inputs. Each MoE call runs expert-parallel collectives, so a rank on a short micro-batch (e.g. the one-token padding micro-batches TokenBasedBatchIterator adds to even out micro-batch counts across DP ranks) made fewer calls than its EP peers and the group deadlocked: Qwen3.8 DAPO at EP16 hung in the forward pass until the 30 min NCCL watchdog fired (15 ranks in the MoE dispatch all-gather, one in the PLE lookup). Always make exactly num_chunks non-empty calls (tensor_split), padding short sequences with masked dummy tokens whose outputs (and router-replay rows) are dropped. Affects glm5_next too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…hon ints mask_mod captured the sequence length and block count as ints, which torch.compile guards on, recompiling per distinct packed length. Pass them as 0-d tensors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…ting test rows The recipe runs the QSA sparse regime and rollout router replay at full size (step 1: 966 s, rollout/train |dlogp| 0.011). test_trainer_full_checkpointing gains qwen3.8 slice rows for LoRA and full fine-tuning (EP4, optimizer on the host for full FT). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QepVYWMiu9bi2gEh5omjVi
…checks; qwen3.8 full-FT recipes compute_advantage_split_logratio_metrics logs the mean logp - old_logp over positive- and negative-advantage tokens (policy/adv_pos_logratio, policy/adv_neg_logratio) from the Megatron and FSDP policy losses. With update_epochs_per_batch > 1 the trainer also logs them per epoch, so a few epochs on one batch show whether the update moves logprobs in the advantage's direction. Adds run_gsm8k_qwen3_8_flash_next_fullft_2node.sh and run_dapo_qwen3_8_flash_next_fullft_sync_2node.sh (8xB200 nodes, host-resident optimizer, bf16 grad reduce). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YbshX4DEqk3oji2hEQBhvz
torch's pinned-host allocator rounds each block up to a power of two (a 2.2 GiB pinned tensor costs 4.3 GiB of host RAM). With the HybridDeviceOptimizer's FP32 masters and grad copies pinned, the first optimizer step pushed a 1.76 TB B200 node past Ray's 95% memory-monitor threshold. With pin_cpu_grads/pin_cpu_params=false, GSM8K full FT peaks at ~1.62 TB per node and runs 5 steps (rollout/train |dlogp| 0.020-0.022, ~300 s/step). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YbshX4DEqk3oji2hEQBhvz
Carry only Qwen4ExpModelState.ngram_context_offsets across a level-2 sleep, keyed by the model_state class name, instead of every model_state tensor. Other models' model_state is no longer copied or rewritten. vLLM #60009 lists this tensor as not fixed upstream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FT1xhhWniiXEKhg52FSrMc
Drop the class-name allowlist: save ngram_context_offsets only when the runner's model_state has it, matching how the other vLLM patches stay a no-op for models they don't apply to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FT1xhhWniiXEKhg52FSrMc
Removed (generic; to go in separate PRs if wanted): - merge_lora sync_lora_targets_only filter (merged_lora_filter.py, config, validation, client keep_weights_on_sleep, docs, tests); it saved ~1.5% for this model and did not fix the merged-LoRA OOM. - Advantage-split logratio metrics and the per-epoch log line (direction-test diagnostics). - WANDB_ENTITY / WANDB_BASE_URL forwarding. - mtp/grad_clip.py shard_float16_groups pair: Qwen3.8 trains no MTP head. Instead, the host-masters optimizer routing now skips MTP models, so their clipping keeps finding FP32 shards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FT1xhhWniiXEKhg52FSrMc
…struction Saving a full-FT distributed checkpoint before the first optimizer step failed with KeyError: 'master_param' in DistributedOptimizer._get_main_param_and_optimizer_states (precision-aware branch): the HybridDeviceOptimizer's state, which carries master_param and the Adam moments, only exists after the first step, because torch's Adam/AdamW allocate it lazily. init_cpu_optimizer_state builds that lazy state eagerly after the HDO's sub-optimizers are created (zero moments, step 0, with torch's own device/dtype placement, including fused=True, which megatron-core uses for the CPU AdamW). The first step is bit-identical; the moments are only allocated earlier. Found by the full-FT row of test_trainer_full_checkpointing.py (Qwen3.8-Flash-Next 4-layer slice, 4x B200), which saves right after build. It now passes (save, resume, second save). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YbshX4DEqk3oji2hEQBhvz
Keep the PR to Qwen3.8 changes: forwarding every NCCL_* variable changes behavior for all models. The recipes no longer export NCCL_NVLS_ENABLE; the GSM8K LoRA recipe documents setting it in the worker environment on 80 GB GPUs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jADLGSS2eEaKAzLuAL7PK
…ports loadable The full-FT HF export (trainer.hf_save_interval) only contains what the Megatron model owns. For Qwen3.8-Flash-Next that leaves out the frozen PLE n-gram tables, the vision tower (language_model_only) and the MTP head (mtp_num_layers=0), so vLLM cannot load it. The script copies those tensors from the base checkpoint (whole PLE-only shards, the rest packed into one extra file), rewrites the index and verifies every tensor's name/dtype/shape against the base. Raw safetensors bytes, no torch. Tested on the step-10 and step-20 exports of a 20-step full-FT DAPO run on 16xB200: 1658/1658 tensors verified, both load in vLLM (TP8) and reproduce the in-training AIME-2024 avg@8 (0.887 / 0.817 vs 0.858 / 0.812). Idempotent on an already completed export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S14hc47TJCuJMDwU84K7HW
…enalty, evals every 5 steps Defaults now match the 16xB200 runs that learned (held-out AIME-2024, 30 problems): - full FT: LR 1e-6 -> 3e-6, overlong penalty factor 1.0 -> 0.5. avg@8 0.467 -> 0.829 (step 10) -> 0.871 (step 20), accuracy on finished responses 0.991. At factor 1.0 full FT learned to cut its reasoning short (finished accuracy 0.855 at step 20); at LR 1e-5 entropy fell to 0.62x LoRA's. - LoRA: LR 1e-5 -> 3e-4, alpha 16 -> 32 (LR 1e-5 / 3e-5 runs stayed flat; 3e-4 reaches 0.85 by step 10), same penalty factor 0.5 as full FT. - both: 32 prompts x 8, mini 16 (2 optimizer steps per batch), 30 steps, AIME eval before training and every 5 steps with 12 samples per problem (as in the GLM-5.3-Flash recipe). All overridable via env. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S14hc47TJCuJMDwU84K7HW
… sleep vLLM's wake path restores only the model's parameters and buffers, so any tensor on Model Runner V2's model_state can come back wrong; Qwen4Exp's ngram_context_offsets (vllm#60009) is one case. Save every model_state tensor on each sleep, at any level, and restore it when the weights wake. patch_vllm_level2_sleep_model_state -> patch_vllm_sleep_model_state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jADLGSS2eEaKAzLuAL7PK
Copying every model_state tensor on each sleep is wasted work. Keep an explicit list, MODEL_STATE_TENSORS_LOST_ON_SLEEP, currently Qwen4Exp's ngram_context_offsets (vllm#60009); the patch still covers any sleep level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jADLGSS2eEaKAzLuAL7PK
The patch kept FP32 master params off the GPU when the optimizer was fully offloaded to the host, which full fine-tuning of Qwen3.8-Flash-Next needed on 16 GPUs (expert-DP 1). On 4x8 B200 (EP16, expert-DP 2) the distributed optimizer's state fits on the GPU, so the recipes no longer offload it and the patch, its CPU test and its README section go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LT2b2yhk8WTqxCaKwZD1ML
Rename the four recipes *_2node.sh -> *_4node.sh: NUM_NODES=4, four vLLM engines at TP8, Megatron TP=CP=PP=1 EP16 (expert-DP 2), and no optimizer CPU offload (drops optimizer_offload_fraction, pin_cpu_* and the related knobs). Header comments updated for the 32-GPU memory budget. Training settings (LR, LoRA rank/alpha, overlong penalty, batch sizes, R3, evals) unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LT2b2yhk8WTqxCaKwZD1ML
…drop the model-type check in inference_servers/utils.py vLLM's custom all-reduce registers CUDA-graph buffers over CUDA IPC, which fails on sleep mode's cumem allocator (colocate_all=true, TP>1). Instead of a qwen4_exp model-type check in build_vllm_cli_args, the four 4-node recipes and the qwen3.8 GPU test rows pass disable_custom_all_reduce through engine_init_kwargs. inference_servers/utils.py is back to main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LT2b2yhk8WTqxCaKwZD1ML
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jADLGSS2eEaKAzLuAL7PK
The row offloaded the optimizer to the host, which only worked with patch_hybrid_optimizer_cpu_masters: stock megatron-core's HybridDeviceOptimizer creates its Adam state lazily, so a checkpoint saved before the first optimizer step can't be resumed (KeyError ...exp_avg from model not in state dict). The 4-node recipes keep the optimizer on the GPU; so does this row now (~44 GB/GPU on the slice). Passes save, resume and second save. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LT2b2yhk8WTqxCaKwZD1ML
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jADLGSS2eEaKAzLuAL7PK
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.
What
Adds Qwen3.8-Flash-Next (HF
qwen4_exp,Qwen4ExpForConditionalGeneration) to the Megatron backend, for both full fine-tuning and LoRA, with vLLM rollouts.The architecture is new to both upstreams. It has 48 layers of 3:1 Gated DeltaNet / gated full attention, and the full-attention layers use a QSA sparse indexer (top-512 blocks of 4 tokens). The MoE has 512 routed experts (top-10) and a shared expert. Every block has gated low-rank hyper-connections over a 4-stream residual, and a frozen 51B-parameter n-gram embedding (PLE) is injected at layer 1. It ships as a VL checkpoint, and SkyRL trains the language model only (
language_model_only=True).No dependency pins change: this runs on
main's vLLM 0.31.0, megatron-core and Megatron-Bridge. What the pinned upstreams don't have yet is underpatches/.Components
patches/megatron/qwen4_exp/: the Megatron-Bridge modelUpstream home: NVIDIA-NeMo/Megatron-Bridge#6123 / #6354, on NVIDIA/Megatron-LM#7393 / #7553 (both open). The modules here were written against HF transformers'
modeling_qwen4_exp.py, and their parameter names mirror the HF classes. The bridge builds on Megatron-Bridge'sQwen35MoEBridgemappings.modules.py:Qwen4ExpGatedResidual/Qwen4ExpFinalMixer: the gated low-rank hyper-connection and the final stream mixer, which takes the block'sfinal_layernormslot.Qwen4ExpGatedDeltaNet: megatron-core's GDN with a sigmoid output gate and SiLU on the conv. megatron-core uses one activation for both.Qwen4ExpPLE: the PLE n-gram embedding.Qwen4ExpQSAIndexerandqsa_sparse_attention: HF's top-k block selection. At up toindexer_budget + compress_ratio - 1= 2051 tokens every block is kept, so attention stays on the fused TE kernel. Past that it runs a FlexAttention path (44 ms per layer fwd+bwd at 8K, vs 138 ms for SDPA's math kernel).layer_specs.py:Qwen4ExpTransformerLayer(expand → PLE → HC-wrapped mixer → HC-wrapped MoE), andQwen4ExpCoreAttention, which switches between TE dense and the QSA sparse path.provider.py:Qwen4ExpModelProvider. It stashesinput_ids/cu_seqlensfor PLE through a forward pre-hook, and requires TP=CP=PP=1.bridge.py:Qwen4ExpBridge. It uses the Qwen3.5 MoE mappings minus the fused pre-norms, plus replicated mappings for the HC, PLE projection and indexer weights. The 51B PLE table, the vision tower and the MTP head aren't bridged.patches/megatron/README.mdis the removal plan: what retires each piece upstream, and what to compare before deleting it.patches/megatron/mcore_ext/mhc_transformer_layer.py: router replay forchunked_mlpThis fixes shared code that
glm5_nextalso uses:RouterReplayreplays one top-k tensor per router call, butchunked_mlpcalls the router once per chunk._ChunkedRouterReplayhands each chunk its slice of the forward target and of the recompute queue, and reassembles recorded indices. Before this, DAPO with R3 crashed at step 1 with an index-size error.mlp_chunks_for_trainingMoE calls, padding short micro-batches with masked tokens. A rank on a one-token padding micro-batch used to make fewer EP collectives and deadlock the group.inference_servers/vllm_compat.py+new_inference_worker_wrap.py: model state lost on sleepvLLM's wake path restores only the model's parameters and buffers. Tensors on Model Runner V2's
model_statethat live in the weights pool can therefore come back wrong. Qwen4Exp's constant n-gram offsets (arange(-2, 0)) come back as zeros, which corrupts every later PLE n-gram context (vllm-project/vllm#60009).patch_vllm_sleep_model_statesaves the tensors listed inMODEL_STATE_TENSORS_LOST_ON_SLEEPbefore any sleep, and restores them when the weights wake.Other SkyRL changes
megatron_worker.py: qwen4_exp is text-only on Megatron, so it rejectslanguage_model_only=falsewith a clear message.model_bridges.pyregisters the bridge.examples/train/qwen3_8_flash_next/, 4 nodes of 8×B200, EP16, 4 vLLM engines × TP8, colocated.run_gsm8k_qwen3_8_flash_next_{lora,fullft}_4node.shrun_dapo_qwen3_8_flash_next_{lora,fullft}_sync_4node.sh: 2k/8k, R3 on, evals every 5 steps.disable_custom_all_reduceinengine_init_kwargs. vLLM's custom all-reduce registers CUDA-graph buffers over CUDA IPC, which fails on sleep mode's cumem allocator, so every TP worker dies at engine start.make_slice.py: the 4-layer CI slice with a small random PLE table (23 GB).complete_export.py: makes a full-FT HF export loadable (see Known limitations).gpu_ci/megatron/test_megatron_models.pyandtest_megatron_lora_models.py, andtest_qwen38_qsa_long_context.py(sparse regime).Validation
Megatron vs vLLM on the 4-layer slice, 4×H100 (EP4, vLLM TP4), threshold 0.1:
qwen3.8-flash-next-4layer_h100_ep4test_qwen38_qsa_long_context.py(~7.7K-token prompts, sparse QSA)LoRA roundtrip (
..._ep4_adapter):Full model, DAPO 2k/8k, R3, 32 prompts × 8 samples, 2 optimizer steps per batch, on B200 with EFA. Held-out AIME-2024 avg@8 (30 problems × 8 samples, temperature 1.0, top-p 0.7):
These runs used 2×8 B200 (EP16, vLLM 2 × TP8) with the optimizer offloaded to the host.
Reruns of the committed 4-node recipes (4×8 B200, EP16 / DP32, vLLM 4 × TP8, optimizer on GPU), 30 steps, R3, 32 × 8, held-out AIME-2024 avg@12 (30 problems × 12 samples):
Also passing:
test_trainer_full_checkpointing.pyLoRA and full-FT rows on the slice (save, resume, second save). The full-FT row uses the GPU-resident optimizer, matching the 4-node recipes.Known limitations
examples/train/qwen3_8_flash_next/complete_export.py <export_dir> <base_dir>to merge them from the base. The MTP drafter is then the base's, so speculative-decoding acceptance may drop.SKYRL_QWEN38_4LAYER_PATH.HybridDeviceOptimizer), a checkpoint saved before the first optimizer step can't be resumed (KeyError ...exp_avg from model not in state dict), because its Adam state is created lazily. The recipes don't offload the optimizer.Upstream issues
reload_weights()materializes the PLE table on GPU (vllm#62063)qwen_sparse_attentionAutoMappingdetects parallelism by exact class name, so subclasses fail🤖 Generated with Claude Code