Coverage for transformer_lens/model_bridge/supported_architectures/llama4.py: 85%
31 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"""Llama 4 (text) architecture adapter.
3Meta's Llama 4 text decoder (``Llama4ForCausalLM``): llama-style RMS-norm
4blocks whose attention adds complex-valued interleaved RoPE, NoPE layers
5with temperature tuning, post-RoPE weightless L2 QK-norm, and chunked
6attention masks — so attention stays delegated to HF. The feed-forward is
7either a sparse MoE (batched 3D experts + top-k sigmoid router + shared
8expert) or a dense gated MLP on non-MoE layers.
9"""
11from typing import Any
13import torch
15from transformer_lens.model_bridge.architecture_adapter import ArchitectureAdapter
16from transformer_lens.model_bridge.generalized_components import (
17 AttentionBridge,
18 BlockBridge,
19 EmbeddingBridge,
20 GatedMLPBridge,
21 LinearBridge,
22 MoEBridge,
23 RMSNormalizationBridge,
24 UnembeddingBridge,
25)
26from transformer_lens.model_bridge.generalized_components.base import (
27 CloneOutputUnderGradMixin,
28)
31class _Llama4SharedExpertBridge(CloneOutputUnderGradMixin, GatedMLPBridge):
32 """Llama4TextMoe accumulates routed output into the shared-expert result
33 with an in-place ``add_``; clone under grad (see mixin)."""
36class _Llama4MoEBridge(MoEBridge):
37 """MoEBridge that fires hook_out in [batch, seq, d_model].
39 Llama4TextMoe flattens to [batch * seq, d_model] internally and the
40 decoder layer views the result back, so hooks are fired on an
41 input-shaped view and the HF-native flat shape is returned.
42 """
44 def forward(self, *args: Any, **kwargs: Any) -> Any:
45 if self.original_component is None: 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 raise RuntimeError(
47 f"Original component not set for {self.name}. Call set_original_component() first."
48 )
49 if len(args) > 0: 49 ↛ 53line 49 didn't jump to line 53 because the condition on line 49 was always true
50 hidden = self.hook_in(args[0])
51 args = (hidden,) + args[1:]
52 else:
53 hidden = self.hook_in(kwargs["hidden_states"])
54 kwargs = {**kwargs, "hidden_states": hidden}
55 output = self.original_component(*args, **kwargs)
56 if isinstance(output, tuple):
57 flat = output[0]
58 if len(output) > 1: 58 ↛ 60line 58 didn't jump to line 60 because the condition on line 58 was always true
59 self.hook_router_scores(output[1])
60 hooked = self.hook_out(flat.view(hidden.shape))
61 return (hooked.view(flat.shape),) + output[1:]
62 assert isinstance(output, torch.Tensor)
63 return self.hook_out(output.view(hidden.shape)).view(output.shape)
66class Llama4ArchitectureAdapter(ArchitectureAdapter):
67 """Architecture adapter for Llama4ForCausalLM models."""
69 def __init__(self, cfg: Any) -> None:
70 """Initialize the Llama 4 architecture adapter."""
71 super().__init__(cfg)
73 self._set_rms_rotary_defaults()
74 self.cfg.attn_implementation = "eager"
76 self.weight_processing_conversions = {
77 **self._qkvo_weight_conversions(),
78 }
80 self.component_mapping = {
81 "embed": EmbeddingBridge(name="model.embed_tokens"),
82 "blocks": BlockBridge(
83 name="model.layers",
84 submodules={
85 "ln1": RMSNormalizationBridge(name="input_layernorm", config=self.cfg),
86 "ln2": RMSNormalizationBridge(name="post_attention_layernorm", config=self.cfg),
87 # Complex-tensor RoPE, NoPE temperature tuning, L2 QK-norm,
88 # and chunked masks live in HF's forward; keep it native.
89 "attn": AttentionBridge(
90 name="self_attn",
91 config=self.cfg,
92 submodules={
93 "q": LinearBridge(name="q_proj"),
94 "k": LinearBridge(name="k_proj"),
95 "v": LinearBridge(name="v_proj"),
96 "o": LinearBridge(name="o_proj"),
97 },
98 maintain_native_attention=True,
99 requires_attention_mask=True,
100 ),
101 # The router returns a (scores, logits) tuple, so it stays
102 # unwrapped; MoEBridge.hook_router_scores captures logits.
103 # Non-MoE layers hold a dense gated MLP under the same name;
104 # its projections map as optional dense_* submodules.
105 "mlp": _Llama4MoEBridge(
106 name="feed_forward",
107 config=self.cfg,
108 submodules={
109 # Dense-layer projections (absent on MoE layers).
110 "dense_gate": LinearBridge(name="gate_proj", optional=True),
111 "dense_in": LinearBridge(name="up_proj", optional=True),
112 "dense_out": LinearBridge(name="down_proj", optional=True),
113 "shared_expert": _Llama4SharedExpertBridge(
114 name="shared_expert",
115 config=self.cfg,
116 optional=True,
117 submodules={
118 "gate": LinearBridge(name="gate_proj"),
119 "in": LinearBridge(name="up_proj"),
120 "out": LinearBridge(name="down_proj"),
121 },
122 ),
123 },
124 ),
125 },
126 ),
127 "ln_final": RMSNormalizationBridge(name="model.norm", config=self.cfg),
128 "unembed": UnembeddingBridge(name="lm_head", config=self.cfg),
129 }