Skip to content

Commit c86af60

Browse files
authored
Add TRANSFORMERS_SDPA_FORCE_GQA envvar in sdpa_attention_forward (#49459)
Add `TRANSFORMERS_SDPA_FORCE_GQA` env var
1 parent 6089332 commit c86af60

3 files changed

Lines changed: 41 additions & 0 deletions

File tree

‎docs/source/en/reference/environment_variables.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Optimized kernels are downloaded from the Hub when [kernels](https://github.com/
5353
| `USE_HUB_KERNELS` | boolean, enabled by default | Set to a false value to fall back to the reference implementations. |
5454
| `TRANSFORMERS_DISABLE_DEEPGEMM_LINEAR` | `1` | Forces the Triton fallback instead of DeepGEMM for FP8 linear layers in finegrained FP8 quantization. |
5555
| `TRANSFORMERS_DISABLE_TORCH_CHECK` | `1` | Skips the minimum PyTorch version check at import. |
56+
| `TRANSFORMERS_SDPA_FORCE_GQA` | `1` | Forces PyTorch SDPA to use `enable_gqa=True` for grouped-query attention. Otherwise, Transformers selects between setting `enable_gqa=True` and repeating key/value heads. |
5657

5758
> [!WARNING]
5859
> `TRANSFORMERS_DISABLE_TORCH_CHECK` removes a guard rather than changing behavior. On an unsupported PyTorch version you get import errors or silently wrong results instead of a clear message.

‎src/transformers/integrations/sdpa_attention.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
13
import torch
24

35
from ..generation.continuous_batching.cache import PagedAttentionCache
@@ -28,6 +30,8 @@ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
2830

2931

3032
def use_gqa_in_sdpa(attention_mask: torch.Tensor | None, key: torch.Tensor, value: torch.Tensor) -> bool:
33+
if os.environ.get("TRANSFORMERS_SDPA_FORCE_GQA", "0") == "1":
34+
return True
3135
# XPU and MPS support masked GQA under more recent torch versions
3236
if _is_torch_xpu_available:
3337
return _is_torch_greater_or_equal_than_2_8

‎tests/utils/test_modeling_utils.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,6 +3290,42 @@ def test_position_bias_in_sdpa_uses_a_fused_kernel(self, use_mask, is_causal):
32903290
)
32913291
self.assertEqual(attn_output.shape, (1, 16, 8, 64))
32923292

3293+
@parameterized.expand([("0", False), ("1", True)])
3294+
def test_transformers_sdpa_force_gqa_env_var_overrides_use_gqa_in_sdpa_attention_forward(
3295+
self, env_var_value, expected_enable_gqa
3296+
):
3297+
from transformers.integrations.sdpa_attention import sdpa_attention_forward
3298+
3299+
module = torch.nn.Module()
3300+
module.num_key_value_groups = 2
3301+
query = torch.randn(1, 4, 16, 1, dtype=torch.float16)
3302+
key = torch.randn(1, 2, 16, 1, dtype=torch.float16)
3303+
value = torch.randn(1, 2, 16, 1, dtype=torch.float16)
3304+
3305+
# use an attention mask and set _is_torch_{xpu,mps}_available to False, so that use_gqa_in_sdpa
3306+
# falls back to returning False when the env var is not set to 1
3307+
attention_mask = torch.zeros(1, 1, 16, 16, dtype=torch.float16)
3308+
3309+
with (
3310+
patch("transformers.integrations.sdpa_attention._is_torch_xpu_available", False),
3311+
patch("transformers.integrations.sdpa_attention._is_torch_mps_available", False),
3312+
patch(
3313+
"torch.nn.functional.scaled_dot_product_attention",
3314+
wraps=torch.nn.functional.scaled_dot_product_attention,
3315+
) as mock_sdpa,
3316+
patch.dict(os.environ, {"TRANSFORMERS_SDPA_FORCE_GQA": env_var_value}),
3317+
):
3318+
sdpa_attention_forward(module, query, key, value, attention_mask=attention_mask)
3319+
3320+
mock_sdpa.assert_called_once()
3321+
self.assertEqual(mock_sdpa.call_args.kwargs.get("enable_gqa", False), expected_enable_gqa)
3322+
self.assertEqual(
3323+
mock_sdpa.call_args.args[1].shape[1], 2 if expected_enable_gqa else 4
3324+
) # check if key was repeated
3325+
self.assertEqual(
3326+
mock_sdpa.call_args.args[2].shape[1], 2 if expected_enable_gqa else 4
3327+
) # check if value was repeated
3328+
32933329
def test_flash_attn_available_no_keyerror_when_missing_from_distribution_map(self):
32943330
# Regression test for https://github.com/huggingface/transformers/issues/45520.
32953331
# When flash_attn is importable but not present in PACKAGE_DISTRIBUTION_MAPPING

0 commit comments

Comments
 (0)