Repository navigation
npu: Qwen2.5-VL's contiguous "mrope" is a position layout, so its text tower loads and answers (Refs #1786) - #2261
Conversation
…t tower loads and answers (Refs #1786) Qwen2.5-VL-3B-Instruct-NPU2 was refused at load: rope_scaling {"type": "mrope", "mrope_section": [16, 24, 24]} was read as a frequency scaling this forward lacks. It is the Qwen2-VL position layout: plain frequencies, CONTIGUOUS sections (transformers apply_multimodal_rotary_pos_emb splits cos by mrope_section * 2), t 0-15, h 16-39, w 40-63 -- not Qwen3-VL's interleave. New mrope_sectioned and q4nx_mrope_axis_sectioned; a block whose sections do not sum to the rotary half-width is refused. With the attention-bias fix of the previous commit the text tower answers 'Paris<|im_end|>' on the host. q4nx_mrope_test: +6 checks (all 64 frequencies against the reference's split; Qwen3-VL unchanged). batch_check_state_qwen2vl: the fabricated Qwen2 declaring that block gives the plain-RoPE reference's logits (corr 1.0000000); with "mrope" refused again it fails. ctest 268/268. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
ready for review at 48a088d |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
|
VERDICT: READY This is the best-shaped test I have reviewed in this stretch, and it is in territory I have worked in, so I want to be specific about why. It builds the reference from transformers' own rule instead of asserting the implementation against itself. The test constructs It pins the boundaries, not just the middle. And it proves the two layouts are distinguishable: The validation is in the right place — a block whose three sections do not sum to the rotary half-width is refused rather than padded or truncated, which is the same conservative choice the gemma3 One note for the docs, since it connects two checks that already exist: this test pins which axis each frequency takes, while the Qwen3-VL MRoPE work verified the positions against HF's Not verified by me, as with the rest of this series: the pack itself on a device — the NPU is off. Everything above is host-only and runs without one. |
|
Hash correction for the ping channel only: my ready-message named |
|
VERDICT: READY at Not a scaling, and not the one already implemented
That is a genuinely dangerous ambiguity handled correctly: the same key ( The resolution is the right shape: And the precondition is checked, with a named refusal
Two asks
Why this family of PRs is going well#2242 (Gemma3's three config facts), #2254 (LFM3's four rules), #2259/#2261 (Qwen2.5-VL's biases then its position layout): each lands with the reference rule named, the borrowed rule's previous home stated ( |
What
FastFlowLM's
Qwen2.5-VL-3B-Instruct-NPU2was refused at load:config.json declares rope scaling "mrope", which this forward does not implement. Refs #1786; host-side only, the NPU is off. It follows #2259, which fixed the same Qwen2 text model's q/k/v biases.Qwen2.5-VL's
rope_scaling: {"type": "mrope", "mrope_section": [16, 24, 24]}is not a frequency scaling. It is a position layout: plain RoPE frequencies, each taking its position from the t, h or w plane.apply_multimodal_rotary_pos_embsplits cos bymrope_section * 2, and chunk k takes plane k % 3. That makes frequencies 0–15 t, 16–39 h and 40–63 w.q4nx_mrope_axis).Changes:
mrope_sectioned(semantics) andq4nx_mrope_axis_sectioned.mrope_plane()picks the rule by layout."mrope"only when its three sections sum to the rotary half-width, and otherwise refuses by name. The startup line names the sections.Evidence (host, 2026-11-12)
On a text-only prompt the three planes are equal, so this is plain RoPE. The vision tower is not loaded here; it has its own item.
Tests
q4nx_mrope_test, +6 checks:type: mropespelling is read with its sections;mrope_section * 2split;batch_check_state_qwen2vl: npu: Qwen2's q/k/v biases are read from the container, so Qwen2.5-3B-Instruct-NPU2 answers (Refs #1786) #2259's fabricated Qwen2 (q/k/v biases, noattention_biaskey) declaring this block. Its logits match the numpy plain-RoPE Qwen2 reference: corr 1.0000000, max |d| 6.6e-06. With"mrope"refused again (main's behaviour) the arm fails (init refused).batch_check_state_ci.pyarm (qwen3, lfm2, lfm3, qwen3_5, qwen2, qwen2vl) was run by hand: 0 FAIL.tools/copyright.py --checkrc 0.scripts/(scripts: usage texts stop leaking code, and hw-gate's no-argument path stops taking a GPU ticket #2258 landed between the run and the rebase;git diffoutsidescripts/is empty).🤖 Generated with Claude Code