sdpa_attention.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. from typing import Optional
  2. import torch
  3. from ..utils import is_torch_npu_available, is_torch_xpu_available, logging
  4. from ..utils.import_utils import is_torch_greater_or_equal
  5. logger = logging.get_logger(__name__)
  6. _is_torch_greater_or_equal_than_2_5 = is_torch_greater_or_equal("2.5", accept_dev=True)
  7. _is_torch_greater_or_equal_than_2_8 = is_torch_greater_or_equal("2.8", accept_dev=True)
  8. _is_torch_xpu_available = is_torch_xpu_available()
  9. _is_torch_npu_available = is_torch_npu_available()
  10. def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
  11. """
  12. This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
  13. num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
  14. """
  15. batch, num_key_value_heads, slen, head_dim = hidden_states.shape
  16. if n_rep == 1:
  17. return hidden_states
  18. hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
  19. return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
  20. def use_gqa_in_sdpa(attention_mask: Optional[torch.Tensor], key: torch.Tensor) -> bool:
  21. # GQA can only be used under the following conditions
  22. # 1.cuda
  23. # - torch version >= 2.5
  24. # - attention_mask is None (otherwise it will fall back to the math kernel)
  25. # - key is not a torch.fx.Proxy (otherwise it will fail with a tracing error)
  26. # 2.xpu
  27. # - torch version >= 2.8
  28. # - key is not a torch.fx.Proxy (otherwise it will fail with a tracing error)
  29. # 3.npu
  30. # - npu is not supported gqa currently
  31. if _is_torch_xpu_available:
  32. return _is_torch_greater_or_equal_than_2_8 and not isinstance(key, torch.fx.Proxy)
  33. if _is_torch_npu_available:
  34. return False
  35. return _is_torch_greater_or_equal_than_2_5 and attention_mask is None and not isinstance(key, torch.fx.Proxy)
  36. def sdpa_attention_forward(
  37. module: torch.nn.Module,
  38. query: torch.Tensor,
  39. key: torch.Tensor,
  40. value: torch.Tensor,
  41. attention_mask: Optional[torch.Tensor],
  42. dropout: float = 0.0,
  43. scaling: Optional[float] = None,
  44. is_causal: Optional[bool] = None,
  45. **kwargs,
  46. ) -> tuple[torch.Tensor, None]:
  47. if kwargs.get("output_attentions", False) or kwargs.get("head_mask") is not None:
  48. logger.warning_once(
  49. "`sdpa` attention does not support `output_attentions=True` or `head_mask`."
  50. " Please set your attention to `eager` if you want any of these features."
  51. )
  52. sdpa_kwargs = {}
  53. if hasattr(module, "num_key_value_groups"):
  54. if not use_gqa_in_sdpa(attention_mask, key):
  55. key = repeat_kv(key, module.num_key_value_groups)
  56. value = repeat_kv(value, module.num_key_value_groups)
  57. else:
  58. sdpa_kwargs = {"enable_gqa": True}
  59. if attention_mask is not None and attention_mask.ndim == 4:
  60. attention_mask = attention_mask[:, :, :, : key.shape[-2]]
  61. # We dispatch to SDPA's Flash Attention or Efficient kernels via this `is_causal` if statement instead of an inline conditional assignment
  62. # in SDPA to support both torch.compile's dynamic shapes and full graph options. An inline conditional prevents dynamic shapes from compiling.
  63. # Note that it is important to check first for the shape, otherwise compile will fail with `argument 'is_causal' must be bool, not SymBool`
  64. if is_causal is None:
  65. # The last condition is for encoder (decoder) models which specify this by passing their own `is_causal` flag
  66. # This is mainly due to those models having mixed implementations for encoder, decoder, and encoder-decoder attns
  67. is_causal = query.shape[2] > 1 and attention_mask is None and getattr(module, "is_causal", True)
  68. # Shapes (e.g. query.shape[2]) are tensors during jit tracing, resulting in `is_causal` being a tensor.
  69. # We convert it to a bool for the SDPA kernel that only accepts bools.
  70. if torch.jit.is_tracing() and isinstance(is_causal, torch.Tensor):
  71. is_causal = is_causal.item()
  72. # When `is_causal = False` and the `attention_mask` is not of boolean type, the Ascend NPU's SDPA interface cannot utilize the FlashAttentionScore operator,
  73. # and falls back to small-operator concatenation. To invoke the FlashAttentionScore, the attention_mask must be converted to boolean type.
  74. # This adaptation ensures the `attention_mask` meets the requirement for using FlashAttentionScore.
  75. if _is_torch_npu_available:
  76. if attention_mask is not None and attention_mask.dtype != torch.bool:
  77. # Convert to boolean type, making sdpa to force call FlashAttentionScore to improve performance.
  78. attention_mask = torch.logical_not(attention_mask.bool()).to(query.device)
  79. attn_output = torch.nn.functional.scaled_dot_product_attention(
  80. query,
  81. key,
  82. value,
  83. attn_mask=attention_mask,
  84. dropout_p=dropout,
  85. scale=scaling,
  86. is_causal=is_causal,
  87. **sdpa_kwargs,
  88. )
  89. attn_output = attn_output.transpose(1, 2).contiguous()
  90. return attn_output, None