Coverage for transformer_lens/patching.py: 95%
295 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-10-10 05:10 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-10-10 05:10 +0000
1"""Patching.
3A module for patching activations in a transformer model, and measuring the effect of the patch on
4the output. This implements the activation patching technique for a range of types of activation.
5The structure is to have a single :func:`generic_activation_patch` function that does everything,
6and to have a range of specialised functions for specific types of activation.
8Context:
10Activation Patching is technique introduced in the `ROME paper <http://rome.baulab.info/>`, which
11uses a causal intervention to identify which activations in a model matter for producing some
12output. It runs the model on input A, replaces (patches) an activation with that same activation on
13input B, and sees how much that shifts the answer from A to B.
15More details: The setup of activation patching is to take two runs of the model on two different
16inputs, the clean run and the corrupted run. The clean run outputs the correct answer and the
17corrupted run does not. The key idea is that we give the model the corrupted input, but then
18intervene on a specific activation and patch in the corresponding activation from the clean run (ie
19replace the corrupted activation with the clean activation), and then continue the run. And we then
20measure how much the output has updated towards the correct answer.
22- We can then iterate over many
23 possible activations and look at how much they affect the corrupted run. If patching in an
24 activation significantly increases the probability of the correct answer, this allows us to
25 localise which activations matter.
26- A key detail is that we move a single activation __from__ the clean run __to __the corrupted run.
27 So if this changes the answer from incorrect to correct, we can be confident that the activation
28 moved was important.
30Intuition:
32The ability to **localise** is a key move in mechanistic interpretability - if the computation is
33diffuse and spread across the entire model, it is likely much harder to form a clean mechanistic
34story for what's going on. But if we can identify precisely which parts of the model matter, we can
35then zoom in and determine what they represent and how they connect up with each other, and
36ultimately reverse engineer the underlying circuit that they represent. And, empirically, on at
37least some tasks activation patching tends to find that computation is extremely localised:
39- This technique helps us precisely identify which parts of the model matter for a certain
40 part of a task. Eg, answering “The Eiffel Tower is in” with “Paris” requires figuring out that
41 the Eiffel Tower is in Paris, and that it’s a factual recall task and that the output is a
42 location. Patching to “The Colosseum is in” controls for everything other than the “Eiffel Tower
43 is located in Paris” feature.
44- It helps a lot if the corrupted prompt has the same number of tokens
46This, unlike direct logit attribution, can identify meaningful parts of a circuit from anywhere
47within the model, rather than just the end.
48"""
50from __future__ import annotations
52import itertools
53from dataclasses import dataclass
54from functools import partial
55from typing import (
56 Callable,
57 Dict,
58 List,
59 Mapping,
60 Optional,
61 Sequence,
62 Tuple,
63 Union,
64 overload,
65)
67import einops
68import pandas as pd
69import torch
70from jaxtyping import Float, Int
71from tqdm.auto import tqdm
72from typing_extensions import Literal
74import transformer_lens.utilities as utils
75from transformer_lens.ActivationCache import ActivationCache
76from transformer_lens.model_protocol import TransformerLensModel
77from transformer_lens.utilities.statistics import (
78 bootstrap_ci,
79 derive_generator,
80 sign_flip_permutation_pvalue,
81 standard_error,
82)
84# %%
85Logits = torch.Tensor
86AxisNames = Literal["layer", "pos", "head_index", "head", "src_pos", "dest_pos"]
89# %%
92def make_df_from_ranges(
93 column_max_ranges: Sequence[int], column_names: Sequence[str]
94) -> pd.DataFrame:
95 """
96 Takes in a list of column names and max ranges for each column, and returns a dataframe with the cartesian product of the range for each column (ie iterating through all combinations from zero to column_max_range - 1, in order, incrementing the final column first)
97 """
98 rows = list(itertools.product(*[range(axis_max_range) for axis_max_range in column_max_ranges]))
99 df = pd.DataFrame(rows, columns=column_names)
100 return df
103# %%
104CorruptedActivation = torch.Tensor
105PatchedActivation = torch.Tensor
106MetricFn = Callable[[Float[torch.Tensor, "batch pos d_vocab"]], torch.Tensor]
107Reduce = Literal["scalar", "mean", "none"]
108_REDUCE_MODES = ("scalar", "mean", "none")
109_RESULT_AXIS_RENAMES = {"head_index": "head", "dest_pos": "pos"}
112@dataclass
113class PatchingResult:
114 """Per-example (or per-metric) activation-patching results with statistics on top.
116 Returned by :func:`generic_activation_patch` and every ``get_act_patch_*``
117 function when ``reduce="none"``, ``baseline=True`` or several metrics are
118 requested. ``values`` is laid out ``[metric?, *axes | n_rows, batch?]``: the
119 metric axis exists only when a mapping of metrics was passed
120 (``has_metric_axis``), the middle axes are ``axis_names`` (or one flat
121 ``n_rows`` axis when an ``index_df`` was supplied), and the trailing batch
122 axis exists only when ``reduced`` is ``False``.
124 Attributes:
125 values: The patched metric values.
126 axis_names: Names of the swept axes, ``[]`` in flat ``index_df`` mode.
127 index_df: One row per swept cell, in the order the cells were run.
128 metric_names: Metric names, ``["metric"]`` for a single callable.
129 reduced: ``True`` when the batch axis has been averaged away.
130 baseline: Unpatched corrupted-run metric, ``[metric?, batch]`` (or
131 ``[metric?]`` when reduced), when the sweep was run with
132 ``baseline=True``.
133 has_metric_axis: Whether ``values`` carries a leading metric axis.
134 patch_type_names: Names of the ``patch_type`` axis entries when the
135 result came from a ``*_every`` helper (``"out"``, ``"q"``, ...).
136 GQA ``k``/``v`` sweeps are zero-padded to ``n_heads`` there, so the
137 padded cells appear as zero-valued rows in ``index_df`` and
138 :meth:`to_dataframe`.
139 """
141 values: torch.Tensor
142 axis_names: List[str]
143 index_df: pd.DataFrame
144 metric_names: List[str]
145 reduced: bool
146 baseline: Optional[torch.Tensor] = None
147 has_metric_axis: bool = False
148 patch_type_names: Optional[List[str]] = None
150 def __getitem__(self, metric_name: str) -> "PatchingResult":
151 """One metric's result, with the metric axis dropped."""
152 if metric_name not in self.metric_names:
153 raise KeyError(f"unknown metric {metric_name!r}; have {self.metric_names}")
154 if not self.has_metric_axis: 154 ↛ 155line 154 didn't jump to line 155 because the condition on line 154 was never true
155 return self
156 i = self.metric_names.index(metric_name)
157 return PatchingResult(
158 values=self.values[i],
159 axis_names=list(self.axis_names),
160 index_df=self.index_df,
161 metric_names=[metric_name],
162 reduced=self.reduced,
163 baseline=None if self.baseline is None else self.baseline[i],
164 has_metric_axis=False,
165 patch_type_names=self.patch_type_names,
166 )
168 def _require_per_example(self, what: str) -> None:
169 if self.reduced:
170 raise ValueError(f"{what} needs per-example values; run the sweep with reduce='none'")
172 def mean(self) -> torch.Tensor:
173 """Batch mean per cell (``values`` itself when already reduced)."""
174 return self.values if self.reduced else self.values.mean(dim=-1)
176 def stderr(self) -> torch.Tensor:
177 """Standard error of the batch mean per cell."""
178 self._require_per_example("stderr")
179 return standard_error(self.values, dim=-1)
181 def _broadcast_baseline(self) -> torch.Tensor:
182 if self.baseline is None:
183 raise ValueError("effect needs a baseline; run the sweep with baseline=True")
184 lead = 1 if self.has_metric_axis else 0
185 middle = self.values.ndim - lead - (0 if self.reduced else 1)
186 shape = (
187 tuple(self.baseline.shape[:lead]) + (1,) * middle + tuple(self.baseline.shape[lead:])
188 )
189 return self.baseline.reshape(shape)
191 def effect(self) -> torch.Tensor:
192 """``values - baseline``: the change each patch makes to the corrupted run, per example."""
193 return self.values - self._broadcast_baseline()
195 def normalized(self, clean_baseline: torch.Tensor) -> torch.Tensor:
196 """``(values - corrupted) / (clean - corrupted)`` per example.
198 ``clean_baseline`` is the same metric evaluated on the unpatched clean run,
199 shaped like ``baseline``. 0 means the patch did nothing, 1 means it
200 restored the clean metric. A prompt whose clean and corrupted metrics
201 coincide has no recovery scale and yields ``inf`` / ``nan`` for that
202 example; filter such prompts out before normalizing.
203 """
204 corrupted = self._broadcast_baseline()
205 baseline = self.baseline
206 assert baseline is not None # _broadcast_baseline raised otherwise
207 clean = torch.as_tensor(clean_baseline, dtype=self.values.dtype, device=self.values.device)
208 if clean.shape != baseline.shape:
209 raise ValueError(
210 f"clean_baseline must match baseline shape {tuple(baseline.shape)}, "
211 f"got {tuple(clean.shape)}"
212 )
213 clean = clean.reshape(corrupted.shape)
214 return (self.values - corrupted) / (clean - corrupted)
216 def bootstrap_ci(
217 self,
218 *,
219 confidence: float = 0.95,
220 n_resamples: int = 1000,
221 seed: int = 0,
222 statistic: str = "mean",
223 of_effect: bool = False,
224 ) -> Tuple[torch.Tensor, torch.Tensor]:
225 """Percentile bootstrap interval of the batch ``statistic`` per cell.
227 ``of_effect=True`` bootstraps ``effect()`` instead of ``values``. Seeded
228 through :func:`~transformer_lens.utilities.statistics.derive_generator`.
229 """
230 self._require_per_example("bootstrap_ci")
231 data = self.effect() if of_effect else self.values
232 return bootstrap_ci(
233 data,
234 dim=-1,
235 statistic=statistic,
236 confidence=confidence,
237 n_resamples=n_resamples,
238 generator=derive_generator(seed, "bootstrap", statistic),
239 )
241 def permutation_pvalue(
242 self,
243 *,
244 n_permutations: int = 1000,
245 seed: int = 0,
246 alternative: str = "two-sided",
247 ) -> torch.Tensor:
248 """Sign-flip permutation p-value that each cell's mean effect is zero.
250 Uses ``effect()`` (needs ``baseline=True``); exact for 12 or fewer
251 examples, Monte Carlo above that.
252 """
253 self._require_per_example("permutation_pvalue")
254 return sign_flip_permutation_pvalue(
255 self.effect(),
256 dim=-1,
257 n_permutations=n_permutations,
258 generator=derive_generator(seed, "permutation"),
259 alternative=alternative,
260 )
262 def to_dataframe(self) -> pd.DataFrame:
263 """Long form: one row per (metric, swept cell, example) with the value."""
264 rows = []
265 n_metrics = len(self.metric_names)
266 values = self.values if self.has_metric_axis else self.values.unsqueeze(0)
267 flat_cells = values.reshape(n_metrics, len(self.index_df), -1).detach().cpu()
268 for m, name in enumerate(self.metric_names):
269 for c, (_, index_row) in enumerate(self.index_df.iterrows()):
270 for b in range(flat_cells.shape[-1]):
271 row = {"metric": name, **index_row.to_dict()}
272 if self.patch_type_names is not None and "patch_type" in row:
273 row["patch_type_name"] = self.patch_type_names[int(row["patch_type"])]
274 if not self.reduced: 274 ↛ 276line 274 didn't jump to line 276 because the condition on line 274 was always true
275 row["example"] = b
276 row["value"] = flat_cells[m, c, b].item()
277 rows.append(row)
278 return pd.DataFrame(rows)
281@overload
282def generic_activation_patch(
283 model: TransformerLensModel,
284 corrupted_tokens: Int[torch.Tensor, "batch pos"],
285 clean_cache: ActivationCache,
286 patching_metric: MetricFn,
287 patch_setter: Callable[
288 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
289 ],
290 activation_name: str,
291 index_axis_names: Optional[Sequence[AxisNames]] = None,
292 index_df: Optional[pd.DataFrame] = None,
293 return_index_df: Literal[False] = False,
294 *,
295 reduce: Literal["scalar", "mean"] = "scalar",
296 baseline: Literal[False] = False,
297) -> torch.Tensor:
298 ...
301@overload
302def generic_activation_patch(
303 model: TransformerLensModel,
304 corrupted_tokens: Int[torch.Tensor, "batch pos"],
305 clean_cache: ActivationCache,
306 patching_metric: MetricFn,
307 patch_setter: Callable[
308 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
309 ],
310 activation_name: str,
311 index_axis_names: Optional[Sequence[AxisNames]],
312 index_df: Optional[pd.DataFrame],
313 return_index_df: Literal[True],
314 *,
315 reduce: Literal["scalar", "mean"] = "scalar",
316 baseline: Literal[False] = False,
317) -> Tuple[torch.Tensor, pd.DataFrame]:
318 ...
321@overload
322def generic_activation_patch(
323 model: TransformerLensModel,
324 corrupted_tokens: Int[torch.Tensor, "batch pos"],
325 clean_cache: ActivationCache,
326 patching_metric: Union[MetricFn, Mapping[str, MetricFn]],
327 patch_setter: Callable[
328 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
329 ],
330 activation_name: str,
331 index_axis_names: Optional[Sequence[AxisNames]] = None,
332 index_df: Optional[pd.DataFrame] = None,
333 return_index_df: bool = False,
334 *,
335 reduce: Literal["none"],
336 baseline: bool = False,
337) -> PatchingResult:
338 ...
341@overload
342def generic_activation_patch(
343 model: TransformerLensModel,
344 corrupted_tokens: Int[torch.Tensor, "batch pos"],
345 clean_cache: ActivationCache,
346 patching_metric: MetricFn,
347 patch_setter: Callable[
348 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
349 ],
350 activation_name: str,
351 index_axis_names: Optional[Sequence[AxisNames]] = None,
352 index_df: Optional[pd.DataFrame] = None,
353 return_index_df: bool = False,
354 *,
355 reduce: Reduce = "scalar",
356 baseline: Literal[True],
357) -> PatchingResult:
358 ...
361@overload
362def generic_activation_patch(
363 model: TransformerLensModel,
364 corrupted_tokens: Int[torch.Tensor, "batch pos"],
365 clean_cache: ActivationCache,
366 patching_metric: Mapping[str, MetricFn],
367 patch_setter: Callable[
368 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
369 ],
370 activation_name: str,
371 index_axis_names: Optional[Sequence[AxisNames]] = None,
372 index_df: Optional[pd.DataFrame] = None,
373 return_index_df: bool = False,
374 *,
375 reduce: Reduce = "scalar",
376 baseline: bool = False,
377) -> PatchingResult:
378 ...
381def generic_activation_patch(
382 model: TransformerLensModel,
383 corrupted_tokens: Int[torch.Tensor, "batch pos"],
384 clean_cache: ActivationCache,
385 patching_metric: Union[MetricFn, Mapping[str, MetricFn]],
386 patch_setter: Callable[
387 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation
388 ],
389 activation_name: str,
390 index_axis_names: Optional[Sequence[AxisNames]] = None,
391 index_df: Optional[pd.DataFrame] = None,
392 return_index_df: bool = False,
393 *,
394 reduce: str = "scalar", # Reduce; widened so the ValueError below fires before beartype
395 baseline: bool = False,
396) -> Union[torch.Tensor, Tuple[torch.Tensor, pd.DataFrame], PatchingResult]:
397 """
398 A generic function to do activation patching, will be specialised to specific use cases.
400 Activation patching is about studying the counterfactual effect of a specific activation between a clean run and a corrupted run. The idea is have two inputs, clean and corrupted, which have two different outputs, and differ in some key detail. Eg "The Eiffel Tower is in" vs "The Colosseum is in". Then to take a cached set of activations from the "clean" run, and a set of corrupted.
402 Internally, the key function comes from three things: A list of tuples of indices (eg (layer, position, head_index)), a index_to_act_name function which identifies the right activation for each index, a patch_setter function which takes the corrupted activation, the index and the clean cache, and a metric for how well the patched model has recovered.
404 The indices can either be given explicitly as a pandas dataframe, or by listing the relevant axis names and having them inferred from the tokens and the model config. It is assumed that the first column is always layer.
406 This function then iterates over every tuple of indices, does the relevant patch, and stores it
408 Every patched forward runs the whole corrupted batch, so per-example values are
409 available for free: ``reduce="none"`` keeps them (the metric must then return a
410 ``[batch]`` tensor) and returns a :class:`PatchingResult` with mean, standard
411 error, bootstrap intervals and permutation p-values on top; ``reduce="mean"``
412 averages a ``[batch]`` metric back to today's tensor; a mapping of metrics
413 evaluates all of them on each patched forward. ``baseline=True`` adds one
414 unpatched corrupted forward so effects and normalized recovery can be reported.
416 Args:
417 model: The relevant model
418 corrupted_tokens: The input tokens for the corrupted run
419 clean_cache: The cached activations from the clean run
420 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc), or a mapping of names to such functions. Must return a scalar under ``reduce="scalar"`` and a ``[batch]`` tensor otherwise.
421 patch_setter: A function which acts on (corrupted_activation, index, clean_cache) to edit the activation and patch in the relevant chunk of the clean activation
422 activation_name: The name of the activation being patched
423 index_axis_names: The names of the axes to (fully) iterate over, implicitly fills in index_df
424 index_df: The dataframe of indices, columns are axis names and each row is a tuple of indices. Will be inferred from index_axis_names if not given. When this is input, the output will be a flattened tensor with an element per row of index_df
425 return_index_df: A Boolean flag for whether to return the dataframe of indices too (tensor return only; a ``PatchingResult`` always carries ``index_df``)
426 reduce: ``"scalar"`` (default, metric returns a scalar, tensor return), ``"mean"`` (metric returns ``[batch]``, averaged, tensor return) or ``"none"`` (metric returns ``[batch]``, kept, ``PatchingResult`` return)
427 baseline: Also evaluate the metric on the unpatched corrupted run and return a ``PatchingResult`` carrying it
429 Returns:
430 patched_output: The tensor of the patching metric for each patch. By default it has one dimension for each index dimension, via index_df set explicitly it is flattened with one element per row.
431 index_df *optional*: The dataframe of indices
432 Or a :class:`PatchingResult` when ``reduce="none"``, ``baseline=True`` or a metric mapping was given.
433 """
434 # Lazy import: tools.analysis imports this module's neighbours at package import.
435 from transformer_lens.tools.analysis._model_state import require_eval_mode
437 require_eval_mode(model, operation="activation patching")
438 if reduce not in _REDUCE_MODES:
439 raise ValueError(f"reduce must be one of {_REDUCE_MODES}, got {reduce!r}")
440 metrics: Dict[str, MetricFn]
441 if isinstance(patching_metric, Mapping):
442 has_metric_axis = True
443 metrics = dict(patching_metric)
444 if not metrics: 444 ↛ 445line 444 didn't jump to line 445 because the condition on line 444 was never true
445 raise ValueError("patching_metric mapping must contain at least one metric")
446 else:
447 has_metric_axis = False
448 metrics = {"metric": patching_metric}
449 want_result = has_metric_axis or reduce == "none" or baseline
450 if want_result and return_index_df:
451 raise ValueError(
452 "return_index_df only applies to the tensor return; a PatchingResult carries index_df"
453 )
455 if index_df is None:
456 assert index_axis_names is not None
457 number_of_heads = model.cfg.n_heads
458 # For some models, the number of key value heads is not the same as the number of attention heads
459 if activation_name in ["k", "v"] and model.cfg.n_key_value_heads is not None:
460 number_of_heads = model.cfg.n_key_value_heads
462 # Get the max range for all possible axes
463 max_axis_range = {
464 "layer": model.cfg.n_layers,
465 "pos": corrupted_tokens.shape[-1],
466 "head_index": number_of_heads,
467 }
468 max_axis_range["src_pos"] = max_axis_range["pos"]
469 max_axis_range["dest_pos"] = max_axis_range["pos"]
470 max_axis_range["head"] = max_axis_range["head_index"]
472 # Get the max range for each axis we iterate over
473 index_axis_max_range = [max_axis_range[axis_name] for axis_name in index_axis_names]
475 # Get the dataframe where each row is a tuple of indices
476 index_df = make_df_from_ranges(index_axis_max_range, index_axis_names)
478 flattened_output = False
479 else:
480 # A dataframe of indices was provided. Verify that we did not *also* receive index_axis_names
481 assert index_axis_names is None
482 index_axis_max_range = index_df.max().to_list()
483 flattened_output = True
485 batch_size = int(corrupted_tokens.shape[0])
486 per_example = reduce != "scalar"
487 cell_shape = (len(index_df),) if flattened_output else tuple(index_axis_max_range)
488 store_shape = (len(metrics),) + cell_shape + ((batch_size,) if per_example else ())
489 # Create an empty tensor to show the patched metric for each patch
490 store = torch.zeros(store_shape, device=model.cfg.device)
492 def evaluate(logits: torch.Tensor) -> torch.Tensor:
493 """Every metric on one set of logits, shaped [metric, batch?] after validation."""
494 rows = []
495 for name, fn in metrics.items():
496 out = torch.as_tensor(fn(logits))
497 if per_example:
498 if out.ndim != 1 or out.shape[0] != batch_size:
499 raise ValueError(
500 f"patching_metric {name!r} must return a [batch] tensor of length "
501 f"{batch_size} under reduce={reduce!r}, got shape {tuple(out.shape)}"
502 )
503 elif out.ndim != 0:
504 raise ValueError(
505 f"patching_metric {name!r} must return a scalar under reduce='scalar', got "
506 f"shape {tuple(out.shape)}; return a [batch] tensor and pass reduce='mean' "
507 "or reduce='none' to keep per-example values"
508 )
509 rows.append(out.detach())
510 return torch.stack(rows).to(store.dtype)
512 baseline_values: Optional[torch.Tensor] = None
513 if baseline: 513 ↛ 518line 513 didn't jump to line 518 because the condition on line 513 was always true
514 # Same entry point as the patched runs, with no hooks attached.
515 baseline_values = evaluate(model.run_with_hooks(corrupted_tokens, fwd_hooks=[]))
517 # A generic patching hook - for each index, it applies the patch_setter appropriately to patch the activation
518 def patching_hook(corrupted_activation, hook, index, clean_activation):
519 if corrupted_activation.requires_grad: 519 ↛ 521line 519 didn't jump to line 521 because the condition on line 519 was always true
520 corrupted_activation = corrupted_activation.clone()
521 return patch_setter(corrupted_activation, index, clean_activation)
523 for c, index_row in enumerate(tqdm((list(index_df.iterrows())))):
524 index = index_row[1].to_list()
526 # The current activation name is just the activation name plus the layer (assumed to be the first element of the input)
527 current_activation_name = utils.get_act_name(activation_name, layer=index[0])
529 # The hook function cannot receive additional inputs, so we use partial to include the specific index and the corresponding clean activation
530 current_hook = partial(
531 patching_hook,
532 index=index,
533 clean_activation=clean_cache[current_activation_name],
534 )
536 patched_logits = model.run_with_hooks(
537 corrupted_tokens, fwd_hooks=[(current_activation_name, current_hook)]
538 )
539 values = evaluate(patched_logits)
540 if flattened_output:
541 store[:, c] = values
542 else:
543 store[(slice(None), *index)] = values
545 if reduce == "mean":
546 store = store.mean(dim=-1)
547 if baseline_values is not None:
548 baseline_values = baseline_values.mean(dim=-1)
550 if not want_result:
551 patched_metric_output = store[0]
552 if return_index_df:
553 return patched_metric_output, index_df
554 return patched_metric_output
556 return PatchingResult(
557 values=store if has_metric_axis else store[0],
558 axis_names=[] if flattened_output else list(index_axis_names or []),
559 index_df=index_df,
560 metric_names=list(metrics),
561 reduced=reduce != "none",
562 baseline=None
563 if baseline_values is None
564 else (baseline_values if has_metric_axis else baseline_values[0]),
565 has_metric_axis=has_metric_axis,
566 )
569# %%
570# Defining patch setters for various shapes of activations
571def layer_pos_patch_setter(corrupted_activation, index, clean_activation):
572 """
573 Applies the activation patch where index = [layer, pos]
575 Implicitly assumes that the activation axis order is [batch, pos, ...], which is true of everything that is not an attention pattern shaped tensor.
576 """
577 assert len(index) == 2
578 layer, pos = index
579 corrupted_activation[:, pos, ...] = clean_activation[:, pos, ...]
580 return corrupted_activation
583def layer_pos_head_vector_patch_setter(
584 corrupted_activation,
585 index,
586 clean_activation,
587):
588 """
589 Applies the activation patch where index = [layer, pos, head_index]
591 Implicitly assumes that the activation axis order is [batch, pos, head_index, ...], which is true of all attention head vector activations (q, k, v, z, result) but *not* of attention patterns.
592 """
593 assert len(index) == 3
594 layer, pos, head_index = index
595 corrupted_activation[:, pos, head_index] = clean_activation[:, pos, head_index]
596 return corrupted_activation
599def layer_head_vector_patch_setter(
600 corrupted_activation,
601 index,
602 clean_activation,
603):
604 """
605 Applies the activation patch where index = [layer, head_index]
607 Implicitly assumes that the activation axis order is [batch, pos, head_index, ...], which is true of all attention head vector activations (q, k, v, z, result) but *not* of attention patterns.
608 """
609 assert len(index) == 2
610 layer, head_index = index
611 corrupted_activation[:, :, head_index] = clean_activation[:, :, head_index]
613 return corrupted_activation
616def layer_head_pattern_patch_setter(
617 corrupted_activation,
618 index,
619 clean_activation,
620):
621 """
622 Applies the activation patch where index = [layer, head_index]
624 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns.
625 """
626 assert len(index) == 2
627 layer, head_index = index
628 corrupted_activation[:, head_index, :, :] = clean_activation[:, head_index, :, :]
630 return corrupted_activation
633def layer_head_pos_pattern_patch_setter(
634 corrupted_activation,
635 index,
636 clean_activation,
637):
638 """
639 Applies the activation patch where index = [layer, head_index, dest_pos]
641 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns.
642 """
643 assert len(index) == 3
644 layer, head_index, dest_pos = index
645 corrupted_activation[:, head_index, dest_pos, :] = clean_activation[:, head_index, dest_pos, :]
647 return corrupted_activation
650def layer_head_dest_src_pos_pattern_patch_setter(
651 corrupted_activation,
652 index,
653 clean_activation,
654):
655 """
656 Applies the activation patch where index = [layer, head_index, dest_pos, src_pos]
658 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns.
659 """
660 assert len(index) == 4
661 layer, head_index, dest_pos, src_pos = index
662 corrupted_activation[:, head_index, dest_pos, src_pos] = clean_activation[
663 :, head_index, dest_pos, src_pos
664 ]
666 return corrupted_activation
669# %%
670# Defining activation patching functions for a range of common activation patches.
671get_act_patch_resid_pre = partial(
672 generic_activation_patch,
673 patch_setter=layer_pos_patch_setter,
674 activation_name="resid_pre",
675 index_axis_names=("layer", "pos"),
676)
677get_act_patch_resid_pre.__doc__ = """
678 Function to get activation patching results for the residual stream (at the start of each block) (by position). Returns a tensor of shape [n_layers, pos]
680 See generic_activation_patch for a more detailed explanation of activation patching
682 Args:
683 model: The relevant model
684 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
685 clean_cache (ActivationCache): The cached activations from the clean run
686 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
688 Returns:
689 patched_output (torch.Tensor): The tensor of the patching metric for each resid_pre patch. Has shape [n_layers, pos]
690 """
692get_act_patch_resid_mid = partial(
693 generic_activation_patch,
694 patch_setter=layer_pos_patch_setter,
695 activation_name="resid_mid",
696 index_axis_names=("layer", "pos"),
697)
698get_act_patch_resid_mid.__doc__ = """
699 Function to get activation patching results for the residual stream (between the attn and MLP layer of each block) (by position). Returns a tensor of shape [n_layers, pos]
701 See generic_activation_patch for a more detailed explanation of activation patching
703 Args:
704 model: The relevant model
705 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
706 clean_cache (ActivationCache): The cached activations from the clean run
707 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
709 Returns:
710 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos]
711 """
713get_act_patch_attn_out = partial(
714 generic_activation_patch,
715 patch_setter=layer_pos_patch_setter,
716 activation_name="attn_out",
717 index_axis_names=("layer", "pos"),
718)
719get_act_patch_attn_out.__doc__ = """
720 Function to get activation patching results for the output of each Attention layer (by position). Returns a tensor of shape [n_layers, pos]
722 See generic_activation_patch for a more detailed explanation of activation patching
724 Args:
725 model: The relevant model
726 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
727 clean_cache (ActivationCache): The cached activations from the clean run
728 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
730 Returns:
731 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos]
732 """
734get_act_patch_mlp_out = partial(
735 generic_activation_patch,
736 patch_setter=layer_pos_patch_setter,
737 activation_name="mlp_out",
738 index_axis_names=("layer", "pos"),
739)
740get_act_patch_mlp_out.__doc__ = """
741 Function to get activation patching results for the output of each MLP layer (by position). Returns a tensor of shape [n_layers, pos]
743 See generic_activation_patch for a more detailed explanation of activation patching
745 Args:
746 model: The relevant model
747 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
748 clean_cache (ActivationCache): The cached activations from the clean run
749 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
751 Returns:
752 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos]
753 """
754# %%
755get_act_patch_attn_head_out_by_pos = partial(
756 generic_activation_patch,
757 patch_setter=layer_pos_head_vector_patch_setter,
758 activation_name="z",
759 index_axis_names=("layer", "pos", "head"),
760)
761get_act_patch_attn_head_out_by_pos.__doc__ = """
762 Function to get activation patching results for the output of each Attention Head (by position). Returns a tensor of shape [n_layers, pos, n_heads]
764 See generic_activation_patch for a more detailed explanation of activation patching
766 Args:
767 model: The relevant model
768 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
769 clean_cache (ActivationCache): The cached activations from the clean run
770 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
772 Returns:
773 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads]
774 """
776get_act_patch_attn_head_q_by_pos = partial(
777 generic_activation_patch,
778 patch_setter=layer_pos_head_vector_patch_setter,
779 activation_name="q",
780 index_axis_names=("layer", "pos", "head"),
781)
782get_act_patch_attn_head_q_by_pos.__doc__ = """
783 Function to get activation patching results for the queries of each Attention Head (by position). Returns a tensor of shape [n_layers, pos, n_heads]
785 See generic_activation_patch for a more detailed explanation of activation patching
787 Args:
788 model: The relevant model
789 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
790 clean_cache (ActivationCache): The cached activations from the clean run
791 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
793 Returns:
794 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads]
795 """
797get_act_patch_attn_head_k_by_pos = partial(
798 generic_activation_patch,
799 patch_setter=layer_pos_head_vector_patch_setter,
800 activation_name="k",
801 index_axis_names=("layer", "pos", "head"),
802)
803get_act_patch_attn_head_k_by_pos.__doc__ = """
804 Function to get activation patching results for the keys of each Attention Head (by position). Returns a tensor of shape [n_layers, pos, n_heads] or [n_layers, pos, n_key_value_heads] if the model has a different number of key value heads than attention heads.
806 See generic_activation_patch for a more detailed explanation of activation patching
808 Args:
809 model: The relevant model
810 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
811 clean_cache (ActivationCache): The cached activations from the clean run
812 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
814 Returns:
815 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads] or [n_layers, pos, n_key_value_heads] if the model has a different number of key value heads than attention heads.
816 """
818get_act_patch_attn_head_v_by_pos = partial(
819 generic_activation_patch,
820 patch_setter=layer_pos_head_vector_patch_setter,
821 activation_name="v",
822 index_axis_names=("layer", "pos", "head"),
823)
824get_act_patch_attn_head_v_by_pos.__doc__ = """
825 Function to get activation patching results for the values of each Attention Head (by position). Returns a tensor of shape [n_layers, pos, n_heads] or [n_layers, pos, n_key_value_heads] if the model has a different number of key value heads than attention heads.
827 See generic_activation_patch for a more detailed explanation of activation patching
829 Args:
830 model: The relevant model
831 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
832 clean_cache (ActivationCache): The cached activations from the clean run
833 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
835 Returns:
836 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads] or [n_layers, pos, n_key_value_heads] if the model has a different number of key value heads than attention heads.
837 """
838# %%
839get_act_patch_attn_head_pattern_by_pos = partial(
840 generic_activation_patch,
841 patch_setter=layer_head_pos_pattern_patch_setter,
842 activation_name="pattern",
843 index_axis_names=("layer", "head_index", "dest_pos"),
844)
845get_act_patch_attn_head_pattern_by_pos.__doc__ = """
846 Function to get activation patching results for the attention pattern of each Attention Head (by destination position). Returns a tensor of shape [n_layers, n_heads, dest_pos]
848 See generic_activation_patch for a more detailed explanation of activation patching
850 Args:
851 model: The relevant model
852 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
853 clean_cache (ActivationCache): The cached activations from the clean run
854 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
856 Returns:
857 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads, dest_pos]
858 """
860get_act_patch_attn_head_pattern_dest_src_pos = partial(
861 generic_activation_patch,
862 patch_setter=layer_head_dest_src_pos_pattern_patch_setter,
863 activation_name="pattern",
864 index_axis_names=("layer", "head_index", "dest_pos", "src_pos"),
865)
866get_act_patch_attn_head_pattern_dest_src_pos.__doc__ = """
867 Function to get activation patching results for each destination, source entry of the attention pattern for each Attention Head. Returns a tensor of shape [n_layers, n_heads, dest_pos, src_pos]
869 See generic_activation_patch for a more detailed explanation of activation patching
871 Args:
872 model: The relevant model
873 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
874 clean_cache (ActivationCache): The cached activations from the clean run
875 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
877 Returns:
878 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads, dest_pos, src_pos]
879 """
881# %%
882get_act_patch_attn_head_out_all_pos = partial(
883 generic_activation_patch,
884 patch_setter=layer_head_vector_patch_setter,
885 activation_name="z",
886 index_axis_names=("layer", "head"),
887)
888get_act_patch_attn_head_out_all_pos.__doc__ = """
889 Function to get activation patching results for the outputs of each Attention Head (across all positions). Returns a tensor of shape [n_layers, n_heads]
891 See generic_activation_patch for a more detailed explanation of activation patching
893 Args:
894 model: The relevant model
895 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
896 clean_cache (ActivationCache): The cached activations from the clean run
897 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
899 Returns:
900 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads]
901 """
903get_act_patch_attn_head_q_all_pos = partial(
904 generic_activation_patch,
905 patch_setter=layer_head_vector_patch_setter,
906 activation_name="q",
907 index_axis_names=("layer", "head"),
908)
909get_act_patch_attn_head_q_all_pos.__doc__ = """
910 Function to get activation patching results for the queries of each Attention Head (across all positions). Returns a tensor of shape [n_layers, n_heads]
912 See generic_activation_patch for a more detailed explanation of activation patching
914 Args:
915 model: The relevant model
916 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
917 clean_cache (ActivationCache): The cached activations from the clean run
918 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
920 Returns:
921 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads]
922 """
924get_act_patch_attn_head_k_all_pos = partial(
925 generic_activation_patch,
926 patch_setter=layer_head_vector_patch_setter,
927 activation_name="k",
928 index_axis_names=("layer", "head"),
929)
930get_act_patch_attn_head_k_all_pos.__doc__ = """
931 Function to get activation patching results for the keys of each Attention Head (across all positions). Returns a tensor of shape [n_layers, n_heads] or [n_layers, n_key_value_heads] if the model has a different number of key value heads than attention heads.
933 See generic_activation_patch for a more detailed explanation of activation patching
935 Args:
936 model: The relevant model
937 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
938 clean_cache (ActivationCache): The cached activations from the clean run
939 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
941 Returns:
942 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads] or [n_layers, n_key_value_heads] if the model has a different number of key value heads than attention heads.
943 """
945get_act_patch_attn_head_v_all_pos = partial(
946 generic_activation_patch,
947 patch_setter=layer_head_vector_patch_setter,
948 activation_name="v",
949 index_axis_names=("layer", "head"),
950)
951get_act_patch_attn_head_v_all_pos.__doc__ = """
952 Function to get activation patching results for the values of each Attention Head (across all positions). Returns a tensor of shape [n_layers, n_heads] or [n_layers, n_key_value_heads] if the model has a different number of key value heads than attention heads.
954 See generic_activation_patch for a more detailed explanation of activation patching
956 Args:
957 model: The relevant model
958 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
959 clean_cache (ActivationCache): The cached activations from the clean run
960 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
962 Returns:
963 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads] or [n_layers, n_key_value_heads] if the model has a different number of key value heads than attention heads.
964 """
966get_act_patch_attn_head_pattern_all_pos = partial(
967 generic_activation_patch,
968 patch_setter=layer_head_pattern_patch_setter,
969 activation_name="pattern",
970 index_axis_names=("layer", "head_index"),
971)
972get_act_patch_attn_head_pattern_all_pos.__doc__ = """
973 Function to get activation patching results for the attention pattern of each Attention Head (across all positions). Returns a tensor of shape [n_layers, n_heads]
975 See generic_activation_patch for a more detailed explanation of activation patching
977 Args:
978 model: The relevant model
979 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
980 clean_cache (ActivationCache): The cached activations from the clean run
981 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
983 Returns:
984 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads]
985 """
987# %%
990_HEAD_TYPES = ("out", "q", "k", "v", "pattern")
991_BLOCK_TYPES = ("resid_pre", "attn_out", "mlp_out")
992_PER_EXAMPLE_NOTE = """
993 Keyword-only ``reduce`` ("scalar" | "mean" | "none") and ``baseline`` are forwarded to
994 generic_activation_patch; with reduce="none", baseline=True or a mapping of metrics the
995 return is a PatchingResult carrying per-example values and statistics instead of a tensor.
996"""
997for _fn in (
998 get_act_patch_resid_pre,
999 get_act_patch_resid_mid,
1000 get_act_patch_attn_out,
1001 get_act_patch_mlp_out,
1002 get_act_patch_attn_head_out_by_pos,
1003 get_act_patch_attn_head_q_by_pos,
1004 get_act_patch_attn_head_k_by_pos,
1005 get_act_patch_attn_head_v_by_pos,
1006 get_act_patch_attn_head_pattern_by_pos,
1007 get_act_patch_attn_head_pattern_dest_src_pos,
1008 get_act_patch_attn_head_out_all_pos,
1009 get_act_patch_attn_head_q_all_pos,
1010 get_act_patch_attn_head_k_all_pos,
1011 get_act_patch_attn_head_v_all_pos,
1012 get_act_patch_attn_head_pattern_all_pos,
1013):
1014 _fn.__doc__ = (_fn.__doc__ or "") + _PER_EXAMPLE_NOTE
1017def _wrap_tensor_result(tensor: torch.Tensor, axis_names: Sequence[str]) -> PatchingResult:
1018 """Give a default tensor sweep the result shape so it can be stacked with typed ones."""
1019 return PatchingResult(
1020 values=tensor,
1021 axis_names=list(axis_names),
1022 index_df=make_df_from_ranges(list(tensor.shape), list(axis_names)),
1023 metric_names=["metric"],
1024 reduced=True,
1025 )
1028def _stack_patch_types(
1029 results: Sequence[Union[torch.Tensor, PatchingResult]],
1030 *,
1031 axis_order: Sequence[str],
1032 n_heads: int,
1033 type_names: Sequence[str],
1034) -> Union[torch.Tensor, PatchingResult]:
1035 """Stack per-activation sweeps on a leading ``patch_type`` axis.
1037 Tensors (the default return) are stacked as-is after the caller padded and
1038 rearranged them. ``PatchingResult`` values are first permuted to
1039 ``axis_order`` (``head_index`` / ``dest_pos`` renamed to ``head`` / ``pos``)
1040 and the head axis zero-padded to ``n_heads`` for GQA ``k``/``v`` sweeps,
1041 keeping the metric axis first and the batch axis last. ``index_df`` is
1042 rebuilt from the padded grid so it stays one row per cell of ``values``;
1043 padded GQA cells therefore appear as zero-valued rows.
1044 """
1045 if all(isinstance(r, torch.Tensor) for r in results): 1045 ↛ 1046line 1045 didn't jump to line 1046 because the condition on line 1045 was never true
1046 return torch.stack([r for r in results if isinstance(r, torch.Tensor)], dim=0)
1047 typed = [r for r in results if isinstance(r, PatchingResult)]
1048 if len(typed) != len(results): 1048 ↛ 1049line 1048 didn't jump to line 1049 because the condition on line 1048 was never true
1049 raise TypeError("cannot stack tensor and PatchingResult sweeps together")
1050 first = typed[0]
1051 for r in typed[1:]:
1052 if r.metric_names != first.metric_names or r.reduced != first.reduced: 1052 ↛ 1053line 1052 didn't jump to line 1053 because the condition on line 1052 was never true
1053 raise ValueError("every sweep must use the same metrics and reduce mode to be stacked")
1054 head_axis = list(axis_order).index("head") if "head" in axis_order else None
1055 values = []
1056 for r in typed:
1057 names = [_RESULT_AXIS_RENAMES.get(n, n) for n in r.axis_names]
1058 if sorted(names) != sorted(axis_order): 1058 ↛ 1059line 1058 didn't jump to line 1059 because the condition on line 1058 was never true
1059 raise ValueError(f"sweep axes {names} do not match {list(axis_order)}")
1060 lead = 1 if r.has_metric_axis else 0
1061 perm = list(range(lead)) + [lead + names.index(n) for n in axis_order]
1062 tail = list(range(lead + len(names), r.values.ndim))
1063 v = r.values.permute(*perm, *tail)
1064 pad = 0 if head_axis is None else n_heads - v.shape[lead + head_axis]
1065 if head_axis is not None and pad > 0:
1066 pad_shape = list(v.shape)
1067 pad_shape[lead + head_axis] = pad
1068 v = torch.cat([v, v.new_zeros(pad_shape)], dim=lead + head_axis)
1069 values.append(v)
1070 stacked = torch.stack(values, dim=1 if first.has_metric_axis else 0)
1071 lead = 1 if first.has_metric_axis else 0
1072 cell_shape = list(stacked.shape[lead : lead + 1 + len(axis_order)])
1073 return PatchingResult(
1074 values=stacked,
1075 axis_names=["patch_type", *axis_order],
1076 index_df=make_df_from_ranges(cell_shape, ["patch_type", *axis_order]),
1077 metric_names=list(first.metric_names),
1078 reduced=first.reduced,
1079 baseline=first.baseline,
1080 has_metric_axis=first.has_metric_axis,
1081 patch_type_names=list(type_names),
1082 )
1085def _run_sweeps(sweeps, axis_names_per_sweep, model, corrupted_tokens, clean_cache, metric, kwargs):
1086 """Run a helper's sweeps, computing a requested baseline once rather than once per sweep."""
1087 baseline = bool(kwargs.pop("baseline", False))
1088 results: List[Union[torch.Tensor, PatchingResult]] = []
1089 for i, (sweep, axis_names) in enumerate(zip(sweeps, axis_names_per_sweep)):
1090 out = sweep(
1091 model, corrupted_tokens, clean_cache, metric, baseline=baseline and i == 0, **kwargs
1092 )
1093 if baseline and isinstance(out, torch.Tensor):
1094 out = _wrap_tensor_result(out, axis_names)
1095 results.append(out)
1096 return results
1099def get_act_patch_attn_head_all_pos_every(
1100 model, corrupted_tokens, clean_cache, metric, **kwargs
1101) -> Union[Float[torch.Tensor, "patch_type layer head"], PatchingResult]:
1102 """Helper function to get activation patching results for every head (across all positions) for every act type (output, query, key, value, pattern). Wrapper around each's patching function, returns a stacked tensor of shape [5, n_layers, n_heads]
1104 Args:
1105 model: The relevant model
1106 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
1107 clean_cache (ActivationCache): The cached activations from the clean run
1108 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
1109 **kwargs: ``reduce`` / ``baseline`` forwarded to each sweep; a ``PatchingResult`` is then returned with a leading ``patch_type`` axis (``patch_type_names`` = out, q, k, v, pattern)
1111 Returns:
1112 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [5, n_layers, n_heads]
1113 """
1114 results = _run_sweeps(
1115 (
1116 get_act_patch_attn_head_out_all_pos,
1117 get_act_patch_attn_head_q_all_pos,
1118 get_act_patch_attn_head_k_all_pos,
1119 get_act_patch_attn_head_v_all_pos,
1120 get_act_patch_attn_head_pattern_all_pos,
1121 ),
1122 (("layer", "head"),) * 4 + (("layer", "head_index"),),
1123 model,
1124 corrupted_tokens,
1125 clean_cache,
1126 metric,
1127 kwargs,
1128 )
1129 if all(isinstance(r, torch.Tensor) for r in results):
1130 tensors = [r for r in results if isinstance(r, torch.Tensor)]
1131 n_heads = tensors[0].size(-1)
1132 # Reshape k and v to be compatible with the rest of the results in case of n_key_value_heads != n_heads
1133 for i in (2, 3):
1134 tensors[i] = torch.nn.functional.pad(tensors[i], (0, n_heads - tensors[i].size(-1)))
1135 return torch.stack(tensors, dim=0)
1136 return _stack_patch_types(
1137 results, axis_order=("layer", "head"), n_heads=model.cfg.n_heads, type_names=_HEAD_TYPES
1138 )
1141def get_act_patch_attn_head_by_pos_every(
1142 model, corrupted_tokens, clean_cache, metric, **kwargs
1143) -> Union[Float[torch.Tensor, "patch_type layer pos head"], PatchingResult]:
1144 """Helper function to get activation patching results for every head (by position) for every act type (output, query, key, value, pattern). Wrapper around each's patching function, returns a stacked tensor of shape [5, n_layers, pos, n_heads]
1146 Args:
1147 model: The relevant model
1148 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
1149 clean_cache (ActivationCache): The cached activations from the clean run
1150 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
1151 **kwargs: ``reduce`` / ``baseline`` forwarded to each sweep; a ``PatchingResult`` is then returned with a leading ``patch_type`` axis (``patch_type_names`` = out, q, k, v, pattern)
1153 Returns:
1154 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [5, n_layers, pos, n_heads]
1155 """
1156 results = _run_sweeps(
1157 (
1158 get_act_patch_attn_head_out_by_pos,
1159 get_act_patch_attn_head_q_by_pos,
1160 get_act_patch_attn_head_k_by_pos,
1161 get_act_patch_attn_head_v_by_pos,
1162 get_act_patch_attn_head_pattern_by_pos,
1163 ),
1164 (("layer", "pos", "head"),) * 4 + (("layer", "head_index", "dest_pos"),),
1165 model,
1166 corrupted_tokens,
1167 clean_cache,
1168 metric,
1169 kwargs,
1170 )
1171 if all(isinstance(r, torch.Tensor) for r in results):
1172 tensors = [r for r in results if isinstance(r, torch.Tensor)]
1173 n_heads = tensors[0].size(-1)
1174 # Reshape k and v to be compatible with the rest of the results in case of n_key_value_heads != n_heads
1175 for i in (2, 3):
1176 tensors[i] = torch.nn.functional.pad(tensors[i], (0, n_heads - tensors[i].size(-1)))
1177 # Reshape pattern to be compatible with the rest of the results
1178 tensors[4] = einops.rearrange(tensors[4], "batch head pos -> batch pos head")
1179 return torch.stack(tensors, dim=0)
1180 return _stack_patch_types(
1181 results,
1182 axis_order=("layer", "pos", "head"),
1183 n_heads=model.cfg.n_heads,
1184 type_names=_HEAD_TYPES,
1185 )
1188def get_act_patch_block_every(
1189 model, corrupted_tokens, clean_cache, metric, **kwargs
1190) -> Union[Float[torch.Tensor, "patch_type layer pos"], PatchingResult]:
1191 """Helper function to get activation patching results for the residual stream (at the start of each block), output of each Attention layer and output of each MLP layer. Wrapper around each's patching function, returns a stacked tensor of shape [3, n_layers, pos]
1193 Args:
1194 model: The relevant model
1195 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos]
1196 clean_cache (ActivationCache): The cached activations from the clean run
1197 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc)
1198 **kwargs: ``reduce`` / ``baseline`` forwarded to each sweep; a ``PatchingResult`` is then returned with a leading ``patch_type`` axis (``patch_type_names`` = resid_pre, attn_out, mlp_out)
1200 Returns:
1201 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [3, n_layers, pos]
1202 """
1203 results = _run_sweeps(
1204 (get_act_patch_resid_pre, get_act_patch_attn_out, get_act_patch_mlp_out),
1205 (("layer", "pos"),) * 3,
1206 model,
1207 corrupted_tokens,
1208 clean_cache,
1209 metric,
1210 kwargs,
1211 )
1212 if all(isinstance(r, torch.Tensor) for r in results):
1213 return torch.stack([r for r in results if isinstance(r, torch.Tensor)], dim=0)
1214 return _stack_patch_types(
1215 results, axis_order=("layer", "pos"), n_heads=0, type_names=_BLOCK_TYPES
1216 )