ankahi / unsloth_compiled_cache /unsloth_compiled_module_gemma4.py
bhriguverma's picture
Add files using upload-large-folder tool
99061da verified
Raw
History Blame Contribute Delete
77.4 kB
"""
2026.4.9
2026.4.8
5.5.0
0.24.0
__UNSLOTH_VERSIONING__
"""
# Unsloth auto generated code
# Copyright 2023-present Daniel Han-Chen, Michael Han-Chen & the Unsloth team. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import sys
import torch
import importlib.util
import math
if importlib.util.find_spec("unsloth_studio") is None:
UNSLOTH_STUDIO_ENABLED = False
else:
UNSLOTH_STUDIO_ENABLED = os.environ.get("UNSLOTH_STUDIO_DISABLED", "0") == "0"
pass
from typing import Any, List, Optional, Tuple, Union, Dict, Set, Callable
import math
UNSLOTH_ENABLE_LOGGING = os.environ.get("UNSLOTH_ENABLE_LOGGING", "0") == "1"
UNSLOTH_ENABLE_CCE = os.environ.get("UNSLOTH_ENABLE_CCE", "1") == "1"
UNSLOTH_COMPILE_DISABLE = os.environ.get("UNSLOTH_COMPILE_DISABLE", "0") in ("1", "partial",)
UNSLOTH_COMPILE_LOCATION = os.environ.get("UNSLOTH_COMPILE_LOCATION", "unsloth_compiled_cache")
if UNSLOTH_COMPILE_LOCATION not in sys.path:
sys.path.insert(0, UNSLOTH_COMPILE_LOCATION)
import logging
logger_compiler = logging.getLogger(__name__)
if UNSLOTH_ENABLE_LOGGING:
logger_compiler.setLevel(logging.DEBUG)
global INFERENCE_RUNS
INFERENCE_RUNS = 0
try:
import torch._dynamo.eval_frame as torch_dynamo_eval_frame
torch_dynamo_eval_frame._stance.stance
torch_compiler_set_stance = torch.compiler.set_stance
except:
torch_dynamo_eval_frame = None
torch_compiler_set_stance = None
pass
from unsloth_zoo import DEVICE_TYPE_TORCH, DEVICE_COUNT
from unsloth_zoo.loss_utils import (
fused_linear_cross_entropy,
unsloth_fused_ce_loss,
)
scaled_dot_product_attention = torch.nn.functional.scaled_dot_product_attention
@torch.compiler.disable(recursive = False)
def disable_compile_scaled_dot_product_attention(*args, **kwargs):
return scaled_dot_product_attention(*args, **kwargs)
pass
from transformers.modeling_flash_attention_utils import is_flash_attn_available
if is_flash_attn_available():
try:
from transformers.modeling_flash_attention_utils import flash_attn_supports_top_left_mask
except:
flash_attn_supports_top_left_mask = None
try:
from transformers.modeling_flash_attention_utils import _flash_attention_forward
except:
_flash_attention_forward = None
try:
from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
except:
FlashAttentionKwargs = None
try:
from transformers.modeling_flash_attention_utils import flash_attn_varlen_func
except:
flash_attn_varlen_func = None
else:
flash_attn_supports_top_left_mask = None
_flash_attention_forward = None
FlashAttentionKwargs = None
flash_attn_varlen_func = None
pass
torch_compile_options = {'epilogue_fusion': True, 'max_autotune': False, 'shape_padding': True, 'trace.enabled': False, 'triton.cudagraphs': False, 'debug': False, 'dce': True, 'memory_planning': True, 'coordinate_descent_tuning': False, 'trace.graph_diagram': False, 'compile_threads': 32, 'group_fusion': True, 'disable_progress': True, 'verbose_progress': False, 'triton.multi_kernel': 0, 'triton.use_block_ptr': False, 'triton.enable_persistent_tma_matmul': True, 'triton.autotune_at_compile_time': False, 'triton.cooperative_reductions': False, 'cuda.compile_opt_level': '-O2', 'cuda.enable_cuda_lto': True, 'combo_kernels': False, 'benchmark_combo_kernel': True, 'combo_kernel_foreach_dynamic_shapes': True}
from torch.nn import CrossEntropyLoss
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def normal_cross_entropy_loss(self, hidden_states, labels):
logits = self.lm_head(hidden_states)
logits = logits.float()
# Shift so that tokens < n predict n
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
# Flatten the tokens
loss_fct = CrossEntropyLoss()
shift_logits = shift_logits.view(-1, self.config.vocab_size)
shift_labels = shift_labels.view(-1)
# Enable model parallelism
shift_labels = shift_labels.to(shift_logits.device)
loss = loss_fct(shift_logits, shift_labels)
return loss, logits
pass
# We need an empty logits flag to warn people logits will not be returned anymore unless asked ie
# os.environ['UNSLOTH_RETURN_LOGITS'] = '1'
LOGITS_ERROR_STRING = \
"Unsloth: Logits are empty from 2024.11 onwards. To get raw logits again, please "\
'set the environment variable `UNSLOTH_RETURN_LOGITS` to `"1" BEFORE starting to train ie before `trainer.train()`. For example:\n'\
"```\nimport os\n"\
"os.environ['UNSLOTH_RETURN_LOGITS'] = '1'\n"\
"trainer.train()\n```\n"\
"No need to restart your console - just add `os.environ['UNSLOTH_RETURN_LOGITS'] = '1'` before trainer.train() and re-run the cell!"
def raise_logits_error(*args, **kwargs): raise NotImplementedError(LOGITS_ERROR_STRING)
def return_none(*args, **kwargs): return None
class EmptyLogits:
def __init__(self): return
def raise_getattr_error(self, attr): return return_none if attr == "to" else raise_logits_error
__getitem__ = raise_logits_error
__getattr__ = raise_getattr_error
def __repr__(self): return LOGITS_ERROR_STRING
def __str__ (self): return LOGITS_ERROR_STRING
pass
EMPTY_LOGITS = EmptyLogits()
functions = dir(torch.Tensor)
for j, function in enumerate(functions):
if function.startswith("__") and function.endswith("__"):
exec(f"def raise_{j}(*args, **kwargs): print('{function}')", globals(), locals())
try: exec(f"EMPTY_LOGITS.{function} = raise_{j}", globals(), locals())
except: continue
pass
def mask_attention_mask_out(labels = None, attention_mask = None):
if labels is not None and attention_mask is not None:
attention_mask = attention_mask.to(device = labels.device)
labels[attention_mask == 0] = -100
return labels
pass
from torch import Tensor
import torch
import torch.nn as nn
from torch.nn import functional as F
from unsloth_zoo.temporary_patches.common import torch_compile
from typing import Any, List, Optional, Tuple, Union, Dict, Set, Callable
from transformers.models.gemma4.modeling_gemma4 import (__name__, F, math, Callable, Optional, torch, nn, init, ACT2FN, Cache, PreTrainedConfig, GenerationMixin, create_causal_mask, create_sliding_window_causal_mask, FlashAttentionKwargs, BaseModelOutputWithPast, ModelOutput, CausalLMOutputWithPast, ROPE_INIT_FUNCTIONS, dynamic_rope_update, ALL_ATTENTION_FUNCTIONS, PreTrainedModel, Unpack, TransformersKwargs, can_return_tuple, maybe_autocast, Gemma4AudioConfig, Gemma4Config, Gemma4TextConfig, Gemma4VisionConfig, Gemma4Model, Gemma4CausalLMOutputWithPast, Gemma4AudioCausalConv1d, Gemma4PreTrainedModel, Gemma4TextModel, Gemma4ForCausalLM, Gemma4ForConditionalGeneration, Gemma4TextExperts, create_causal_mask, create_masks_for_generate, create_sliding_window_causal_mask)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4ClippableLinear_forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
if self.use_clipped_linears:
hidden_states = torch.clamp(hidden_states, self.input_min, self.input_max)
hidden_states = self.linear(hidden_states)
if self.use_clipped_linears:
hidden_states = torch.clamp(hidden_states, self.output_min, self.output_max)
return hidden_states
class Gemma4ClippableLinear(nn.Module):
def __init__(
self,
config: Gemma4VisionConfig | Gemma4AudioConfig,
in_features: int,
out_features: int,
) -> None:
super().__init__()
self.use_clipped_linears = config.use_clipped_linears
self.linear = nn.Linear(in_features, out_features, bias=False)
if self.use_clipped_linears:
self.register_buffer("input_min", torch.tensor(-float("inf")))
self.register_buffer("input_max", torch.tensor(float("inf")))
self.register_buffer("output_min", torch.tensor(-float("inf")))
self.register_buffer("output_max", torch.tensor(float("inf")))
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
return Gemma4ClippableLinear_forward(self, hidden_states=hidden_states)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def Gemma4RMSNorm_forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
normed_output = self._norm(hidden_states.float())
if self.with_scale:
normed_output = normed_output * self.weight.float()
return normed_output.type_as(hidden_states)
class Gemma4RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6, with_scale: bool = True):
super().__init__()
self.eps = eps
self.with_scale = with_scale
if self.with_scale:
self.weight = nn.Parameter(torch.ones(dim), requires_grad=True)
def _norm(self, hidden_states: torch.Tensor):
mean_squared = hidden_states.pow(2).mean(-1, keepdim=True) + self.eps
# Use torch.pow() (over torch.sqrt() or torch.rsqrt()) to addess compiler differences between Torch and JAX
return hidden_states * torch.pow(mean_squared, -0.5)
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
return Gemma4RMSNorm_forward(self, hidden_states=hidden_states)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
@torch.no_grad()
def Gemma4AudioRelPositionalEncoding_forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
position_ids = torch.arange(12, -1, -1, device=hidden_states.device)
position_ids = position_ids[..., None]
scaled_time = position_ids * self.inv_timescales.to(device=hidden_states.device)
pos_embed = torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=-1)
return pos_embed.to(dtype=hidden_states.dtype)
class Gemma4AudioRelPositionalEncoding(nn.Module):
"""Sinusoidal relative positional encoding for the audio encoder.
Produces position embeddings of shape [1, 2*context_size - 1, hidden_size] with
concatenated [sin..., cos...] layout matching the original Gemma4 convention.
"""
inv_timescales: torch.Tensor
def __init__(self, config: Gemma4AudioConfig):
super().__init__()
self.hidden_size = config.hidden_size
self.context_size = (
config.attention_chunk_size + config.attention_context_left - 1 + config.attention_context_right
)
min_timescale = 1.0
max_timescale = 10000.0
num_timescales = self.hidden_size // 2
log_timescale_increment = math.log(max_timescale / min_timescale) / max(num_timescales - 1, 1)
inv_timescales = min_timescale * torch.exp(torch.arange(num_timescales) * -log_timescale_increment)
self.register_buffer("inv_timescales", inv_timescales.unsqueeze(0).unsqueeze(0), persistent=False)
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
return Gemma4AudioRelPositionalEncoding_forward(self, hidden_states=hidden_states)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def Gemma4AudioSubSampleConvProjectionLayer_forward(self, hidden_states: torch.Tensor, mask: torch.Tensor | None = None):
if mask is not None:
mask = mask.to(device=hidden_states.device)
hidden_states = hidden_states * mask[:, None, :, None]
hidden_states = self.conv(hidden_states.to(self.conv.weight.dtype))
hidden_states = self.act(self.norm(hidden_states.permute(0, 2, 3, 1)).permute(0, 3, 1, 2).contiguous())
if mask is not None:
mask = mask[:, ::2]
return hidden_states, mask
class Gemma4AudioSubSampleConvProjectionLayer(nn.Module):
def __init__(self, in_channels, out_channels, norm_eps):
super().__init__()
self.conv = nn.Conv2d(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=(3, 3),
stride=(2, 2),
padding=1,
bias=False,
)
self.norm = nn.LayerNorm(out_channels, eps=norm_eps, elementwise_affine=True, bias=False)
self.act = nn.ReLU()
def forward(self, hidden_states: torch.Tensor, mask: torch.Tensor | None = None):
return Gemma4AudioSubSampleConvProjectionLayer_forward(self, hidden_states=hidden_states, mask=mask)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4AudioSubSampleConvProjection_forward(
self,
input_features: torch.Tensor,
input_features_mask: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
hidden_states = input_features.unsqueeze(1)
hidden_states, mask = self.layer0(hidden_states, input_features_mask)
hidden_states, mask = self.layer1(hidden_states, mask)
batch_size, _, seq_len, _ = hidden_states.shape
hidden_states = hidden_states.permute(0, 2, 3, 1).contiguous().reshape(batch_size, seq_len, -1)
return self.input_proj_linear(hidden_states), mask
class Gemma4AudioSubSampleConvProjection(nn.Module):
def __init__(self, config: Gemma4AudioConfig):
super().__init__()
self.layer0 = Gemma4AudioSubSampleConvProjectionLayer(
in_channels=1,
out_channels=config.subsampling_conv_channels[0],
norm_eps=config.rms_norm_eps,
)
self.layer1 = Gemma4AudioSubSampleConvProjectionLayer(
in_channels=config.subsampling_conv_channels[0],
out_channels=config.subsampling_conv_channels[1],
norm_eps=config.rms_norm_eps,
)
proj_input_dim = (config.subsampling_conv_channels[0] // 4) * config.subsampling_conv_channels[1]
self.input_proj_linear = nn.Linear(proj_input_dim, config.hidden_size, bias=False)
def forward(
self,
input_features: torch.Tensor,
input_features_mask: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
return Gemma4AudioSubSampleConvProjection_forward(self, input_features=input_features, input_features_mask=input_features_mask)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4AudioFeedForward_forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
# This is needed to avoid any underflow/overflow issues when clipping
gradient_clipping = min(self.gradient_clipping, torch.finfo(self.ffw_layer_1.linear.weight.dtype).max)
residual = hidden_states
hidden_states = torch.clamp(hidden_states, -gradient_clipping, gradient_clipping)
hidden_states = self.pre_layer_norm(hidden_states)
hidden_states = self.ffw_layer_1(hidden_states)
hidden_states = self.act_fn(hidden_states)
hidden_states = self.ffw_layer_2(hidden_states)
hidden_states = torch.clamp(hidden_states, -gradient_clipping, gradient_clipping)
hidden_states = self.post_layer_norm(hidden_states)
hidden_states *= self.post_layer_scale
hidden_states += residual
return hidden_states
class Gemma4AudioFeedForward(nn.Module):
def __init__(self, config: Gemma4AudioConfig):
super().__init__()
self.config = config
self.ffw_layer_1 = Gemma4ClippableLinear(config, config.hidden_size, config.hidden_size * 4)
self.ffw_layer_2 = Gemma4ClippableLinear(config, config.hidden_size * 4, config.hidden_size)
self.pre_layer_norm = Gemma4RMSNorm(config.hidden_size)
self.post_layer_norm = Gemma4RMSNorm(config.hidden_size)
self.act_fn = ACT2FN[config.hidden_act]
self.gradient_clipping = config.gradient_clipping
self.post_layer_scale = config.residual_weight
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
return Gemma4AudioFeedForward_forward(self, hidden_states=hidden_states)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4AudioLightConv1d_forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
residual = hidden_states
hidden_states = self.pre_layer_norm(hidden_states)
hidden_states = self.linear_start(hidden_states)
hidden_states = nn.functional.glu(hidden_states, dim=-1)
hidden_states = self.depthwise_conv1d(hidden_states.transpose(1, 2)).transpose(1, 2)
# This is needed to avoid any underflow/overflow issues when clipping
gradient_clipping = min(self.gradient_clipping, torch.finfo(self.linear_start.linear.weight.dtype).max)
hidden_states = torch.clamp(hidden_states, -gradient_clipping, gradient_clipping)
hidden_states = self.conv_norm(hidden_states)
hidden_states = self.act_fn(hidden_states)
hidden_states = self.linear_end(hidden_states)
hidden_states += residual
return hidden_states
class Gemma4AudioLightConv1d(nn.Module):
def __init__(self, config: Gemma4AudioConfig):
super().__init__()
self.config = config
self.linear_start = Gemma4ClippableLinear(config, config.hidden_size, config.hidden_size * 2)
self.linear_end = Gemma4ClippableLinear(config, config.hidden_size, config.hidden_size)
self.depthwise_conv1d = Gemma4AudioCausalConv1d(
in_channels=config.hidden_size,
out_channels=config.hidden_size,
kernel_size=config.conv_kernel_size,
groups=config.hidden_size,
bias=False,
)
self.pre_layer_norm = Gemma4RMSNorm(config.hidden_size, eps=config.rms_norm_eps, with_scale=True)
self.conv_norm = Gemma4RMSNorm(config.hidden_size, eps=config.rms_norm_eps, with_scale=True)
self.act_fn = ACT2FN[config.hidden_act]
self.gradient_clipping = config.gradient_clipping
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
return Gemma4AudioLightConv1d_forward(self, hidden_states=hidden_states)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4VisionMLP_forward(self, x):
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
return down_proj
class Gemma4VisionMLP(nn.Module):
def __init__(self, config: Gemma4VisionConfig):
super().__init__()
self.config = config
self.hidden_size = config.hidden_size
self.intermediate_size = config.intermediate_size
self.gate_proj = Gemma4ClippableLinear(config, self.hidden_size, self.intermediate_size)
self.up_proj = Gemma4ClippableLinear(config, self.hidden_size, self.intermediate_size)
self.down_proj = Gemma4ClippableLinear(config, self.intermediate_size, self.hidden_size)
self.act_fn = ACT2FN[config.hidden_activation]
def forward(self, x):
return Gemma4VisionMLP_forward(self, x=x)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
@torch.no_grad()
@dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
def Gemma4VisionRotaryEmbedding_forward(self, x, position_ids):
inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
# Multidimensional positions: [batch, num_patches, ndim]. Apply rotations to each spatial dim separately
all_cos, all_sin = [], []
for i in range(2):
dim_position_ids = position_ids[:, :, i]
dim_position_ids_expanded = dim_position_ids[:, None, :].float()
with maybe_autocast(device_type=device_type, enabled=False): # Force float32
freqs = (inv_freq_expanded.float() @ dim_position_ids_expanded.float()).transpose(1, 2)
emb = torch.cat((freqs, freqs), dim=-1)
cos = emb.cos() * self.attention_scaling
sin = emb.sin() * self.attention_scaling
all_cos.append(cos)
all_sin.append(sin)
cos = torch.cat(all_cos, dim=-1).to(dtype=x.dtype)
sin = torch.cat(all_sin, dim=-1).to(dtype=x.dtype)
return cos, sin
class Gemma4VisionRotaryEmbedding(nn.Module):
inv_freq: torch.Tensor # fix linting for `register_buffer`
def __init__(self, config: Gemma4VisionConfig, device=None):
super().__init__()
self.max_seq_len_cached = config.max_position_embeddings
self.original_max_seq_len = config.max_position_embeddings
self.config = config
self.rope_type = self.config.rope_parameters["rope_type"]
rope_init_fn: Callable = self.compute_default_rope_parameters
if self.rope_type != "default":
rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
self.register_buffer("inv_freq", inv_freq, persistent=False)
self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
@staticmethod
def compute_default_rope_parameters(
config: Gemma4VisionConfig | None = None,
device: torch.device | None = None,
seq_len: int | None = None,
) -> tuple["torch.Tensor", float]:
"""
Computes the inverse frequencies according to the original RoPE implementation
Args:
config ([`~transformers.PreTrainedConfig`]):
The model configuration.
device (`torch.device`):
The device to use for initialization of the inverse frequencies.
seq_len (`int`, *optional*):
The current sequence length. Unused for this type of RoPE.
Returns:
Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
"""
base = config.rope_parameters["rope_theta"]
dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
# The reference implementation computes RoPE frequencies INDEPENDENTLY
# for each spatial dimension using the partitioned head_dim (head_dim // ndim),
# so both x and y dimensions get identical frequency ranges.
# This is different from splitting the global inv_freq between dimensions.
spatial_dim = dim // 2
attention_factor = 1.0 # Unused in this type of RoPE
inv_freq = 1.0 / (
base
** (torch.arange(0, spatial_dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / spatial_dim)
)
return inv_freq, attention_factor
def forward(self, x, position_ids):
return Gemma4VisionRotaryEmbedding_forward(self, x=x, position_ids=position_ids)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def rotate_half(x):
"""Rotates half the hidden dims of the input."""
x1 = x[..., : x.shape[-1] // 2]
x2 = x[..., x.shape[-1] // 2 :]
return torch.cat((-x2, x1), dim=-1)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def apply_rotary_pos_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, unsqueeze_dim: int = 1):
"""Applies Rotary Position Embedding to the query and key tensors.
Args:
x (`torch.Tensor`): The tensor to embed.
cos (`torch.Tensor`): The cosine part of the rotary embedding.
sin (`torch.Tensor`): The sine part of the rotary embedding.
unsqueeze_dim (`int`, *optional*, defaults to 1):
The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
Returns:
`tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
"""
cos = cos.unsqueeze(unsqueeze_dim)
sin = sin.unsqueeze(unsqueeze_dim)
return (x * cos) + (rotate_half(x) * sin)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
"""
This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
"""
batch, num_key_value_heads, slen, head_dim = hidden_states.shape
if n_rep == 1:
return hidden_states
hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def eager_attention_forward(
module: nn.Module,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attention_mask: torch.Tensor | None,
dropout: float | int = 0.0,
scaling: float | None = None,
softcap: float | None = None,
**kwargs,
) -> tuple[torch.Tensor, torch.Tensor]:
if scaling is None:
scaling = module.head_dim**-0.5
key_states = repeat_kv(key, module.num_key_value_groups)
value_states = repeat_kv(value, module.num_key_value_groups)
attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
if softcap is not None:
attn_weights = attn_weights / softcap
attn_weights = torch.tanh(attn_weights)
attn_weights = attn_weights * softcap
if attention_mask is not None:
if isinstance(attention_mask, dict):
attention_mask = attention_mask.get(getattr(module, 'layer_type', None), None)
if attention_mask is not None:
attn_weights = attn_weights + attention_mask
# upcast attention to fp32
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype = torch.float32).to(attn_weights.dtype).to(query.dtype)
attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
attn_output = torch.matmul(attn_weights, value_states)
attn_output = attn_output.transpose(1, 2).contiguous()
return attn_output, attn_weights
@torch.compile(fullgraph = True, dynamic = True, options = torch_compile_options)
def apply_multidimensional_rope(
x: torch.Tensor,
cos: torch.Tensor,
sin: torch.Tensor,
position_ids: torch.Tensor,
unsqueeze_dim: int = 2,
) -> torch.Tensor:
"""Applies multidimensional RoPE to inputs.
Args:
x (`torch.Tensor`): The tensor to embed.
cos (`torch.Tensor`): The cosine part of the rotary embedding.
sin (`torch.Tensor`): The sine part of the rotary embedding.
position_ids (`torch.Tensor`, *optional*):
If position_ids.ndim + 2 == x.ndim, then this function passes through to `apply_rotary_pos_emb()`.
Otherwise, position_ids is used to split the inputs, x, into multiple pieces, where each piece is fed to
`apply_rotary_pos_emb()`, and then concatenated back together.
unsqueeze_dim (`int`, *optional*, defaults to 1):
The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
Returns:
Tensor of shape [B, L, N, H] with RoPE applied.
"""
ndim = position_ids.shape[-1]
num_input_channels = x.shape[-1]
num_rotated_channels_per_dim = 2 * (num_input_channels // (2 * ndim))
if num_rotated_channels_per_dim <= 0:
raise ValueError(
"Invalid configuration: num_rotated_channels_per_dim must be > 0, got"
f" {num_rotated_channels_per_dim} (num_input_channels={num_input_channels},"
f" ndim={ndim})"
)
# Correctly split the input tensor into ndim parts
split_sizes = [num_rotated_channels_per_dim] * ndim
x_parts = torch.split(x, split_sizes, dim=-1)
cos_parts = torch.split(cos, split_sizes, dim=-1)
sin_parts = torch.split(sin, split_sizes, dim=-1)
y_parts = [
apply_rotary_pos_emb(
x=x_parts[k],
cos=cos_parts[k],
sin=sin_parts[k],
unsqueeze_dim=unsqueeze_dim,
)
for k in range(ndim)
]
return torch.cat(y_parts, dim=-1)
@torch.compiler.disable(recursive = False)
def Gemma4VisionAttention_forward(
self,
hidden_states: torch.Tensor,
position_embeddings: torch.Tensor = None,
attention_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
**kwargs: Unpack[TransformersKwargs],
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
input_shape = hidden_states.shape[:-1]
hidden_shape = (*input_shape, -1, self.head_dim)
cos, sin = position_embeddings
query_states = self.q_proj(hidden_states).view(hidden_shape)
query_states = self.q_norm(query_states)
query_states = apply_multidimensional_rope(query_states, cos, sin, position_ids)
query_states = query_states.transpose(1, 2)
key_states = self.k_proj(hidden_states).view(hidden_shape)
key_states = self.k_norm(key_states)
key_states = apply_multidimensional_rope(key_states, cos, sin, position_ids)
key_states = key_states.transpose(1, 2)
value_states = self.v_proj(hidden_states).view(hidden_shape)
value_states = self.v_norm(value_states)
value_states = value_states.transpose(1, 2)
attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
self.config._attn_implementation, eager_attention_forward
)
attn_output, attn_weights = attention_interface(
self,
query_states,
key_states,
value_states,
attention_mask,
dropout=self.attention_dropout if self.training else 0.0,
scaling=self.scaling,
**kwargs,
)
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
attn_output = self.o_proj(attn_output)
return attn_output, attn_weights
class Gemma4VisionAttention(nn.Module):
"""Multi-headed attention from 'Attention Is All You Need' paper"""
def __init__(self, config: Gemma4VisionConfig, layer_idx: int):
super().__init__()
self.layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None
self.config = config
self.layer_idx = layer_idx
self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
self.scaling = 1.0
self.attention_dropout = self.config.attention_dropout
self.is_causal = False
self.q_proj = Gemma4ClippableLinear(config, config.hidden_size, config.num_attention_heads * self.head_dim)
self.k_proj = Gemma4ClippableLinear(config, config.hidden_size, config.num_key_value_heads * self.head_dim)
self.v_proj = Gemma4ClippableLinear(config, config.hidden_size, config.num_key_value_heads * self.head_dim)
self.o_proj = Gemma4ClippableLinear(config, config.num_attention_heads * self.head_dim, config.hidden_size)
self.q_norm = Gemma4RMSNorm(dim=config.head_dim, eps=config.rms_norm_eps)
self.k_norm = Gemma4RMSNorm(dim=config.head_dim, eps=config.rms_norm_eps)
self.v_norm = Gemma4RMSNorm(self.head_dim, eps=config.rms_norm_eps, with_scale=False)
def forward(
self,
hidden_states: torch.Tensor,
position_embeddings: torch.Tensor = None,
attention_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
**kwargs: Unpack[TransformersKwargs],
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
return Gemma4VisionAttention_forward(self, hidden_states=hidden_states, position_embeddings=position_embeddings, attention_mask=attention_mask, position_ids=position_ids, **kwargs)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4TextMLP_forward(self, x):
gate = self.gate_proj(x)
# Check matmul output dtype so autocast / PEFT fp16 casts are caught.
if gate.dtype != torch.float16:
return self.down_proj(self.act_fn(gate) * self.up_proj(x))
product = self.act_fn(gate.float()) * self.up_proj(x).float()
product = torch.clamp(product, min=-_SAFE_FP16, max=_SAFE_FP16)
out = self.down_proj(product.to(gate.dtype))
# Zero overflows so the residual identity path survives.
return torch.nan_to_num(out, nan=0.0, posinf=0.0, neginf=0.0)
class Gemma4TextMLP(nn.Module):
def __init__(self, config: Gemma4TextConfig, layer_idx: int):
super().__init__()
first_kv_shared_layer_idx = config.num_hidden_layers - config.num_kv_shared_layers
is_kv_shared_layer = layer_idx >= first_kv_shared_layer_idx > 0
use_double_wide_mlp = config.use_double_wide_mlp and is_kv_shared_layer
self.config = config
self.hidden_size = config.hidden_size
self.intermediate_size = config.intermediate_size * (2 if use_double_wide_mlp else 1)
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
self.act_fn = ACT2FN[config.hidden_activation]
def forward(self, x):
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
return down_proj
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
@torch.no_grad()
@dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
def Gemma4TextRotaryEmbedding_forward(self, x, position_ids, layer_type=None):
inv_freq = getattr(self, f"{layer_type}_inv_freq")
attention_scaling = getattr(self, f"{layer_type}_attention_scaling")
inv_freq_expanded = inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
position_ids_expanded = position_ids[:, None, :].float()
device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
with maybe_autocast(device_type=device_type, enabled=False): # Force float32
freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
emb = torch.cat((freqs, freqs), dim=-1)
cos = emb.cos() * attention_scaling
sin = emb.sin() * attention_scaling
return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
class Gemma4TextRotaryEmbedding(nn.Module):
inv_freq: torch.Tensor # fix linting for `register_buffer`
def __init__(self, config: Gemma4TextConfig, device=None, layer_type=None):
super().__init__()
self.max_seq_len_cached = config.max_position_embeddings
self.original_max_seq_len = config.max_position_embeddings
self.config = config
self.layer_types = set(config.layer_types)
self.rope_init_fns: dict[str, Callable[..., tuple[torch.Tensor, float]]] = {}
self.rope_type: dict[str, str] = {}
for layer_type in self.layer_types:
rope_params = self.config.rope_parameters[layer_type]
if rope_params is None:
continue
if (rope_type := rope_params["rope_type"]) != "default":
rope_init_fn = ROPE_INIT_FUNCTIONS[rope_type]
else:
rope_init_fn = self.compute_default_rope_parameters
self.rope_init_fns[layer_type] = rope_init_fn
self.rope_type[layer_type] = rope_type
rope_init_fn_kwargs = {"device": device, "layer_type": layer_type}
if layer_type == "full_attention" and rope_type == "proportional":
rope_init_fn_kwargs["head_dim_key"] = "global_head_dim"
curr_inv_freq, curr_attention_scaling = rope_init_fn(self.config, **rope_init_fn_kwargs)
self.register_buffer(f"{layer_type}_inv_freq", curr_inv_freq, persistent=False)
self.register_buffer(f"{layer_type}_original_inv_freq", curr_inv_freq.clone(), persistent=False)
setattr(self, f"{layer_type}_attention_scaling", curr_attention_scaling)
@staticmethod
def compute_default_rope_parameters(
config: Gemma4TextConfig | None = None,
device: Optional["torch.device"] = None,
seq_len: int | None = None,
layer_type: str | None = None,
) -> tuple["torch.Tensor", float]:
"""
Computes the inverse frequencies according to the original RoPE implementation
Args:
config ([`~transformers.PreTrainedConfig`]):
The model configuration.
device (`torch.device`):
The device to use for initialization of the inverse frequencies.
seq_len (`int`, *optional*):
The current sequence length. Unused for this type of RoPE.
layer_type (`str`, *optional*):
The current layer type if the model has different RoPE parameters per type.
Should not be used unless `config.layer_types is not None`
Returns:
Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
"""
# For backward compatibility standardize the `rope_parameters_dict` if it uses old format
base = config.rope_parameters[layer_type]["rope_theta"]
dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
attention_factor = 1.0 # Unused in this type of RoPE
# Compute the inverse frequencies
inv_freq = 1.0 / (
base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
)
return inv_freq, attention_factor
def forward(self, x, position_ids, layer_type=None):
return Gemma4TextRotaryEmbedding_forward(self, x=x, position_ids=position_ids, layer_type=layer_type)
@torch.compiler.disable(recursive = False)
def Gemma4TextAttention_forward(
self,
hidden_states: torch.Tensor,
position_embeddings: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None = None,
**kwargs: Unpack[FlashAttentionKwargs],
) -> tuple[torch.Tensor, torch.Tensor | None]:
input_shape = hidden_states.shape[:-1]
hidden_shape = (*input_shape, -1, self.head_dim)
cos, sin = position_embeddings
query_states = self.q_proj(hidden_states).view(hidden_shape)
query_states = self.q_norm(query_states)
query_states = apply_rotary_pos_emb(query_states, cos, sin, unsqueeze_dim=2)
query_states = query_states.transpose(1, 2)
# For layers with shared KV (from kv sharing point onwards), we reuse the same keys/values states as the last non-sharing layer
if self.is_kv_shared_layer and past_key_values is not None:
key_states, value_states = past_key_values.shared_layers[self.kv_shared_layer_index]
# Device of past layer may be different from current one
key_states = key_states.to(query_states.device)
value_states = value_states.to(query_states.device)
else:
key_states = self.k_proj(hidden_states).view(hidden_shape)
value_states = self.v_proj(hidden_states).view(hidden_shape) if self.v_proj is not None else key_states
key_states = self.k_norm(key_states)
key_states = apply_rotary_pos_emb(key_states, cos, sin, unsqueeze_dim=2)
key_states = key_states.transpose(1, 2)
value_states = self.v_norm(value_states)
value_states = value_states.transpose(1, 2)
if past_key_values is not None:
if not self.is_kv_shared_layer:
key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
if self.store_full_length_kv:
if not hasattr(past_key_values, "shared_layers"):
past_key_values.shared_layers = {}
past_key_values.shared_layers[self.layer_idx] = key_states, value_states
attention_interface: Callable = eager_attention_forward
if self.config._attn_implementation != "eager":
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
attn_output, attn_weights = attention_interface(
self,
query_states,
key_states,
value_states,
attention_mask,
dropout=self.attention_dropout if self.training else 0.0,
scaling=self.scaling,
sliding_window=self.sliding_window,
**kwargs,
)
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
attn_output = self.o_proj(attn_output)
return attn_output, attn_weights
class Gemma4TextAttention(nn.Module):
"""Multi-headed attention from 'Attention Is All You Need' paper"""
def __init__(self, config: Gemma4TextConfig, layer_idx: int):
super().__init__()
self.layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None
self.config = config
self.layer_idx = layer_idx
self.is_sliding = self.layer_type == "sliding_attention"
self.sliding_window = config.sliding_window if self.is_sliding else None
self.head_dim = config.global_head_dim if not self.is_sliding and config.global_head_dim else config.head_dim
self.use_alternative_attention = config.attention_k_eq_v and not self.is_sliding
num_key_value_heads = (
config.num_global_key_value_heads if self.use_alternative_attention else config.num_key_value_heads
)
self.num_key_value_groups = config.num_attention_heads // num_key_value_heads
self.scaling = 1.0
self.attention_dropout = self.config.attention_dropout
self.is_causal = config.use_bidirectional_attention != "all"
# Shared kv cache
first_kv_shared_layer_idx = self.config.num_hidden_layers - getattr(self.config, "num_kv_shared_layers", 0)
self.is_kv_shared_layer = layer_idx >= first_kv_shared_layer_idx > 0
prev_layers = config.layer_types[:first_kv_shared_layer_idx]
if self.is_kv_shared_layer:
# For shared layers, find the last non-shared layer of the same type before sharing starts
self.kv_shared_layer_index = len(prev_layers) - 1 - prev_layers[::-1].index(config.layer_types[layer_idx])
self.store_full_length_kv = False
else:
self.kv_shared_layer_index = None
# For non-shared layers, store full-length kv if this is the last non-shared layer of its type
self.store_full_length_kv = layer_idx == len(prev_layers) - 1 - prev_layers[::-1].index(
config.layer_types[layer_idx]
)
self.q_norm = Gemma4RMSNorm(dim=self.head_dim, eps=config.rms_norm_eps)
self.k_norm = Gemma4RMSNorm(dim=self.head_dim, eps=config.rms_norm_eps)
self.v_norm = Gemma4RMSNorm(self.head_dim, eps=config.rms_norm_eps, with_scale=False)
self.k_proj = nn.Linear(config.hidden_size, num_key_value_heads * self.head_dim, bias=config.attention_bias)
self.q_proj = nn.Linear(
config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
)
self.v_proj = (
nn.Linear(config.hidden_size, num_key_value_heads * self.head_dim, bias=config.attention_bias)
if not self.use_alternative_attention
else None
)
self.o_proj = nn.Linear(
config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
)
def forward(
self,
hidden_states: torch.Tensor,
position_embeddings: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None = None,
**kwargs: Unpack[FlashAttentionKwargs],
) -> tuple[torch.Tensor, torch.Tensor | None]:
return Gemma4TextAttention_forward(self, hidden_states=hidden_states, position_embeddings=position_embeddings, attention_mask=attention_mask, past_key_values=past_key_values, **kwargs)
@torch.compiler.disable(recursive = False)
def Gemma4TextExperts_forward(
self,
hidden_states: torch.Tensor,
top_k_index: torch.Tensor,
top_k_weights: torch.Tensor,
) -> torch.Tensor:
final_hidden_states = torch.zeros_like(hidden_states)
with torch.no_grad():
expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
expert_mask = expert_mask.permute(2, 1, 0)
expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
for expert_idx in expert_hit:
expert_idx = expert_idx[0]
if expert_idx == self.num_experts:
continue
top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
current_state = hidden_states[token_idx]
gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
current_hidden_states = self.act_fn(gate) * up
current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
return final_hidden_states
class Gemma4TextExperts(nn.Module):
"""Collection of expert weights stored as 3D tensors."""
def __init__(self, config: Gemma4TextConfig):
super().__init__()
self.num_experts = config.num_experts
self.hidden_dim = config.hidden_size
self.intermediate_dim = config.moe_intermediate_size
self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
self.act_fn = ACT2FN[config.hidden_activation]
def forward(
self,
hidden_states: torch.Tensor,
top_k_index: torch.Tensor,
top_k_weights: torch.Tensor,
) -> torch.Tensor:
return Gemma4TextExperts_forward(self, hidden_states=hidden_states, top_k_index=top_k_index, top_k_weights=top_k_weights)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4TextRouter_forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
hidden_states = self.norm(hidden_states)
hidden_states = hidden_states * self.scale * self.scalar_root_size
expert_scores = self.proj(hidden_states) # [B*S, E]
router_probabilities = nn.functional.softmax(expert_scores, dim=-1, dtype = torch.float32).to(expert_scores.dtype).to(expert_scores.dtype)
# topk returns both values (probabilities) and indices directly
top_k_weights, top_k_index = torch.topk(
router_probabilities,
k=self.config.top_k_experts,
dim=-1,
) # both [B*S, K]
# Normalize the top-k weights so they sum to 1 per token
top_k_weights /= top_k_weights.sum(dim=-1, keepdim=True)
# Apply per-expert scale directly to the weights
top_k_weights = top_k_weights * self.per_expert_scale[top_k_index]
return router_probabilities, top_k_weights, top_k_index
class Gemma4TextRouter(nn.Module):
def __init__(self, config: Gemma4TextConfig):
super().__init__()
self.config = config
self.hidden_size = config.hidden_size
self.scalar_root_size = self.hidden_size**-0.5
self.eps = config.rms_norm_eps
self.norm = Gemma4RMSNorm(self.hidden_size, eps=self.eps, with_scale=False)
self.proj = nn.Linear(config.hidden_size, config.num_experts, bias=False)
self.scale = nn.Parameter(torch.ones(self.hidden_size))
self.per_expert_scale = nn.Parameter(torch.ones(config.num_experts))
def forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
return Gemma4TextRouter_forward(self, hidden_states=hidden_states)
@torch.compiler.disable(recursive = False)
@can_return_tuple
def Gemma4ForCausalLM_forward(
self,
input_ids: torch.LongTensor | None = None,
attention_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
past_key_values: Cache | None = None,
inputs_embeds: torch.FloatTensor | None = None,
labels: torch.LongTensor | None = None,
use_cache: bool | None = None,
logits_to_keep: int | torch.Tensor = 0,
**kwargs: Unpack[TransformersKwargs],
) -> CausalLMOutputWithPast:
r"""
Example:
```python
>>> from transformers import AutoTokenizer, Gemma4ForCausalLM
>>> model = Gemma4ForCausalLM.from_pretrained("google/gemma-2-9b")
>>> tokenizer = AutoTokenizer.from_pretrained("google/gemma-2-9b")
>>> prompt = "What is your favorite condiment?"
>>> inputs = tokenizer(prompt, return_tensors="pt")
>>> # Generate
>>> generate_ids = model.generate(inputs.input_ids, max_length=30)
>>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
"What is your favorite condiment?"
```"""
# decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
outputs: BaseModelOutputWithPast = self.model(
input_ids=input_ids,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
use_cache=use_cache,
**kwargs,
)
hidden_states = outputs.last_hidden_state
# Only compute necessary logits, and do not upcast them to float if we are not computing the loss
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
logits = self.lm_head(hidden_states[:, slice_indices, :]) if os.environ.get('UNSLOTH_RETURN_LOGITS', '0') == '1' else EMPTY_LOGITS
loss = None
NOT_RETURN_LOGITS = os.environ.get('UNSLOTH_RETURN_LOGITS', '0') == '0'
RETURN_HIDDEN_STATES = os.environ.get("UNSLOTH_RETURN_HIDDEN_STATES", "0") == "1"
n_items = None
if (kwargs) != () and type(kwargs) is dict:
n_items = (kwargs).get("num_items_in_batch", None)
if n_items is None: n_items = (kwargs).get("n_items", None)
if n_items is None:
all_locals = locals()
if 'loss_kwargs' in all_locals:
__kwargs = all_locals['loss_kwargs']
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
if n_items is None and 'kwargs' in all_locals:
__kwargs = all_locals['kwargs']
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
if n_items is None:
all_locals = all_locals.values()
for __kwargs in all_locals:
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
break
pass
requires_grad_ = self.lm_head.weight.requires_grad
requires_grad_ = requires_grad_ or self.lm_head.weight.dtype == torch.float32
if RETURN_HIDDEN_STATES:
logits = hidden_states[:, slice_indices, :]
elif labels is None:
# Set compiler stance to fail on recompiles for inference
global INFERENCE_RUNS
if torch_dynamo_eval_frame is not None:
old_stance = torch_dynamo_eval_frame._stance.stance
else:
old_stance = None
if old_stance is not None and INFERENCE_RUNS == 1:
# Skip guards and return to eager -> we still need guards!
torch_compiler_set_stance(stance = "eager_on_recompile", skip_guard_eval_unsafe = False)
if UNSLOTH_ENABLE_LOGGING:
logger_compiler.info(
f"Unsloth: Removing compiler guards after 1 inference run. "\
f"DYNAMO_STANCE.stance = {torch_dynamo_eval_frame._stance.stance} "\
f"DYNAMO_STANCE.skip_guard_eval_unsafe = {torch_dynamo_eval_frame._stance.skip_guard_eval_unsafe}"
)
elif old_stance == "eager_on_recompile":
pass
elif old_stance == "default" and INFERENCE_RUNS > 1:
# Reset compiler stance
torch_compiler_set_stance(stance = "default", skip_guard_eval_unsafe = False)
if UNSLOTH_ENABLE_LOGGING:
logger_compiler.info(
f"Unsloth: Reseting guards. "\
f"DYNAMO_STANCE.stance = {torch_dynamo_eval_frame._stance.stance} "\
f"DYNAMO_STANCE.skip_guard_eval_unsafe = {torch_dynamo_eval_frame._stance.skip_guard_eval_unsafe}"
)
INFERENCE_RUNS = 0
INFERENCE_RUNS += 1
logits = self.lm_head(hidden_states[:, slice_indices, :])
elif (() == () and () == ()) and (UNSLOTH_ENABLE_CCE) and NOT_RETURN_LOGITS and self.loss_function.__name__.endswith("ForCausalLMLoss") and labels is not None and not requires_grad_:
loss = fused_linear_cross_entropy(
hidden_states = hidden_states[:, slice_indices, :],
lm_weight = self.lm_head.weight,
labels = labels.to(self.lm_head.weight.device),
num_items_in_batch = n_items,
logit_softcapping = None if (self.config.final_logit_softcapping) == () else (self.config.final_logit_softcapping),
)
elif self.loss_function.__name__.endswith("ForCausalLMLoss") and labels is not None:
lm_head_weight = self.lm_head.weight
lm_head_bias = getattr(self.lm_head, "bias", None)
# ========= NEW fused =========
_hidden_states = hidden_states[:, slice_indices, :]
torch._dynamo.mark_dynamic(_hidden_states, 1)
torch._dynamo.mark_dynamic(labels, 1)
loss = unsloth_fused_ce_loss(
trainer = None,
hidden_states = _hidden_states,
lm_head_weight = lm_head_weight,
lm_head_bias = lm_head_bias,
labels = labels,
mask = None,
n_items = n_items,
scaling = getattr(self, "accelerator_scaler", None),
target_gb = None,
torch_compile = not UNSLOTH_COMPILE_DISABLE,
logit_scale_multiply = () if () != () else 0,
logit_scale_divide = () if () != () else 0,
logit_softcapping = (self.config.final_logit_softcapping) if (self.config.final_logit_softcapping) != () else 0,
)
else:
logits = self.lm_head(hidden_states[:, slice_indices, :])
if () != ():
logits = logits * ()
if () != ():
logits = logits / ()
if (self.config.final_logit_softcapping) not in (None, (),):
logits = logits / (self.config.final_logit_softcapping)
logits = torch.tanh(logits)
logits = logits * (self.config.final_logit_softcapping)
loss = self.loss_function(logits, labels.to(self.lm_head.weight.device), vocab_size=self.vocab_size, **kwargs)
return CausalLMOutputWithPast(
loss=loss,
logits=logits,
past_key_values=outputs.past_key_values,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
)
class Gemma4ForCausalLM(Gemma4PreTrainedModel, GenerationMixin):
_tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
_tp_plan = {"lm_head": "colwise_gather_output"}
_pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
config: Gemma4TextConfig
base_model_prefix = "model"
def __init__(self, config: Gemma4TextConfig):
super().__init__(config)
self.model = Gemma4TextModel(config)
self.vocab_size = config.vocab_size
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
# Initialize weights and apply final processing
self.post_init()
def forward(
self,
input_ids: torch.LongTensor | None = None,
attention_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
past_key_values: Cache | None = None,
inputs_embeds: torch.FloatTensor | None = None,
labels: torch.LongTensor | None = None,
use_cache: bool | None = None,
logits_to_keep: int | torch.Tensor = 0,
**kwargs: Unpack[TransformersKwargs],
) -> CausalLMOutputWithPast:
return Gemma4ForCausalLM_forward(self, input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, labels=labels, use_cache=use_cache, logits_to_keep=logits_to_keep, **kwargs)
def sliding_window_mask_function(sliding_window: tuple[int, int]) -> Callable:
"""
This creates uni/bidirectional attention mask with sliding window.
"""
def inner_mask(batch_idx: int, head_idx: int, q_idx: int, kv_idx: int) -> bool:
left_window_size, right_window_size = sliding_window
dist = q_idx - kv_idx
left_mask = (dist >= 0) & (dist < left_window_size)
right_mask = (dist < 0) & (-dist < right_window_size)
return left_mask | right_mask
return inner_mask
@torch.compiler.disable(recursive = False)
@can_return_tuple
def Gemma4ForConditionalGeneration_forward(
self,
input_ids: torch.LongTensor | None = None,
pixel_values: torch.FloatTensor | None = None,
pixel_values_videos: torch.FloatTensor | None = None,
input_features: torch.FloatTensor | None = None,
attention_mask: torch.Tensor | None = None,
input_features_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
image_position_ids: torch.LongTensor | None = None,
video_position_ids: torch.LongTensor | None = None,
past_key_values: Cache | None = None,
mm_token_type_ids: torch.LongTensor | None = None,
inputs_embeds: torch.FloatTensor | None = None,
labels: torch.LongTensor | None = None,
use_cache: bool | None = None,
logits_to_keep: int | torch.Tensor = 0,
**kwargs: Unpack[TransformersKwargs],
) -> Gemma4CausalLMOutputWithPast:
r"""
input_features_mask (`torch.FloatTensor]` of shape `(num_images, seq_length)`):
The attention mask for the input audio.
image_position_ids (`torch.LongTensor` of shape `(batch_size, max_patches, 2)`, *optional*):
2D patch position coordinates from the image processor, with `(-1, -1)` indicating padding.
Passed through to the vision encoder for positional embedding computation.
video_position_ids (`torch.LongTensor` of shape `(num_videos, num_frames, max_patches, 2)`, *optional*):
2D patch position coordinates from the video processor, with `(-1, -1)` indicating padding.
Passed through to the vision encoder for positional embedding computation.
"""
outputs = self.model(
input_ids=input_ids,
pixel_values=pixel_values,
pixel_values_videos=pixel_values_videos,
input_features=input_features,
attention_mask=attention_mask,
input_features_mask=input_features_mask,
position_ids=position_ids,
past_key_values=past_key_values,
mm_token_type_ids=mm_token_type_ids,
inputs_embeds=inputs_embeds,
labels=labels,
use_cache=use_cache,
image_position_ids=image_position_ids,
video_position_ids=video_position_ids,
return_dict=True,
**kwargs,
)
hidden_states = outputs.last_hidden_state
# Only compute necessary logits, and do not upcast them to float if we are not computing the loss
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
logits = self.lm_head(hidden_states[:, slice_indices, :]) if os.environ.get('UNSLOTH_RETURN_LOGITS', '0') == '1' else EMPTY_LOGITS
loss = None
NOT_RETURN_LOGITS = os.environ.get('UNSLOTH_RETURN_LOGITS', '0') == '0'
RETURN_HIDDEN_STATES = os.environ.get("UNSLOTH_RETURN_HIDDEN_STATES", "0") == "1"
all_locals = locals()
n_items = None
if 'loss_kwargs' in all_locals:
__kwargs = all_locals['loss_kwargs']
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
if n_items is None and 'kwargs' in all_locals:
__kwargs = all_locals['kwargs']
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
if n_items is None:
all_locals = all_locals.values()
for __kwargs in all_locals:
if type(__kwargs) is dict:
n_items = __kwargs.get("num_items_in_batch", None)
if n_items is None: n_items = __kwargs.get("n_items", None)
break
pass
requires_grad_ = self.lm_head.weight.requires_grad
requires_grad_ = requires_grad_ or self.lm_head.weight.dtype == torch.float32
if RETURN_HIDDEN_STATES:
logits = hidden_states[:, slice_indices, :]
elif labels is None:
# Set compiler stance to fail on recompiles for inference
global INFERENCE_RUNS
if torch_dynamo_eval_frame is not None:
old_stance = torch_dynamo_eval_frame._stance.stance
else:
old_stance = None
if old_stance is not None and INFERENCE_RUNS == 1:
# Skip guards and return to eager -> we still need guards!
torch_compiler_set_stance(stance = "eager_on_recompile", skip_guard_eval_unsafe = False)
if UNSLOTH_ENABLE_LOGGING:
logger_compiler.info(
f"Unsloth: Removing compiler guards after 1 inference run. "\
f"DYNAMO_STANCE.stance = {torch_dynamo_eval_frame._stance.stance} "\
f"DYNAMO_STANCE.skip_guard_eval_unsafe = {torch_dynamo_eval_frame._stance.skip_guard_eval_unsafe}"
)
elif old_stance == "eager_on_recompile":
pass
elif old_stance == "default" and INFERENCE_RUNS > 1:
# Reset compiler stance
torch_compiler_set_stance(stance = "default", skip_guard_eval_unsafe = False)
if UNSLOTH_ENABLE_LOGGING:
logger_compiler.info(
f"Unsloth: Reseting guards. "\
f"DYNAMO_STANCE.stance = {torch_dynamo_eval_frame._stance.stance} "\
f"DYNAMO_STANCE.skip_guard_eval_unsafe = {torch_dynamo_eval_frame._stance.skip_guard_eval_unsafe}"
)
INFERENCE_RUNS = 0
INFERENCE_RUNS += 1
logits = self.lm_head(hidden_states[:, slice_indices, :])
else:
lm_head_weight = self.lm_head.weight
lm_head_bias = getattr(self.lm_head, "bias", None)
# ========= NEW fused =========
_hidden_states = hidden_states[:, slice_indices, :]
torch._dynamo.mark_dynamic(_hidden_states, 1)
torch._dynamo.mark_dynamic(labels, 1)
if attention_mask is not None:
torch._dynamo.mark_dynamic(attention_mask, 1)
loss = unsloth_fused_ce_loss(
trainer = None,
hidden_states = _hidden_states,
lm_head_weight = lm_head_weight,
lm_head_bias = lm_head_bias,
labels = labels,
mask = attention_mask,
n_items = n_items,
scaling = getattr(self, "accelerator_scaler", None),
target_gb = None,
torch_compile = not UNSLOTH_COMPILE_DISABLE,
logit_scale_multiply = () if () != () else 0,
logit_scale_divide = () if () != () else 0,
logit_softcapping = (self.config.get_text_config().final_logit_softcapping) if (self.config.get_text_config().final_logit_softcapping) != () else 0,
)
return Gemma4CausalLMOutputWithPast(
loss=loss,
logits=logits,
past_key_values=outputs.past_key_values,
hidden_states=outputs.hidden_states,
attentions=outputs.attentions,
image_hidden_states=outputs.image_hidden_states,
audio_hidden_states=outputs.audio_hidden_states,
)
class Gemma4ForConditionalGeneration(Gemma4PreTrainedModel, GenerationMixin):
_tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
base_model_prefix = "model"
def __init__(self, config: Gemma4Config):
super().__init__(config)
self.model = Gemma4Model(config)
self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
self.post_init()
def get_input_embeddings(self):
return self.model.get_input_embeddings()
def set_input_embeddings(self, value):
self.model.set_input_embeddings(value)
def get_image_features(
self,
pixel_values: torch.FloatTensor,
image_position_ids: torch.LongTensor | None = None,
**kwargs: Unpack[TransformersKwargs],
):
r"""
image_position_ids (`torch.LongTensor` of shape `(batch_size, max_patches, 2)`, *optional*):
2D patch position coordinates from the image processor, with `(-1, -1)` indicating padding.
Passed through to the vision encoder for positional embedding computation.
"""
return self.model.get_image_features(pixel_values, image_position_ids, **kwargs)
def forward(
self,
input_ids: torch.LongTensor | None = None,
pixel_values: torch.FloatTensor | None = None,
pixel_values_videos: torch.FloatTensor | None = None,
input_features: torch.FloatTensor | None = None,
attention_mask: torch.Tensor | None = None,
input_features_mask: torch.Tensor | None = None,
position_ids: torch.LongTensor | None = None,
image_position_ids: torch.LongTensor | None = None,
video_position_ids: torch.LongTensor | None = None,
past_key_values: Cache | None = None,
mm_token_type_ids: torch.LongTensor | None = None,
inputs_embeds: torch.FloatTensor | None = None,
labels: torch.LongTensor | None = None,
use_cache: bool | None = None,
logits_to_keep: int | torch.Tensor = 0,
**kwargs: Unpack[TransformersKwargs],
) -> Gemma4CausalLMOutputWithPast:
return Gemma4ForConditionalGeneration_forward(self, input_ids=input_ids, pixel_values=pixel_values, pixel_values_videos=pixel_values_videos, input_features=input_features, attention_mask=attention_mask, input_features_mask=input_features_mask, position_ids=position_ids, image_position_ids=image_position_ids, video_position_ids=video_position_ids, past_key_values=past_key_values, mm_token_type_ids=mm_token_type_ids, inputs_embeds=inputs_embeds, labels=labels, use_cache=use_cache, logits_to_keep=logits_to_keep, **kwargs)
def prepare_inputs_for_generation(
self,
input_ids,
past_key_values=None,
inputs_embeds=None,
position_ids=None,
pixel_values=None,
pixel_values_videos=None,
input_features=None,
attention_mask=None,
input_features_mask=None,
token_type_ids=None,
use_cache=True,
logits_to_keep=None,
labels=None,
is_first_iteration=False,
**kwargs,
):
# Overwritten -- custom `position_ids` and `pixel_values` handling
model_inputs = super().prepare_inputs_for_generation(
input_ids,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
attention_mask=attention_mask,
position_ids=position_ids,
use_cache=use_cache,
logits_to_keep=logits_to_keep,
token_type_ids=token_type_ids,
is_first_iteration=is_first_iteration,
**kwargs,
)
# If we're in cached decoding stage, multimodal inputs are already cached and can be dropped
if is_first_iteration or not use_cache:
model_inputs["pixel_values"] = pixel_values
model_inputs["pixel_values_videos"] = pixel_values_videos
model_inputs["input_features"] = input_features
model_inputs["input_features_mask"] = input_features_mask
return model_inputs
@staticmethod
def create_masks_for_generate(
config: PreTrainedConfig,
inputs_embeds: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None,
position_ids: torch.Tensor | None,
mm_token_type_ids: torch.Tensor | None = None,
is_first_iteration: bool | None = False,
**kwargs,
) -> dict:
if getattr(config.get_text_config(), "use_bidirectional_attention", None) == "vision":
# Larger Gemma 4 models use Gemma 3's bidirectional attention mask for vision inputs
return create_causal_mask_mapping(
config,
inputs_embeds,
attention_mask,
past_key_values,
position_ids,
mm_token_type_ids,
is_first_iteration=is_first_iteration,
**{k: v for k, v in kwargs.items() if k != "pixel_values"},
)
else:
# Smaller Gemma models use a conventional casual attention mask
return create_masks_for_generate(
config, inputs_embeds, attention_mask, past_key_values, position_ids, **kwargs
)
@torch.compile(fullgraph = False, dynamic = True, options = torch_compile_options)
def Gemma4MultimodalEmbedder_forward(self, inputs_embeds: torch.Tensor) -> torch.Tensor:
"""Embeds token ids or soft tokens for multimodal content into language model space.
Args:
inputs_embeds: A torch.Tensor containing the soft tokens to embed.
Returns:
A torch.Tensor of embeddings with shape `[batch_size, seq_len, self.config.text_config.hidden_size]`.
"""
embs_normed = self.embedding_pre_projection_norm(inputs_embeds)
return self.embedding_projection(embs_normed)
class Gemma4MultimodalEmbedder(nn.Module):
"""Embeds token ids or soft tokens for multimodal content into language model space."""
def __init__(
self,
multimodal_config: Gemma4AudioConfig | Gemma4VisionConfig,
text_config: Gemma4TextConfig,
):
super().__init__()
self.multimodal_hidden_size = getattr(multimodal_config, "output_proj_dims", multimodal_config.hidden_size)
self.eps = multimodal_config.rms_norm_eps
self.text_hidden_size = text_config.hidden_size
self.embedding_projection = nn.Linear(self.multimodal_hidden_size, self.text_hidden_size, bias=False)
self.embedding_pre_projection_norm = Gemma4RMSNorm(self.multimodal_hidden_size, eps=self.eps, with_scale=False)
def forward(self, inputs_embeds: torch.Tensor) -> torch.Tensor:
return Gemma4MultimodalEmbedder_forward(self, inputs_embeds=inputs_embeds)
def token_type_ids_mask_function(
token_type_ids: torch.Tensor | None,
image_group_ids: torch.Tensor | None,
) -> Callable | None:
"""
This function adds the correct offsets to the `q_idx` and `kv_idx` as the torch API can only accept lengths,
not start and end indices.
"""
# Do not return an additional mask in this case
if token_type_ids is None:
return None
def inner_mask(batch_idx: int, head_idx: int, q_idx: int, kv_idx: int) -> bool:
seq_length = image_group_ids.shape[-1]
# clamp indices because with static cache they can go beyond `image_group_ids.shape[-1]`
q_idx_clamped = q_idx.clamp(max=seq_length - 1)
kv_idx_clamped = kv_idx.clamp(max=seq_length - 1)
# Unmask if the q and kv come from same group which is not -1 (i.e. non-text)
q_group = image_group_ids[batch_idx, q_idx_clamped]
kv_group = image_group_ids[batch_idx, kv_idx_clamped]
q_group = torch.where(q_idx < seq_length, q_group, -1)
kv_group = torch.where(kv_idx < seq_length, kv_group, -1)
return (q_group == kv_group) & (q_group >= 0)
return inner_mask
def create_causal_mask_mapping(
config: PreTrainedConfig,
inputs_embeds: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None,
position_ids: torch.Tensor | None,
mm_token_type_ids: torch.Tensor | None = None,
pixel_values: torch.FloatTensor | None = None,
is_training: bool = False,
is_first_iteration: bool | None = None,
**kwargs,
) -> dict:
"""
Overwrites the base `create_masks_for_generate` with `token_type_ids` masking to create the causal mask mapping
for all kinds of forward passes. Gemma4 uses a bidirectional mask for images.
Uses `pixel_values` as an optional input to disambiguate edge cases.
"""
if is_training and mm_token_type_ids is None:
raise ValueError("`mm_token_type_ids` is required as a model input when training")
mask_kwargs = {
"config": config.get_text_config(),
"inputs_embeds": inputs_embeds,
"attention_mask": attention_mask,
"past_key_values": past_key_values,
"position_ids": position_ids,
}
sliding_mask_kwargs = mask_kwargs.copy()
# NOTE: this `may_have_image_input` logic is not flawless, it fails when we're using a cache eagerly initialized
# (e.g. compiled prefill) AND `pixel_values` are not provided (i.e. the image data is provided through other
# means). Determining prefill in that case requires checking data values, which is not compile-compatible.
is_first_iteration = (
is_first_iteration
if is_first_iteration is not None
else (past_key_values is None or not past_key_values.is_initialized or pixel_values is not None)
)
if mm_token_type_ids is not None and is_first_iteration:
# We need to pass an additional mask function to account for token type ids, and it needs to be an `or` (to
# undo the causal masking)
# First find where a new vision block starts. Vision tokens cannot attend to
# future vision tokens, but can attend to all prev tokens and to itself bidirectionally
is_vision = (mm_token_type_ids == 1) | (mm_token_type_ids == 2)
is_prev_vision = torch.roll(is_vision, shifts=1, dims=-1)
is_prev_vision[..., 0] = False
new_vision_starts = is_vision & ~is_prev_vision
vision_group_ids = torch.cumsum(new_vision_starts.int(), dim=1) - 1
vision_group_ids = torch.where(is_vision, vision_group_ids, -1)
sliding_mask_kwargs["or_mask_function"] = token_type_ids_mask_function(
mm_token_type_ids.to(inputs_embeds.device), vision_group_ids
)
return {
"full_attention": create_causal_mask(**mask_kwargs),
"sliding_attention": create_sliding_window_causal_mask(**sliding_mask_kwargs),
}