fix(gpt-oss): prefer flex attention over sdpa (#5701)
* fix(gpt-oss): prefer flex attention over sdpa * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(gpt-oss): use eager config for unsupported backends * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
df2d31fea8
commit
ed1e3929ad
3 changed files with 83 additions and 5 deletions
56
tests/test_attention_implementation.py
Normal file
56
tests/test_attention_implementation.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import unsloth # noqa: F401
|
||||||
|
from transformers.utils import import_utils
|
||||||
|
|
||||||
|
from unsloth.models import _utils
|
||||||
|
|
||||||
|
|
||||||
|
class SupportsFlexAndSdpa:
|
||||||
|
_supports_flash_attn_2 = True
|
||||||
|
_supports_flex_attn = True
|
||||||
|
_supports_sdpa = True
|
||||||
|
|
||||||
|
|
||||||
|
def _config(model_type, **kwargs):
|
||||||
|
values = {"model_type": model_type, "attention_dropout": 0}
|
||||||
|
values.update(kwargs)
|
||||||
|
return SimpleNamespace(**values)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_flex_available(monkeypatch, available):
|
||||||
|
monkeypatch.setenv("UNSLOTH_ENABLE_FLEX_ATTENTION", "1")
|
||||||
|
monkeypatch.setattr(
|
||||||
|
import_utils,
|
||||||
|
"is_torch_flex_attn_available",
|
||||||
|
lambda: available,
|
||||||
|
raising = False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt_oss_uses_eager_instead_of_flash_flex_or_sdpa(monkeypatch):
|
||||||
|
_set_flex_available(monkeypatch, True)
|
||||||
|
config = _config("gpt_oss")
|
||||||
|
|
||||||
|
impl = _utils.resolve_attention_implementation(
|
||||||
|
SupportsFlexAndSdpa,
|
||||||
|
config,
|
||||||
|
supports_sdpa = True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert impl == "eager"
|
||||||
|
assert config._attn_implementation == "eager"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt_oss_falls_back_to_eager_when_flex_unavailable(monkeypatch):
|
||||||
|
_set_flex_available(monkeypatch, False)
|
||||||
|
config = _config("gpt_oss")
|
||||||
|
|
||||||
|
impl = _utils.resolve_attention_implementation(
|
||||||
|
SupportsFlexAndSdpa,
|
||||||
|
config,
|
||||||
|
supports_sdpa = True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert impl == "eager"
|
||||||
|
assert config._attn_implementation == "eager"
|
||||||
|
|
@ -226,8 +226,10 @@ def apply_unsloth_gradient_checkpointing(
|
||||||
return use_gradient_checkpointing
|
return use_gradient_checkpointing
|
||||||
|
|
||||||
|
|
||||||
# Models that don't work with flex_attention:
|
# Models that don't work with flex_attention as the global Transformers
|
||||||
# GPT-OSS: left padding issues cause incorrect outputs.
|
# attention implementation:
|
||||||
|
# GPT-OSS: training uses the custom flex sink patch, but inference intentionally
|
||||||
|
# falls back to eager because flex decoding gives incorrect outputs.
|
||||||
# Mllama: BlockMask Q_LEN!=KV_LEN ValueError on decode.
|
# Mllama: BlockMask Q_LEN!=KV_LEN ValueError on decode.
|
||||||
# NemotronH: hybrid Mamba-2 + Transformer, raises NotImplementedError.
|
# NemotronH: hybrid Mamba-2 + Transformer, raises NotImplementedError.
|
||||||
# Gemma3N: timm vision wrappers don't support flex_attention.
|
# Gemma3N: timm vision wrappers don't support flex_attention.
|
||||||
|
|
@ -235,6 +237,8 @@ def apply_unsloth_gradient_checkpointing(
|
||||||
# access on some GPU architectures (B200). Falls back to eager safely.
|
# access on some GPU architectures (B200). Falls back to eager safely.
|
||||||
_FLEX_EXCLUDED_MODELS = ("gpt_oss", "mllama", "nemotron_h", "modernbert")
|
_FLEX_EXCLUDED_MODELS = ("gpt_oss", "mllama", "nemotron_h", "modernbert")
|
||||||
_FLEX_PREFERRED_MODELS = ("gemma3", "gemma3_text", "shieldgemma2")
|
_FLEX_PREFERRED_MODELS = ("gemma3", "gemma3_text", "shieldgemma2")
|
||||||
|
_SDPA_EXCLUDED_MODELS = ("gpt_oss",)
|
||||||
|
_FLASH_EXCLUDED_MODELS = ("gpt_oss",)
|
||||||
_EAGER_ONLY_PREFIXES = ("gemma3n",)
|
_EAGER_ONLY_PREFIXES = ("gemma3n",)
|
||||||
_FLASH_ATTENTION_MAX_HEAD_DIM = 256
|
_FLASH_ATTENTION_MAX_HEAD_DIM = 256
|
||||||
_FLASH_ATTENTION_DISABLED_WARNED = set()
|
_FLASH_ATTENTION_DISABLED_WARNED = set()
|
||||||
|
|
@ -244,6 +248,14 @@ def _is_flex_excluded(model_type):
|
||||||
return model_type in _FLEX_EXCLUDED_MODELS
|
return model_type in _FLEX_EXCLUDED_MODELS
|
||||||
|
|
||||||
|
|
||||||
|
def _is_sdpa_excluded(model_type):
|
||||||
|
return model_type in _SDPA_EXCLUDED_MODELS
|
||||||
|
|
||||||
|
|
||||||
|
def _is_flash_excluded(model_type):
|
||||||
|
return model_type in _FLASH_EXCLUDED_MODELS
|
||||||
|
|
||||||
|
|
||||||
def _config_prefers_flex_attention(config):
|
def _config_prefers_flex_attention(config):
|
||||||
return any(
|
return any(
|
||||||
_config_get(attention_config, "model_type", "").lower()
|
_config_get(attention_config, "model_type", "").lower()
|
||||||
|
|
@ -360,6 +372,9 @@ def _get_max_attention_head_dim(config):
|
||||||
|
|
||||||
|
|
||||||
def _get_flash_attention_disable_reason(config):
|
def _get_flash_attention_disable_reason(config):
|
||||||
|
model_type = _config_get(config, "model_type", "").lower()
|
||||||
|
if _is_flash_excluded(model_type):
|
||||||
|
return f"{model_type} uses custom sink attention kernels"
|
||||||
max_head_dim = _get_max_attention_head_dim(config)
|
max_head_dim = _get_max_attention_head_dim(config)
|
||||||
if max_head_dim is not None and max_head_dim > _FLASH_ATTENTION_MAX_HEAD_DIM:
|
if max_head_dim is not None and max_head_dim > _FLASH_ATTENTION_MAX_HEAD_DIM:
|
||||||
return (
|
return (
|
||||||
|
|
@ -489,9 +504,15 @@ def resolve_attention_implementation(
|
||||||
supports_sdpa = model_class is not None and getattr(
|
supports_sdpa = model_class is not None and getattr(
|
||||||
model_class, "_supports_sdpa", False
|
model_class, "_supports_sdpa", False
|
||||||
)
|
)
|
||||||
supports_flash_attention = model_class is not None and (
|
if _is_sdpa_excluded(model_type):
|
||||||
getattr(model_class, "_supports_flash_attn_2", False)
|
supports_sdpa = False
|
||||||
or getattr(model_class, "_supports_flash_attn", False)
|
supports_flash_attention = (
|
||||||
|
model_class is not None
|
||||||
|
and (
|
||||||
|
getattr(model_class, "_supports_flash_attn_2", False)
|
||||||
|
or getattr(model_class, "_supports_flash_attn", False)
|
||||||
|
)
|
||||||
|
and not _is_flash_excluded(model_type)
|
||||||
)
|
)
|
||||||
supports_flex_attention = _supports_flex_attention(model_class, config, model_type)
|
supports_flex_attention = _supports_flex_attention(model_class, config, model_type)
|
||||||
disable_reason = _get_flash_attention_disable_reason(config)
|
disable_reason = _get_flash_attention_disable_reason(config)
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ global DISABLE_SDPA_MODEL_NAMES
|
||||||
DISABLE_SDPA_MODEL_NAMES = [
|
DISABLE_SDPA_MODEL_NAMES = [
|
||||||
"gemma3,", # Add comma bc gemma3 will match gemma3n
|
"gemma3,", # Add comma bc gemma3 will match gemma3n
|
||||||
"gemma3_text", # Gemma3TextModel (EmbeddingGemma) - substring match, keep underscore
|
"gemma3_text", # Gemma3TextModel (EmbeddingGemma) - substring match, keep underscore
|
||||||
|
"gpt_oss",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue