Coverage for transformer_lens/model_bridge/supported_architectures/deepseek_v2.py: 100%
18 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-01 16:23 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-01 16:23 +0000
1"""DeepSeek V2 architecture adapter.
3Supports DeepSeek-V2, DeepSeek-V2-Lite, and DeepSeek-Coder-V2 models
4(all use DeepseekV2ForCausalLM).
6Key features:
7- Multi-Head Latent Attention (MLA): Q and KV compressed via LoRA-style projections.
8 DeepSeek-V2-Lite sets q_lora_rank=None, skipping Q compression and using a direct
9 q_proj instead — MLAAttentionBridge.forward handles both paths automatically.
10- Mixture of Experts (MoE) with shared experts on most layers
11- Dense MLP on first `first_k_dense_replace` layers
12"""
14from typing import Any
16from transformer_lens.model_bridge.architecture_adapter import ArchitectureAdapter
17from transformer_lens.model_bridge.generalized_components import (
18 EmbeddingBridge,
19 LinearBridge,
20 MLAAttentionBridge,
21 MLABlockBridge,
22 MoEBridge,
23 RMSNormalizationBridge,
24 RotaryEmbeddingBridge,
25 UnembeddingBridge,
26)
27from transformer_lens.model_bridge.generalized_components.base import (
28 GeneralizedComponent,
29)
32class DeepSeekV2ArchitectureAdapter(ArchitectureAdapter):
33 """Architecture adapter for DeepSeek V2 / V2-Lite / Coder-V2 models.
35 Uses RMSNorm, MLA with compressed Q/KV projections (or direct Q projection
36 when q_lora_rank is None), partial RoPE, MoE on most layers (dense MLP on
37 first few), and no biases.
38 """
40 _testing_eager = None
42 def __init__(self, cfg: Any) -> None:
43 super().__init__(cfg)
45 self.cfg.normalization_type = "RMS"
46 self.cfg.positional_embedding_type = "rotary"
47 self.cfg.gated_mlp = True
48 self.cfg.final_rms = True
49 self.cfg.uses_rms_norm = True
51 # MLA has no per-head q/k/v to fold into; skip LN folding.
52 self.supports_fold_ln = False
54 self.weight_processing_conversions = {}
56 self.component_mapping = {
57 "embed": EmbeddingBridge(name="model.embed_tokens"),
58 "rotary_emb": RotaryEmbeddingBridge(name="model.rotary_emb", config=self.cfg),
59 "blocks": MLABlockBridge(
60 name="model.layers",
61 submodules={
62 "ln1": RMSNormalizationBridge(name="input_layernorm", config=self.cfg),
63 "ln2": RMSNormalizationBridge(name="post_attention_layernorm", config=self.cfg),
64 "attn": MLAAttentionBridge(
65 name="self_attn",
66 config=self.cfg,
67 submodules={
68 # V2-full (q_lora_rank set): two-stage LoRA Q compression.
69 # These are absent in V2-Lite — marked optional so bridge
70 # setup skips them gracefully. The actual forward call is
71 # handled inside MLAAttentionBridge which checks q_lora_rank.
72 "q_a_proj": LinearBridge(name="q_a_proj", optional=True),
73 # q_a_layernorm is a norm inside the attention block; its
74 # forward is called directly by MLAAttentionBridge, so a
75 # plain GeneralizedComponent (with optional support) suffices.
76 "q_a_layernorm": GeneralizedComponent(
77 name="q_a_layernorm", optional=True
78 ),
79 "q_b_proj": LinearBridge(name="q_b_proj", optional=True),
80 # V2-Lite only: direct Q projection, no compression.
81 "q_proj": LinearBridge(name="q_proj", optional=True),
82 # KV path — always present across all V2 variants.
83 "kv_a_proj_with_mqa": LinearBridge(name="kv_a_proj_with_mqa"),
84 "kv_a_layernorm": RMSNormalizationBridge(
85 name="kv_a_layernorm", config=self.cfg
86 ),
87 "kv_b_proj": LinearBridge(name="kv_b_proj"),
88 "o": LinearBridge(name="o_proj"),
89 },
90 ),
91 # Layers before first_k_dense_replace are DeepseekV2MLP:
92 # the MoE parts are skipped and the dense_* projections bind,
93 # which is what gives those layers gated-MLP neuron hooks.
94 "mlp": self._build_mlp_bridge(),
95 },
96 ),
97 "ln_final": RMSNormalizationBridge(name="model.norm", config=self.cfg),
98 "unembed": UnembeddingBridge(name="lm_head"),
99 }
101 def _build_mlp_bridge(self):
102 """Routed MoE with optional shared experts; Youtu (all-dense) overrides.
104 Dense-prefix layers (idx < first_k_dense_replace) bind as gated MLPs
105 with neuron-basis hook_pre/hook_pre_linear/hook_post (#1645).
106 """
107 return MoEBridge(
108 name="mlp",
109 config=self.cfg,
110 sparse_required=("gate",),
111 submodules={
112 # Router is a custom Module, not nn.Linear.
113 "gate": GeneralizedComponent(name="gate", optional=True),
114 "shared_experts": self._gated_mlp(name="shared_experts", optional=True),
115 # Dense-layer projections (present only on layers before
116 # first_k_dense_replace); their presence is what makes
117 # MoEBridge bind gated-MLP neuron hooks there (#1645).
118 "dense_gate": LinearBridge(name="gate_proj", optional=True),
119 "dense_in": LinearBridge(name="up_proj", optional=True),
120 "dense_out": LinearBridge(name="down_proj", optional=True),
121 },
122 )