Coverage for transformer_lens/cache/key_value_cache.py: 38%
37 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-21 19:27 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-09-21 19:27 +0000
1"""Key-Value cache for TransformerLens.
3Defines the TransformerLensKeyValueCache which manages a list of per-layer
4cache entries and attention masks.
5"""
7from dataclasses import dataclass
8from typing import List, Union
10import torch
11from jaxtyping import Int
13from transformer_lens.config.transformer_lens_config import TransformerLensConfig
14from transformer_lens.utilities.multi_gpu import get_device_for_block_index
16from .key_value_cache_entry import TransformerLensKeyValueCacheEntry
19@dataclass
20class TransformerLensKeyValueCache:
21 """
22 A cache for storing past keys and values for the Transformer. This is important for generating text - we can cache a lot of past computation and avoid repeating ourselves!
24 This cache is a list of TransformerLensKeyValueCacheEntry objects, one for each layer in the Transformer. Each object stores a [batch, pos_so_far, n_heads, d_head] tensor for both keys and values, and each entry has an append method to add a single new key and value.
26 The cache can be frozen so that it is not updated during the forward pass. This is useful when we want to run many inputs with the same prefix.
27 """
29 entries: List[TransformerLensKeyValueCacheEntry]
30 previous_attention_mask: Int[torch.Tensor, "batch pos_so_far"]
31 frozen: bool = False
33 @classmethod
34 def init_cache(
35 cls,
36 cfg: TransformerLensConfig,
37 device: Union[torch.device, str, None],
38 batch_size: int = 1,
39 ):
40 # Determine device for each layer
41 if hasattr(cfg, "n_devices"):
42 # Configs that track n_devices (TransformerBridgeConfig): per-block placement
43 device_for_layer = lambda i: get_device_for_block_index(i, cfg, device)
44 else:
45 # Fallback when no model is provided - use single device
46 fallback_device = device if device is not None else cfg.device
47 if fallback_device is None:
48 fallback_device = torch.device("cpu")
49 device_for_layer = lambda i: fallback_device
51 return cls(
52 entries=[
53 TransformerLensKeyValueCacheEntry.init_cache_entry(
54 cfg,
55 device_for_layer(i),
56 batch_size,
57 )
58 for i in range(cfg.n_layers)
59 ],
60 previous_attention_mask=torch.empty(
61 # This may actually be an int64, but type promotion will handle it:
62 # See: https://pytorch.org/docs/stable/tensor_attributes.html#type-promotion-doc
63 # See: https://github.com/pytorch/pytorch/issues/35014
64 (batch_size, 0),
65 device=device,
66 dtype=torch.int,
67 ),
68 )
70 def freeze(self):
71 self.frozen = True
72 for entry in self.entries:
73 entry.frozen = True
75 def unfreeze(self):
76 self.frozen = False
77 for entry in self.entries:
78 entry.frozen = False
80 def append_attention_mask(self, attention_mask: Int[torch.Tensor, "batch new_tokens"]):
81 attention_mask = attention_mask.to(self.previous_attention_mask.device)
82 updated_attention_mask = torch.cat([self.previous_attention_mask, attention_mask], dim=-1)
83 if not self.frozen:
84 self.previous_attention_mask = updated_attention_mask
85 return updated_attention_mask
87 def __getitem__(self, idx):
88 return self.entries[idx]