Skip to content

feat(models): dense Qwen3.5 custom attention#2943

Merged
samsja merged 3 commits into
feat/sft-renderer-onlyfrom
feat/qwen35-dense-attention
Jul 9, 2026
Merged

feat(models): dense Qwen3.5 custom attention#2943
samsja merged 3 commits into
feat/sft-renderer-onlyfrom
feat/qwen35-dense-attention

Conversation

@hubert-marek

@hubert-marek hubert-marek commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Part 2/7 of the split of #2485. Based on #2942 (feat/sft-renderer-only) — only for stack ordering; the code depends on main alone.

Implements dense Qwen3.5 (Qwen3_5ForCausalLM / Qwen3_5Model) on the PrimeRL attention modules, reusing the Qwen3.5-MoE building blocks (GatedDeltaNet, gated SDPA/flash attention, RMSNorm, rotary embedding) so ring/ulysses CP substitution patches dense and MoE consistently:

  • New models/qwen3_5 package: decoder layers dispatch on layer_types (linear_attention / full_attention), flash varlen cu_seqlens from position_ids, identity state-dict conversion (dense HF layout matches PrimeRL layout).
  • Shared normalize_qwen3_5_attn_implementation on the MoE module (eagersdpa, kernels-community/vllm-flash-attn3flash_attention_3), consumed by both models' attention factories and _check_and_adjust_attn_implementation.
  • Registers Qwen3_5TextConfig/Qwen3_5ForCausalLM in AutoConfig and the custom causal-LM mapping; hooks dense Qwen3_5GatedFlashAttention into substitute_ring_attn/substitute_ulysses_attn.

Engagement note: hub Qwen3.5 checkpoints ship composite (VLM) configs, and composite→custom dispatch (_CUSTOM_VLM_MAPPING) lands in #2945 together with the state-dict remap that extracts the text tower — registering it here would crash get_model on any composite checkpoint. Standalone, this PR's class engages for text-config checkpoints and is covered by the dispatch/state-dict unit tests; the e2e custom-vs-hf parity and perf comparison run at the #2945 tip (same tip, same data pipeline, only model.impl differs), where the attention code exercised is exactly this PR's.

KL mismatch table

20 RL steps, hendrycks-math, batch 64, Qwen3.5-0.8B, impl=custom, vLLM 0.24 (run):

steps mismatch KL
0–19 (all) 0.0003

Bar is < 0.015; measured 50× under it, flat across all steps.

E2E (W&B project pr2485)

Loss parity + perf, custom vs hf at identical config/data/seed (8×H100, FA3, seq 8192, batch 64, cat packing; loaded class verified in logs). Run at the #2945 tip where composite→custom dispatch exists:

model impl loss @ steps 7/8/9 tok/s runs
0.8B hf 0.8243 / 0.7225 / 0.8211 87.0k j4364q4m
0.8B custom 0.8243 / 0.7225 / 0.8210 80.1k thga61ca
9B hf 0.5182 / 0.4532 / 0.5256 29.8k zzazx2av
9B custom 0.5182 / 0.4532 / 0.5255 27.6k oionqht8

