Coverage for transformer_lens/model_bridge/supported_architectures/qwen3_5_moe.py: 97%
30 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-08-11 18:50 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-08-11 18:50 +0000
1"""Qwen3.5-MoE architecture adapter.
3Hybrid linear-attention (GatedDeltaNet) + full-attention with sparse MoE MLP
4(256 experts, top-8 routing, shared expert in public checkpoints). Same hybrid
5design as Qwen3.5 dense and the same MoE block family as Qwen3-Next.
7Two adapters: text-only ``Qwen3_5MoeForCausalLM`` and the vision-language
8``Qwen3_5MoeForConditionalGeneration`` (text backbone nested under
9``model.language_model`` plus the Qwen3.5 vision tower).
10"""
12from typing import Any
14import torch
16from transformer_lens.model_bridge.generalized_components import (
17 LinearBridge,
18 MoEBridge,
19 MoERouterBridge,
20)
21from transformer_lens.model_bridge.supported_architectures.qwen3 import (
22 Qwen3ArchitectureAdapter,
23)
24from transformer_lens.model_bridge.supported_architectures.qwen3_5_multimodal import (
25 Qwen3_5MultimodalArchitectureAdapter,
26)
29def _sparse_moe_mlp(adapter):
30 """Qwen3.5 sparse MoE block: 3-tuple router, batched experts, shared expert."""
31 return MoEBridge(
32 name="mlp",
33 config=adapter.cfg,
34 submodules={
35 "gate": MoERouterBridge(name="gate"),
36 "experts": MoEBridge(name="experts", config=adapter.cfg),
37 "shared_expert": adapter._gated_mlp(name="shared_expert"),
38 "shared_expert_gate": LinearBridge(name="shared_expert_gate"),
39 },
40 )
43class Qwen3_5MoeArchitectureAdapter(Qwen3ArchitectureAdapter):
44 """Text-only Qwen3.5-MoE: hybrid GatedDeltaNet + full attention, sparse MoE MLP."""
46 def __init__(self, cfg: Any) -> None:
47 setattr(cfg, "gated_q_proj", True)
48 super().__init__(cfg, hybrid=True)
50 def _build_mlp_bridge(self):
51 """Sparse MoE MLP (router + batched experts + shared expert)."""
52 return _sparse_moe_mlp(self)
54 def prepare_loading(self, model_name: str, model_kwargs: dict) -> None:
55 """Swap to ``text_config`` so AutoModelForCausalLM loads the text-only model
56 (checkpoints ship the ConditionalGeneration architecture)."""
57 config = model_kwargs.get("config")
58 if config is not None and hasattr(config, "text_config"): 58 ↛ exitline 58 didn't return from function 'prepare_loading' because the condition on line 58 was always true
59 model_kwargs["config"] = config.text_config
61 def prepare_model(self, hf_model: Any) -> None:
62 """Reject full multimodal Qwen3.5-MoE models on this text-only adapter."""
63 config = getattr(hf_model, "config", None)
64 architectures = getattr(config, "architectures", []) or []
65 class_name = type(hf_model).__name__
67 is_conditional_generation = (
68 class_name == "Qwen3_5MoeForConditionalGeneration"
69 or "Qwen3_5MoeForConditionalGeneration" in architectures
70 )
71 still_has_top_level_multimodal_config = hasattr(config, "text_config")
72 if is_conditional_generation or still_has_top_level_multimodal_config:
73 raise ValueError(
74 "This adapter is text-only. Pass a Qwen3_5MoeForCausalLM / "
75 "Qwen3_5MoeTextConfig model, or load by model id with "
76 "TransformerBridge.boot_transformers(...) so Qwen3_5MoeForConditionalGeneration "
77 "checkpoints route to the multimodal adapter automatically."
78 )
80 def preprocess_weights(self, state_dict: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
81 """Slice query half from gated q_proj.weight for weight-space analysis."""
82 return self._preprocess_gated_q_proj(state_dict, self.cfg.n_heads, self.cfg.d_head)
85class Qwen3_5MoeMultimodalArchitectureAdapter(Qwen3_5MultimodalArchitectureAdapter):
86 """Vision-language adapter for Qwen3_5MoeForConditionalGeneration.
88 Reuses the Qwen3.5 multimodal wiring (language model under
89 ``model.language_model`` + vision tower) with the MLP swapped for sparse MoE.
90 """
92 def _build_mlp_bridge(self):
93 """Sparse MoE MLP (router + batched experts + shared expert)."""
94 return _sparse_moe_mlp(self)