Coverage for transformer_lens/patching.py: 73%

142 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2026-09-01 16:23 +0000

1"""Patching. 

2 

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. 

7 

8Context: 

9 

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. 

14 

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. 

21 

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. 

29 

30Intuition: 

31 

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: 

38 

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 

45 

46This, unlike direct logit attribution, can identify meaningful parts of a circuit from anywhere 

47within the model, rather than just the end. 

48""" 

49 

50from __future__ import annotations 

51 

52import itertools 

53from functools import partial 

54from typing import Callable, Optional, Sequence, Tuple, Union, overload 

55 

56import einops 

57import pandas as pd 

58import torch 

59from jaxtyping import Float, Int 

60from tqdm.auto import tqdm 

61from typing_extensions import Literal 

62 

63import transformer_lens.utilities as utils 

64from transformer_lens.ActivationCache import ActivationCache 

65from transformer_lens.HookedTransformer import HookedTransformer 

66 

67# %% 

68Logits = torch.Tensor 

69AxisNames = Literal["layer", "pos", "head_index", "head", "src_pos", "dest_pos"] 

70 

71 

72# %% 

73 

74 

75def make_df_from_ranges( 

76 column_max_ranges: Sequence[int], column_names: Sequence[str] 

77) -> pd.DataFrame: 

78 """ 

79 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) 

80 """ 

81 rows = list(itertools.product(*[range(axis_max_range) for axis_max_range in column_max_ranges])) 

82 df = pd.DataFrame(rows, columns=column_names) 

83 return df 

84 

85 

86# %% 

87CorruptedActivation = torch.Tensor 

88PatchedActivation = torch.Tensor 

89 

90 

91@overload 

92def generic_activation_patch( 

93 model: HookedTransformer, 

94 corrupted_tokens: Int[torch.Tensor, "batch pos"], 

95 clean_cache: ActivationCache, 

96 patching_metric: Callable[[Float[torch.Tensor, "batch pos d_vocab"]], Float[torch.Tensor, ""]], 

97 patch_setter: Callable[ 

98 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation 

99 ], 

100 activation_name: str, 

101 index_axis_names: Optional[Sequence[AxisNames]] = None, 

102 index_df: Optional[pd.DataFrame] = None, 

103 return_index_df: Literal[False] = False, 

104) -> torch.Tensor: 

105 ... 

106 

107 

108@overload 

109def generic_activation_patch( 

110 model: HookedTransformer, 

111 corrupted_tokens: Int[torch.Tensor, "batch pos"], 

112 clean_cache: ActivationCache, 

113 patching_metric: Callable[[Float[torch.Tensor, "batch pos d_vocab"]], Float[torch.Tensor, ""]], 

114 patch_setter: Callable[ 

115 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation 

116 ], 

117 activation_name: str, 

118 index_axis_names: Optional[Sequence[AxisNames]], 

119 index_df: Optional[pd.DataFrame], 

120 return_index_df: Literal[True], 

121) -> Tuple[torch.Tensor, pd.DataFrame]: 

122 ... 

123 

124 

125def generic_activation_patch( 

126 model: HookedTransformer, 

127 corrupted_tokens: Int[torch.Tensor, "batch pos"], 

128 clean_cache: ActivationCache, 

129 patching_metric: Callable[[Float[torch.Tensor, "batch pos d_vocab"]], Float[torch.Tensor, ""]], 

130 patch_setter: Callable[ 

131 [CorruptedActivation, Sequence[int], ActivationCache], PatchedActivation 

132 ], 

133 activation_name: str, 

134 index_axis_names: Optional[Sequence[AxisNames]] = None, 

135 index_df: Optional[pd.DataFrame] = None, 

136 return_index_df: bool = False, 

137) -> Union[torch.Tensor, Tuple[torch.Tensor, pd.DataFrame]]: 

138 """ 

139 A generic function to do activation patching, will be specialised to specific use cases. 

140 

141 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. 

142 

143 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. 

144 

145 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. 

146 

147 This function then iterates over every tuple of indices, does the relevant patch, and stores it 

148 

149 Args: 

150 model: The relevant model 

151 corrupted_tokens: The input tokens for the corrupted run 

152 clean_cache: The cached activations from the clean run 

153 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

154 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 

155 activation_name: The name of the activation being patched 

156 index_axis_names: The names of the axes to (fully) iterate over, implicitly fills in index_df 

157 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 

158 return_index_df: A Boolean flag for whether to return the dataframe of indices too 

159 

160 Returns: 

161 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. 

162 index_df *optional*: The dataframe of indices 

163 """ 