Parity to 1e-4 at both scales. The custom impl carries a consistent ~7–8% dense-text throughput cost (profiling follow-up; likely the gated-attention epilogue / varlen bookkeeping) — its value is capability: native packed-boundary consumption, CP hooks (+4% over the old sync path at cp=2, see #2946), MoE EP, and the VLM path, none of which the hf impl supports.

Review fix: Removed the redundant Qwen3.5 varlen batch-size guard because this path always receives one packed row. The other automated review findings are addressed by later PRs in the stack or unreachable — see the inline replies.

🤖 Generated with Claude Code


Note

Medium Risk
New training forward path for a flagship architecture (hybrid linear/full attention + custom flash varlen); mistakes would affect loss/CP correctness, but scope is additive with tests and shared MoE primitives.

Overview
Adds a dense Qwen3.5 custom path (Qwen3_5ForCausalLM / Qwen3_5Model) wired into AutoModelForCausalLMPrimeRL via Qwen3_5TextConfig, reusing MoE building blocks (GatedDeltaNet, gated SDPA/flash, RMSNorm, rotary) with dense MLP instead of experts.

Decoder layers follow layer_types (linear_attention vs full_attention). Flash paths derive varlen cu_seqlens from position_ids for packed training; KV cache is explicitly unsupported.

Shared normalize_qwen3_5_attn_implementation maps eagersdpa and hub FA3 aliases→flash_attention_3, used by both dense and MoE factories plus _check_and_adjust_attn_implementation. Dense Qwen3_5GatedFlashAttention is hooked into ring and Ulysses CP substitution alongside MoE.

State-dict conversion is identity for dense HF layouts. Unit tests cover HF key parity on meta, attention dispatch/normalization, and ring patching.

Reviewed by Cursor Bugbot for commit fd38616. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 05690b5b6b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/prime_rl/trainer/models/qwen3_5/modeling_qwen3_5.py
Comment thread src/prime_rl/trainer/models/qwen3_5/modeling_qwen3_5.py
Comment thread src/prime_rl/trainer/models/qwen3_5/modeling_qwen3_5.py
Comment thread src/prime_rl/trainer/models/qwen3_5_moe/modeling_qwen3_5_moe.py
@hubert-marek hubert-marek force-pushed the feat/qwen35-dense-attention branch from 05690b5 to 72676ec Compare July 6, 2026 19:38
@hubert-marek hubert-marek force-pushed the feat/sft-renderer-only branch from 2fd0cf7 to 7d84eda Compare July 6, 2026 19:39
hubert-marek and others added 2 commits July 6, 2026 19:50
Implement dense Qwen3.5 (Qwen3_5ForCausalLM / Qwen3_5Model) on the
PrimeRL attention modules, reusing the Qwen3.5-MoE building blocks
(GatedDeltaNet, gated SDPA/flash attention, RMSNorm, rotary embedding)
so ring/ulysses CP substitution patches the dense and MoE models
consistently:

- New models/qwen3_5 package: decoder layer dispatching on
  layer_types (linear_attention / full_attention), flash varlen
  cu_seqlens from position_ids, identity state-dict conversion
  (dense HF layout matches PrimeRL layout).
- Shared normalize_qwen3_5_attn_implementation on the MoE module
  (eager→sdpa, kernels-community/vllm-flash-attn3→flash_attention_3),
  consumed by both models' attention factories and
  _check_and_adjust_attn_implementation.
- Register Qwen3_5TextConfig / Qwen3_5ForCausalLM in AutoConfig and
  the custom causal-LM mapping; hook dense Qwen3_5GatedFlashAttention
  into substitute_ring_attn / substitute_ulysses_attn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The varlen kernel consumes a single packed row; batch rows beyond the
first were silently dropped by the [0] indexing while cu_seqlens counted
all rows' tokens. Fail loudly instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hubert-marek hubert-marek force-pushed the feat/sft-renderer-only branch from 7d84eda to e6b95f4 Compare July 6, 2026 19:51
@hubert-marek hubert-marek force-pushed the feat/qwen35-dense-attention branch from 60df70d to 4fd7c5d Compare July 6, 2026 19:51
Comment thread src/prime_rl/trainer/models/qwen3_5_moe/modeling_qwen3_5_moe.py Outdated

@mikasenghaas mikasenghaas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no strong opinions here, looks fine to me

Comment thread src/prime_rl/trainer/models/qwen3_5_moe/modeling_qwen3_5_moe.py Outdated

@S1ro1 S1ro1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a comment, else lgtm

The custom attention path always receives one packed row, so avoid validating an invariant already guaranteed by the caller.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hubert-marek hubert-marek reopened this Jul 9, 2026
@hubert-marek hubert-marek marked this pull request as draft July 9, 2026 21:53
@hubert-marek hubert-marek marked this pull request as ready for review July 9, 2026 21:53
@samsja samsja merged commit 29b63aa into feat/sft-renderer-only Jul 9, 2026
6 of 28 checks passed
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.

4 participants