Compare commits

...
Sign in to create a new pull request.

9 commits

Author SHA1 Message Date
Daniel Han
405bb58284 Fix config stamping when SDPA is not supported
When supports_sdpa=False and the attention hierarchy selected sdpa,
the kwarg was deleted but config._attn_implementation remained as
"sdpa". Stamp it to "eager" for consistency before deleting the kwarg.
2026-03-31 17:29:07 +00:00
Daniel Han
3b8860c039 Consistent config stamping in vision.py attention fallbacks
Use _set_attn_impl for gemma3n eager and unmapped sdpa fallbacks
in vision.py so config._attn_implementation is consistent with
what gets passed to from_pretrained via kwargs.
2026-03-31 17:17:37 +00:00
Daniel Han
f77a42e0ab Fix attention selection regressions in PR #4250
- Extract _set_attn_impl helper to reduce config-stamping boilerplate
- Add nemotron_h to FA2 and flex exclusion lists (from main)
- Add attention_dropout > 0 check for flex attention (from main)
- Restore gemma3n eager default in vision.py (timm vision towers)
- Preserve sdpa fallback for unmapped/remote-code vision configs
- Restore original SDPA warning default (print when env var unset)
- Make config=None safe in determine_attention_implementation
2026-03-31 16:47:48 +00:00
Datta Nimmaturi
0bfe7e3de1 Merge remote-tracking branch 'origin/main' into flexattn_refactor 2026-03-13 04:50:25 +00:00
Datta Nimmaturi
c4f01056ae fix comment and match old behaviour 2026-03-13 04:50:06 +00:00
Datta Nimmaturi
6d443b91b5 Merge remote-tracking branch 'datta0/flexattn_refactor' into flexattn_refactor 2026-03-12 10:52:29 +00:00
Datta Nimmaturi
c230eb525c Gemma3 vs gemma3n issue resolve 2026-03-12 10:51:57 +00:00
pre-commit-ci[bot]
b85e7d0488 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-03-12 10:01:36 +00:00
Datta Nimmaturi
e5a2d39d57 refactor attn impl replacement 2026-03-12 10:01:06 +00:00
3 changed files with 84 additions and 44 deletions

View file

@ -64,7 +64,7 @@ __all__ = [
"patch_compiled_autograd", "patch_compiled_autograd",
"process_vision_info", "process_vision_info",
"unsloth_compile_transformers", "unsloth_compile_transformers",
"prefer_flex_attn_if_supported", "determine_attention_implementation",
"patch_fast_lora", "patch_fast_lora",
"validate_loftq_config", "validate_loftq_config",
"RaiseUninitialized", "RaiseUninitialized",
@ -222,36 +222,70 @@ def apply_unsloth_gradient_checkpointing(
return use_gradient_checkpointing return use_gradient_checkpointing
def prefer_flex_attn_if_supported(model_class, config): def _set_attn_impl(config, impl):
if os.environ.get("UNSLOTH_ENABLE_FLEX_ATTENTION", "1") == "0": """Stamp the chosen attention implementation onto the config object."""
return None if config is not None:
try: setattr(config, "_attn_implementation", impl)
from transformers.utils.import_utils import is_torch_flex_attn_available if hasattr(config, "attn_implementation"):
setattr(config, "attn_implementation", impl)
return impl
if not is_torch_flex_attn_available():
return None def determine_attention_implementation(model_class, config):
if model_class is None or not getattr( model_type = getattr(config, "model_type", "").lower() if config else ""
model_class, "_supports_flex_attn", False
): # 1. Flash Attention 2
return None if (
# GPT-OSS, Mllama and Gemma3N use eager/sdpa attention during HAS_FLASH_ATTENTION
# inference since flex attention returns incorrect results or errors out. and model_type not in ("gpt_oss", "mllama", "nemotron_h")
# GPT-OSS: left padding issues cause incorrect outputs. and not model_type.startswith("gemma3n")
# Mllama: _update_causal_mask uses make_flex_block_causal_mask which ):
# creates BlockMask with Q_LEN=KV_LEN=total_seq_len, but during supports_fa2 = False
# decode q_len=1, causing ValueError. Needs transformers update. if model_class is not None:
# Gemma3N: timm vision wrappers (eg Gemma3nVisionConfig) do not supports_fa2 = getattr(
# support flex_attention. model_class, "_supports_flash_attn_2", False
model_type = getattr(config, "model_type", "") if config else "" ) or getattr(model_class, "_supports_flash_attn", False)
if model_type in ("gpt_oss", "mllama") or str(model_type).startswith("gemma3n"):
return None if supports_fa2:
if config is not None: return _set_attn_impl(config, "flash_attention_2")
setattr(config, "_attn_implementation", "flex_attention")
if hasattr(config, "attn_implementation"): # 2. Flex Attention
setattr(config, "attn_implementation", "flex_attention") if os.environ.get("UNSLOTH_ENABLE_FLEX_ATTENTION", "1") != "0":
return "flex_attention" try:
except Exception: from transformers.utils.import_utils import is_torch_flex_attn_available
return None
if (
is_torch_flex_attn_available()
and (model_class is not None)
and getattr(model_class, "_supports_flex_attn", False)
):
attention_dropout = getattr(config, "attention_dropout", 0) or 0
# GPT-OSS, Mllama, Gemma3N and NemotronH use eager/sdpa attention
# during inference since flex attention returns incorrect results
# or errors out.
# GPT-OSS: left padding issues cause incorrect outputs.
# Mllama: _update_causal_mask uses make_flex_block_causal_mask which
# creates BlockMask with Q_LEN=KV_LEN=total_seq_len, but during
# decode q_len=1, causing ValueError. Needs transformers update.
# Gemma3N: timm vision wrappers (eg Gemma3nVisionConfig) do not
# support flex_attention.
# NemotronH: hybrid Mamba-2 + Transformer model that does not
# support flex_attention (raises NotImplementedError from transformers).
is_excluded = (
model_type in ("gpt_oss", "mllama", "nemotron_h")
or model_type.startswith("gemma3n")
)
if attention_dropout == 0 and not is_excluded:
return _set_attn_impl(config, "flex_attention")
except Exception:
pass
# 3. SDPA
if model_class is not None and getattr(model_class, "_supports_sdpa", False):
return _set_attn_impl(config, "sdpa")
# 4. Eager
return _set_attn_impl(config, "eager")
def _run_temporary_patches(phase): def _run_temporary_patches(phase):

View file

@ -2341,8 +2341,8 @@ class FastLlamaModel:
model_function = MODEL_FOR_CAUSAL_LM_MAPPING[model_config.__class__] model_function = MODEL_FOR_CAUSAL_LM_MAPPING[model_config.__class__]
IS_FALCON_H1 = model_config.model_type.startswith("falcon_h1") IS_FALCON_H1 = model_config.model_type.startswith("falcon_h1")
preferred_attn_impl = ( preferred_attn_impl = determine_attention_implementation(
prefer_flex_attn_if_supported(model_function, model_config) or "eager" model_function, model_config
) )
has_rope_scaling = False has_rope_scaling = False

View file

@ -29,7 +29,7 @@ except:
from ..kernels import ( from ..kernels import (
post_patch_loss_function, post_patch_loss_function,
) )
from ._utils import __version__, importlib_version, _prepare_model_for_qat from ._utils import __version__, importlib_version, _prepare_model_for_qat, _set_attn_impl
from ._utils import * from ._utils import *
from .loader_utils import _get_fp8_mode_and_check_settings from .loader_utils import _get_fp8_mode_and_check_settings
from ..save import patch_saving_functions from ..save import patch_saving_functions
@ -597,8 +597,7 @@ class FastBaseModel:
custom_datatype = None custom_datatype = None
correct_dtype = None correct_dtype = None
# Stop SDPA for some archs like Pixtral / Mistral3 # Unified hierarchical attention fallback: Flash > Flex > SDPA > Eager
flex_attn_impl = None
if auto_config is None: if auto_config is None:
auto_config = AutoConfig.from_pretrained( auto_config = AutoConfig.from_pretrained(
model_name, model_name,
@ -609,7 +608,20 @@ class FastBaseModel:
model_class = auto_model._model_mapping[auto_config.__class__] model_class = auto_model._model_mapping[auto_config.__class__]
except Exception: except Exception:
model_class = None model_class = None
flex_attn_impl = prefer_flex_attn_if_supported(model_class, auto_config)
model_type = str(getattr(auto_config, "model_type", "")).lower()
if model_type.startswith("gemma3n"):
# Gemma3N variants use timm-based vision towers which do not support
# flex_attention. The old code defaulted gemma3n to eager; preserve
# that behavior rather than letting the hierarchy pick sdpa.
attn_impl = _set_attn_impl(auto_config, "eager")
elif model_class is None and supports_sdpa:
# When model_class cannot be resolved (remote-code or unmapped
# configs), the old code defaulted to sdpa. Preserve that fallback
# instead of falling through to eager.
attn_impl = _set_attn_impl(auto_config, "sdpa")
else:
attn_impl = determine_attention_implementation(model_class, auto_config)
# Handle FP8 models: get_model_name has already redirected this to BF16 sibling if the model ships with # Handle FP8 models: get_model_name has already redirected this to BF16 sibling if the model ships with
# FP8 weights. We just need to update it here for sanity. # FP8 weights. We just need to update it here for sanity.
@ -620,20 +632,14 @@ class FastBaseModel:
except Exception: except Exception:
model_class = None model_class = None
model_type = str(getattr(auto_config, "model_type", "")).lower()
if model_type.startswith("gemma3n"):
# Gemma3N variants initialize timm-based vision towers which do
# not support flex_attention, so default to eager unless overridden.
default_attn_impl = "eager"
else:
default_attn_impl = "flex_attention" if flex_attn_impl else "sdpa"
if not ("attn_implementation" in kwargs): if not ("attn_implementation" in kwargs):
kwargs["attn_implementation"] = default_attn_impl kwargs["attn_implementation"] = attn_impl
if not supports_sdpa and kwargs.get("attn_implementation") == "sdpa": if not supports_sdpa and kwargs.get("attn_implementation") == "sdpa":
if os.environ.get("UNSLOTH_ENABLE_FLEX_ATTENTION", "0") == "0": if os.environ.get("UNSLOTH_ENABLE_FLEX_ATTENTION", "0") == "0":
print( print(
f"Unsloth: {model_type_arch.title()} does not support SDPA - switching to fast eager." f"Unsloth: {model_type_arch.title()} does not support SDPA - switching to fast eager."
) )
_set_attn_impl(auto_config, "eager")
del kwargs["attn_implementation"] del kwargs["attn_implementation"]
bnb_config = None bnb_config = None