Skip to content

[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
NovaSky-AI:mainfrom
xinyuangui2:qwen38-flash-next
Draft

xinyuangui2 wants to merge 41 commits into
NovaSky-AI:mainfrom
xinyuangui2:qwen38-flash-next

Conversation

@xinyuangui2

@xinyuangui2 xinyuangui2 commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

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 under patches/.

Components

patches/megatron/qwen4_exp/: the Megatron-Bridge model

Upstream 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's Qwen35MoEBridge mappings.

  • modules.py:
    • Qwen4ExpGatedResidual / Qwen4ExpFinalMixer: the gated low-rank hyper-connection and the final stream mixer, which takes the block's final_layernorm slot.
    • 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.
      • Hashing is packed-sequence aware.
      • The table is sharded by n-gram head over the EP group and kept frozen in pinned host memory.
      • Lookup is all-gather ids → local CPU embed → all-to-all.
    • Qwen4ExpQSAIndexer and qsa_sparse_attention: HF's top-k block selection. At up to indexer_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), and Qwen4ExpCoreAttention, which switches between TE dense and the QSA sparse path.
  • provider.py: Qwen4ExpModelProvider. It stashes input_ids / cu_seqlens for 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.md is 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 for chunked_mlp

This fixes shared code that glm5_next also uses:

  • RouterReplay replays one top-k tensor per router call, but chunked_mlp calls the router once per chunk. _ChunkedRouterReplay hands 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.
  • Every rank now makes exactly mlp_chunks_for_training MoE 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 sleep

vLLM's wake path restores only the model's parameters and buffers. Tensors on Model Runner V2's model_state that 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_state saves the tensors listed in MODEL_STATE_TENSORS_LOST_ON_SLEEP before any sleep, and restores them when the weights wake.
  • Before this, rollout↔train |Δlogp| jumped from 0.07 to 1.15 after the first weight sync, even with bit-exact weights.

Other SkyRL changes

  • megatron_worker.py: qwen4_exp is text-only on Megatron, so it rejects language_model_only=false with a clear message. model_bridges.py registers the bridge.
  • Recipes: 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.sh
    • run_dapo_qwen3_8_flash_next_{lora,fullft}_sync_4node.sh: 2k/8k, R3 on, evals every 5 steps.
      • Full FT: LR 3e-6.
      • LoRA: r16 α32, LR 3e-4.
      • Both: overlong penalty factor 0.5.
    • They set disable_custom_all_reduce in engine_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).
  • Tests:
    • CPU: qwen4_exp modules and the QSA indexer vs HF (outputs and grads, packed), chunked-MoE R3 and equal call counts, the sleep model-state patch.
    • GPU: qwen3.8 4-layer rows in gpu_ci/megatron/test_megatron_models.py and test_megatron_lora_models.py, and test_qwen38_qsa_long_context.py (sparse regime).

Validation

Megatron vs vLLM on the 4-layer slice, 4×H100 (EP4, vLLM TP4), threshold 0.1:

row Megatron vs vLLM post weight sync (common prefix)
qwen3.8-flash-next-4layer_h100_ep4 0.051 0.021
test_qwen38_qsa_long_context.py (~7.7K-token prompts, sparse QSA) 0.056

LoRA roundtrip (..._ep4_adapter):

phase diff check
zero adapter 0.052 < 0.1
perturbed trainer vs stale sampler 0.239 > 0.1
synced adapter 0.056 < 0.1

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):

step 0 10 20
full fine-tuning (LR 3e-6, overlong penalty 0.5) 0.467 0.829 0.871
full fine-tuning, overlong penalty 1.0 0.463 0.858 0.812
LoRA (r16 α32, LR 3e-4) 0.446 0.846 0.821
  • The model learns to finish its reasoning within the 8K budget: truncation goes from ~54% to 5–17%.
  • Full fine-tuning at penalty 0.5:
    • Accuracy on finished responses is 0.991 at step 20 (LoRA: 0.985).
    • The rollout↔train |Δlogp| stays at 0.009–0.012 throughout.
    • The completed step-20 export serves in standalone vLLM at 0.883.
  • Penalty 1.0 over-compresses full fine-tuning: accuracy on finished responses falls to 0.855 by step 20.
  • LR 1e-5 for full fine-tuning sharpens too fast: entropy fell to 0.62× LoRA's on the same batches by step 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):

step 0 5 10 15 20 25 30
LoRA (r16 α32, LR 3e-4) 0.489 0.678 0.808 0.850 0.886 0.867 0.856
full fine-tuning (LR 3e-6) 0.467 0.839 0.858 0.872 0.878 0.819 0.844
  • Both peak at step 20. Accuracy on finished responses there is 0.981 for both.
  • Full FT learns faster early, then drifts after step 20: finished-but-wrong answers go 6 → 31 → 23, and they are long, coherent guesses near the 8K budget. LoRA holds its level (finished accuracy 0.972 at step 30).
  • On the same score scale as GLM-5.3-Flash in [megatron] GLM-5.3-Flash (glm5_next) support: KDA + k-pool DSA + mHC MoE, full fine-tuning and LoRA sync to vLLM (merge_lora=false) #2179 (avg_score 0.072 → 0.711 over 30 steps, i.e. accuracy 0.54 → 0.86), Qwen3.8 reaches +0.772 (LoRA) / +0.756 (full FT) at step 20.
  • Rollout↔train |Δlogp| stays at 0.008–0.012 in both runs, and no stop rule fired.
  • Steady step time: full FT 180–230 s, LoRA 250–340 s. LoRA is slower because vLLM serves 512-expert LoRA adapters.

Also passing: test_trainer_full_checkpointing.py LoRA 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

  • Megatron parallelism is limited to TP=CP=PP=1 (PLE needs each packed sequence whole on one rank) and EP ≤ 16 (the PLE table is sharded by its 16 heads over the EP group). The recipes use EP16 with DP32 on 32 GPUs, so each expert is held by 2 ranks. Text only; no MTP training.
  • The full-FT HF export omits the frozen PLE tables, the vision tower and the MTP head. Run 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.
  • QSA sparse attention is ~6× the cost of dense attention at 8K; a block-sparse kernel would close that. On sm100, vLLM falls back from FA4 to FA2 for head_size 256 with GDN's KV block size of 400.
  • The 4-layer CI slice isn't published on HF yet. The rows default to a placeholder id; override it with SKYRL_QWEN38_4LAYER_PATH.
  • Multi-node runs need EFA/RDMA. Over TCP, NCCL took 97% of a full-FT step.
  • With stock megatron-core's CPU-offloaded optimizer (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

  • vLLM:
    • model-state tensors lost on sleep (vllm#60009)
    • custom all-reduce fails on cumem memory under sleep mode
    • "Qwen4Exp HC does not support sequence-parallel MoE" blocks DP > 1 + EP + TP > 1 rollout
    • reload_weights() materializes the PLE table on GPU (vllm#62063)
  • megatron-core:
    • GDN uses one activation for the conv and the output gate
    • no hyper-connection, PLE or QSA modules (Megatron-LM#7393 / #7553)
  • Megatron-Bridge:
    • the Qwen3.5 layer-type helper rejects transformers ≥ 5.16's qwen_sparse_attention
    • AutoMapping detects parallelism by exact class name, so subclasses fail
    • no qwen4_exp bridge yet (#6123 / #6354)

🤖 Generated with Claude Code

xinyuangui2 and others added 22 commits October 17, 2026 09:15
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
…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_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
…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
@xinyuangui2 xinyuangui2 changed the title [megatron][vllm] Qwen3.8-Flash-Next (qwen4_exp) support: Megatron bridge, vLLM sleep fix, GSM8K LoRA recipe [megatron][vllm] Qwen3.8-Flash-Next (qwen4_exp) support: Megatron bridge, QSA sparse attention, GSM8K + DAPO recipes Oct 18, 2026
xinyuangui2 and others added 7 commits October 18, 2026 15:07
…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
xinyuangui2 and others added 12 commits October 20, 2026 09:24
…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
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

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant