Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/transformers/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"replace_kernel_forward_from_hub",
"use_kernel_forward_from_hub",
"use_kernel_func_from_hub",
"use_kernel_func_from_hub_with_fallback",
"use_kernelized_func",
],
"integration_utils": [
Expand Down Expand Up @@ -241,6 +242,7 @@
replace_kernel_forward_from_hub,
use_kernel_forward_from_hub,
use_kernel_func_from_hub,
use_kernel_func_from_hub_with_fallback,
use_kernelized_func,
)
from .integration_utils import (
Expand Down
31 changes: 29 additions & 2 deletions src/transformers/integrations/hub_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import importlib
import inspect
import os
import re
import sys
Expand All @@ -31,6 +33,7 @@
is_kernels_available,
is_rocm_platform,
is_torch_available,
resolve_internal_import,
)
from .flash_attention import flash_attention_forward

Expand Down Expand Up @@ -531,8 +534,6 @@ def lazy_load_kernel(kernel_name: str, mapping: dict[str, ModuleType | None] = _

else:
# Try to import is_{kernel_name}_available from ..utils
import importlib

new_kernel_name = kernel_name.replace("-", "_")
func_name = f"is_{new_kernel_name}_available"

Expand Down Expand Up @@ -655,6 +656,32 @@ def new_init(self, *args, **kwargs):
return decorator


def use_kernel_func_from_hub_with_fallback(package: str, func_name: str, internal_path: str | None = None):
# TODO: change when we sync to 0.16.x+
kernel_wrapper_decorator = use_kernel_func_from_hub(func_name)

def decorator(torch_function: Callable) -> Callable:
implementation = None
try:
module = importlib.import_module(package)
implementation = resolve_internal_import(module, internal_path or func_name)
except Exception:
implementation = torch_function
finally:
implementation = torch_function if implementation is None else implementation

applicable_params = inspect.signature(implementation).parameters

@functools.wraps(torch_function)
def wrapped(*args, **kwargs):
kwargs = {k: v for k, v in kwargs.items() if k in applicable_params}
return implementation(*args, **kwargs)

return kernel_wrapper_decorator(wrapped)

return decorator


# Whether to allow hub kernels coming from untrusted repos, i.e. repos outside `kernels-community`
ALLOW_ALL_KERNELS = False

Expand Down
85 changes: 49 additions & 36 deletions src/transformers/models/qwen3_5/modeling_qwen3_5.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from ...activations import ACT2FN
from ...cache_utils import Cache, DynamicCache
from ...generation import GenerationMixin
from ...integrations import use_kernel_forward_from_hub
from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub_with_fallback
from ...integrations.accelerate import force_accelerate_hooks
from ...masking_utils import create_causal_mask, create_recurrent_attention_mask
from ...modeling_flash_attention_utils import FlashAttentionKwargs
Expand All @@ -58,18 +58,13 @@
maybe_autocast,
merge_with_config_defaults,
)
from ...utils.import_utils import is_causal_conv1d_available, is_flash_linear_attention_available
from ...utils.import_utils import is_flash_linear_attention_available
from ...utils.output_capturing import capture_outputs
from ...vision_utils import get_vision_bilinear_indices_and_weights, get_vision_cu_seqlens, get_vision_position_ids
from ..auto.modeling_auto import AutoModel
from .configuration_qwen3_5 import Qwen3_5Config, Qwen3_5TextConfig, Qwen3_5VisionConfig


if is_causal_conv1d_available():
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
else:
causal_conv1d_update, causal_conv1d_fn = None, None

if is_flash_linear_attention_available():
from fla.modules import FusedRMSNormGated
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
Expand Down Expand Up @@ -216,27 +211,50 @@ def apply_mask_to_padding_states(hidden_states, attention_mask):
return hidden_states


is_fast_path_available = all(
(causal_conv1d_fn, causal_conv1d_update, chunk_gated_delta_rule, fused_recurrent_gated_delta_rule)
)
is_fast_path_available = all((chunk_gated_delta_rule, fused_recurrent_gated_delta_rule))


def torch_causal_conv1d_update(
hidden_states,
conv_state,
weight,
bias=None,
activation=None,
@use_kernel_func_from_hub_with_fallback("causal_conv1d_update", "causal_conv1d")
def causal_conv1d_update(
hidden_states: torch.Tensor,
conv_state: torch.Tensor,
weight: nn.Parameter,
bias: nn.Parameter | None = None,
activation: str | None = None,
):
_, hidden_size, seq_len = hidden_states.shape
state_len = conv_state.shape[-1]

hidden_states_new = torch.cat([conv_state, hidden_states], dim=-1).to(weight.dtype)
conv_state.copy_(hidden_states_new[:, :, -state_len:])
out = F.conv1d(hidden_states_new, weight.unsqueeze(1), bias, padding=0, groups=hidden_size)
out = F.silu(out[:, :, -seq_len:])
out = out.to(hidden_states.dtype)
return out
out = out[:, :, -seq_len:]
if activation is not None:
out = ACT2FN[activation](out)
return out.to(hidden_states.dtype)


@use_kernel_func_from_hub_with_fallback("causal_conv1d_fn", "causal_conv1d")
def causal_conv1d_fn(
hidden_states: torch.Tensor,
weight: nn.Parameter,
bias: nn.Parameter | None = None,
activation: str | None = None,
**kwargs,
):
_, hidden_size, seq_len = hidden_states.shape
padding = weight.shape[-1] - 1

out = F.conv1d(
hidden_states.to(weight.dtype),
weight=weight.unsqueeze(1),
bias=bias,
padding=padding,
groups=hidden_size,
)[:, :, :seq_len]
if activation is not None:
out = ACT2FN[activation](out)
return out.to(hidden_states.dtype)


def l2norm(x: torch.FloatTensor, dim: int = -1, eps: float = 1e-6):
Expand Down Expand Up @@ -385,7 +403,6 @@ def __init__(self, config: Qwen3_5Config, layer_idx: int):
self.conv_kernel_size = config.linear_conv_kernel_dim
self.layer_idx = layer_idx
self.activation = config.hidden_act
self.act = ACT2FN[config.hidden_act]
self.layer_norm_epsilon = config.rms_norm_eps

# QKV
Expand Down Expand Up @@ -418,16 +435,13 @@ def __init__(self, config: Qwen3_5Config, layer_idx: int):

self.out_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)

self.causal_conv1d_fn = causal_conv1d_fn
self.causal_conv1d_update = causal_conv1d_update or torch_causal_conv1d_update
self.chunk_gated_delta_rule = chunk_gated_delta_rule or torch_chunk_gated_delta_rule
self.recurrent_gated_delta_rule = fused_recurrent_gated_delta_rule or torch_recurrent_gated_delta_rule

if not is_fast_path_available:
logger.warning_once(
"The fast path is not available because one of the required library is not installed. Falling back to "
"torch implementation. To install follow https://github.com/fla-org/flash-linear-attention#installation and"
" https://github.com/Dao-AILab/causal-conv1d"
"The fast path is not available because the required library is not installed. Falling back to "
"torch implementation. To install follow https://github.com/fla-org/flash-linear-attention#installation"
)

self.layer_type = config.layer_types[layer_idx]
Expand Down Expand Up @@ -472,7 +486,7 @@ def forward(

if use_precomputed_states and seq_len == 1:
# Single-token cached decode: the fused per-step kernel updates the conv state in-place.
mixed_qkv = self.causal_conv1d_update(
mixed_qkv = causal_conv1d_update(
mixed_qkv,
conv_state,
self.conv1d.weight.squeeze(1),
Expand All @@ -489,16 +503,15 @@ def forward(
if cache_params is not None:
new_conv_state = F.pad(mixed_qkv, (self.conv_kernel_size - mixed_qkv.shape[-1], 0))
cache_params.update_conv_state(new_conv_state, self.layer_idx)
if self.causal_conv1d_fn is not None:
mixed_qkv = self.causal_conv1d_fn(
x=mixed_qkv,
weight=self.conv1d.weight.squeeze(1),
bias=self.conv1d.bias,
activation=self.activation,
seq_idx=kwargs.get("seq_idx"),
)
else:
mixed_qkv = F.silu(self.conv1d(mixed_qkv)[:, :, : mixed_qkv.shape[-1]])

mixed_qkv = causal_conv1d_fn(
mixed_qkv,
self.conv1d.weight.squeeze(1),
self.conv1d.bias,
activation=self.activation,
seq_idx=kwargs.get("seq_idx"),
)

if use_precomputed_states:
mixed_qkv = mixed_qkv[:, :, -seq_len:]

Expand Down
23 changes: 12 additions & 11 deletions src/transformers/models/qwen3_5/modular_qwen3_5.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
Qwen3NextPreTrainedModel,
Qwen3NextRMSNorm,
apply_mask_to_padding_states,
causal_conv1d_fn,
causal_conv1d_update,
)
from ..qwen3_vl.configuration_qwen3_vl import Qwen3VLConfig, Qwen3VLVisionConfig
from ..qwen3_vl.modeling_qwen3_vl import (
Expand Down Expand Up @@ -251,7 +253,7 @@ def forward(

if use_precomputed_states and seq_len == 1:
# Single-token cached decode: the fused per-step kernel updates the conv state in-place.
mixed_qkv = self.causal_conv1d_update(
mixed_qkv = causal_conv1d_update(
mixed_qkv,
conv_state,
self.conv1d.weight.squeeze(1),
Expand All @@ -268,16 +270,15 @@ def forward(
if cache_params is not None:
new_conv_state = F.pad(mixed_qkv, (self.conv_kernel_size - mixed_qkv.shape[-1], 0))
cache_params.update_conv_state(new_conv_state, self.layer_idx)
if self.causal_conv1d_fn is not None:
mixed_qkv = self.causal_conv1d_fn(
x=mixed_qkv,
weight=self.conv1d.weight.squeeze(1),
bias=self.conv1d.bias,
activation=self.activation,
seq_idx=kwargs.get("seq_idx"),
)
else:
mixed_qkv = F.silu(self.conv1d(mixed_qkv)[:, :, : mixed_qkv.shape[-1]])

mixed_qkv = causal_conv1d_fn(
mixed_qkv,
self.conv1d.weight.squeeze(1),
self.conv1d.bias,
activation=self.activation,
seq_idx=kwargs.get("seq_idx"),
)

if use_precomputed_states:
mixed_qkv = mixed_qkv[:, :, -seq_len:]

Expand Down
Loading
Loading