Coverage for transformer_lens/model_bridge/generalized_components/t5gemma2_merged_attention.py: 93%
24 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"""Bridge for T5Gemma2's merged self+cross decoder attention.
3T5Gemma2MergedAttention runs decoder self-attention and encoder-decoder
4cross-attention through one module with shared q/k/v/o projections: it projects
5``encoder_hidden_states`` through the same ``k_proj``/``v_proj``, concatenates the
6encoder K/V onto the decoder K/V, and does a single softmax. In eager mode it
7returns ``(attn_output, self_attn_weights, cross_attn_weights)``, where the self
8and cross weights are the leading and trailing key-position slices of the merged
9pattern.
11This bridge delegates the math to the native module (the merged/cross logic
12cannot be reimplemented by the manual attention path) while exposing both pattern
13slices: ``hook_pattern`` for the self-attention slice (handled by the base class)
14and ``hook_cross_pattern`` for the cross-attention slice.
15"""
16from __future__ import annotations
18from typing import Any, Dict, Optional
20import torch
22from transformer_lens.hook_points import HookPoint
23from transformer_lens.model_bridge.generalized_components.attention import (
24 AttentionBridge,
25)
28class T5Gemma2MergedAttentionBridge(AttentionBridge):
29 """Native-delegating attention bridge that also hooks the cross-attention pattern."""
31 def __init__(self, *args: Any, **kwargs: Any) -> None:
32 # The native module unpacks position_embeddings unconditionally, so
33 # component testing must always supply a (cos, sin) tuple.
34 kwargs.setdefault("requires_position_embeddings", True)
35 super().__init__(*args, **kwargs)
36 self.hook_cross_pattern = HookPoint()
38 def get_random_inputs(
39 self,
40 batch_size: int = 2,
41 seq_len: int = 8,
42 device: Optional[torch.device] = None,
43 dtype: Optional[torch.dtype] = None,
44 ) -> Dict[str, Any]:
45 """Add the merged-attention inputs the native module requires positionally."""
46 inputs = super().get_random_inputs(
47 batch_size=batch_size, seq_len=seq_len, device=device, dtype=dtype
48 )
49 hidden_states = inputs["hidden_states"]
50 inputs["merged_attention_mask"] = None
51 inputs["encoder_hidden_states"] = torch.randn_like(hidden_states)
52 return inputs
54 def forward(self, *args: Any, **kwargs: Any) -> Any:
55 # Base forward delegates to native and hooks output[0] (hook_out) and the
56 # self-attention weights output[1] (hook_pattern). Native returns a third
57 # element — the cross-attention weights — which we hook here.
58 output = super().forward(*args, **kwargs)
59 if isinstance(output, tuple) and len(output) >= 3: 59 ↛ 64line 59 didn't jump to line 64 because the condition on line 59 was always true
60 cross_weights = output[2]
61 if isinstance(cross_weights, torch.Tensor) and cross_weights.dim() == 4: 61 ↛ 64line 61 didn't jump to line 64 because the condition on line 61 was always true
62 cross_weights = self.hook_cross_pattern(cross_weights)
63 output = output[:2] + (cross_weights,) + output[3:]
64 return output