Coverage for transformer_lens/model_bridge/generalized_components/embedding.py: 66%
45 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-10-23 20:45 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-10-23 20:45 +0000
1"""Embedding bridge component.
3This module contains the bridge component for embedding layers.
4"""
5import inspect
6from typing import Any, Dict, Optional
8import torch
10from transformer_lens.model_bridge.generalized_components.base import (
11 GeneralizedComponent,
12)
15class EmbeddingBridge(GeneralizedComponent):
16 """Embedding bridge that wraps transformer embedding layers.
18 This component provides standardized input/output hooks.
19 """
21 property_aliases = {"W_E": "e.weight", "W_pos": "pos.weight"}
23 def __init__(
24 self,
25 name: str,
26 config: Optional[Any] = None,
27 submodules: Optional[Dict[str, GeneralizedComponent]] = {},
28 output_contribution_scale: float = 1.0,
29 ):
30 """Initialize the embedding bridge.
32 Args:
33 name: The name of this component
34 config: Optional configuration (unused for EmbeddingBridge)
35 submodules: Dictionary of GeneralizedComponent submodules to register
36 output_contribution_scale: Outer HF runtime scale exposed by hook_out.
37 Observation preserves the raw forward; edits are unscaled before returning.
38 """
39 super().__init__(name, config, submodules=submodules)
40 self._output_contribution_scale = float(output_contribution_scale)
42 @property
43 def W_E(self) -> torch.Tensor:
44 """Return the embedding weight matrix."""
45 if hasattr(self, "_use_processed_weights") and self._use_processed_weights: 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 if hasattr(self, "_processed_weight"):
47 return self._processed_weight
48 if self.original_component is None: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true
49 raise RuntimeError(f"Original component not set for {self.name}")
50 if hasattr(self.original_component, "inv_freq") and ( 50 ↛ 53line 50 didn't jump to line 53 because the condition on line 50 was never true
51 not hasattr(self.original_component, "weight")
52 ):
53 inv_freq = self.original_component.inv_freq
54 assert isinstance(inv_freq, torch.Tensor), f"inv_freq is not a tensor for {self.name}"
55 return inv_freq
56 assert hasattr(
57 self.original_component, "weight"
58 ), f"Component {self.name} has neither weight nor inv_freq attribute"
59 weight = self.original_component.weight
60 assert isinstance(weight, torch.Tensor), f"Weight is not a tensor for {self.name}"
61 return weight
63 def forward(
64 self, input_ids: torch.Tensor, position_ids: torch.Tensor | None = None, **kwargs: Any
65 ) -> torch.Tensor:
66 """Forward pass through the embedding bridge.
68 Args:
69 input_ids: Input token IDs
70 position_ids: Optional position IDs (ignored if not supported)
71 **kwargs: Additional arguments to pass to the original component
73 Returns:
74 Embedded output
75 """
76 if self.original_component is None: 76 ↛ 77line 76 didn't jump to line 77 because the condition on line 76 was never true
77 raise RuntimeError(
78 f"Original component not set for {self.name}. Call set_original_component() first."
79 )
80 target_dtype = None
81 try:
82 target_dtype = next(self.original_component.parameters()).dtype
83 except StopIteration:
84 pass
85 input_ids = self.hook_in(input_ids)
86 sig = inspect.signature(self.original_component.forward)
87 supports_position_ids = "position_ids" in sig.parameters
88 if not hasattr(self.original_component, "forward") or not supports_position_ids: 88 ↛ 92line 88 didn't jump to line 92 because the condition on line 88 was always true
89 kwargs.pop("position_ids", None)
90 output = self.original_component(input_ids, **kwargs)
91 else:
92 output = self.original_component(input_ids, position_ids=position_ids, **kwargs)
93 if isinstance(output, tuple): 93 ↛ 94line 93 didn't jump to line 94 because the condition on line 93 was never true
94 output = output[0]
95 if target_dtype is not None and output.dtype != target_dtype: 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 output = output.to(dtype=target_dtype)
97 output = self._hook_scaled_output(output, self.hook_out, self._output_contribution_scale)
98 return output