AoiKazama commited on
Commit
df4846f
1 Parent(s): 24354f1

Upload modeling_attn_mask_utils.py

Browse files
Files changed (1) hide show
  1. modeling_attn_mask_utils.py +492 -0
modeling_attn_mask_utils.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2023 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from dataclasses import dataclass
15
+ from typing import List, Optional, Tuple, Union
16
+
17
+ import torch
18
+
19
+
20
+ @dataclass
21
+ class AttentionMaskConverter:
22
+ """
23
+ A utility attention mask class that allows one to:
24
+ - Create a causal 4d mask
25
+ - Create a causal 4d mask with slided window
26
+ - Convert a 2d attention mask (batch_size, query_length) to a 4d attention mask (batch_size, 1, query_length,
27
+ key_value_length) that can be multiplied with attention scores
28
+
29
+ Examples:
30
+
31
+ ```python
32
+ >>> import torch
33
+ >>> from transformers.modeling_attn_mask_utils import AttentionMaskConverter
34
+
35
+ >>> converter = AttentionMaskConverter(True)
36
+ >>> converter.to_4d(torch.tensor([[0, 0, 0, 1, 1]]), 5, key_value_length=5, dtype=torch.float32)
37
+ tensor([[[[-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
38
+ [-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
39
+ [-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
40
+ [-3.4028e+38, -3.4028e+38, -3.4028e+38, 0.0000e+00, -3.4028e+38],
41
+ [-3.4028e+38, -3.4028e+38, -3.4028e+38, 0.0000e+00, 0.0000e+00]]]])
42
+ ```
43
+
44
+ Parameters:
45
+ is_causal (`bool`):
46
+ Whether the attention mask should be a uni-directional (causal) or bi-directional mask.
47
+
48
+ sliding_window (`int`, *optional*):
49
+ Optionally, the sliding window masks can be created if `sliding_window` is defined to a positive integer.
50
+ """
51
+
52
+ is_causal: bool
53
+ sliding_window: int
54
+
55
+ def __init__(self, is_causal: bool, sliding_window: Optional[int] = None):
56
+ self.is_causal = is_causal
57
+ self.sliding_window = sliding_window
58
+
59
+ if self.sliding_window is not None and self.sliding_window <= 0:
60
+ raise ValueError(
61
+ f"Make sure that when passing `sliding_window` that its value is a strictly positive integer, not `{self.sliding_window}`"
62
+ )
63
+
64
+ def to_causal_4d(
65
+ self,
66
+ batch_size: int,
67
+ query_length: int,
68
+ key_value_length: int,
69
+ dtype: torch.dtype,
70
+ device: Union[torch.device, "str"] = "cpu",
71
+ ) -> Optional[torch.Tensor]:
72
+ """
73
+ Creates a causal 4D mask of (bsz, head_dim=1, query_length, key_value_length) shape and adds large negative
74
+ bias to upper right hand triangular matrix (causal mask).
75
+ """
76
+ if not self.is_causal:
77
+ raise ValueError(f"Please use `to_causal_4d` only if {self.__class__} has `is_causal` set to True.")
78
+
79
+ # If shape is not cached, create a new causal mask and cache it
80
+ input_shape = (batch_size, query_length)
81
+ past_key_values_length = key_value_length - query_length
82
+
83
+ # create causal mask
84
+ # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
85
+ causal_4d_mask = None
86
+ if input_shape[-1] > 1 or self.sliding_window is not None:
87
+ causal_4d_mask = self._make_causal_mask(
88
+ input_shape,
89
+ dtype,
90
+ device=device,
91
+ past_key_values_length=past_key_values_length,
92
+ sliding_window=self.sliding_window,
93
+ )
94
+
95
+ return causal_4d_mask
96
+
97
+ def to_4d(
98
+ self,
99
+ attention_mask_2d: torch.Tensor,
100
+ query_length: int,
101
+ dtype: torch.dtype,
102
+ key_value_length: Optional[int] = None,
103
+ ) -> torch.Tensor:
104
+ """
105
+ Converts 2D attention mask to 4D attention mask by expanding mask to (bsz, head_dim=1, query_length,
106
+ key_value_length) shape and by adding a large negative bias to not-attended positions. If attention_mask is
107
+ causal, a causal mask will be added.
108
+ """
109
+ input_shape = (attention_mask_2d.shape[0], query_length)
110
+
111
+ # create causal mask
112
+ # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
113
+ causal_4d_mask = None
114
+ if (input_shape[-1] > 1 or self.sliding_window is not None) and self.is_causal:
115
+ if key_value_length is None:
116
+ raise ValueError(
117
+ "This attention mask converter is causal. Make sure to pass `key_value_length` to correctly create a causal mask."
118
+ )
119
+
120
+ past_key_values_length = key_value_length - query_length
121
+ causal_4d_mask = self._make_causal_mask(
122
+ input_shape,
123
+ dtype,
124
+ device=attention_mask_2d.device,
125
+ past_key_values_length=past_key_values_length,
126
+ sliding_window=self.sliding_window,
127
+ )
128
+ elif self.sliding_window is not None:
129
+ raise NotImplementedError("Sliding window is currently only implemented for causal masking")
130
+
131
+ # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
132
+ expanded_attn_mask = self._expand_mask(attention_mask_2d, dtype, tgt_len=input_shape[-1]).to(
133
+ attention_mask_2d.device
134
+ )
135
+
136
+ if causal_4d_mask is not None:
137
+ expanded_attn_mask = causal_4d_mask.masked_fill(expanded_attn_mask.bool(), torch.finfo(dtype).min)
138
+
139
+ # expanded_attn_mask + causal_4d_mask can cause some overflow
140
+ expanded_4d_mask = expanded_attn_mask
141
+
142
+ return expanded_4d_mask
143
+
144
+ @staticmethod
145
+ def _make_causal_mask(
146
+ input_ids_shape: torch.Size,
147
+ dtype: torch.dtype,
148
+ device: torch.device,
149
+ past_key_values_length: int = 0,
150
+ sliding_window: Optional[int] = None,
151
+ ):
152
+ """
153
+ Make causal mask used for bi-directional self-attention.
154
+ """
155
+ bsz, tgt_len = input_ids_shape
156
+ mask = torch.full((tgt_len, tgt_len), torch.finfo(dtype).min, device=device)
157
+ mask_cond = torch.arange(mask.size(-1), device=device)
158
+ mask.masked_fill_(mask_cond < (mask_cond + 1).view(mask.size(-1), 1), 0)
159
+
160
+ mask = mask.to(dtype)
161
+
162
+ if past_key_values_length > 0:
163
+ mask = torch.cat([torch.zeros(tgt_len, past_key_values_length, dtype=dtype, device=device), mask], dim=-1)
164
+
165
+ # add lower triangular sliding window mask if necessary
166
+ if sliding_window is not None:
167
+ diagonal = past_key_values_length - sliding_window - 1
168
+
169
+ context_mask = torch.tril(torch.ones_like(mask, dtype=torch.bool), diagonal=diagonal)
170
+ mask.masked_fill_(context_mask, torch.finfo(dtype).min)
171
+
172
+ return mask[None, None, :, :].expand(bsz, 1, tgt_len, tgt_len + past_key_values_length)
173
+
174
+ @staticmethod
175
+ def _expand_mask(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
176
+ """
177
+ Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`.
178
+ """
179
+ bsz, src_len = mask.size()
180
+ tgt_len = tgt_len if tgt_len is not None else src_len
181
+
182
+ expanded_mask = mask[:, None, None, :].expand(bsz, 1, tgt_len, src_len).to(dtype)
183
+
184
+ inverted_mask = 1.0 - expanded_mask
185
+
186
+ return inverted_mask.masked_fill(inverted_mask.to(torch.bool), torch.finfo(dtype).min)
187
+
188
+ @staticmethod
189
+ def _unmask_unattended(
190
+ expanded_mask: torch.FloatTensor,
191
+ min_dtype: float,
192
+ ):
193
+ # fmt: off
194
+ """
195
+ Attend to all tokens in masked rows from the expanded attention mask, for example the relevant first rows when
196
+ using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
197
+ Details: https://github.com/pytorch/pytorch/issues/110213
198
+
199
+ `expanded_mask` is [bsz, num_masks, tgt_seq_len, src_seq_len] or [bsz, tgt_seq_len, src_seq_len].
200
+ `attention_mask` is [bsz, src_seq_len].
201
+
202
+ The dimension num_masks of `expanded_mask` is most often 1, but it can also be the number of heads in the case of alibi attention bias.
203
+
204
+ For example, if `expanded_mask` is (e.g. here left-padding case)
205
+ ```
206
+ [[[[0, 0, 0],
207
+ [0, 0, 0],
208
+ [0, 0, 1]]],
209
+ [[[1, 0, 0],
210
+ [1, 1, 0],
211
+ [1, 1, 1]]],
212
+ [[[0, 0, 0],
213
+ [0, 1, 0],
214
+ [0, 1, 1]]]]
215
+ ```
216
+ then the modified `expanded_mask` will be
217
+ ```
218
+ [[[[1, 1, 1], <-- modified
219
+ [1, 1, 1], <-- modified
220
+ [0, 0, 1]]],
221
+ [[[1, 0, 0],
222
+ [1, 1, 0],
223
+ [1, 1, 1]]],
224
+ [[[1, 1, 1], <-- modified
225
+ [0, 1, 0],
226
+ [0, 1, 1]]]]
227
+ ```
228
+ """
229
+ # fmt: on
230
+ if expanded_mask.dtype == torch.bool:
231
+ raise ValueError(
232
+ "AttentionMaskConverter._unmask_unattended expects a float `expanded_mask`, got a BoolTensor."
233
+ )
234
+
235
+ return expanded_mask.mul(~torch.all(expanded_mask == min_dtype, dim=-1, keepdim=True))
236
+
237
+ @staticmethod
238
+ def _ignore_causal_mask_sdpa(
239
+ attention_mask: Optional[torch.Tensor],
240
+ inputs_embeds: torch.Tensor,
241
+ past_key_values_length: int,
242
+ sliding_window: Optional[int] = None,
243
+ ) -> bool:
244
+ """
245
+ Detects whether the optional user-specified attention_mask & the automatically created causal mask can be ignored in case PyTorch's SDPA is used, rather relying on SDPA's `is_causal` argument.
246
+
247
+ In case no token is masked in the `attention_mask` argument, if `query_length == 1` or
248
+ `key_value_length == query_length`, we rather rely on SDPA `is_causal` argument to use causal/non-causal masks,
249
+ allowing to dispatch to the flash attention kernel (that can otherwise not be used if a custom `attn_mask` is passed).
250
+ """
251
+
252
+ batch_size, query_length = inputs_embeds.shape[0], inputs_embeds.shape[1]
253
+ key_value_length = query_length + past_key_values_length
254
+
255
+ is_tracing = (
256
+ torch.jit.is_tracing()
257
+ or isinstance(inputs_embeds, torch.fx.Proxy)
258
+ or (hasattr(torch, "_dynamo") and torch._dynamo.is_compiling())
259
+ )
260
+
261
+ ignore_causal_mask = False
262
+
263
+ if attention_mask is None:
264
+ # TODO: When tracing with TorchDynamo with fullgraph=True, the model is recompiled depending on the input shape, thus SDPA's `is_causal` argument is rightfully updated (see https://gist.github.com/fxmarty/1313f39037fc1c112508989628c57363). However, when using `torch.export` or
265
+ # or `torch.onnx.dynamo_export`, we must pass an example input, and `is_causal` behavior is hard-coded. If a user exports a model with q_len > 1, the exported model will hard-code `is_causal=True` which is in general wrong (see https://github.com/pytorch/pytorch/issues/108108).
266
+ # Thus, we currently can NOT set `ignore_causal_mask = True` here. We would need a `torch._dynamo.is_exporting()` flag.
267
+ #
268
+ # Besides, jit.trace can not handle the `q_len > 1` condition for `is_causal` (`TypeError: scaled_dot_product_attention(): argument 'is_causal' must be bool, not Tensor`).
269
+ if (
270
+ not is_tracing
271
+ and (query_length == 1 or key_value_length == query_length)
272
+ and (sliding_window is None or key_value_length < sliding_window)
273
+ ):
274
+ ignore_causal_mask = True
275
+ elif sliding_window is None or key_value_length < sliding_window:
276
+ if len(attention_mask.shape) == 4:
277
+ expected_shape = (batch_size, 1, query_length, key_value_length)
278
+ if tuple(attention_mask.shape) != expected_shape:
279
+ raise ValueError(
280
+ f"Incorrect 4D attention_mask shape: {tuple(attention_mask.shape)}; expected: {expected_shape}."
281
+ )
282
+ elif not is_tracing and torch.all(attention_mask == 1):
283
+ if query_length == 1 or key_value_length == query_length:
284
+ # For query_length == 1, causal attention and bi-directional attention are the same.
285
+ ignore_causal_mask = True
286
+
287
+ # Unfortunately, for query_length > 1 and key_value_length != query_length, we cannot generally ignore the attention mask, as SDPA causal mask generation
288
+ # may be wrong. We will set `is_causal=False` in SDPA and rely on Transformers attention_mask instead, hence not setting it to None here.
289
+ # Reference: https://github.com/pytorch/pytorch/issues/108108
290
+ # TODO: maybe revisit this with https://github.com/pytorch/pytorch/pull/114823 in PyTorch 2.3.
291
+
292
+ return ignore_causal_mask
293
+
294
+
295
+ def _prepare_4d_causal_attention_mask(
296
+ attention_mask: Optional[torch.Tensor],
297
+ input_shape: Union[torch.Size, Tuple, List],
298
+ inputs_embeds: torch.Tensor,
299
+ past_key_values_length: int,
300
+ sliding_window: Optional[int] = None,
301
+ ):
302
+ """
303
+ Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
304
+ `(batch_size, key_value_length)`
305
+
306
+ Args:
307
+ attention_mask (`torch.Tensor` or `None`):
308
+ A 2D attention mask of shape `(batch_size, key_value_length)`
309
+ input_shape (`tuple(int)` or `list(int)` or `torch.Size`):
310
+ The input shape should be a tuple that defines `(batch_size, query_length)`.
311
+ inputs_embeds (`torch.Tensor`):
312
+ The embedded inputs as a torch Tensor.
313
+ past_key_values_length (`int`):
314
+ The length of the key value cache.
315
+ sliding_window (`int`, *optional*):
316
+ If the model uses windowed attention, a sliding window should be passed.
317
+ """
318
+ attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
319
+
320
+ key_value_length = input_shape[-1] + past_key_values_length
321
+
322
+ # 4d mask is passed through the layers
323
+ if attention_mask is not None and len(attention_mask.shape) == 2:
324
+ attention_mask = attn_mask_converter.to_4d(
325
+ attention_mask, input_shape[-1], key_value_length=key_value_length, dtype=inputs_embeds.dtype
326
+ )
327
+ elif attention_mask is not None and len(attention_mask.shape) == 4:
328
+ expected_shape = (input_shape[0], 1, input_shape[1], key_value_length)
329
+ if tuple(attention_mask.shape) != expected_shape:
330
+ raise ValueError(
331
+ f"Incorrect 4D attention_mask shape: {tuple(attention_mask.shape)}; expected: {expected_shape}."
332
+ )
333
+ else:
334
+ # if the 4D mask has correct shape - invert it and fill with negative infinity
335
+ inverted_mask = 1.0 - attention_mask
336
+ attention_mask = inverted_mask.masked_fill(
337
+ inverted_mask.to(torch.bool), torch.finfo(inputs_embeds.dtype).min
338
+ )
339
+ else:
340
+ attention_mask = attn_mask_converter.to_causal_4d(
341
+ input_shape[0], input_shape[-1], key_value_length, dtype=inputs_embeds.dtype, device=inputs_embeds.device
342
+ )
343
+
344
+ return attention_mask
345
+
346
+
347
+ # Adapted from _prepare_4d_causal_attention_mask
348
+ def _prepare_4d_causal_attention_mask_for_sdpa(
349
+ attention_mask: Optional[torch.Tensor],
350
+ input_shape: Union[torch.Size, Tuple, List],
351
+ inputs_embeds: torch.Tensor,
352
+ past_key_values_length: int,
353
+ sliding_window: Optional[int] = None,
354
+ ):
355
+ """
356
+ Prepares the correct `attn_mask` argument to be used by `torch.nn.functional.scaled_dot_product_attention`.
357
+
358
+ In case no token is masked in the `attention_mask` argument, we simply set it to `None` for the cases `query_length == 1` and
359
+ `key_value_length == query_length`, and rely instead on SDPA `is_causal` argument to use causal/non-causal masks,
360
+ allowing to dispatch to the flash attention kernel (that can otherwise not be used if a custom `attn_mask` is passed).
361
+ """
362
+ attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
363
+
364
+ key_value_length = input_shape[-1] + past_key_values_length
365
+
366
+ # torch.jit.trace, symbolic_trace and torchdynamo with fullgraph=True are unable to capture the controlflow `is_causal=attention_mask is None and q_len > 1`
367
+ # used as an SDPA argument. We keep compatibility with these tracing tools by always using SDPA's `attn_mask` argument in case we are tracing.
368
+ # TODO: For dynamo, rather use a check on fullgraph=True once this is possible (https://github.com/pytorch/pytorch/pull/120400).
369
+ is_tracing = (
370
+ torch.jit.is_tracing()
371
+ or isinstance(inputs_embeds, torch.fx.Proxy)
372
+ or (hasattr(torch, "_dynamo") and torch._dynamo.is_compiling())
373
+ )
374
+
375
+ ignore_causal_mask = AttentionMaskConverter._ignore_causal_mask_sdpa(
376
+ attention_mask=attention_mask,
377
+ inputs_embeds=inputs_embeds,
378
+ past_key_values_length=past_key_values_length,
379
+ sliding_window=sliding_window,
380
+ )
381
+
382
+ if ignore_causal_mask:
383
+ expanded_4d_mask = None
384
+ elif attention_mask is None:
385
+ expanded_4d_mask = attn_mask_converter.to_causal_4d(
386
+ input_shape[0], input_shape[-1], key_value_length, dtype=inputs_embeds.dtype, device=inputs_embeds.device
387
+ )
388
+ else:
389
+ expanded_4d_mask = attn_mask_converter.to_4d(
390
+ attention_mask,
391
+ input_shape[-1],
392
+ dtype=inputs_embeds.dtype,
393
+ key_value_length=key_value_length,
394
+ )
395
+
396
+ # Attend to all tokens in masked rows from the causal_mask, for example the relevant first rows when
397
+ # using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
398
+ # Details: https://github.com/pytorch/pytorch/issues/110213
399
+ if not is_tracing and expanded_4d_mask.device.type == "cuda":
400
+ expanded_4d_mask = AttentionMaskConverter._unmask_unattended(
401
+ expanded_4d_mask, min_dtype=torch.finfo(inputs_embeds.dtype).min
402
+ )
403
+
404
+ return expanded_4d_mask
405
+
406
+
407
+ def _prepare_4d_attention_mask(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
408
+ """
409
+ Creates a non-causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
410
+ `(batch_size, key_value_length)`
411
+
412
+ Args:
413
+ mask (`torch.Tensor` or `None`):
414
+ A 2D attention mask of shape `(batch_size, key_value_length)`
415
+ dtype (`torch.dtype`):
416
+ The torch dtype the created mask shall have.
417
+ tgt_len (`int`):
418
+ The target length or query length the created mask shall have.
419
+ """
420
+ return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len)
421
+
422
+
423
+ def _prepare_4d_attention_mask_for_sdpa(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
424
+ """
425
+ Creates a non-causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
426
+ `(batch_size, key_value_length)`
427
+
428
+ Args:
429
+ mask (`torch.Tensor` or `None`):
430
+ A 2D attention mask of shape `(batch_size, key_value_length)`
431
+ dtype (`torch.dtype`):
432
+ The torch dtype the created mask shall have.
433
+ tgt_len (`int`):
434
+ The target length or query length the created mask shall have.
435
+ """
436
+ batch_size, key_value_length = mask.shape
437
+ tgt_len = tgt_len if tgt_len is not None else key_value_length
438
+
439
+ # torch.jit.trace, symbolic_trace and torchdynamo with fullgraph=True are unable to capture the controlflow `is_causal=attention_mask is None and q_len > 1`
440
+ # used as an SDPA argument. We keep compatibility with these tracing tools by always using SDPA's `attn_mask` argument in case we are tracing.
441
+ # TODO: For dynamo, rather use a check on fullgraph=True once this is possible (https://github.com/pytorch/pytorch/pull/120400).
442
+ is_tracing = (
443
+ torch.jit.is_tracing()
444
+ or isinstance(mask, torch.fx.Proxy)
445
+ or (hasattr(torch, "_dynamo") and torch._dynamo.is_compiling())
446
+ )
447
+
448
+ if torch.all(mask == 1):
449
+ if is_tracing:
450
+ pass
451
+ elif tgt_len == 1:
452
+ # For query_length == 1, causal attention and bi-directional attention are the same.
453
+ return None
454
+ elif key_value_length == tgt_len:
455
+ return None
456
+ else:
457
+ # Unfortunately, for query_length > 1 and key_value_length != query_length, we can not generally ignore the attention mask, as SDPA causal mask generation
458
+ # may be wrong. We will set is_causal=False in SDPA and rely on Transformers attention_mask instead, hence not setting it to None here.
459
+ # Reference: https://github.com/pytorch/pytorch/issues/108108
460
+ return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len)
461
+ else:
462
+ return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len)
463
+
464
+
465
+ def _create_4d_causal_attention_mask(
466
+ input_shape: Union[torch.Size, Tuple, List],
467
+ dtype: torch.dtype,
468
+ device: torch.device,
469
+ past_key_values_length: int = 0,
470
+ sliding_window: Optional[int] = None,
471
+ ) -> Optional[torch.Tensor]:
472
+ """
473
+ Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)`
474
+
475
+ Args:
476
+ input_shape (`tuple(int)` or `list(int)` or `torch.Size`):
477
+ The input shape should be a tuple that defines `(batch_size, query_length)`.
478
+ dtype (`torch.dtype`):
479
+ The torch dtype the created mask shall have.
480
+ device (`int`):
481
+ The torch device the created mask shall have.
482
+ sliding_window (`int`, *optional*):
483
+ If the model uses windowed attention, a sliding window should be passed.
484
+ """
485
+ attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
486
+
487
+ key_value_length = past_key_values_length + input_shape[-1]
488
+ attention_mask = attn_mask_converter.to_causal_4d(
489
+ input_shape[0], input_shape[-1], key_value_length, dtype=dtype, device=device
490
+ )
491
+
492
+ return attention_mask