buxtcodes commited on
Commit
9619ab5
·
verified ·
1 Parent(s): 73893fa
Files changed (1) hide show
  1. custom_generate/generate.py +231 -231
custom_generate/generate.py CHANGED
@@ -1,231 +1,231 @@
1
- import torch
2
- from typing import Any, Dict, List, Optional, Tuple
3
-
4
- from transformers import Cache, GenerationConfig
5
-
6
-
7
- UNSUPPORTED_GENERATION_ARGS = [
8
- "cache_implementation", # cache-related arguments, here we always use SinkCache
9
- "cache_config",
10
- "return_legacy_cache",
11
- "num_beams", # beam search (and cousin techniques) are not supported
12
- "compile_config", # SinkCache doesn't support torch.compile
13
- "assistant_model", # it also doesn't support speculative decoding
14
- ]
15
-
16
- class SinkCache(Cache):
17
- """
18
- A cache that as described in the [Attention Sinks paper](https://arxiv.org/abs/2309.17453). It allows the model to
19
- generate beyond the length of its context window, without losing fluency in the conversation. As it discards past
20
- tokens, the model will lose the ability to generate tokens that depend on the context that was discarded.
21
-
22
- It stores the Key and Value states as a list of tensors, one for each layer. The expected shape for each tensor is
23
- `[batch_size, num_heads, seq_len, head_dim]`.
24
-
25
- This class was copied from transformers 4.52.0, with minor modifications.
26
-
27
- Parameters:
28
- window_length (`int`):
29
- The length of the context window.
30
- num_sink_tokens (`int`):
31
- The number of sink tokens. See the original paper for more information.
32
- """
33
-
34
- def __init__(self, window_length: int, num_sink_tokens: int) -> None:
35
- super().__init__(layer_class_to_replicate=None)
36
- self.key_cache: List[torch.Tensor] = []
37
- self.value_cache: List[torch.Tensor] = []
38
- self.window_length = window_length
39
- self.num_sink_tokens = num_sink_tokens
40
- self.cos_sin_rerotation_cache = {}
41
- self._cos_cache = None
42
- self._sin_cache = None
43
-
44
- @staticmethod
45
- def _rotate_half(x):
46
- x1 = x[..., : x.shape[-1] // 2]
47
- x2 = x[..., x.shape[-1] // 2 :]
48
- return torch.cat((-x2, x1), dim=-1)
49
-
50
- def _apply_key_rotary_pos_emb(
51
- self, key_states: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
52
- ) -> torch.Tensor:
53
- rotated_key_states = (key_states * cos) + (self._rotate_half(key_states) * sin)
54
- return rotated_key_states
55
-
56
- def _get_rerotation_cos_sin(
57
- self, key_states: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
58
- ) -> Tuple[torch.Tensor, torch.Tensor]:
59
- if key_states.shape[-2] not in self.cos_sin_rerotation_cache:
60
- # Upcast to float32 temporarily for better accuracy
61
- cos = cos.to(torch.float32)
62
- sin = sin.to(torch.float32)
63
-
64
- # Compute the cos and sin required for back- and forward-rotating to one position earlier in the sequence
65
- original_cos = cos[self.num_sink_tokens + key_states.shape[-2] :]
66
- shifted_cos = cos[self.num_sink_tokens : -key_states.shape[-2]]
67
- original_sin = sin[self.num_sink_tokens + key_states.shape[-2] :]
68
- shifted_sin = sin[self.num_sink_tokens : -key_states.shape[-2]]
69
- rerotation_cos = original_cos * shifted_cos + original_sin * shifted_sin
70
- rerotation_sin = -original_sin * shifted_cos + original_cos * shifted_sin
71
-
72
- self.cos_sin_rerotation_cache[key_states.shape[-2]] = (
73
- rerotation_cos.to(key_states.dtype).unsqueeze(0),
74
- rerotation_sin.to(key_states.dtype).unsqueeze(0),
75
- )
76
- return self.cos_sin_rerotation_cache[key_states.shape[-2]]
77
-
78
- def get_seq_length(self, layer_idx: Optional[int] = 0) -> int:
79
- """Returns the sequence length of the cached states. A layer index can be optionally passed."""
80
- if len(self.key_cache) <= layer_idx:
81
- return 0
82
- return self.key_cache[layer_idx].shape[-2]
83
-
84
- def get_max_cache_shape(self) -> Optional[int]:
85
- """Returns the maximum sequence length of the cache object, in case of SinkCache it is the window length."""
86
- return self.window_length
87
-
88
- def update(
89
- self,
90
- key_states: torch.Tensor,
91
- value_states: torch.Tensor,
92
- layer_idx: int,
93
- cache_kwargs: Optional[Dict[str, Any]] = None,
94
- ) -> Tuple[torch.Tensor, torch.Tensor]:
95
- """
96
- Updates the cache with the new `key_states` and `value_states` for the layer `layer_idx`.
97
-
98
- Parameters:
99
- key_states (`torch.Tensor`):
100
- The new key states to cache.
101
- value_states (`torch.Tensor`):
102
- The new value states to cache.
103
- layer_idx (`int`):
104
- The index of the layer to cache the states for.
105
- cache_kwargs (`Dict[str, Any]`, `optional`):
106
- Additional arguments for the cache subclass. The following arguments can be used in `SinkCache`: `sin`,
107
- `cos` and `partial_rotation_size`. These arguments are used with models using RoPE, to recompute the
108
- rotation as the tokens are shifted.
109
-
110
- Return:
111
- A tuple containing the updated key and value states.
112
- """
113
- # Optional kwargs for `SinkCache` -- needed on models using RoPE. `partial_rotation_size` is used on models
114
- # with partially rotated position embeddings, like Phi or Persimmon.
115
- if cache_kwargs is None:
116
- cache_kwargs = {}
117
- sin = cache_kwargs.get("sin")
118
- cos = cache_kwargs.get("cos")
119
- partial_rotation_size = cache_kwargs.get("partial_rotation_size")
120
- using_rope = cos is not None and sin is not None
121
-
122
- # Update the sin/cos cache, which holds sin/cos values for all possible positions
123
- if using_rope and layer_idx == 0:
124
- # BC: some models still pass `sin`/`cos` with 2 dims. In those models, they are the full sin/cos. Remove
125
- # after all RoPE models have a llama-like cache utilization.
126
- if cos.dim() == 2:
127
- self._cos_cache = cos
128
- self._sin_cache = sin
129
- else:
130
- if self._cos_cache is None:
131
- self._cos_cache = cos[0, ...]
132
- self._sin_cache = sin[0, ...]
133
- elif self._cos_cache.shape[0] < self.window_length:
134
- self._cos_cache = torch.cat([self._cos_cache, cos[0, ...]], dim=0)
135
- self._sin_cache = torch.cat([self._sin_cache, sin[0, ...]], dim=0)
136
-
137
- # [bsz, num_heads, seq_len, head_dim]
138
- if len(self.key_cache) <= layer_idx:
139
- # Empty cache
140
- self.key_cache.append(key_states)
141
- self.value_cache.append(value_states)
142
-
143
- elif key_states.shape[-2] + self.get_seq_length(layer_idx) < self.window_length:
144
- # Growing cache
145
- self.key_cache[layer_idx] = torch.cat([self.key_cache[layer_idx], key_states], dim=-2)
146
- self.value_cache[layer_idx] = torch.cat([self.value_cache[layer_idx], value_states], dim=-2)
147
-
148
- else:
149
- # Shifting cache
150
- keys_to_keep = self.key_cache[layer_idx][
151
- :, :, -self.window_length + self.num_sink_tokens + key_states.shape[-2] :
152
- ]
153
-
154
- # On RoPE models, we need to recompute the Key rotation as the tokens are shifted
155
- if using_rope:
156
- rerotation_cos, rerotation_sin = self._get_rerotation_cos_sin(
157
- key_states, self._cos_cache[: self.window_length], self._sin_cache[: self.window_length]
158
- )
159
- if partial_rotation_size is not None:
160
- keys_to_keep, keys_pass = (
161
- keys_to_keep[..., :partial_rotation_size],
162
- keys_to_keep[..., partial_rotation_size:],
163
- )
164
- keys_to_keep = self._apply_key_rotary_pos_emb(keys_to_keep, rerotation_cos, rerotation_sin)
165
- if partial_rotation_size is not None:
166
- keys_to_keep = torch.cat((keys_to_keep, keys_pass), dim=-1)
167
-
168
- # Concatenate sink tokens, shifted & rotated tokens (if needed), and new tokens
169
- sink_keys = self.key_cache[layer_idx][:, :, : self.num_sink_tokens]
170
- self.key_cache[layer_idx] = torch.cat([sink_keys, keys_to_keep, key_states], dim=-2)
171
-
172
- sink_values = self.value_cache[layer_idx][:, :, : self.num_sink_tokens]
173
- values_to_keep = self.value_cache[layer_idx][
174
- :, :, -self.window_length + self.num_sink_tokens + value_states.shape[-2] :
175
- ]
176
- self.value_cache[layer_idx] = torch.cat([sink_values, values_to_keep, value_states], dim=-2)
177
-
178
- return self.key_cache[layer_idx], self.value_cache[layer_idx]
179
-
180
-
181
- def generate(model, window_length=256, num_sink_tokens=4, **kwargs):
182
- """Custom generate function for SinkCache.
183
-
184
- Args:
185
- model (`PreTrainedModel`):
186
- The model to generate from.
187
- window_length (`int`, *optional*, defaults to 256):
188
- The length of the context window.
189
- num_sink_tokens (`int`, *optional*, defaults to 4):
190
- The number of sink tokens. See the original paper for more information.
191
- """
192
- # 1. General sanity checks
193
- # 1.a. A few arguments are not allowed, especially arguments that control caches.
194
- generation_config = kwargs.get("generation_config")
195
- default_global_generation_config = GenerationConfig()
196
- default_model_generation_config = model.generation_config
197
- for arg in UNSUPPORTED_GENERATION_ARGS:
198
- has_custom_gen_config_arg = (
199
- generation_config is not None
200
- # = and not (match global default or match model-specific default)
201
- and not (
202
- getattr(default_model_generation_config, arg) == getattr(generation_config, arg)
203
- or getattr(default_global_generation_config, arg) == getattr(generation_config, arg)
204
- )
205
- )
206
- kwargs_has_arg = arg in kwargs and kwargs[arg] is not None
207
- if kwargs_has_arg or has_custom_gen_config_arg:
208
- raise ValueError(
209
- f"`{arg}` is set, but it's not supported in this custom generate function. List of "
210
- f"unsupported arguments: {UNSUPPORTED_GENERATION_ARGS}"
211
- )
212
-
213
- # 1.b. The model must be decoder-only
214
- if model.config.is_encoder_decoder:
215
- raise ValueError("This custom generate function only works with decoder-only models")
216
-
217
- # 1.c. compatibility with transformers 4.52: we must pop `custom_generate` from kwargs, otherwise it will result
218
- # in an infinite loop when we call `model.generate`. This is solved in transformers 4.53.
219
- kwargs.pop("custom_generate", None)
220
-
221
- # 2. Generate with SinkCache
222
- # 2.a. prepare the cache, if it was not passed.
223
- past_key_values = kwargs.pop("past_key_values", None)
224
- if past_key_values is None:
225
- past_key_values = SinkCache(window_length=window_length, num_sink_tokens=num_sink_tokens)
226
- elif not isinstance(past_key_values, SinkCache):
227
- raise ValueError(f"`past_key_values` must be a `SinkCache` instance, got a {type(past_key_values)} instance")
228
-
229
- # 2.b. generate with the cache
230
- generation_outputs = model.generate(**kwargs, past_key_values=past_key_values, use_cache=True)
231
- return generation_outputs
 
1
+ import torch
2
+ from typing import Any, Dict, List, Optional, Tuple
3
+
4
+ from transformers import Cache, GenerationConfig
5
+
6
+
7
+ UNSUPPORTED_GENERATION_ARGS = [
8
+ "cache_implementation", # cache-related arguments, here we always use SinkCache
9
+ "cache_config",
10
+ "return_legacy_cache",
11
+ "num_beams", # beam search (and cousin techniques) are not supported
12
+ "compile_config", # SinkCache doesn't support torch.compile
13
+ "assistant_model", # it also doesn't support speculative decoding
14
+ ]
15
+
16
+ class SinkCache(Cache):
17
+ """
18
+ A cache that as described in the [Attention Sinks paper](https://arxiv.org/abs/2309.17453). It allows the model to
19
+ generate beyond the length of its context window, without losing fluency in the conversation. As it discards past
20
+ tokens, the model will lose the ability to generate tokens that depend on the context that was discarded.
21
+
22
+ It stores the Key and Value states as a list of tensors, one for each layer. The expected shape for each tensor is
23
+ `[batch_size, num_heads, seq_len, head_dim]`.
24
+
25
+ This class was copied from transformers 4.52.0, with minor modifications.
26
+
27
+ Parameters:
28
+ window_length (`int`):
29
+ The length of the context window.
30
+ num_sink_tokens (`int`):
31
+ The number of sink tokens. See the original paper for more information.
32
+ """
33
+
34
+ def __init__(self, window_length: int, num_sink_tokens: int) -> None:
35
+ super().__init__(layer_class_to_replicate=CacheLayer)
36
+ self.key_cache: List[torch.Tensor] = []
37
+ self.value_cache: List[torch.Tensor] = []
38
+ self.window_length = window_length
39
+ self.num_sink_tokens = num_sink_tokens
40
+ self.cos_sin_rerotation_cache = {}
41
+ self._cos_cache = None
42
+ self._sin_cache = None
43
+
44
+ @staticmethod
45
+ def _rotate_half(x):
46
+ x1 = x[..., : x.shape[-1] // 2]
47
+ x2 = x[..., x.shape[-1] // 2 :]
48
+ return torch.cat((-x2, x1), dim=-1)
49
+
50
+ def _apply_key_rotary_pos_emb(
51
+ self, key_states: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
52
+ ) -> torch.Tensor:
53
+ rotated_key_states = (key_states * cos) + (self._rotate_half(key_states) * sin)
54
+ return rotated_key_states
55
+
56
+ def _get_rerotation_cos_sin(
57
+ self, key_states: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
58
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
59
+ if key_states.shape[-2] not in self.cos_sin_rerotation_cache:
60
+ # Upcast to float32 temporarily for better accuracy
61
+ cos = cos.to(torch.float32)
62
+ sin = sin.to(torch.float32)
63
+
64
+ # Compute the cos and sin required for back- and forward-rotating to one position earlier in the sequence
65
+ original_cos = cos[self.num_sink_tokens + key_states.shape[-2] :]
66
+ shifted_cos = cos[self.num_sink_tokens : -key_states.shape[-2]]
67
+ original_sin = sin[self.num_sink_tokens + key_states.shape[-2] :]
68
+ shifted_sin = sin[self.num_sink_tokens : -key_states.shape[-2]]
69
+ rerotation_cos = original_cos * shifted_cos + original_sin * shifted_sin
70
+ rerotation_sin = -original_sin * shifted_cos + original_cos * shifted_sin
71
+
72
+ self.cos_sin_rerotation_cache[key_states.shape[-2]] = (
73
+ rerotation_cos.to(key_states.dtype).unsqueeze(0),
74
+ rerotation_sin.to(key_states.dtype).unsqueeze(0),
75
+ )
76
+ return self.cos_sin_rerotation_cache[key_states.shape[-2]]
77
+
78
+ def get_seq_length(self, layer_idx: Optional[int] = 0) -> int:
79
+ """Returns the sequence length of the cached states. A layer index can be optionally passed."""
80
+ if len(self.key_cache) <= layer_idx:
81
+ return 0
82
+ return self.key_cache[layer_idx].shape[-2]
83
+
84
+ def get_max_cache_shape(self) -> Optional[int]:
85
+ """Returns the maximum sequence length of the cache object, in case of SinkCache it is the window length."""
86
+ return self.window_length
87
+
88
+ def update(
89
+ self,
90
+ key_states: torch.Tensor,
91
+ value_states: torch.Tensor,
92
+ layer_idx: int,
93
+ cache_kwargs: Optional[Dict[str, Any]] = None,
94
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
95
+ """
96
+ Updates the cache with the new `key_states` and `value_states` for the layer `layer_idx`.
97
+
98
+ Parameters:
99
+ key_states (`torch.Tensor`):
100
+ The new key states to cache.
101
+ value_states (`torch.Tensor`):
102
+ The new value states to cache.
103
+ layer_idx (`int`):
104
+ The index of the layer to cache the states for.
105
+ cache_kwargs (`Dict[str, Any]`, `optional`):
106
+ Additional arguments for the cache subclass. The following arguments can be used in `SinkCache`: `sin`,
107
+ `cos` and `partial_rotation_size`. These arguments are used with models using RoPE, to recompute the
108
+ rotation as the tokens are shifted.
109
+
110
+ Return:
111
+ A tuple containing the updated key and value states.
112
+ """
113
+ # Optional kwargs for `SinkCache` -- needed on models using RoPE. `partial_rotation_size` is used on models
114
+ # with partially rotated position embeddings, like Phi or Persimmon.
115
+ if cache_kwargs is None:
116
+ cache_kwargs = {}
117
+ sin = cache_kwargs.get("sin")
118
+ cos = cache_kwargs.get("cos")
119
+ partial_rotation_size = cache_kwargs.get("partial_rotation_size")
120
+ using_rope = cos is not None and sin is not None
121
+
122
+ # Update the sin/cos cache, which holds sin/cos values for all possible positions
123
+ if using_rope and layer_idx == 0:
124
+ # BC: some models still pass `sin`/`cos` with 2 dims. In those models, they are the full sin/cos. Remove
125
+ # after all RoPE models have a llama-like cache utilization.
126
+ if cos.dim() == 2:
127
+ self._cos_cache = cos
128
+ self._sin_cache = sin
129
+ else:
130
+ if self._cos_cache is None:
131
+ self._cos_cache = cos[0, ...]
132
+ self._sin_cache = sin[0, ...]
133
+ elif self._cos_cache.shape[0] < self.window_length:
134
+ self._cos_cache = torch.cat([self._cos_cache, cos[0, ...]], dim=0)
135
+ self._sin_cache = torch.cat([self._sin_cache, sin[0, ...]], dim=0)
136
+
137
+ # [bsz, num_heads, seq_len, head_dim]
138
+ if len(self.key_cache) <= layer_idx:
139
+ # Empty cache
140
+ self.key_cache.append(key_states)
141
+ self.value_cache.append(value_states)
142
+
143
+ elif key_states.shape[-2] + self.get_seq_length(layer_idx) < self.window_length:
144
+ # Growing cache
145
+ self.key_cache[layer_idx] = torch.cat([self.key_cache[layer_idx], key_states], dim=-2)
146
+ self.value_cache[layer_idx] = torch.cat([self.value_cache[layer_idx], value_states], dim=-2)
147
+
148
+ else:
149
+ # Shifting cache
150
+ keys_to_keep = self.key_cache[layer_idx][
151
+ :, :, -self.window_length + self.num_sink_tokens + key_states.shape[-2] :
152
+ ]
153
+
154
+ # On RoPE models, we need to recompute the Key rotation as the tokens are shifted
155
+ if using_rope:
156
+ rerotation_cos, rerotation_sin = self._get_rerotation_cos_sin(
157
+ key_states, self._cos_cache[: self.window_length], self._sin_cache[: self.window_length]
158
+ )
159
+ if partial_rotation_size is not None:
160
+ keys_to_keep, keys_pass = (
161
+ keys_to_keep[..., :partial_rotation_size],
162
+ keys_to_keep[..., partial_rotation_size:],
163
+ )
164
+ keys_to_keep = self._apply_key_rotary_pos_emb(keys_to_keep, rerotation_cos, rerotation_sin)
165
+ if partial_rotation_size is not None:
166
+ keys_to_keep = torch.cat((keys_to_keep, keys_pass), dim=-1)
167
+
168
+ # Concatenate sink tokens, shifted & rotated tokens (if needed), and new tokens
169
+ sink_keys = self.key_cache[layer_idx][:, :, : self.num_sink_tokens]
170
+ self.key_cache[layer_idx] = torch.cat([sink_keys, keys_to_keep, key_states], dim=-2)
171
+
172
+ sink_values = self.value_cache[layer_idx][:, :, : self.num_sink_tokens]
173
+ values_to_keep = self.value_cache[layer_idx][
174
+ :, :, -self.window_length + self.num_sink_tokens + value_states.shape[-2] :
175
+ ]
176
+ self.value_cache[layer_idx] = torch.cat([sink_values, values_to_keep, value_states], dim=-2)
177
+
178
+ return self.key_cache[layer_idx], self.value_cache[layer_idx]
179
+
180
+
181
+ def generate(model, window_length=256, num_sink_tokens=4, **kwargs):
182
+ """Custom generate function for SinkCache.
183
+
184
+ Args:
185
+ model (`PreTrainedModel`):
186
+ The model to generate from.
187
+ window_length (`int`, *optional*, defaults to 256):
188
+ The length of the context window.
189
+ num_sink_tokens (`int`, *optional*, defaults to 4):
190
+ The number of sink tokens. See the original paper for more information.
191
+ """
192
+ # 1. General sanity checks
193
+ # 1.a. A few arguments are not allowed, especially arguments that control caches.
194
+ generation_config = kwargs.get("generation_config")
195
+ default_global_generation_config = GenerationConfig()
196
+ default_model_generation_config = model.generation_config
197
+ for arg in UNSUPPORTED_GENERATION_ARGS:
198
+ has_custom_gen_config_arg = (
199
+ generation_config is not None
200
+ # = and not (match global default or match model-specific default)
201
+ and not (
202
+ getattr(default_model_generation_config, arg) == getattr(generation_config, arg)
203
+ or getattr(default_global_generation_config, arg) == getattr(generation_config, arg)
204
+ )
205
+ )
206
+ kwargs_has_arg = arg in kwargs and kwargs[arg] is not None
207
+ if kwargs_has_arg or has_custom_gen_config_arg:
208
+ raise ValueError(
209
+ f"`{arg}` is set, but it's not supported in this custom generate function. List of "
210
+ f"unsupported arguments: {UNSUPPORTED_GENERATION_ARGS}"
211
+ )
212
+
213
+ # 1.b. The model must be decoder-only
214
+ if model.config.is_encoder_decoder:
215
+ raise ValueError("This custom generate function only works with decoder-only models")
216
+
217
+ # 1.c. compatibility with transformers 4.52: we must pop `custom_generate` from kwargs, otherwise it will result
218
+ # in an infinite loop when we call `model.generate`. This is solved in transformers 4.53.
219
+ kwargs.pop("custom_generate", None)
220
+
221
+ # 2. Generate with SinkCache
222
+ # 2.a. prepare the cache, if it was not passed.
223
+ past_key_values = kwargs.pop("past_key_values", None)
224
+ if past_key_values is None:
225
+ past_key_values = SinkCache(window_length=window_length, num_sink_tokens=num_sink_tokens)
226
+ elif not isinstance(past_key_values, SinkCache):
227
+ raise ValueError(f"`past_key_values` must be a `SinkCache` instance, got a {type(past_key_values)} instance")
228
+
229
+ # 2.b. generate with the cache
230
+ generation_outputs = model.generate(**kwargs, past_key_values=past_key_values, use_cache=True)
231
+ return generation_outputs