-
Notifications
You must be signed in to change notification settings - Fork 34.1k
Phi3: fix attn for sliding window #33586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
156a85b
2e33d0d
0faeaf3
52fb350
2351ab2
5b27f5f
a19c7f5
c174f25
21ede7f
25b5852
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,72 @@ | |
| _CONFIG_FOR_DOC = "MistralConfig" | ||
|
|
||
|
|
||
| # Copied from transformers.models.phi3.modeling_phi3._prepare_4d_causal_attention_mask_with_cache_position with Phi3->Mistral | ||
| def _prepare_4d_causal_attention_mask_with_cache_position( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because of #33677, this function is part of the model class -- I think you will have to move the diff there, otherwise tests may fail on (see the diff in that PR for |
||
| attention_mask: torch.Tensor, | ||
| sequence_length: int, | ||
| target_length: int, | ||
| dtype: torch.dtype, | ||
| device: torch.device, | ||
| min_dtype: float, | ||
| cache_position: torch.Tensor, | ||
| batch_size: int, | ||
| config: MistralConfig, | ||
| past_key_values: Cache, | ||
| ): | ||
| """ | ||
| Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape | ||
| `(batch_size, key_value_length)`, or if the input `attention_mask` is already 4D, do nothing. | ||
|
|
||
| Args: | ||
| attention_mask (`torch.Tensor`): | ||
| A 2D attention mask of shape `(batch_size, key_value_length)` or a 4D attention mask of shape `(batch_size, 1, query_length, key_value_length)`. | ||
| sequence_length (`int`): | ||
| The sequence length being processed. | ||
| target_length (`int`): | ||
| The target length: when generating with static cache, the mask should be as long as the static cache, to account for the 0 padding, the part of the cache that is not filled yet. | ||
| dtype (`torch.dtype`): | ||
| The dtype to use for the 4D attention mask. | ||
| device (`torch.device`): | ||
| The device to plcae the 4D attention mask on. | ||
| min_dtype (`float`): | ||
| The minimum value representable with the dtype `dtype`. | ||
| cache_position (`torch.Tensor`): | ||
| Indices depicting the position of the input sequence tokens in the sequence. | ||
| batch_size (`torch.Tensor`): | ||
| Batch size. | ||
| config (`MistralConfig`): | ||
| The model's configuration class | ||
| past_key_values (`Cache`): | ||
| The cache class that is being used currently to generate | ||
| """ | ||
| if attention_mask is not None and attention_mask.dim() == 4: | ||
| # In this case we assume that the mask comes already in inverted form and requires no inversion or slicing. | ||
| causal_mask = attention_mask | ||
| else: | ||
| causal_mask = torch.full((sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device) | ||
| exclude_mask = torch.arange(target_length, device=device) > cache_position.reshape(-1, 1) | ||
| if config.sliding_window is not None: | ||
| if not isinstance(past_key_values, SlidingWindowCache) or sequence_length > config.sliding_window: | ||
| exclude_mask.bitwise_or_( | ||
| torch.arange(target_length, device=device) | ||
| <= (cache_position.reshape(-1, 1) - config.sliding_window) | ||
| ) | ||
| causal_mask *= exclude_mask | ||
| causal_mask = causal_mask[None, None, :, :].expand(batch_size, 1, -1, -1) | ||
| if attention_mask is not None: | ||
| causal_mask = causal_mask.clone() # copy to contiguous memory for in-place edit | ||
| if attention_mask.shape[-1] > target_length: | ||
| attention_mask = attention_mask[:, :target_length] | ||
| mask_length = attention_mask.shape[-1] | ||
| padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :] | ||
| padding_mask = padding_mask == 0 | ||
| causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill( | ||
| padding_mask, min_dtype | ||
| ) | ||
| return causal_mask | ||
|
|
||
|
|
||
| # Copied from transformers.models.llama.modeling_llama.LlamaRMSNorm with Llama->Mistral | ||
| class MistralRMSNorm(nn.Module): | ||
| def __init__(self, hidden_size, eps=1e-6): | ||
|
|
@@ -853,7 +919,7 @@ def _update_causal_mask( | |
| use_cache: bool, | ||
| output_attentions: bool, | ||
| ): | ||
| if self._attn_implementation == "flash_attention_2": | ||
| if self.config._attn_implementation == "flash_attention_2": | ||
| if attention_mask is not None and use_cache: | ||
| is_padding_right = attention_mask[:, -1].sum().item() != input_tensor.size()[0] | ||
| if is_padding_right: | ||
|
|
@@ -869,12 +935,11 @@ def _update_causal_mask( | |
| # For SDPA, when possible, we will rely on its `is_causal` argument instead of its `attn_mask` argument, in | ||
| # order to dispatch on Flash Attention 2. This feature is not compatible with static cache, as SDPA will fail | ||
| # to infer the attention mask. | ||
|
|
||
| # cache_position must be valid here no matter which cache we use | ||
| past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0 | ||
| using_static_cache = isinstance(past_key_values, StaticCache) | ||
| using_sliding_window_cache = isinstance(past_key_values, SlidingWindowCache) | ||
|
|
||
| # When output attentions is True, sdpa implementation's forward method calls the eager implementation's forward | ||
| if ( | ||
| self.config._attn_implementation == "sdpa" | ||
| and not (using_static_cache or using_sliding_window_cache) | ||
|
|
@@ -892,44 +957,31 @@ def _update_causal_mask( | |
| dtype, device = input_tensor.dtype, input_tensor.device | ||
| min_dtype = torch.finfo(dtype).min | ||
| sequence_length = input_tensor.shape[1] | ||
| # SlidingWindowCache | ||
| if using_sliding_window_cache: | ||
| target_length = max(sequence_length, self.config.sliding_window) | ||
| # StaticCache | ||
| sliding_window = self.config.sliding_window if self.config.sliding_window is not None else sequence_length | ||
| target_length = max(sequence_length, sliding_window) | ||
| elif using_static_cache: | ||
| target_length = past_key_values.get_max_length() | ||
| # DynamicCache or no cache | ||
| else: | ||
| target_length = ( | ||
| attention_mask.shape[-1] | ||
| if isinstance(attention_mask, torch.Tensor) | ||
| else past_seen_tokens + sequence_length + 1 | ||
| ) | ||
|
|
||
| if attention_mask is not None and attention_mask.dim() == 4: | ||
| causal_mask = attention_mask | ||
| else: | ||
| causal_mask = torch.full( | ||
| (sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device | ||
| ) | ||
| exclude_mask = torch.arange(target_length, device=device) > cache_position.reshape(-1, 1) | ||
| if self.config.sliding_window is not None: | ||
| if not using_sliding_window_cache or sequence_length > self.config.sliding_window: | ||
| exclude_mask.bitwise_or_( | ||
| torch.arange(target_length, device=device) | ||
| <= (cache_position.reshape(-1, 1) - self.config.sliding_window) | ||
| ) | ||
| causal_mask *= exclude_mask | ||
| causal_mask = causal_mask[None, None, :, :].expand(input_tensor.shape[0], 1, -1, -1) | ||
| if attention_mask is not None: | ||
| causal_mask = causal_mask.clone() # copy to contiguous memory for in-place edit | ||
| if attention_mask.dim() == 2: | ||
| mask_length = attention_mask.shape[-1] | ||
| padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :] | ||
| padding_mask = padding_mask == 0 | ||
| causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill( | ||
| padding_mask, min_dtype | ||
| ) | ||
| # In case the provided `attention` mask is 2D, we generate a causal mask here (4D). | ||
| causal_mask = _prepare_4d_causal_attention_mask_with_cache_position( | ||
| attention_mask, | ||
| sequence_length=sequence_length, | ||
| target_length=target_length, | ||
| dtype=dtype, | ||
| device=device, | ||
| min_dtype=min_dtype, | ||
| cache_position=cache_position, | ||
| batch_size=input_tensor.shape[0], | ||
| config=self.config, | ||
| past_key_values=past_key_values, | ||
| ) | ||
|
|
||
| if ( | ||
| self.config._attn_implementation == "sdpa" | ||
|
|
@@ -1112,9 +1164,42 @@ def prepare_inputs_for_generation( | |
|
|
||
| # if `inputs_embeds` are passed, we only want to use them in the 1st generation step | ||
| if inputs_embeds is not None and cache_position[0] == 0: | ||
| model_inputs = {"inputs_embeds": inputs_embeds} | ||
| model_inputs = {"inputs_embeds": inputs_embeds, "input_ids": None} | ||
|
zucchini-nlp marked this conversation as resolved.
|
||
| else: | ||
| model_inputs = {"input_ids": input_ids.contiguous()} # `contiguous()` needed for compilation use cases | ||
| # `contiguous()` needed for compilation use cases | ||
| model_inputs = {"input_ids": input_ids.contiguous(), "inputs_embeds": None} | ||
|
|
||
| if isinstance(past_key_values, StaticCache) and attention_mask.ndim == 2: | ||
| if model_inputs["inputs_embeds"] is not None: | ||
| batch_size, sequence_length, _ = model_inputs["inputs_embeds"].shape | ||
| device = model_inputs["inputs_embeds"].device | ||
| else: | ||
| batch_size, sequence_length = model_inputs["input_ids"].shape | ||
| device = model_inputs["input_ids"].device | ||
|
|
||
| dtype = self.lm_head.weight.dtype | ||
| min_dtype = torch.finfo(dtype).min | ||
|
|
||
| if isinstance(past_key_values, SlidingWindowCache): | ||
| sliding_window = ( | ||
| self.config.sliding_window if self.config.sliding_window is not None else sequence_length | ||
| ) | ||
| target_length = max(sequence_length, sliding_window) | ||
| else: | ||
| target_length = past_key_values.get_max_length() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why we need to do this: if there is a sliding WindowCache it was init from the Then,
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it should but the PR for getting So, to update you on our discussions with @gante which are currently in different PR comments: some cache classes now do not have a So, in a different PR I changed naming to
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alright sounds good. I just don't want us to add too much complexity to the code! 🤗 |
||
|
|
||
| attention_mask = _prepare_4d_causal_attention_mask_with_cache_position( | ||
| attention_mask, | ||
| sequence_length=sequence_length, | ||
| target_length=target_length, | ||
| dtype=dtype, | ||
| device=device, | ||
| min_dtype=min_dtype, | ||
| cache_position=cache_position, | ||
| batch_size=batch_size, | ||
| config=self.config, | ||
| past_key_values=past_key_values, | ||
| ) | ||
|
|
||
| if num_logits_to_keep is not None: | ||
| model_inputs["num_logits_to_keep"] = num_logits_to_keep | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ | |
| from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss | ||
|
|
||
| from ...activations import ACT2FN | ||
| from ...cache_utils import Cache, DynamicCache, StaticCache | ||
| from ...cache_utils import Cache, DynamicCache, SlidingWindowCache, StaticCache | ||
| from ...modeling_attn_mask_utils import AttentionMaskConverter | ||
| from ...modeling_outputs import ( | ||
| BaseModelOutputWithPast, | ||
|
|
@@ -56,7 +56,6 @@ | |
| _CONFIG_FOR_DOC = "Phi3Config" | ||
|
|
||
|
|
||
| # Copied from transformers.models.llama.modeling_llama._prepare_4d_causal_attention_mask_with_cache_position | ||
| def _prepare_4d_causal_attention_mask_with_cache_position( | ||
| attention_mask: torch.Tensor, | ||
| sequence_length: int, | ||
|
|
@@ -66,6 +65,8 @@ def _prepare_4d_causal_attention_mask_with_cache_position( | |
| min_dtype: float, | ||
| cache_position: torch.Tensor, | ||
| batch_size: int, | ||
| config: Phi3Config, | ||
| past_key_values: Cache, | ||
| ): | ||
| """ | ||
| Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape | ||
|
|
@@ -88,25 +89,35 @@ def _prepare_4d_causal_attention_mask_with_cache_position( | |
| Indices depicting the position of the input sequence tokens in the sequence. | ||
| batch_size (`torch.Tensor`): | ||
| Batch size. | ||
| config (`Phi3Config`): | ||
| The model's configuration class | ||
| past_key_values (`Cache`): | ||
| The cache class that is being used currently to generate | ||
| """ | ||
| if attention_mask is not None and attention_mask.dim() == 4: | ||
| # In this case we assume that the mask comes already in inverted form and requires no inversion or slicing. | ||
| causal_mask = attention_mask | ||
| else: | ||
| causal_mask = torch.full((sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device) | ||
| if sequence_length != 1: | ||
| causal_mask = torch.triu(causal_mask, diagonal=1) | ||
| causal_mask *= torch.arange(target_length, device=device) > cache_position.reshape(-1, 1) | ||
| exclude_mask = torch.arange(target_length, device=device) > cache_position.reshape(-1, 1) | ||
|
zucchini-nlp marked this conversation as resolved.
Outdated
|
||
| if config.sliding_window is not None: | ||
| if not isinstance(past_key_values, SlidingWindowCache) or sequence_length > config.sliding_window: | ||
| exclude_mask.bitwise_or_( | ||
| torch.arange(target_length, device=device) | ||
| <= (cache_position.reshape(-1, 1) - config.sliding_window) | ||
|
zucchini-nlp marked this conversation as resolved.
Outdated
|
||
| ) | ||
| causal_mask *= exclude_mask | ||
| causal_mask = causal_mask[None, None, :, :].expand(batch_size, 1, -1, -1) | ||
| if attention_mask is not None: | ||
| causal_mask = causal_mask.clone() # copy to contiguous memory for in-place edit | ||
| if attention_mask.shape[-1] > target_length: | ||
| attention_mask = attention_mask[:, :target_length] | ||
|
zucchini-nlp marked this conversation as resolved.
Outdated
|
||
| mask_length = attention_mask.shape[-1] | ||
| padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :] | ||
| padding_mask = padding_mask == 0 | ||
| causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill( | ||
| padding_mask, min_dtype | ||
| ) | ||
|
|
||
| return causal_mask | ||
|
|
||
|
|
||
|
|
@@ -1086,7 +1097,6 @@ def forward( | |
| attentions=all_self_attns, | ||
| ) | ||
|
|
||
| # Copied from transformers.models.llama.modeling_llama.LlamaModel._update_causal_mask | ||
| def _update_causal_mask( | ||
| self, | ||
| attention_mask: torch.Tensor, | ||
|
|
@@ -1105,21 +1115,30 @@ def _update_causal_mask( | |
| # to infer the attention mask. | ||
| past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0 | ||
| using_static_cache = isinstance(past_key_values, StaticCache) | ||
| using_sliding_window_cache = isinstance(past_key_values, SlidingWindowCache) | ||
|
|
||
| # When output attentions is True, sdpa implementation's forward method calls the eager implementation's forward | ||
| if self.config._attn_implementation == "sdpa" and not using_static_cache and not output_attentions: | ||
| if ( | ||
| self.config._attn_implementation == "sdpa" | ||
| and not (using_static_cache or using_sliding_window_cache) | ||
| and not output_attentions | ||
| ): | ||
| if AttentionMaskConverter._ignore_causal_mask_sdpa( | ||
| attention_mask, | ||
| inputs_embeds=input_tensor, | ||
| past_key_values_length=past_seen_tokens, | ||
| sliding_window=self.config.sliding_window, | ||
| is_training=self.training, | ||
| ): | ||
| return None | ||
|
|
||
| dtype, device = input_tensor.dtype, input_tensor.device | ||
| min_dtype = torch.finfo(dtype).min | ||
| sequence_length = input_tensor.shape[1] | ||
| if using_static_cache: | ||
| if using_sliding_window_cache: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we now have a significant code block to determine suggestion to avoid this disconnection:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the target length, maybe we merge #32421 first where the max length should be easily accessible through cache no matter cache type. We earlier discussed returning max cache shape, and sliding window has a max capacity for cache class And then yeah, we can move that part to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed with the plan -- shall we leave a TODO for us in this PR, linking to your comment?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I'll add a TODO for us so we can make the required change in all models at once, to not mix different updates in one PR :) |
||
| sliding_window = self.config.sliding_window if self.config.sliding_window is not None else sequence_length | ||
| target_length = max(sequence_length, sliding_window) | ||
| elif using_static_cache: | ||
| target_length = past_key_values.get_max_length() | ||
| else: | ||
| target_length = ( | ||
|
|
@@ -1138,6 +1157,8 @@ def _update_causal_mask( | |
| min_dtype=min_dtype, | ||
| cache_position=cache_position, | ||
| batch_size=input_tensor.shape[0], | ||
| config=self.config, | ||
| past_key_values=past_key_values, | ||
| ) | ||
|
|
||
| if ( | ||
|
|
@@ -1364,15 +1385,25 @@ def prepare_inputs_for_generation( | |
| dtype = self.lm_head.weight.dtype | ||
| min_dtype = torch.finfo(dtype).min | ||
|
|
||
| if isinstance(past_key_values, SlidingWindowCache): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (same comment as above here) |
||
| sliding_window = ( | ||
| self.config.sliding_window if self.config.sliding_window is not None else sequence_length | ||
| ) | ||
| target_length = max(sequence_length, sliding_window) | ||
| else: | ||
| target_length = past_key_values.get_max_length() | ||
|
|
||
| attention_mask = _prepare_4d_causal_attention_mask_with_cache_position( | ||
| attention_mask, | ||
| sequence_length=sequence_length, | ||
| target_length=past_key_values.get_max_length(), | ||
| target_length=target_length, | ||
| dtype=dtype, | ||
| device=device, | ||
| min_dtype=min_dtype, | ||
| cache_position=cache_position, | ||
| batch_size=batch_size, | ||
| config=self.config, | ||
| past_key_values=past_key_values, | ||
| ) | ||
|
|
||
| if num_logits_to_keep is not None: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.