[Multimodal][Model] Make Qwen3.5-VL work with packed sequences - #2233
Open
TobyYang7 wants to merge 1 commit into
Open
[Multimodal][Model] Make Qwen3.5-VL work with packed sequences#2233TobyYang7 wants to merge 1 commit into
TobyYang7 wants to merge 1 commit into
Conversation
Megatron Bridge has shipped Qwen35VLBridge / Qwen35VLMoEBridge since v0.4.0 and registers them on import, so the forked Megatron Bridge pinned by examples/geo3k_vlm/run_geo3k_qwen35.sh is no longer needed. What is missing is the interaction with our data layout. The providers build their Gated DeltaNet layers from megatron-core's experimental_attention_variant="gated_delta_net", and megatron-core's GatedDeltaNet.forward raises NotImplementedError when it is given packed sequences. Since THUDM#2100 removed BSHD every microbatch is packed THD, so the first GDN layer raises and the bridge path cannot run Qwen3.5-VL at all. With --micro-batch-size 1 and no dynamic batching, which GDN already requires, a THD microbatch is a single sequence plus right padding and the hidden states are [T, 1, H] -- the layout the unpacked path expects, and GDN is causal so the trailing padding cannot affect the real tokens. Subclass the megatron-core module and drop the packed metadata once that regime is verified. The subclass adds no parameters and renames nothing, so the official GDN weight mappings still work for checkpoint load, --save-hf and weight sync to SGLang. Configurations that would put several real sequences in one microbatch, and context parallel, are rejected instead of silently fused into one recurrence. Verified on Qwen3.5-2B with Megatron Bridge 0.5.0 and megatron-core 0.16.0rc0. The unpatched model raises in the first GDN layer. The patched one: * runs a packed forward whose logits agree with HuggingFace on 47/48 argmax positions (bf16), with every Megatron top-1 inside the HuggingFace top-5, at both TP=1 and TP=2; * runs a real image forward with pixel_values and image_grid_thw; * exports back to HuggingFace as 621/632 tensors -- the 11 absent ones are MTP layers this configuration does not build -- including all 162 GDN tensors, with a maximum weight difference of one bf16 ulp; * shards the GDN weights under TP=2, which is what the AutoMapping registration controls; * produces finite gradients at TP=2 with sequence length 512, and completes an optimizer step with grad_norm 135.6. Not covered: MoE, weight sync to a live SGLang engine, and PP above 1. Also forward moe_aux_loss_coeff and the freeze_* flags to bridge providers, so the geo3k_vlm README no longer has to ask users to edit model_provider.py.
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.
Qwen3.5-VL cannot currently run through the bridge path, and the example still installs a forked Megatron Bridge to work around it. This makes it run against the official package.
What is broken today
The Qwen3.5-VL providers build their Gated DeltaNet layers from megatron-core's
experimental_attention_variant="gated_delta_net", and megatron-core'sGatedDeltaNet.forwardrefuses packed sequences outright:Since #2100 removed BSHD, every microbatch is packed THD, so the first GDN layer raises. Reproduced on
mainwithQwen/Qwen3.5-2Band the current image:Megatron Bridge has shipped
Qwen35VLBridge/Qwen35VLMoEBridgesince v0.4.0 and registers them on import, so nothing needs to be registered on our side — the missing piece is only the interaction with our data layout.Approach
With
--micro-batch-size 1and no dynamic batching, which GDN already requires, a THD microbatch is a single sequence followed by right padding, and the hidden states are[T, 1, H]— exactly the layout the unpacked path expects. GDN is causal, so the trailing padding cannot affect the outputs of the real tokens. So the module is subclassed and the packed metadata dropped, but only after checking that we really are in that regime.The subclass adds no parameters and renames nothing, so every official GDN weight mapping (
in_proj/conv1d/A_log/dt_bias/out_norm/out_proj) keeps working for checkpoint load,--save-hfand weight sync to SGLang.AutoMappingdispatches on the exact class name, so the subclass is registered the same way megatron-bridge registersGatedDeltaNetitself.Rejected rather than silently miscomputed:
cu_seqlensstays global, and the recurrence cannot be split that way;cu_seqlenssegments, so it is checked when the model is built.Lifting the last restriction means forwarding
cu_seqlensinto the varlen kernels instead of dropping it; that seemed worth keeping separate from this change.Verification
Qwen/Qwen3.5-2B, Megatron Bridge 0.5.0, megatron-core 0.16.0rc0, single node,slimerl/slime:latest.pixel_valuesandimage_grid_thwlayers.0.self_attention.in_proj.weightis half size per rank, i.e. theAutoMappingregistration takes effectgrad_norm135.6Not covered: MoE, weight sync against a live SGLang engine, and PP above 1.
One note that cost me some time and is now in the README: the gradient checks need the example's
--attention-backend flash. Letting Megatron choose the backend selects Transformer Engine's cuDNN fused attention, whose backward goes non-finite for this model under bf16 with packed sequences and TP above 1. Qwen3-VL is unaffected under the same settings.Also in this PR
--moe-aux-loss-coeffand new--freeze-language-model/--freeze-vision-model/--freeze-vision-projectionflags are forwarded to bridge providers when the provider defines them, soexamples/geo3k_vlm/README.mdno longer has to ask users to hand-editmodel_provider.py. The forwarding is a name allowlist withhasattrchecks on both sides, so it is a no-op for providers without those fields.examples/geo3k_vlm/run_geo3k_qwen35.shdrops thecoding-famer/Megatron-Bridge-slime@qwen35install.Tests are CPU-only (
NUM_GPUS = 0) and registered inpr-test.yml.j2with the workflow regenerated.This overlaps with #2075, which takes the registration-shim approach; that path stops at the GDN refusal above, which is why this one goes further.