Compare commits

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

4 commits

Author SHA1 Message Date
pre-commit-ci[bot]
b715eaad6d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-06-21 13:22:59 +00:00
danielhanchen
2ef4039911 is_gemma4_shared_kv_model: read config via _config_get for dict configs
getattr on a dict config silently misses model_type / text_config /
num_kv_shared_layers (some serialization and init paths pass a dict). Use the
existing _config_get helper so the Gemma 4 E-series detection is correct whether
the config is an object or a dict. Behavior is unchanged for object configs.
2026-06-21 13:22:17 +00:00
pre-commit-ci[bot]
952fa1916e [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-06-20 07:45:07 +00:00
Daniel Han
925d4b0675 Gemma 4 E-series: default generation to use_cache=False
Gemma 4 E-series models (E2B / E4B) share KV across their last layers
(num_kv_shared_layers > 0). Under transformers 5.5.0 their KV-cache
generation path is broken (huggingface/transformers#45242): a cached forward
diverges from the cache-free forward, so default greedy decode emits garbage
even when the model has clearly learned the target (teacher-forced loss 0 on
the cache-free path).

Add is_gemma4_shared_kv_model and, in the text and vision fast-generate
wrappers, default these models to use_cache=False (and skip the forced
cache_implementation) unless the caller passed use_cache explicitly. This is
gated to model_type starting with gemma4 and num_kv_shared_layers > 0, so
Gemma 4 31B / 26B-A4B (num_kv_shared_layers == 0), Gemma 3, Gemma 3n and all
other models are untouched. Users keep the cached path via an explicit
generate(..., use_cache=True).
2026-06-20 07:43:52 +00:00
3 changed files with 42 additions and 0 deletions

View file

@ -82,6 +82,7 @@ __all__ = [
"_get_inference_mode_context_manager",
"hf_login",
"is_moe_model",
"is_gemma4_shared_kv_model",
"get_moe_target_parameters",
"make_fast_generate_wrapper",
]
@ -3280,6 +3281,30 @@ def is_moe_model(model) -> bool:
return num_experts is not None and num_experts > 0
def is_gemma4_shared_kv_model(model) -> bool:
"""
Detect Gemma 4 E-series models (E2B / E4B) that share KV across layers.
These have num_kv_shared_layers > 0, and in transformers 5.5.0 their
KV cache generation path is broken (huggingface/transformers#45242):
generating with a KV cache (use_cache=True / cache_implementation set)
yields different, garbled logits to the cache-free path. Gemma 4 31B /
26B-A4B have num_kv_shared_layers == 0 and are unaffected.
"""
config = getattr(model, "config", model)
# Use _config_get so a dict config (some serialization / init paths) is read
# correctly; plain getattr would silently miss the field on a dict.
text_config = _config_get(config, "text_config", None) or config
model_type = _config_get(config, "model_type", "") or ""
text_model_type = _config_get(text_config, "model_type", "") or ""
if not (
(isinstance(model_type, str) and model_type.startswith("gemma4"))
or (isinstance(text_model_type, str) and text_model_type.startswith("gemma4"))
):
return False
return (_config_get(text_config, "num_kv_shared_layers", 0) or 0) > 0
def _resolve_moe_parameter_name(model, default_name: str, alternate_name: str) -> str:
"""
Resolve the actual parameter path for MoE expert weights.

View file

@ -2156,6 +2156,14 @@ def unsloth_fast_generate(self, *args, **kwargs):
# For newer HF
kwargs["cache_implementation"] = "dynamic"
# Gemma 4 E-series (E2B / E4B) share KV across layers and their KV cache
# generation path is broken in transformers 5.5.0
# (huggingface/transformers#45242): a cached forward diverges from the
# cache-free forward, so default greedy decode emits garbage. Default to the
# correct cache-free path unless the caller explicitly opts into a cache.
if "use_cache" not in kwargs and is_gemma4_shared_kv_model(self):
kwargs["use_cache"] = False
kwargs["cache_implementation"] = None
# transformers 4.50 renamed num_logits_to_keep -> logits_to_keep; pop both,
# re-emit under the spelling forward() accepts.
_provided_num = kwargs.pop("num_logits_to_keep", None)

View file

@ -405,6 +405,15 @@ def unsloth_base_fast_generate(self, *args, **kwargs):
if do_bfloat16_mixed_precision:
cache_implementation = None
# Gemma 4 E-series (E2B / E4B) share KV across layers and their KV cache
# generation path is broken in transformers 5.5.0
# (huggingface/transformers#45242): a cached forward diverges from the
# cache-free forward, so default greedy decode emits garbage. Default to the
# correct cache-free path unless the caller explicitly opts into a cache.
if "use_cache" not in kwargs and is_gemma4_shared_kv_model(self):
kwargs["use_cache"] = False
cache_implementation = None
if "generation_config" in kwargs:
kwargs["generation_config"].cache_implementation = cache_implementation
if cache_implementation is not None: