Skip to content

fix(vllm): align RADIO LayerScale patch with the super_vl_rl fork loader - #3990

Merged
yfw merged 1 commit into
super-v3.5-posttrainingfrom
danialtaheri/fix-radio-layerscale-patch-alignment
Sep 5, 2026
Merged

yfw merged 1 commit into
super-v3.5-posttrainingfrom
danialtaheri/fix-radio-layerscale-patch-alignment

Conversation

@DanialTaheri

Copy link
Copy Markdown
Contributor

What

_patch_vllm_radio_layerscale_loader matches RadioModel.load_weights as a
single 20-line snippet. The super_vl_rl_v0.25.1
fork that the nemo-ci rl-gym images ship reshaped that body in three ways:

  1. the legacy branch is gated on is_legacy
  2. an elif not is_legacy: native-mapping branch was inserted before the
    if vllm_key ... tail (it maps layer_scale{1,2}.lambda1 -> ls{1,2})
  3. the loader call became shard-aware

Any one of those breaks the match, so the patch takes its logger.warning path
and returns without applying.

Why it matters

The failure is silent and expensive:

  • Super Omni checkpoints are folded exports carrying no ls1/ls2 tensors
  • megatron_cfg.freeze_vision_model: true keeps the vision tower out of the refit
  • NeMo-RL forces load_format=dummy (overriding the recipe's auto)

So this patch's initializer_factor fill is the only code path that ever sets
LayerScale. When it no-ops, every residual branch of the RADIO tower is scaled
by dummy-init noise — with unchanged step time and no exception. The sole
evidence is one line in ray-driver.log:

WARNING:vllm_patch:patches.py:580: Could not apply vLLM RADIO LayerScale loader
patch: expected vLLM 0.25.1 source shape was not found in .../radio.py

Measured on a 16-node Super Omni GRPO run — identical recipe, data and
checkpoint, step 1:

reward train/token_mult_prob_error mean_seq_mult_prob_error masked seqs
patch applies (stock 0.25.1) 0.574 1.0305 1.141 11 / 2048
patch no-ops (fork) 0.216 1.4503 16,906,104 1775 / 2048
after this change (fork) 0.556 1.0305 1.057 6 / 2048

87% of the batch was being masked out by the logprob-error threshold.

How

Split the rewrite into two independent edits anchored on text stable across
both variants, so a future reshape of one branch cannot silently disable the
other:

  • Edit 1 (optional) — drop the legacy ls1/ls2 skip, so explicit
    LayerScale weights load rather than only being backfilled.
  • Edit 2 (required) — insert the initializer_factor fill before
    return loaded_params, anchored on
    loaded_params.add(vllm_key)\n\n return loaded_params (unique in both
    variants).

The required edit now warns and refuses to write a half-applied patch rather
than returning quietly, so the next drift is diagnosable from one log line.
self.config.initializer_factor also became
getattr(self.config, "initializer_factor", 1.0) — the checkpoint configs omit
the field and currently rely on RadioConfig's default.

Test plan

Applied against both stock vLLM 0.25.1 and the fork's radio.py, asserting
that it applies, the result parses, the fill loop lands inside load_weights
before its return, the fork's native-mapping branch and shard-aware
weight_loader survive, and a second application is a no-op. All pass on both.

End-to-end: 16-node Super Omni GRPO run on the fork-based container with
load_format=dummy unchanged — Could not apply ... went from 40+ occurrences
across the cluster to 0, and step-1 metrics match the stock-vLLM baseline to
five decimal places (see table above).

🤖 Generated with Claude Code

_patch_vllm_radio_layerscale_loader matched RadioModel.load_weights as a single
20-line snippet. The super_vl_rl_v0.25.1 fork that the nemo-ci rl-gym images
ship reshaped that body -- it gates the legacy branch on is_legacy, inserts an
"elif not is_legacy:" native-mapping branch (layer_scale{1,2}.lambda1 ->
ls{1,2}), and makes the weight_loader call shard-aware -- so the match fails and
the patch returns without applying.

That failure is silent and expensive. Super Omni checkpoints are folded exports
carrying no ls1/ls2 tensors, megatron_cfg.freeze_vision_model keeps the vision
tower out of the refit, and NeMo-RL forces load_format=dummy, so this patch's
initializer_factor fill is the only code path that ever sets LayerScale. Without
it every residual branch of the vision tower is scaled by dummy-init noise, at
unchanged step time and with no error raised.

Measured on a 16-node Super Omni GRPO run, identical recipe, data and
checkpoint, step 1:

                      reward   token_mult_prob_error   masked seqs
  patch applies       0.574    1.0305                  11 / 2048
  patch no-ops        0.216    1.4503                  1775 / 2048
  after this change   0.556    1.0305                  6 / 2048

Split the rewrite into two independent edits anchored on text that is stable
across both variants, so a future reshape of one branch cannot silently disable
the other. The required edit now warns and refuses to write a half-applied patch
rather than returning quietly.

Verified against both stock vLLM 0.25.1 and the fork's radio.py: the patch
applies, the result parses, the fill loop lands inside load_weights before its
return, the fork's native-mapping branch and shard-aware weight_loader survive,
and a second application is a no-op.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: DanialTaheri <smohsenitahe@nvidia.com>
@DanialTaheri
DanialTaheri requested a review from a team as a code owner September 4, 2026 06:07
@copy-pr-bot

copy-pr-bot Bot commented Sep 4, 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.

@yfw
yfw merged commit 90624d2 into super-v3.5-posttraining Sep 5, 2026
10 checks passed
@yfw
yfw deleted the danialtaheri/fix-radio-layerscale-patch-alignment branch September 5, 2026 04:26
yfw pushed a commit that referenced this pull request Oct 16, 2026
…der (#3990) (#4602)

Signed-off-by: DanialTaheri <smohsenitahe@nvidia.com>
Signed-off-by: Danial Mohseni Taheri <smohsenitahe@nvidia.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.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.

2 participants