164 

165 if index_df is None: 165 ↛ 192line 165 didn't jump to line 192 because the condition on line 165 was always true

166 assert index_axis_names is not None 

167 

168 number_of_heads = model.cfg.n_heads 

169 # For some models, the number of key value heads is not the same as the number of attention heads 

170 if activation_name in ["k", "v"] and model.cfg.n_key_value_heads is not None: 170 ↛ 171line 170 didn't jump to line 171 because the condition on line 170 was never true

171 number_of_heads = model.cfg.n_key_value_heads 

172 

173 # Get the max range for all possible axes 

174 max_axis_range = { 

175 "layer": model.cfg.n_layers, 

176 "pos": corrupted_tokens.shape[-1], 

177 "head_index": number_of_heads, 

178 } 

179 max_axis_range["src_pos"] = max_axis_range["pos"] 

180 max_axis_range["dest_pos"] = max_axis_range["pos"] 

181 max_axis_range["head"] = max_axis_range["head_index"] 

182 

183 # Get the max range for each axis we iterate over 

184 index_axis_max_range = [max_axis_range[axis_name] for axis_name in index_axis_names] 

185 

186 # Get the dataframe where each row is a tuple of indices 

187 index_df = make_df_from_ranges(index_axis_max_range, index_axis_names) 

188 

189 flattened_output = False 

190 else: 

191 # A dataframe of indices was provided. Verify that we did not *also* receive index_axis_names 

192 assert index_axis_names is None 

193 index_axis_max_range = index_df.max().to_list() 

194 

195 flattened_output = True 

196 

197 # Create an empty tensor to show the patched metric for each patch 

198 if flattened_output: 198 ↛ 199line 198 didn't jump to line 199 because the condition on line 198 was never true

199 patched_metric_output = torch.zeros(len(index_df), device=model.cfg.device) 

200 else: 

201 patched_metric_output = torch.zeros(index_axis_max_range, device=model.cfg.device) 

202 

203 # A generic patching hook - for each index, it applies the patch_setter appropriately to patch the activation 

204 def patching_hook(corrupted_activation, hook, index, clean_activation): 

205 if corrupted_activation.requires_grad: 205 ↛ 207line 205 didn't jump to line 207 because the condition on line 205 was always true

206 corrupted_activation = corrupted_activation.clone() 

207 return patch_setter(corrupted_activation, index, clean_activation) 

208 

209 for c, index_row in enumerate(tqdm((list(index_df.iterrows())))): 

210 index = index_row[1].to_list() 

211 

212 # The current activation name is just the activation name plus the layer (assumed to be the first element of the input) 

213 current_activation_name = utils.get_act_name(activation_name, layer=index[0]) 

214 

215 # The hook function cannot receive additional inputs, so we use partial to include the specific index and the corresponding clean activation 

216 current_hook = partial( 

217 patching_hook, 

218 index=index, 

219 clean_activation=clean_cache[current_activation_name], 

220 ) 

221 

222 patched_logits = model.run_with_hooks( 

223 corrupted_tokens, fwd_hooks=[(current_activation_name, current_hook)] 

224 ) 

225 

226 if flattened_output: 226 ↛ 227line 226 didn't jump to line 227 because the condition on line 226 was never true

227 patched_metric_output[c] = patching_metric(patched_logits).item() 

228 else: 

229 patched_metric_output[tuple(index)] = patching_metric(patched_logits).item() 

230 

231 if return_index_df: 231 ↛ 232line 231 didn't jump to line 232 because the condition on line 231 was never true

232 return patched_metric_output, index_df 

233 else: 

234 return patched_metric_output 

235 

236 

237# %% 

238# Defining patch setters for various shapes of activations 

239def layer_pos_patch_setter(corrupted_activation, index, clean_activation): 

240 """ 

241 Applies the activation patch where index = [layer, pos] 

242 

243 Implicitly assumes that the activation axis order is [batch, pos, ...], which is true of everything that is not an attention pattern shaped tensor. 

244 """ 

245 assert len(index) == 2 

246 layer, pos = index 

247 corrupted_activation[:, pos, ...] = clean_activation[:, pos, ...] 

248 return corrupted_activation 

249 

250 

251def layer_pos_head_vector_patch_setter( 

252 corrupted_activation, 

253 index, 

254 clean_activation, 

255): 

256 """ 

257 Applies the activation patch where index = [layer, pos, head_index] 

258 

259 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. 

260 """ 

261 assert len(index) == 3 

262 layer, pos, head_index = index 

263 corrupted_activation[:, pos, head_index] = clean_activation[:, pos, head_index] 

264 return corrupted_activation 

265 

266 

267def layer_head_vector_patch_setter( 

268 corrupted_activation, 

269 index, 

270 clean_activation, 

271): 

272 """ 

273 Applies the activation patch where index = [layer, head_index] 

274 

275 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. 

276 """ 

277 assert len(index) == 2 

278 layer, head_index = index 

279 corrupted_activation[:, :, head_index] = clean_activation[:, :, head_index] 

280 

281 return corrupted_activation 

282 

283 

284def layer_head_pattern_patch_setter( 

285 corrupted_activation, 

286 index, 

287 clean_activation, 

288): 

289 """ 

290 Applies the activation patch where index = [layer, head_index] 

291 

292 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns. 

293 """ 

294 assert len(index) == 2 

295 layer, head_index = index 

296 corrupted_activation[:, head_index, :, :] = clean_activation[:, head_index, :, :] 

297 

298 return corrupted_activation 

299 

300 

301def layer_head_pos_pattern_patch_setter( 

302 corrupted_activation, 

303 index, 

304 clean_activation, 

305): 

306 """ 

307 Applies the activation patch where index = [layer, head_index, dest_pos] 

308 

309 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns. 

310 """ 

311 assert len(index) == 3 

312 layer, head_index, dest_pos = index 

313 corrupted_activation[:, head_index, dest_pos, :] = clean_activation[:, head_index, dest_pos, :] 

314 

315 return corrupted_activation 

316 

317 

318def layer_head_dest_src_pos_pattern_patch_setter( 

319 corrupted_activation, 

320 index, 

321 clean_activation, 

322): 

323 """ 

324 Applies the activation patch where index = [layer, head_index, dest_pos, src_pos] 

325 

326 Implicitly assumes that the activation axis order is [batch, head_index, dest_pos, src_pos], which is true of attention scores and patterns. 

327 """ 

328 assert len(index) == 4 

329 layer, head_index, dest_pos, src_pos = index 

330 corrupted_activation[:, head_index, dest_pos, src_pos] = clean_activation[ 

331 :, head_index, dest_pos, src_pos 

332 ] 

333 

334 return corrupted_activation 

335 

336 

337# %% 

338# Defining activation patching functions for a range of common activation patches. 

339get_act_patch_resid_pre = partial( 

340 generic_activation_patch, 

341 patch_setter=layer_pos_patch_setter, 

342 activation_name="resid_pre", 

343 index_axis_names=("layer", "pos"), 

344) 

345get_act_patch_resid_pre.__doc__ = """ 

346 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] 

347 

348 See generic_activation_patch for a more detailed explanation of activation patching  

349 

350 Args: 

351 model: The relevant model 

352 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

353 clean_cache (ActivationCache): The cached activations from the clean run 

354 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

355 

356 Returns: 

357 patched_output (torch.Tensor): The tensor of the patching metric for each resid_pre patch. Has shape [n_layers, pos] 

358 """ 

359 

360get_act_patch_resid_mid = partial( 

361 generic_activation_patch, 

362 patch_setter=layer_pos_patch_setter, 

363 activation_name="resid_mid", 

364 index_axis_names=("layer", "pos"), 

365) 

366get_act_patch_resid_mid.__doc__ = """ 

367 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] 

368 

369 See generic_activation_patch for a more detailed explanation of activation patching  

370 

371 Args: 

372 model: The relevant model 

373 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

374 clean_cache (ActivationCache): The cached activations from the clean run 

375 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

376 

377 Returns: 

378 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos] 

379 """ 

380 

381get_act_patch_attn_out = partial( 

382 generic_activation_patch, 

383 patch_setter=layer_pos_patch_setter, 

384 activation_name="attn_out", 

385 index_axis_names=("layer", "pos"), 

386) 

387get_act_patch_attn_out.__doc__ = """ 

388 Function to get activation patching results for the output of each Attention layer (by position). Returns a tensor of shape [n_layers, pos] 

389 

390 See generic_activation_patch for a more detailed explanation of activation patching  

391 

392 Args: 

393 model: The relevant model 

394 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

395 clean_cache (ActivationCache): The cached activations from the clean run 

396 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

397 

398 Returns: 

399 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos] 

400 """ 

401 

402get_act_patch_mlp_out = partial( 

403 generic_activation_patch, 

404 patch_setter=layer_pos_patch_setter, 

405 activation_name="mlp_out", 

406 index_axis_names=("layer", "pos"), 

407) 

408get_act_patch_mlp_out.__doc__ = """ 

409 Function to get activation patching results for the output of each MLP layer (by position). Returns a tensor of shape [n_layers, pos] 

410 

411 See generic_activation_patch for a more detailed explanation of activation patching  

412 

413 Args: 

414 model: The relevant model 

415 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

416 clean_cache (ActivationCache): The cached activations from the clean run 

417 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

418 

419 Returns: 

420 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos] 

421 """ 

422# %% 

423get_act_patch_attn_head_out_by_pos = partial( 

424 generic_activation_patch, 

425 patch_setter=layer_pos_head_vector_patch_setter, 

426 activation_name="z", 

427 index_axis_names=("layer", "pos", "head"), 

428) 

429get_act_patch_attn_head_out_by_pos.__doc__ = """ 

430 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] 

431 

432 See generic_activation_patch for a more detailed explanation of activation patching  

433 

434 Args: 

435 model: The relevant model 

436 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

437 clean_cache (ActivationCache): The cached activations from the clean run 

438 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

439 

440 Returns: 

441 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads] 

442 """ 

443 

444get_act_patch_attn_head_q_by_pos = partial( 

445 generic_activation_patch, 

446 patch_setter=layer_pos_head_vector_patch_setter, 

447 activation_name="q", 

448 index_axis_names=("layer", "pos", "head"), 

449) 

450get_act_patch_attn_head_q_by_pos.__doc__ = """ 

451 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] 

452 

453 See generic_activation_patch for a more detailed explanation of activation patching  

454 

455 Args: 

456 model: The relevant model 

457 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

458 clean_cache (ActivationCache): The cached activations from the clean run 

459 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

460 

461 Returns: 

462 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, pos, n_heads] 

463 """ 

464 

465get_act_patch_attn_head_k_by_pos = partial( 

466 generic_activation_patch, 

467 patch_setter=layer_pos_head_vector_patch_setter, 

468 activation_name="k", 

469 index_axis_names=("layer", "pos", "head"), 

470) 

471get_act_patch_attn_head_k_by_pos.__doc__ = """ 

472 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. 

473 

474 See generic_activation_patch for a more detailed explanation of activation patching  

475 

476 Args: 

477 model: The relevant model 

478 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

479 clean_cache (ActivationCache): The cached activations from the clean run 

480 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

481 

482 Returns: 

483 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. 

484 """ 

485 

486get_act_patch_attn_head_v_by_pos = partial( 

487 generic_activation_patch, 

488 patch_setter=layer_pos_head_vector_patch_setter, 

489 activation_name="v", 

490 index_axis_names=("layer", "pos", "head"), 

491) 

492get_act_patch_attn_head_v_by_pos.__doc__ = """ 

493 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. 

494 

495 See generic_activation_patch for a more detailed explanation of activation patching  

496 

497 Args: 

498 model: The relevant model 

499 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

500 clean_cache (ActivationCache): The cached activations from the clean run 

501 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

502 

503 Returns: 

504 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. 

505 """ 

506# %% 

507get_act_patch_attn_head_pattern_by_pos = partial( 

508 generic_activation_patch, 

509 patch_setter=layer_head_pos_pattern_patch_setter, 

510 activation_name="pattern", 

511 index_axis_names=("layer", "head_index", "dest_pos"), 

512) 

513get_act_patch_attn_head_pattern_by_pos.__doc__ = """ 

514 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] 

515 

516 See generic_activation_patch for a more detailed explanation of activation patching  

517 

518 Args: 

519 model: The relevant model 

520 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

521 clean_cache (ActivationCache): The cached activations from the clean run 

522 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

523 

524 Returns: 

525 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads, dest_pos] 

526 """ 

527 

528get_act_patch_attn_head_pattern_dest_src_pos = partial( 

529 generic_activation_patch, 

530 patch_setter=layer_head_dest_src_pos_pattern_patch_setter, 

531 activation_name="pattern", 

532 index_axis_names=("layer", "head_index", "dest_pos", "src_pos"), 

533) 

534get_act_patch_attn_head_pattern_dest_src_pos.__doc__ = """ 

535 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] 

536 

537 See generic_activation_patch for a more detailed explanation of activation patching  

538 

539 Args: 

540 model: The relevant model 

541 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

542 clean_cache (ActivationCache): The cached activations from the clean run 

543 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

544 

545 Returns: 

546 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads, dest_pos, src_pos] 

547 """ 

548 

549# %% 

550get_act_patch_attn_head_out_all_pos = partial( 

551 generic_activation_patch, 

552 patch_setter=layer_head_vector_patch_setter, 

553 activation_name="z", 

554 index_axis_names=("layer", "head"), 

555) 

556get_act_patch_attn_head_out_all_pos.__doc__ = """ 

557 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] 

558 

559 See generic_activation_patch for a more detailed explanation of activation patching  

560 

561 Args: 

562 model: The relevant model 

563 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

564 clean_cache (ActivationCache): The cached activations from the clean run 

565 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

566 

567 Returns: 

568 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads] 

569 """ 

570 

571get_act_patch_attn_head_q_all_pos = partial( 

572 generic_activation_patch, 

573 patch_setter=layer_head_vector_patch_setter, 

574 activation_name="q", 

575 index_axis_names=("layer", "head"), 

576) 

577get_act_patch_attn_head_q_all_pos.__doc__ = """ 

578 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] 

579 

580 See generic_activation_patch for a more detailed explanation of activation patching  

581 

582 Args: 

583 model: The relevant model 

584 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

585 clean_cache (ActivationCache): The cached activations from the clean run 

586 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

587 

588 Returns: 

589 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads] 

590 """ 

591 

592get_act_patch_attn_head_k_all_pos = partial( 

593 generic_activation_patch, 

594 patch_setter=layer_head_vector_patch_setter, 

595 activation_name="k", 

596 index_axis_names=("layer", "head"), 

597) 

598get_act_patch_attn_head_k_all_pos.__doc__ = """ 

599 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. 

600 

601 See generic_activation_patch for a more detailed explanation of activation patching  

602 

603 Args: 

604 model: The relevant model 

605 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

606 clean_cache (ActivationCache): The cached activations from the clean run 

607 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

608 

609 Returns: 

610 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. 

611 """ 

612 

613get_act_patch_attn_head_v_all_pos = partial( 

614 generic_activation_patch, 

615 patch_setter=layer_head_vector_patch_setter, 

616 activation_name="v", 

617 index_axis_names=("layer", "head"), 

618) 

619get_act_patch_attn_head_v_all_pos.__doc__ = """ 

620 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. 

621 

622 See generic_activation_patch for a more detailed explanation of activation patching  

623 

624 Args: 

625 model: The relevant model 

626 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

627 clean_cache (ActivationCache): The cached activations from the clean run 

628 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

629 

630 Returns: 

631 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. 

632 """ 

633 

634get_act_patch_attn_head_pattern_all_pos = partial( 

635 generic_activation_patch, 

636 patch_setter=layer_head_pattern_patch_setter, 

637 activation_name="pattern", 

638 index_axis_names=("layer", "head_index"), 

639) 

640get_act_patch_attn_head_pattern_all_pos.__doc__ = """ 

641 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] 

642 

643 See generic_activation_patch for a more detailed explanation of activation patching  

644 

645 Args: 

646 model: The relevant model 

647 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

648 clean_cache (ActivationCache): The cached activations from the clean run 

649 patching_metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

650 

651 Returns: 

652 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [n_layers, n_heads] 

653 """ 

654 

655# %% 

656 

657 

658def get_act_patch_attn_head_all_pos_every( 

659 model, corrupted_tokens, clean_cache, metric 

660) -> Float[torch.Tensor, "patch_type layer head"]: 

661 """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] 

662 

663 Args: 

664 model: The relevant model 

665 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

666 clean_cache (ActivationCache): The cached activations from the clean run 

667 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

668 

669 Returns: 

670 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [5, n_layers, n_heads] 

671 """ 

672 act_patch_results: list[torch.Tensor] = [] 

673 act_patch_results.append( 

674 get_act_patch_attn_head_out_all_pos(model, corrupted_tokens, clean_cache, metric) 

675 ) 

676 act_patch_results.append( 

677 get_act_patch_attn_head_q_all_pos(model, corrupted_tokens, clean_cache, metric) 

678 ) 

679 

680 # Reshape k and v to be compatible with the rest of the results in case of n_key_value_heads != n_heads 

681 k_results = get_act_patch_attn_head_k_all_pos(model, corrupted_tokens, clean_cache, metric) 

682 act_patch_results.append( 

683 torch.nn.functional.pad(k_results, (0, act_patch_results[-1].size(-1) - k_results.size(-1))) 

684 ) 

685 v_results = get_act_patch_attn_head_v_all_pos(model, corrupted_tokens, clean_cache, metric) 

686 act_patch_results.append( 

687 torch.nn.functional.pad(v_results, (0, act_patch_results[-1].size(-1) - v_results.size(-1))) 

688 ) 

689 

690 act_patch_results.append( 

691 get_act_patch_attn_head_pattern_all_pos(model, corrupted_tokens, clean_cache, metric) 

692 ) 

693 return torch.stack(act_patch_results, dim=0) 

694 

695 

696def get_act_patch_attn_head_by_pos_every( 

697 model, corrupted_tokens, clean_cache, metric 

698) -> Float[torch.Tensor, "patch_type layer pos head"]: 

699 """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] 

700 

701 Args: 

702 model: The relevant model 

703 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

704 clean_cache (ActivationCache): The cached activations from the clean run 

705 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

706 

707 Returns: 

708 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [5, n_layers, pos, n_heads] 

709 """ 

710 act_patch_results = [] 

711 act_patch_results.append( 

712 get_act_patch_attn_head_out_by_pos(model, corrupted_tokens, clean_cache, metric) 

713 ) 

714 act_patch_results.append( 

715 get_act_patch_attn_head_q_by_pos(model, corrupted_tokens, clean_cache, metric) 

716 ) 

717 

718 # Reshape k and v to be compatible with the rest of the results in case of n_key_value_heads != n_heads 

719 k_results = get_act_patch_attn_head_k_by_pos(model, corrupted_tokens, clean_cache, metric) 

720 act_patch_results.append( 

721 torch.nn.functional.pad(k_results, (0, act_patch_results[-1].size(-1) - k_results.size(-1))) 

722 ) 

723 v_results = get_act_patch_attn_head_v_by_pos(model, corrupted_tokens, clean_cache, metric) 

724 act_patch_results.append( 

725 torch.nn.functional.pad(v_results, (0, act_patch_results[-1].size(-1) - v_results.size(-1))) 

726 ) 

727 

728 # Reshape pattern to be compatible with the rest of the results 

729 pattern_results = get_act_patch_attn_head_pattern_by_pos( 

730 model, corrupted_tokens, clean_cache, metric 

731 ) 

732 act_patch_results.append(einops.rearrange(pattern_results, "batch head pos -> batch pos head")) 

733 return torch.stack(act_patch_results, dim=0) 

734 

735 

736def get_act_patch_block_every( 

737 model, corrupted_tokens, clean_cache, metric 

738) -> Float[torch.Tensor, "patch_type layer pos"]: 

739 """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] 

740 

741 Args: 

742 model: The relevant model 

743 corrupted_tokens (torch.Tensor): The input tokens for the corrupted run. Has shape [batch, pos] 

744 clean_cache (ActivationCache): The cached activations from the clean run 

745 metric: A function from the model's output logits to some metric (eg loss, logit diff, etc) 

746 

747 Returns: 

748 patched_output (torch.Tensor): The tensor of the patching metric for each patch. Has shape [3, n_layers, pos] 

749 """ 

750 act_patch_results = [] 

751 act_patch_results.append(get_act_patch_resid_pre(model, corrupted_tokens, clean_cache, metric)) 

752 act_patch_results.append(get_act_patch_attn_out(model, corrupted_tokens, clean_cache, metric)) 

753 act_patch_results.append(get_act_patch_mlp_out(model, corrupted_tokens, clean_cache, metric)) 

754 return torch.stack(act_patch_results, dim=0)