Add fixed FLA folder for Quasar generation
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- README.md +2 -3
- fla/__init__.py +8 -0
- fla/__pycache__/__init__.cpython-312.pyc +0 -0
- fla/__pycache__/utils.cpython-312.pyc +0 -0
- fla/distributed_compat.py +57 -0
- fla/layers/__init__.py +3 -0
- fla/layers/__pycache__/__init__.cpython-312.pyc +0 -0
- fla/layers/__pycache__/attn.cpython-312.pyc +0 -0
- fla/layers/__pycache__/quasar.cpython-312.pyc +0 -0
- fla/layers/__pycache__/utils.cpython-312.pyc +0 -0
- fla/layers/abc.py +231 -0
- fla/layers/attn.py +176 -0
- fla/layers/based.py +93 -0
- fla/layers/bitattn.py +162 -0
- fla/layers/comba.py +328 -0
- fla/layers/delta_net.py +287 -0
- fla/layers/deltaformer.py +152 -0
- fla/layers/forgetting_attn.py +133 -0
- fla/layers/gated_deltanet.py +316 -0
- fla/layers/gated_deltaproduct.py +287 -0
- fla/layers/gla.py +304 -0
- fla/layers/gsa.py +237 -0
- fla/layers/hgrn.py +174 -0
- fla/layers/hgrn2.py +210 -0
- fla/layers/kda.py +277 -0
- fla/layers/lightnet.py +238 -0
- fla/layers/linear_attn.py +196 -0
- fla/layers/log_linear_mamba2.py +684 -0
- fla/layers/mamba.py +395 -0
- fla/layers/mamba2.py +649 -0
- fla/layers/mesa_net.py +221 -0
- fla/layers/mla.py +225 -0
- fla/layers/mom.py +831 -0
- fla/layers/multiscale_retention.py +303 -0
- fla/layers/nsa.py +137 -0
- fla/layers/path_attn.py +216 -0
- fla/layers/quasar.py +439 -0
- fla/layers/rebased.py +144 -0
- fla/layers/rodimus.py +397 -0
- fla/layers/rwkv6.py +360 -0
- fla/layers/rwkv7.py +347 -0
- fla/layers/simple_gla.py +273 -0
- fla/layers/utils.py +218 -0
- fla/models/__init__.py +3 -0
- fla/models/__pycache__/__init__.cpython-312.pyc +0 -0
- fla/models/__pycache__/utils.cpython-312.pyc +0 -0
- fla/models/abc/__init__.py +12 -0
- fla/models/abc/configuration_abc.py +105 -0
- fla/models/abc/modeling_abc.py +371 -0
- fla/models/bitnet/__init__.py +12 -0
README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
Repo-local Raven dependency and Quasar-Preview runner used with `silx-ai/Quasar-Preview` revision `48d9dc1d2dcdb2eeb087c7b79c89300d3e46a70b`.
|
| 4 |
|
| 5 |
-
This packages the Raven implementation from `goombalab/raven` under `raven/` plus a simple script that wires
|
| 6 |
|
| 7 |
## Run
|
| 8 |
|
|
@@ -20,8 +20,7 @@ The runner handles:
|
|
| 20 |
- restoring `experts_w3` tensors that Transformers may leave as meta tensors
|
| 21 |
- running direct `model.generate()`
|
| 22 |
|
| 23 |
-
The Raven/FLA generation-cache fix
|
| 24 |
-
`fla/ops/gsa/fused_recurrent.py`.
|
| 25 |
|
| 26 |
## Simpler script
|
| 27 |
|
|
|
|
| 2 |
|
| 3 |
Repo-local Raven dependency and Quasar-Preview runner used with `silx-ai/Quasar-Preview` revision `48d9dc1d2dcdb2eeb087c7b79c89300d3e46a70b`.
|
| 4 |
|
| 5 |
+
This packages the Raven implementation from `goombalab/raven` under `raven/`, the fixed Quasar FLA folder under `fla/`, plus a simple script that wires them for Quasar greedy generation.
|
| 6 |
|
| 7 |
## Run
|
| 8 |
|
|
|
|
| 20 |
- restoring `experts_w3` tensors that Transformers may leave as meta tensors
|
| 21 |
- running direct `model.generate()`
|
| 22 |
|
| 23 |
+
The Raven/FLA generation-cache fix is included here under `fla/ops/gsa/fused_recurrent.py` and has also been pushed to `silx-ai/Quasar-Preview`.
|
|
|
|
| 24 |
|
| 25 |
## Simpler script
|
| 26 |
|
fla/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Lightweight package initializer for the vendored FLA subset used by Quasar.
|
| 2 |
+
#
|
| 3 |
+
# The upstream file eagerly imports every layer and model, which is slow and can
|
| 4 |
+
# hang on fresh training containers while optional kernels are being resolved.
|
| 5 |
+
# Import concrete modules directly, e.g. `from fla.layers.quasar import ...`.
|
| 6 |
+
|
| 7 |
+
__version__ = "0.1.0"
|
| 8 |
+
__all__ = []
|
fla/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (203 Bytes). View file
|
|
|
fla/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (26.6 kB). View file
|
|
|
fla/distributed_compat.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
"""
|
| 3 |
+
Centralized compatibility module for torch.distributed imports.
|
| 4 |
+
All distributed-related imports should go through here to handle environments
|
| 5 |
+
where distributed tensor APIs are not available.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
# DeviceMesh
|
| 11 |
+
try:
|
| 12 |
+
from torch.distributed import DeviceMesh
|
| 13 |
+
except ImportError:
|
| 14 |
+
try:
|
| 15 |
+
from torch.distributed.device_mesh import DeviceMesh
|
| 16 |
+
except ImportError:
|
| 17 |
+
DeviceMesh = None
|
| 18 |
+
|
| 19 |
+
# DTensor
|
| 20 |
+
try:
|
| 21 |
+
from torch.distributed.tensor import DTensor
|
| 22 |
+
except (ImportError, AttributeError):
|
| 23 |
+
DTensor = None
|
| 24 |
+
|
| 25 |
+
# Replicate, Shard, distribute_module, Placement
|
| 26 |
+
try:
|
| 27 |
+
from torch.distributed.tensor import Placement, Replicate, Shard, distribute_module
|
| 28 |
+
except (ImportError, AttributeError):
|
| 29 |
+
Placement = Replicate = Shard = distribute_module = None
|
| 30 |
+
|
| 31 |
+
# ParallelStyle
|
| 32 |
+
try:
|
| 33 |
+
from torch.distributed.tensor.parallel import ParallelStyle
|
| 34 |
+
except (ImportError, AttributeError):
|
| 35 |
+
ParallelStyle = None
|
| 36 |
+
|
| 37 |
+
# Convenience flag
|
| 38 |
+
HAS_DISTRIBUTED = all([
|
| 39 |
+
DeviceMesh is not None,
|
| 40 |
+
DTensor is not None,
|
| 41 |
+
Placement is not None,
|
| 42 |
+
Replicate is not None,
|
| 43 |
+
Shard is not None,
|
| 44 |
+
distribute_module is not None,
|
| 45 |
+
ParallelStyle is not None,
|
| 46 |
+
])
|
| 47 |
+
|
| 48 |
+
__all__ = [
|
| 49 |
+
'DeviceMesh',
|
| 50 |
+
'DTensor',
|
| 51 |
+
'Placement',
|
| 52 |
+
'Replicate',
|
| 53 |
+
'Shard',
|
| 54 |
+
'distribute_module',
|
| 55 |
+
'ParallelStyle',
|
| 56 |
+
'HAS_DISTRIBUTED',
|
| 57 |
+
]
|
fla/layers/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep layer package imports lazy. Import specific layer modules directly.
|
| 2 |
+
|
| 3 |
+
__all__ = []
|
fla/layers/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (181 Bytes). View file
|
|
|
fla/layers/__pycache__/attn.cpython-312.pyc
ADDED
|
Binary file (7.97 kB). View file
|
|
|
fla/layers/__pycache__/quasar.cpython-312.pyc
ADDED
|
Binary file (20.1 kB). View file
|
|
|
fla/layers/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (10.2 kB). View file
|
|
|
fla/layers/abc.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, RotaryEmbedding, ShortConvolution
|
| 14 |
+
from fla.modules.activations import swiglu, swish
|
| 15 |
+
from fla.ops.abc.chunk import chunk_abc
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ABCAttention(nn.Module):
|
| 22 |
+
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
hidden_size: int = 1024,
|
| 26 |
+
expand_k: float = 0.5,
|
| 27 |
+
expand_v: float = 1.0,
|
| 28 |
+
num_heads: int = 4,
|
| 29 |
+
use_short_conv: bool = False,
|
| 30 |
+
conv_size: int = 4,
|
| 31 |
+
conv_bias: bool = False,
|
| 32 |
+
num_slots: int | None = None,
|
| 33 |
+
elementwise_affine: bool | None = True,
|
| 34 |
+
norm_eps: float = 1e-5,
|
| 35 |
+
gate_low_rank_dim: int = 16,
|
| 36 |
+
gate_logit_normalizer: int = 16,
|
| 37 |
+
use_rope: bool = True,
|
| 38 |
+
use_input_gate: bool = False,
|
| 39 |
+
use_output_gate: bool = True,
|
| 40 |
+
use_norm: bool = True,
|
| 41 |
+
clamp_min: float | None = -32,
|
| 42 |
+
clamp_max: float | None = 32,
|
| 43 |
+
layer_idx: int | None = None,
|
| 44 |
+
**kwargs,
|
| 45 |
+
) -> ABCAttention:
|
| 46 |
+
super().__init__()
|
| 47 |
+
|
| 48 |
+
self.hidden_size = hidden_size
|
| 49 |
+
self.expand_k = expand_k
|
| 50 |
+
self.expand_v = expand_v
|
| 51 |
+
self.num_heads = num_heads
|
| 52 |
+
self.key_dim = int(self.hidden_size * self.expand_k)
|
| 53 |
+
self.value_dim = int(self.hidden_size * self.expand_v)
|
| 54 |
+
self.head_k_dim = self.key_dim // self.num_heads
|
| 55 |
+
self.head_v_dim = self.value_dim // self.num_heads
|
| 56 |
+
|
| 57 |
+
self.use_short_conv = use_short_conv
|
| 58 |
+
self.conv_size = conv_size
|
| 59 |
+
self.conv_bias = conv_bias
|
| 60 |
+
|
| 61 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 62 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 63 |
+
|
| 64 |
+
self.use_rope = use_rope
|
| 65 |
+
self.use_input_gate = use_input_gate
|
| 66 |
+
self.use_output_gate = use_output_gate
|
| 67 |
+
self.use_norm = use_norm
|
| 68 |
+
|
| 69 |
+
if num_slots is None:
|
| 70 |
+
num_slots = self.head_k_dim
|
| 71 |
+
self.num_slots = num_slots
|
| 72 |
+
|
| 73 |
+
self.norm_eps = norm_eps
|
| 74 |
+
|
| 75 |
+
self.clamp_min = clamp_min
|
| 76 |
+
self.clamp_max = clamp_max
|
| 77 |
+
self.layer_idx = layer_idx
|
| 78 |
+
|
| 79 |
+
if layer_idx is None:
|
| 80 |
+
warnings.warn(
|
| 81 |
+
f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
|
| 82 |
+
"to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
|
| 83 |
+
"when creating this class.",
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 87 |
+
self.k_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 88 |
+
self.v_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 89 |
+
|
| 90 |
+
if use_output_gate:
|
| 91 |
+
self.g_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 92 |
+
self.s_proj = nn.Linear(self.hidden_size, self.num_heads * self.num_slots, bias=False)
|
| 93 |
+
self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
|
| 94 |
+
|
| 95 |
+
if use_short_conv:
|
| 96 |
+
self.conv_size = conv_size
|
| 97 |
+
self.q_conv1d = ShortConvolution(
|
| 98 |
+
hidden_size=self.key_dim,
|
| 99 |
+
kernel_size=conv_size,
|
| 100 |
+
bias=conv_bias,
|
| 101 |
+
activation='silu',
|
| 102 |
+
)
|
| 103 |
+
self.k_conv1d = ShortConvolution(
|
| 104 |
+
hidden_size=self.key_dim,
|
| 105 |
+
kernel_size=conv_size,
|
| 106 |
+
bias=conv_bias,
|
| 107 |
+
activation='silu',
|
| 108 |
+
)
|
| 109 |
+
self.v_conv1d = ShortConvolution(
|
| 110 |
+
hidden_size=self.value_dim,
|
| 111 |
+
kernel_size=conv_size,
|
| 112 |
+
bias=conv_bias,
|
| 113 |
+
activation='silu',
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
if self.use_norm:
|
| 117 |
+
if self.use_output_gate:
|
| 118 |
+
self.g_norm = FusedRMSNormGated(
|
| 119 |
+
hidden_size=self.head_v_dim,
|
| 120 |
+
elementwise_affine=elementwise_affine,
|
| 121 |
+
eps=norm_eps,
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
self.g_norm = RMSNorm(
|
| 125 |
+
hidden_size=self.head_v_dim,
|
| 126 |
+
elementwise_affine=elementwise_affine,
|
| 127 |
+
eps=norm_eps,
|
| 128 |
+
dtype=torch.float32,
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
if self.use_rope:
|
| 132 |
+
self.rotary = RotaryEmbedding(self.head_k_dim)
|
| 133 |
+
|
| 134 |
+
def forward(
|
| 135 |
+
self,
|
| 136 |
+
hidden_states: torch.Tensor,
|
| 137 |
+
attention_mask: torch.Tensor | None = None,
|
| 138 |
+
past_key_values: Cache | None = None,
|
| 139 |
+
use_cache: bool | None = False,
|
| 140 |
+
output_attentions: bool | None = False,
|
| 141 |
+
**kwargs,
|
| 142 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 143 |
+
if attention_mask is not None:
|
| 144 |
+
assert len(attention_mask.shape) == 2, (
|
| 145 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 146 |
+
"for padding purposes (0 indicating padding). "
|
| 147 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 151 |
+
|
| 152 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 153 |
+
if cu_seqlens is not None:
|
| 154 |
+
raise NotImplementedError("Training with cu_seqlens is not supported yet for ABCAttention")
|
| 155 |
+
if self.use_short_conv:
|
| 156 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 157 |
+
if last_state is not None:
|
| 158 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 159 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 160 |
+
q, conv_state_q = self.q_conv1d(
|
| 161 |
+
x=self.q_proj(hidden_states),
|
| 162 |
+
mask=conv_mask,
|
| 163 |
+
cache=conv_state_q,
|
| 164 |
+
output_final_state=use_cache,
|
| 165 |
+
cu_seqlens=cu_seqlens,
|
| 166 |
+
)
|
| 167 |
+
k, conv_state_k = self.k_conv1d(
|
| 168 |
+
x=self.k_proj(hidden_states),
|
| 169 |
+
mask=conv_mask,
|
| 170 |
+
cache=conv_state_k,
|
| 171 |
+
output_final_state=use_cache,
|
| 172 |
+
cu_seqlens=cu_seqlens,
|
| 173 |
+
)
|
| 174 |
+
v, conv_state_v = self.v_conv1d(
|
| 175 |
+
x=self.v_proj(hidden_states),
|
| 176 |
+
mask=conv_mask,
|
| 177 |
+
cache=conv_state_v,
|
| 178 |
+
output_final_state=use_cache,
|
| 179 |
+
cu_seqlens=cu_seqlens,
|
| 180 |
+
)
|
| 181 |
+
else:
|
| 182 |
+
q = self.q_proj(hidden_states)
|
| 183 |
+
k = self.k_proj(hidden_states)
|
| 184 |
+
v = self.v_proj(hidden_states)
|
| 185 |
+
|
| 186 |
+
if self.use_input_gate:
|
| 187 |
+
q, k, v = map(lambda x: swish(x), (q, k, v))
|
| 188 |
+
# dealing with left-padding
|
| 189 |
+
if attention_mask is not None:
|
| 190 |
+
v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
|
| 191 |
+
|
| 192 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 193 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 194 |
+
if self.use_rope:
|
| 195 |
+
seqlen_offset = 0
|
| 196 |
+
if past_key_values is not None:
|
| 197 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 198 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset)
|
| 199 |
+
|
| 200 |
+
s = rearrange(self.s_proj(hidden_states), '... (h m) -> ... h m', m=self.num_slots)
|
| 201 |
+
s = s.clamp_(self.clamp_min, self.clamp_max)
|
| 202 |
+
|
| 203 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 204 |
+
o, recurrent_state = chunk_abc(
|
| 205 |
+
q=q,
|
| 206 |
+
k=k,
|
| 207 |
+
v=v,
|
| 208 |
+
s=s,
|
| 209 |
+
initial_state=recurrent_state,
|
| 210 |
+
output_final_state=use_cache,
|
| 211 |
+
)
|
| 212 |
+
update_layer_cache(
|
| 213 |
+
self,
|
| 214 |
+
past_key_values,
|
| 215 |
+
recurrent_state=recurrent_state,
|
| 216 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 217 |
+
offset=q.shape[1],
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
if self.use_norm and not self.use_output_gate:
|
| 221 |
+
o = self.g_norm(o)
|
| 222 |
+
elif self.use_output_gate:
|
| 223 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 224 |
+
o = self.g_norm(o, g) if self.use_norm else swiglu(g, o)
|
| 225 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 226 |
+
o = self.o_proj(o)
|
| 227 |
+
|
| 228 |
+
return o, None, past_key_values
|
| 229 |
+
|
| 230 |
+
def state_size(self, seq_len: int = 2048):
|
| 231 |
+
return 2 * self.num_slots * self.hidden_size
|
fla/layers/attn.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 15 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 22 |
+
except ImportError:
|
| 23 |
+
warnings.warn(
|
| 24 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 25 |
+
category=ImportWarning,
|
| 26 |
+
)
|
| 27 |
+
flash_attn_func = None
|
| 28 |
+
|
| 29 |
+
logger = logging.get_logger(__name__)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class Attention(nn.Module):
|
| 33 |
+
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
hidden_size: int = 2048,
|
| 37 |
+
num_heads: int = 32,
|
| 38 |
+
num_kv_heads: int | None = None,
|
| 39 |
+
qkv_bias: bool = False,
|
| 40 |
+
qk_norm: bool = False,
|
| 41 |
+
window_size: int | None = None,
|
| 42 |
+
rope_theta: float | None = 10000.,
|
| 43 |
+
max_position_embeddings: int | None = None,
|
| 44 |
+
use_nope: bool = False,
|
| 45 |
+
layer_idx: int = None,
|
| 46 |
+
):
|
| 47 |
+
super().__init__()
|
| 48 |
+
|
| 49 |
+
self.hidden_size = hidden_size
|
| 50 |
+
self.num_heads = num_heads
|
| 51 |
+
if num_kv_heads is None:
|
| 52 |
+
self.num_kv_heads = self.num_heads
|
| 53 |
+
else:
|
| 54 |
+
self.num_kv_heads = num_kv_heads
|
| 55 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 56 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 57 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 58 |
+
self.qkv_bias = qkv_bias
|
| 59 |
+
self.qk_norm = qk_norm
|
| 60 |
+
|
| 61 |
+
self.window_size = window_size
|
| 62 |
+
self.rope_theta = rope_theta
|
| 63 |
+
self.max_position_embeddings = max_position_embeddings
|
| 64 |
+
self.use_nope = use_nope
|
| 65 |
+
self.layer_idx = layer_idx
|
| 66 |
+
|
| 67 |
+
if flash_attn_func is None:
|
| 68 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 69 |
+
|
| 70 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 71 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 72 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 73 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 74 |
+
|
| 75 |
+
if qk_norm:
|
| 76 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 77 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 78 |
+
|
| 79 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 80 |
+
|
| 81 |
+
def forward(
|
| 82 |
+
self,
|
| 83 |
+
hidden_states: torch.Tensor,
|
| 84 |
+
attention_mask: torch.LongTensor | None = None,
|
| 85 |
+
past_key_values: Cache | None = None,
|
| 86 |
+
output_attentions: bool = False,
|
| 87 |
+
use_cache: bool = False,
|
| 88 |
+
**kwargs,
|
| 89 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 90 |
+
if attention_mask is not None:
|
| 91 |
+
assert len(attention_mask.shape) == 2, (
|
| 92 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 93 |
+
"for padding purposes (0 indicating padding). "
|
| 94 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 98 |
+
|
| 99 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 100 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 101 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 102 |
+
|
| 103 |
+
if self.qk_norm:
|
| 104 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 105 |
+
|
| 106 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 107 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 108 |
+
|
| 109 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 110 |
+
if past_key_values is not None:
|
| 111 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 112 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 113 |
+
|
| 114 |
+
if attention_mask is not None:
|
| 115 |
+
# to deliminate the offsets of padding tokens
|
| 116 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 117 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 118 |
+
|
| 119 |
+
if self.max_position_embeddings is not None:
|
| 120 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 121 |
+
if not self.use_nope:
|
| 122 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 123 |
+
|
| 124 |
+
if past_key_values is not None:
|
| 125 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 126 |
+
k_cached, v_cached = past_key_values.update(
|
| 127 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 128 |
+
layer_idx=self.layer_idx,
|
| 129 |
+
offset=q_len,
|
| 130 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 131 |
+
)['attn_state']
|
| 132 |
+
if cache_has_content:
|
| 133 |
+
k, v = k_cached, v_cached
|
| 134 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 135 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 136 |
+
|
| 137 |
+
# Contains at least one padding token in the sequence
|
| 138 |
+
if attention_mask is not None:
|
| 139 |
+
if q.shape[1] == 1 and self.window_size is not None:
|
| 140 |
+
attention_mask = attention_mask[:, -self.window_size:]
|
| 141 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 142 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 143 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 144 |
+
o = flash_attn_varlen_func(
|
| 145 |
+
q, k, v,
|
| 146 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 147 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 148 |
+
max_seqlen_q=max_seqlen_q,
|
| 149 |
+
max_seqlen_k=max_seqlen_k,
|
| 150 |
+
causal=True,
|
| 151 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 152 |
+
)
|
| 153 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 154 |
+
elif cu_seqlens is not None:
|
| 155 |
+
o = flash_attn_varlen_func(
|
| 156 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 157 |
+
cu_seqlens_q=cu_seqlens,
|
| 158 |
+
cu_seqlens_k=cu_seqlens,
|
| 159 |
+
max_seqlen_q=max_seqlen,
|
| 160 |
+
max_seqlen_k=max_seqlen,
|
| 161 |
+
causal=True,
|
| 162 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 163 |
+
).unsqueeze(0)
|
| 164 |
+
else:
|
| 165 |
+
o = flash_attn_func(
|
| 166 |
+
q, k, v,
|
| 167 |
+
causal=True,
|
| 168 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 169 |
+
)
|
| 170 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 171 |
+
o = self.o_proj(o)
|
| 172 |
+
|
| 173 |
+
if not output_attentions:
|
| 174 |
+
attentions = None
|
| 175 |
+
|
| 176 |
+
return o, attentions, past_key_values
|
fla/layers/based.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Linear attention in Based.
|
| 5 |
+
https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
|
| 12 |
+
from fla.modules.feature_map import TaylorFeatureMap
|
| 13 |
+
from fla.ops.based import parallel_based
|
| 14 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class BasedLinearAttention(nn.Module):
|
| 18 |
+
|
| 19 |
+
def __init__(
|
| 20 |
+
self,
|
| 21 |
+
hidden_size: int,
|
| 22 |
+
feature_dim: int = 16,
|
| 23 |
+
num_key_value_heads: int = 12,
|
| 24 |
+
num_heads: int = 12,
|
| 25 |
+
feature_name: str = "taylor_exp",
|
| 26 |
+
eps: float = 1e-12,
|
| 27 |
+
causal: bool = True,
|
| 28 |
+
mode: str = "parallel",
|
| 29 |
+
):
|
| 30 |
+
super().__init__()
|
| 31 |
+
|
| 32 |
+
self.hidden_size = hidden_size
|
| 33 |
+
self.mode = mode
|
| 34 |
+
self.feature_name = feature_name
|
| 35 |
+
self.feature_dim = feature_dim
|
| 36 |
+
self.num_key_value_heads = num_key_value_heads
|
| 37 |
+
self.num_heads = num_heads
|
| 38 |
+
self.head_dim = self.hidden_size // self.num_key_value_heads
|
| 39 |
+
assert self.hidden_size % self.head_dim == 0
|
| 40 |
+
self.causal = causal
|
| 41 |
+
|
| 42 |
+
self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 43 |
+
self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 44 |
+
self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
|
| 45 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 46 |
+
self.dropout = nn.Identity()
|
| 47 |
+
self.feature_map = TaylorFeatureMap(feature_dim)
|
| 48 |
+
self.eps = eps
|
| 49 |
+
|
| 50 |
+
def forward(self, hidden_states: torch.Tensor, **kwargs):
|
| 51 |
+
mode = self.mode
|
| 52 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 53 |
+
q, k, v = map(lambda x: rearrange(x, "... (h d) -> ... h d", d=self.head_dim), [q, k, v])
|
| 54 |
+
if mode == "fused_chunk":
|
| 55 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 56 |
+
o, _ = fused_chunk_linear_attn(q, k, v, normalize=True, scale=1)
|
| 57 |
+
elif mode == 'chunk':
|
| 58 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 59 |
+
o, _ = chunk_linear_attn(q, k, v, normalize=True, scale=1)
|
| 60 |
+
elif mode == 'parallel':
|
| 61 |
+
assert q.shape[-1] <= 128
|
| 62 |
+
o = parallel_based(q, k, v, scale=1, use_norm=True)
|
| 63 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 64 |
+
o = self.o_proj(o)
|
| 65 |
+
o = self.dropout(o)
|
| 66 |
+
return o
|
| 67 |
+
|
| 68 |
+
def forward_reference(self, hidden_states: torch.Tensor, **kwargs):
|
| 69 |
+
"""
|
| 70 |
+
x (torch.Tensor): tensor of shape (b, d, t)
|
| 71 |
+
y (torch.Tensor): tensor of shape (b, d, t)
|
| 72 |
+
"""
|
| 73 |
+
# hidden_states = hidden_states.transpose(1, 2)
|
| 74 |
+
b, t, _ = hidden_states.size()
|
| 75 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 76 |
+
|
| 77 |
+
q = q.view(b, t, self.num_heads, self.feature_dim).transpose(1, 2)
|
| 78 |
+
k = k.view(b, t, self.num_key_value_heads, self.feature_dim).transpose(1, 2)
|
| 79 |
+
v = v.view(b, t, self.num_key_value_heads, self.head_dim).transpose(1, 2)
|
| 80 |
+
|
| 81 |
+
# Linear attention
|
| 82 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 83 |
+
q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
|
| 84 |
+
|
| 85 |
+
# Compute attention
|
| 86 |
+
if self.causal:
|
| 87 |
+
y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
|
| 88 |
+
else:
|
| 89 |
+
y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
|
| 90 |
+
y = rearrange(y, 'b h t d -> b t (h d)')
|
| 91 |
+
y = self.o_proj(y.to(hidden_states.dtype))
|
| 92 |
+
y = self.dropout(y)
|
| 93 |
+
return y.to(hidden_states.dtype)
|
fla/layers/bitattn.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RotaryEmbedding
|
| 15 |
+
from fla.modules.fused_bitlinear import FusedBitLinear
|
| 16 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from fla.models.utils import Cache
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 23 |
+
except ImportError:
|
| 24 |
+
warnings.warn(
|
| 25 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 26 |
+
category=ImportWarning,
|
| 27 |
+
)
|
| 28 |
+
flash_attn_func = None
|
| 29 |
+
|
| 30 |
+
logger = logging.get_logger(__name__)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class BitAttention(nn.Module):
|
| 34 |
+
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
hidden_size: int = 2048,
|
| 38 |
+
num_heads: int = 32,
|
| 39 |
+
num_kv_heads: int | None = None,
|
| 40 |
+
window_size: int | None = None,
|
| 41 |
+
rope_theta: float | None = 10000.,
|
| 42 |
+
max_position_embeddings: int | None = None,
|
| 43 |
+
norm_eps: float = 1e-5,
|
| 44 |
+
layer_idx: int = None,
|
| 45 |
+
):
|
| 46 |
+
super().__init__()
|
| 47 |
+
|
| 48 |
+
self.num_heads = num_heads
|
| 49 |
+
if num_kv_heads is None:
|
| 50 |
+
self.num_kv_heads = self.num_heads
|
| 51 |
+
else:
|
| 52 |
+
self.num_kv_heads = num_kv_heads
|
| 53 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 54 |
+
self.hidden_size = hidden_size
|
| 55 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 56 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 57 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 58 |
+
self.window_size = window_size
|
| 59 |
+
self.rope_theta = rope_theta
|
| 60 |
+
self.max_position_embeddings = max_position_embeddings
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
self.q_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
|
| 64 |
+
self.k_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
|
| 65 |
+
self.v_proj = FusedBitLinear(self.hidden_size, self.kv_dim, bias=False)
|
| 66 |
+
self.o_proj = FusedBitLinear(self.hidden_size, self.hidden_size, bias=False)
|
| 67 |
+
|
| 68 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 69 |
+
|
| 70 |
+
def forward(
|
| 71 |
+
self,
|
| 72 |
+
hidden_states: torch.Tensor,
|
| 73 |
+
attention_mask: torch.LongTensor | None = None,
|
| 74 |
+
past_key_values: Cache | None = None,
|
| 75 |
+
output_attentions: bool = False,
|
| 76 |
+
use_cache: bool = False,
|
| 77 |
+
**kwargs,
|
| 78 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 79 |
+
if attention_mask is not None:
|
| 80 |
+
assert len(attention_mask.shape) == 2, (
|
| 81 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 82 |
+
"for padding purposes (0 indicating padding). "
|
| 83 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 87 |
+
|
| 88 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 89 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 90 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 91 |
+
|
| 92 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 93 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 94 |
+
|
| 95 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 96 |
+
if past_key_values is not None:
|
| 97 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 98 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 99 |
+
|
| 100 |
+
if attention_mask is not None:
|
| 101 |
+
# to deliminate the offsets of padding tokens
|
| 102 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 103 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 104 |
+
|
| 105 |
+
if self.max_position_embeddings is not None:
|
| 106 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 107 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 108 |
+
|
| 109 |
+
if past_key_values is not None:
|
| 110 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 111 |
+
k_cached, v_cached = past_key_values.update(
|
| 112 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 113 |
+
layer_idx=self.layer_idx,
|
| 114 |
+
offset=q_len,
|
| 115 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 116 |
+
)['attn_state']
|
| 117 |
+
if cache_has_content:
|
| 118 |
+
k, v = k_cached, v_cached
|
| 119 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 120 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 121 |
+
|
| 122 |
+
if flash_attn_func is None:
|
| 123 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 124 |
+
|
| 125 |
+
# Contains at least one padding token in the sequence
|
| 126 |
+
if attention_mask is not None:
|
| 127 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 128 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 129 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 130 |
+
o = flash_attn_varlen_func(
|
| 131 |
+
q, k, v,
|
| 132 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 133 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 134 |
+
max_seqlen_q=max_seqlen_q,
|
| 135 |
+
max_seqlen_k=max_seqlen_k,
|
| 136 |
+
causal=True,
|
| 137 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 138 |
+
)
|
| 139 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 140 |
+
elif cu_seqlens is not None:
|
| 141 |
+
o = flash_attn_varlen_func(
|
| 142 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 143 |
+
cu_seqlens_q=cu_seqlens,
|
| 144 |
+
cu_seqlens_k=cu_seqlens,
|
| 145 |
+
max_seqlen_q=max_seqlen,
|
| 146 |
+
max_seqlen_k=max_seqlen,
|
| 147 |
+
causal=True,
|
| 148 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 149 |
+
).unsqueeze(0)
|
| 150 |
+
else:
|
| 151 |
+
o = flash_attn_func(
|
| 152 |
+
q, k, v,
|
| 153 |
+
causal=True,
|
| 154 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 155 |
+
)
|
| 156 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 157 |
+
o = self.o_proj(o)
|
| 158 |
+
|
| 159 |
+
if not output_attentions:
|
| 160 |
+
attentions = None
|
| 161 |
+
|
| 162 |
+
return o, attentions, past_key_values
|
fla/layers/comba.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.comba import chunk_comba, fused_recurrent_comba
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class Comba(nn.Module):
|
| 25 |
+
"""
|
| 26 |
+
The layer implementaion for [Comba: Improving Bilinear RNNs with Closed-loop Control](https://arxiv.org/abs/2506.02475).
|
| 27 |
+
|
| 28 |
+
Similar to Mamba2 and Gated-DeltaNet, each layer contains around 6*hidden_size*hidden_size parameters.
|
| 29 |
+
|
| 30 |
+
Parameter alloation when use_output_gate=True:
|
| 31 |
+
- 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 32 |
+
- 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
|
| 33 |
+
- Others are ignorably small.
|
| 34 |
+
- In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
|
| 35 |
+
NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
|
| 36 |
+
|
| 37 |
+
Parameter allocation when use_output_gate=False:
|
| 38 |
+
- 1 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 39 |
+
- 2 * hidden_size * hidden_size for the v_proj and o_proj each
|
| 40 |
+
- Others are ignorably small.
|
| 41 |
+
- In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
|
| 42 |
+
|
| 43 |
+
Args:
|
| 44 |
+
hidden_size (int, Optional):
|
| 45 |
+
The hidden size of the input. Default: 2048.
|
| 46 |
+
expand_v (float, Optional):
|
| 47 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 48 |
+
head_dim (int, Optional):
|
| 49 |
+
The dimension of each head. Default: 256.
|
| 50 |
+
num_heads (int, Optional):
|
| 51 |
+
The number of heads. Default: 4.
|
| 52 |
+
num_v_heads (int, Optional):
|
| 53 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 54 |
+
GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 55 |
+
mode (str, Optional):
|
| 56 |
+
Which Gated DeltaNet kernel to use.
|
| 57 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 58 |
+
Default: `chunk`.
|
| 59 |
+
use_beta (bool, Optional):
|
| 60 |
+
Whether to use beta. Default: `True`.
|
| 61 |
+
use_output_gate (bool, Optional):
|
| 62 |
+
Whether to use output gate. Default: `True`.
|
| 63 |
+
use_output_correction (bool, Optional):
|
| 64 |
+
Whether to use <q-dk>. Default: `True`.
|
| 65 |
+
use_short_conv (bool, Optional):
|
| 66 |
+
Whether to use short convolutions. Default: `True`.
|
| 67 |
+
conv_size (int, Optional):
|
| 68 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 69 |
+
conv_bias (bool, Optional):
|
| 70 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 71 |
+
layer_idx (int, Optional):
|
| 72 |
+
The index of the layer. Default: None.
|
| 73 |
+
norm_eps (float, Optional):
|
| 74 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
def __init__(
|
| 78 |
+
self,
|
| 79 |
+
hidden_size: int = 2048,
|
| 80 |
+
expand_v: float = 2,
|
| 81 |
+
head_dim: int = 256,
|
| 82 |
+
num_heads: int = 6,
|
| 83 |
+
num_v_heads: int = None,
|
| 84 |
+
mode: str = 'chunk',
|
| 85 |
+
use_short_conv: bool = True,
|
| 86 |
+
use_output_gate: bool = True,
|
| 87 |
+
use_output_correction: bool = True,
|
| 88 |
+
use_inner_decay: bool = True,
|
| 89 |
+
correction_factor: float = 1.,
|
| 90 |
+
conv_size: int = 4,
|
| 91 |
+
conv_bias: bool = False,
|
| 92 |
+
layer_idx: int = None,
|
| 93 |
+
norm_eps: float = 1e-5,
|
| 94 |
+
**kwargs,
|
| 95 |
+
) -> Comba:
|
| 96 |
+
super().__init__()
|
| 97 |
+
|
| 98 |
+
self.mode = mode
|
| 99 |
+
|
| 100 |
+
self.hidden_size = hidden_size
|
| 101 |
+
self.expand_v = expand_v
|
| 102 |
+
|
| 103 |
+
self.use_short_conv = use_short_conv
|
| 104 |
+
self.use_output_gate = use_output_gate
|
| 105 |
+
self.use_output_correction = use_output_correction
|
| 106 |
+
self.use_inner_decay = use_inner_decay
|
| 107 |
+
self.conv_size = conv_size
|
| 108 |
+
self.conv_bias = conv_bias
|
| 109 |
+
|
| 110 |
+
self.head_dim = head_dim
|
| 111 |
+
self.num_heads = num_heads
|
| 112 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 113 |
+
|
| 114 |
+
self.head_k_dim = head_dim
|
| 115 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 116 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 117 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 118 |
+
self.layer_idx = layer_idx
|
| 119 |
+
|
| 120 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 121 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 122 |
+
raise ValueError(
|
| 123 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 124 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 125 |
+
)
|
| 126 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 127 |
+
raise ValueError(
|
| 128 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 132 |
+
raise ValueError(
|
| 133 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 134 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 135 |
+
)
|
| 136 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 137 |
+
|
| 138 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 139 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 140 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 141 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 142 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 143 |
+
|
| 144 |
+
if use_inner_decay:
|
| 145 |
+
self.decay = nn.Parameter(torch.ones(self.num_heads))
|
| 146 |
+
|
| 147 |
+
if use_output_correction:
|
| 148 |
+
warnings.warn(
|
| 149 |
+
"The correction_factor is set to 1 by default similar to Mamba2. "
|
| 150 |
+
"However, we find that sometimes correction_factor = 0.02 works better for small-scale models. "
|
| 151 |
+
"In practice, we recommend trying both settings. ",
|
| 152 |
+
)
|
| 153 |
+
self.D = nn.Parameter(torch.ones(self.num_heads) * correction_factor)
|
| 154 |
+
self.D._no_weight_decay = True
|
| 155 |
+
|
| 156 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 157 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 158 |
+
self.A_log._no_weight_decay = True
|
| 159 |
+
# hard coded for now
|
| 160 |
+
dt_min = 0.001
|
| 161 |
+
dt_max = 0.1
|
| 162 |
+
dt_init_floor = 1e-4
|
| 163 |
+
dt = torch.exp(
|
| 164 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 165 |
+
+ math.log(dt_min),
|
| 166 |
+
)
|
| 167 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 168 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 169 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 170 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 171 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 172 |
+
# name.endswith("bias") in param_grouping.py
|
| 173 |
+
self.dt_bias._no_weight_decay = True
|
| 174 |
+
|
| 175 |
+
if use_short_conv:
|
| 176 |
+
self.conv_size = conv_size
|
| 177 |
+
self.q_conv1d = ShortConvolution(
|
| 178 |
+
hidden_size=self.key_dim,
|
| 179 |
+
kernel_size=conv_size,
|
| 180 |
+
bias=conv_bias,
|
| 181 |
+
activation='silu',
|
| 182 |
+
)
|
| 183 |
+
self.k_conv1d = ShortConvolution(
|
| 184 |
+
hidden_size=self.key_dim,
|
| 185 |
+
kernel_size=conv_size,
|
| 186 |
+
bias=conv_bias,
|
| 187 |
+
activation='silu',
|
| 188 |
+
)
|
| 189 |
+
self.v_conv1d = ShortConvolution(
|
| 190 |
+
hidden_size=self.value_dim,
|
| 191 |
+
kernel_size=conv_size,
|
| 192 |
+
bias=conv_bias,
|
| 193 |
+
activation='silu',
|
| 194 |
+
)
|
| 195 |
+
else:
|
| 196 |
+
warnings.warn(
|
| 197 |
+
"ShortConvolution is crucial to the performance. "
|
| 198 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 199 |
+
)
|
| 200 |
+
if use_output_gate:
|
| 201 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 202 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, activation='sigmoid', eps=norm_eps)
|
| 203 |
+
else:
|
| 204 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 205 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 206 |
+
|
| 207 |
+
def forward(
|
| 208 |
+
self,
|
| 209 |
+
hidden_states: torch.Tensor,
|
| 210 |
+
attention_mask: torch.Tensor | None = None,
|
| 211 |
+
past_key_values: Cache | None = None,
|
| 212 |
+
use_cache: bool | None = False,
|
| 213 |
+
output_attentions: bool | None = False,
|
| 214 |
+
**kwargs: Unpack[dict],
|
| 215 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 216 |
+
if attention_mask is not None:
|
| 217 |
+
assert len(attention_mask.shape) == 2, (
|
| 218 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 219 |
+
"for padding purposes (0 indicating padding). "
|
| 220 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 224 |
+
# change to inference mode.
|
| 225 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 226 |
+
if self.training:
|
| 227 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 228 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 229 |
+
|
| 230 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 231 |
+
if attention_mask is not None:
|
| 232 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 233 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 234 |
+
|
| 235 |
+
if self.use_short_conv:
|
| 236 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 237 |
+
if last_state is not None:
|
| 238 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 239 |
+
q, conv_state_q = self.q_conv1d(
|
| 240 |
+
x=self.q_proj(hidden_states),
|
| 241 |
+
cache=conv_state_q,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
cu_seqlens=cu_seqlens,
|
| 244 |
+
)
|
| 245 |
+
k, conv_state_k = self.k_conv1d(
|
| 246 |
+
x=self.k_proj(hidden_states),
|
| 247 |
+
cache=conv_state_k,
|
| 248 |
+
output_final_state=use_cache,
|
| 249 |
+
cu_seqlens=cu_seqlens,
|
| 250 |
+
)
|
| 251 |
+
v, conv_state_v = self.v_conv1d(
|
| 252 |
+
x=self.v_proj(hidden_states),
|
| 253 |
+
cache=conv_state_v,
|
| 254 |
+
output_final_state=use_cache,
|
| 255 |
+
cu_seqlens=cu_seqlens,
|
| 256 |
+
)
|
| 257 |
+
else:
|
| 258 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 259 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 260 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 261 |
+
|
| 262 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 263 |
+
|
| 264 |
+
if self.use_inner_decay:
|
| 265 |
+
p = k * self.decay[None, None, :, None].sigmoid()
|
| 266 |
+
else:
|
| 267 |
+
p = k
|
| 268 |
+
|
| 269 |
+
if self.use_output_correction:
|
| 270 |
+
q = q - self.D[None, None, :, None] * p
|
| 271 |
+
|
| 272 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 273 |
+
|
| 274 |
+
if self.num_v_heads > self.num_heads:
|
| 275 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 276 |
+
|
| 277 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 278 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 279 |
+
|
| 280 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 281 |
+
if mode == 'chunk':
|
| 282 |
+
o, recurrent_state = chunk_comba(
|
| 283 |
+
q=q,
|
| 284 |
+
k=k,
|
| 285 |
+
v=v,
|
| 286 |
+
p=p,
|
| 287 |
+
g=g,
|
| 288 |
+
beta=beta,
|
| 289 |
+
initial_state=recurrent_state,
|
| 290 |
+
output_final_state=use_cache,
|
| 291 |
+
cu_seqlens=cu_seqlens,
|
| 292 |
+
use_qk_l2norm_in_kernel=True,
|
| 293 |
+
)
|
| 294 |
+
elif mode == 'fused_recurrent':
|
| 295 |
+
o, recurrent_state = fused_recurrent_comba(
|
| 296 |
+
q=q,
|
| 297 |
+
k=k,
|
| 298 |
+
v=v,
|
| 299 |
+
p=p,
|
| 300 |
+
g=g,
|
| 301 |
+
beta=beta,
|
| 302 |
+
initial_state=recurrent_state,
|
| 303 |
+
output_final_state=use_cache,
|
| 304 |
+
cu_seqlens=cu_seqlens,
|
| 305 |
+
use_qk_l2norm_in_kernel=True,
|
| 306 |
+
)
|
| 307 |
+
else:
|
| 308 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 309 |
+
|
| 310 |
+
update_layer_cache(
|
| 311 |
+
self,
|
| 312 |
+
past_key_values,
|
| 313 |
+
recurrent_state=recurrent_state,
|
| 314 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 315 |
+
offset=q_len,
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
if self.use_output_gate:
|
| 319 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 320 |
+
o = self.o_norm(o, g)
|
| 321 |
+
else:
|
| 322 |
+
o = self.o_norm(o)
|
| 323 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 324 |
+
o = self.o_proj(o)
|
| 325 |
+
if attention_mask is not None:
|
| 326 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 327 |
+
|
| 328 |
+
return o, None, past_key_values
|
fla/layers/delta_net.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 15 |
+
from fla.ops.delta_rule import chunk_delta_rule, fused_recurrent_delta_rule
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from transformers.processing_utils import Unpack
|
| 19 |
+
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def elu_p1(x):
|
| 24 |
+
return (F.elu(x, 1., False) + 1.).to(x)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def sum_norm(x):
|
| 28 |
+
return (x / x.sum(-1, keepdim=True)).to(x)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class DeltaNet(nn.Module):
|
| 32 |
+
r"""
|
| 33 |
+
The layer implementaion for [Parallelizing Linear Transformers with the Delta Rule over Sequence Length](https://arxiv.org/abs/2406.06484). # noqa:
|
| 34 |
+
DeltaNet was originally proposed in [Linear Transformers Are Secretly Fast Weight Programmers](https://arxiv.org/abs/2102.11174). # noqa
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
mode (str, Optional):
|
| 38 |
+
Which DeltaNet kernel to use.
|
| 39 |
+
Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
|
| 40 |
+
Default: `chunk`.
|
| 41 |
+
hidden_size (int, Optional):
|
| 42 |
+
The hidden size of the input. Default: 1024.
|
| 43 |
+
expand_k (float, Optional):
|
| 44 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 45 |
+
expand_v (float, Optional):
|
| 46 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 47 |
+
num_heads (int, Optional):
|
| 48 |
+
The number of heads. Default: 4.
|
| 49 |
+
use_beta (bool, Optional):
|
| 50 |
+
Whether to use beta. Default: `True`.
|
| 51 |
+
use_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `False`.
|
| 53 |
+
use_short_conv (bool, Optional):
|
| 54 |
+
Whether to use short convolutions. Default: `True`.
|
| 55 |
+
conv_size (int, Optional):
|
| 56 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 57 |
+
conv_bias (bool, Optional):
|
| 58 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 59 |
+
allow_neg_eigval (bool, Optional):
|
| 60 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 61 |
+
See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 62 |
+
layer_idx (int, Optional):
|
| 63 |
+
The index of the layer. Default: None.
|
| 64 |
+
norm_eps (float, Optional):
|
| 65 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 66 |
+
qk_activation (str, Optional):
|
| 67 |
+
The activation function for the query and key. Default: `silu`.
|
| 68 |
+
qk_norm (str, Optional):
|
| 69 |
+
The normalization method for the query and key. Default: `l2`.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
mode: str = 'chunk',
|
| 75 |
+
d_model: int = None,
|
| 76 |
+
hidden_size: int = 1024,
|
| 77 |
+
expand_k: float = 1.0,
|
| 78 |
+
expand_v: float = 1.0,
|
| 79 |
+
num_heads: int = 4,
|
| 80 |
+
use_beta: bool = True,
|
| 81 |
+
use_gate: bool = False,
|
| 82 |
+
use_short_conv: bool = True,
|
| 83 |
+
conv_size: int = 4,
|
| 84 |
+
conv_bias: bool = False,
|
| 85 |
+
allow_neg_eigval: bool = False,
|
| 86 |
+
layer_idx: int = None,
|
| 87 |
+
qk_activation: str = 'silu',
|
| 88 |
+
qk_norm: str = 'l2',
|
| 89 |
+
norm_eps: float = 1e-5,
|
| 90 |
+
**kwargs,
|
| 91 |
+
) -> DeltaNet:
|
| 92 |
+
super().__init__()
|
| 93 |
+
|
| 94 |
+
self.mode = mode
|
| 95 |
+
self.qk_activation = qk_activation
|
| 96 |
+
self.qk_norm = qk_norm
|
| 97 |
+
|
| 98 |
+
assert self.qk_activation in ['silu', 'relu', 'elu', 'identity']
|
| 99 |
+
assert self.qk_norm in ['l2', 'sum']
|
| 100 |
+
|
| 101 |
+
if d_model is not None:
|
| 102 |
+
hidden_size = d_model
|
| 103 |
+
self.hidden_size = hidden_size
|
| 104 |
+
self.expand_k = expand_k
|
| 105 |
+
self.expand_v = expand_v
|
| 106 |
+
self.num_heads = num_heads
|
| 107 |
+
self.use_gate = use_gate
|
| 108 |
+
self.use_short_conv = use_short_conv
|
| 109 |
+
self.conv_size = conv_size
|
| 110 |
+
self.conv_bias = conv_bias
|
| 111 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 112 |
+
|
| 113 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 114 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 115 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 116 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 117 |
+
self.layer_idx = layer_idx
|
| 118 |
+
|
| 119 |
+
if mode == 'fused_chunk':
|
| 120 |
+
raise NotImplementedError("fused_chunk_delta_rule is now deprecated. Please use `chunk_delta_rule` instead.")
|
| 121 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 122 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 123 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 124 |
+
|
| 125 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 126 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 127 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 128 |
+
|
| 129 |
+
self.use_beta = use_beta
|
| 130 |
+
if self.use_beta:
|
| 131 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 132 |
+
if use_short_conv:
|
| 133 |
+
self.conv_size = conv_size
|
| 134 |
+
self.q_conv1d = ShortConvolution(
|
| 135 |
+
hidden_size=self.key_dim,
|
| 136 |
+
kernel_size=conv_size,
|
| 137 |
+
bias=conv_bias,
|
| 138 |
+
activation='silu' if qk_activation == 'silu' else None,
|
| 139 |
+
)
|
| 140 |
+
self.k_conv1d = ShortConvolution(
|
| 141 |
+
hidden_size=self.key_dim,
|
| 142 |
+
kernel_size=conv_size,
|
| 143 |
+
bias=conv_bias,
|
| 144 |
+
activation='silu' if qk_activation == 'silu' else None,
|
| 145 |
+
)
|
| 146 |
+
self.v_conv1d = ShortConvolution(
|
| 147 |
+
hidden_size=self.value_dim,
|
| 148 |
+
kernel_size=conv_size,
|
| 149 |
+
bias=conv_bias,
|
| 150 |
+
activation='silu',
|
| 151 |
+
)
|
| 152 |
+
else:
|
| 153 |
+
warnings.warn(
|
| 154 |
+
"ShortConvolution is crucial to the performance. "
|
| 155 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 156 |
+
)
|
| 157 |
+
if use_gate:
|
| 158 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 159 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 160 |
+
else:
|
| 161 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 162 |
+
|
| 163 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 164 |
+
|
| 165 |
+
def forward(
|
| 166 |
+
self,
|
| 167 |
+
hidden_states: torch.Tensor,
|
| 168 |
+
attention_mask: torch.Tensor | None = None,
|
| 169 |
+
past_key_values: Cache | None = None,
|
| 170 |
+
use_cache: bool | None = False,
|
| 171 |
+
output_attentions: bool | None = False,
|
| 172 |
+
**kwargs: Unpack[dict],
|
| 173 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 174 |
+
if attention_mask is not None:
|
| 175 |
+
assert len(attention_mask.shape) == 2, (
|
| 176 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 177 |
+
"for padding purposes (0 indicating padding). "
|
| 178 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 182 |
+
# change to inference mode.
|
| 183 |
+
mode = 'fused_recurrent' if q_len <= 64 else self.mode
|
| 184 |
+
|
| 185 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 186 |
+
|
| 187 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 190 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 191 |
+
|
| 192 |
+
if self.use_short_conv:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 194 |
+
if last_state is not None:
|
| 195 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 196 |
+
q, conv_state_q = self.q_conv1d(
|
| 197 |
+
x=self.q_proj(hidden_states),
|
| 198 |
+
cache=conv_state_q,
|
| 199 |
+
output_final_state=use_cache,
|
| 200 |
+
cu_seqlens=cu_seqlens,
|
| 201 |
+
)
|
| 202 |
+
k, conv_state_k = self.k_conv1d(
|
| 203 |
+
x=self.k_proj(hidden_states),
|
| 204 |
+
cache=conv_state_k,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
cu_seqlens=cu_seqlens,
|
| 207 |
+
)
|
| 208 |
+
v, conv_state_v = self.v_conv1d(
|
| 209 |
+
x=self.v_proj(hidden_states),
|
| 210 |
+
cache=conv_state_v,
|
| 211 |
+
output_final_state=use_cache,
|
| 212 |
+
cu_seqlens=cu_seqlens,
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
q = self.q_proj(hidden_states)
|
| 216 |
+
k = self.k_proj(hidden_states)
|
| 217 |
+
if self.qk_activation == 'silu':
|
| 218 |
+
q, k = F.silu(q), F.silu(k)
|
| 219 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 220 |
+
|
| 221 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 222 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 223 |
+
if self.qk_activation != 'silu':
|
| 224 |
+
if self.qk_activation == 'relu':
|
| 225 |
+
q, k = q.relu(), k.relu()
|
| 226 |
+
elif self.qk_activation == 'elu':
|
| 227 |
+
q, k = elu_p1(q), elu_p1(k)
|
| 228 |
+
elif self.qk_activation != 'identity':
|
| 229 |
+
raise NotImplementedError
|
| 230 |
+
|
| 231 |
+
if self.qk_norm == 'sum':
|
| 232 |
+
q = sum_norm(q).to(q)
|
| 233 |
+
k = sum_norm(k).to(k)
|
| 234 |
+
|
| 235 |
+
if self.use_beta:
|
| 236 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 237 |
+
else:
|
| 238 |
+
beta = torch.ones_like(q[..., 0])
|
| 239 |
+
|
| 240 |
+
if self.allow_neg_eigval:
|
| 241 |
+
beta = beta * 2.
|
| 242 |
+
|
| 243 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 244 |
+
if mode == 'fused_recurrent':
|
| 245 |
+
o, recurrent_state = fused_recurrent_delta_rule(
|
| 246 |
+
q=q,
|
| 247 |
+
k=k,
|
| 248 |
+
v=v,
|
| 249 |
+
beta=beta,
|
| 250 |
+
initial_state=recurrent_state,
|
| 251 |
+
output_final_state=use_cache,
|
| 252 |
+
cu_seqlens=cu_seqlens,
|
| 253 |
+
use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
|
| 254 |
+
)
|
| 255 |
+
elif mode == 'chunk':
|
| 256 |
+
o, recurrent_state = chunk_delta_rule(
|
| 257 |
+
q=q,
|
| 258 |
+
k=k,
|
| 259 |
+
v=v,
|
| 260 |
+
beta=beta,
|
| 261 |
+
initial_state=recurrent_state,
|
| 262 |
+
output_final_state=use_cache,
|
| 263 |
+
cu_seqlens=cu_seqlens,
|
| 264 |
+
use_qk_l2norm_in_kernel=(self.qk_norm == 'l2'),
|
| 265 |
+
)
|
| 266 |
+
else:
|
| 267 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 268 |
+
|
| 269 |
+
update_layer_cache(
|
| 270 |
+
self,
|
| 271 |
+
past_key_values,
|
| 272 |
+
recurrent_state=recurrent_state,
|
| 273 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 274 |
+
offset=q_len,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
if self.use_gate:
|
| 278 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 279 |
+
o = self.o_norm(o, g)
|
| 280 |
+
else:
|
| 281 |
+
o = self.o_norm(o)
|
| 282 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 283 |
+
o = self.o_proj(o)
|
| 284 |
+
if attention_mask is not None:
|
| 285 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 286 |
+
|
| 287 |
+
return o, None, past_key_values
|
fla/layers/deltaformer.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 13 |
+
from fla.ops.deltaformer import deltaformer_attn
|
| 14 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
from fla.models.utils import Cache
|
| 18 |
+
|
| 19 |
+
logger = logging.get_logger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class DeltaFormerAttention(nn.Module):
|
| 23 |
+
|
| 24 |
+
r"""
|
| 25 |
+
The layer implementation for DeltaFormer,
|
| 26 |
+
[Understanding Transformer from the Perspective of Associative Memory]
|
| 27 |
+
(https://arxiv.org/pdf/2505.19488).
|
| 28 |
+
|
| 29 |
+
Notes
|
| 30 |
+
- DeltaFormer attention is implemented with Triton kernels in `fla.ops.deltaformer` and is tuned
|
| 31 |
+
for typical head dimensions (e.g., 64/128). It currently supports fixed-length inputs.
|
| 32 |
+
- For variable-length inputs (padding masks), the deltaformer computation falls back to using the
|
| 33 |
+
fixed-length path, while the second stage (softmax attention over U) uses FlashAttention's
|
| 34 |
+
varlen path when an attention mask is provided.
|
| 35 |
+
- K/V grouping (GQA) is supported natively by FlashAttention via `num_kv_heads`.
|
| 36 |
+
- Uses K-K similarity in deltaformer computation instead of Q-K similarity for better performance.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
hidden_size (int, Optional):
|
| 40 |
+
The hidden size of the input. Default: 2048.
|
| 41 |
+
num_heads (int, Optional):
|
| 42 |
+
The number of attention heads. Default: 32.
|
| 43 |
+
num_kv_heads (int, Optional):
|
| 44 |
+
The number of key/value heads for grouped-query attention. If None, equals `num_heads`.
|
| 45 |
+
Default: None.
|
| 46 |
+
qkv_bias (bool, Optional):
|
| 47 |
+
Whether to use bias for Q/K/V projections. Default: False.
|
| 48 |
+
qk_norm (bool, Optional):
|
| 49 |
+
Whether to apply per-head RMSNorm to Q and K before attention. Default: False.
|
| 50 |
+
rope_theta (float, Optional):
|
| 51 |
+
The base frequency for rotary position embedding. Default: 10000.
|
| 52 |
+
max_position_embeddings (int, Optional):
|
| 53 |
+
The maximum position embeddings. Default: None.
|
| 54 |
+
layer_idx (int, Optional):
|
| 55 |
+
The index of the layer (used for cache compatibility). Default: None.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
def __init__(
|
| 59 |
+
self,
|
| 60 |
+
hidden_size: int = 2048,
|
| 61 |
+
num_heads: int = 32,
|
| 62 |
+
num_kv_heads: int | None = None,
|
| 63 |
+
qkv_bias: bool = False,
|
| 64 |
+
qk_norm: bool = False,
|
| 65 |
+
rope_theta: float = 10000.,
|
| 66 |
+
max_position_embeddings: int | None = None,
|
| 67 |
+
layer_idx: int | None = None,
|
| 68 |
+
):
|
| 69 |
+
super().__init__()
|
| 70 |
+
|
| 71 |
+
self.hidden_size = hidden_size
|
| 72 |
+
self.num_heads = num_heads
|
| 73 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 74 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 75 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 76 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 77 |
+
self.qkv_bias = qkv_bias
|
| 78 |
+
self.qk_norm = qk_norm
|
| 79 |
+
self.rope_theta = rope_theta
|
| 80 |
+
self.max_position_embeddings = max_position_embeddings
|
| 81 |
+
self.layer_idx = layer_idx
|
| 82 |
+
|
| 83 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 84 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 85 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 86 |
+
self.b_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 87 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 88 |
+
|
| 89 |
+
if qk_norm:
|
| 90 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 91 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 92 |
+
|
| 93 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 94 |
+
|
| 95 |
+
def forward(
|
| 96 |
+
self,
|
| 97 |
+
hidden_states: torch.Tensor,
|
| 98 |
+
attention_mask: torch.LongTensor | None = None,
|
| 99 |
+
past_key_values: Cache | None = None,
|
| 100 |
+
output_attentions: bool = False,
|
| 101 |
+
use_cache: bool = False,
|
| 102 |
+
**kwargs,
|
| 103 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 104 |
+
attentions = None
|
| 105 |
+
if attention_mask is not None:
|
| 106 |
+
assert len(attention_mask.shape) == 2, (
|
| 107 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 108 |
+
"for padding purposes (0 indicating padding). "
|
| 109 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 113 |
+
|
| 114 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 115 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 116 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 117 |
+
beta = self.b_proj(hidden_states)
|
| 118 |
+
|
| 119 |
+
if self.qk_norm:
|
| 120 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 121 |
+
|
| 122 |
+
cu_seqlens_kw = kwargs.get('cu_seqlens')
|
| 123 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 124 |
+
if past_key_values is not None:
|
| 125 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 126 |
+
max_seqlen = q_len + seqlen_offset
|
| 127 |
+
|
| 128 |
+
if attention_mask is not None:
|
| 129 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 130 |
+
max_seqlen = q_len + max(seqlen_offset)
|
| 131 |
+
|
| 132 |
+
if self.max_position_embeddings is not None:
|
| 133 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 134 |
+
|
| 135 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens_kw)
|
| 136 |
+
|
| 137 |
+
o = deltaformer_attn(
|
| 138 |
+
q=q,
|
| 139 |
+
k=k,
|
| 140 |
+
v=v,
|
| 141 |
+
beta=beta,
|
| 142 |
+
attention_mask=attention_mask,
|
| 143 |
+
cu_seqlens=cu_seqlens_kw,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 147 |
+
o = self.o_proj(o)
|
| 148 |
+
|
| 149 |
+
if not output_attentions:
|
| 150 |
+
attentions = None
|
| 151 |
+
|
| 152 |
+
return o, attentions, past_key_values
|
fla/layers/forgetting_attn.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
import torch.utils.checkpoint
|
| 11 |
+
from einops import rearrange
|
| 12 |
+
from transformers.utils import logging
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 15 |
+
from fla.modules import GroupNorm
|
| 16 |
+
from fla.ops.attn.decoding import attn_decoding_one_step
|
| 17 |
+
from fla.ops.forgetting_attn.parallel import parallel_forgetting_attn
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
logger = logging.get_logger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class ForgettingAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
hidden_size: int = 2048,
|
| 30 |
+
num_heads: int = 32,
|
| 31 |
+
num_kv_heads: int | None = None,
|
| 32 |
+
qkv_bias: bool = False,
|
| 33 |
+
qk_norm: bool = False,
|
| 34 |
+
window_size: int | None = None,
|
| 35 |
+
use_output_gate: bool = False,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.hidden_size = hidden_size
|
| 41 |
+
self.num_heads = num_heads
|
| 42 |
+
if num_kv_heads is None:
|
| 43 |
+
self.num_kv_heads = self.num_heads
|
| 44 |
+
else:
|
| 45 |
+
self.num_kv_heads = num_kv_heads
|
| 46 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 47 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
self.qkv_bias = qkv_bias
|
| 50 |
+
self.qk_norm = qk_norm
|
| 51 |
+
|
| 52 |
+
self.window_size = window_size
|
| 53 |
+
self.use_output_gate = use_output_gate
|
| 54 |
+
self.layer_idx = layer_idx
|
| 55 |
+
|
| 56 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 57 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 58 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 59 |
+
self.f_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 60 |
+
|
| 61 |
+
if use_output_gate:
|
| 62 |
+
self.g_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 63 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 64 |
+
|
| 65 |
+
if qk_norm:
|
| 66 |
+
self.q_norm = GroupNorm(
|
| 67 |
+
num_groups=self.num_heads,
|
| 68 |
+
hidden_size=self.hidden_size,
|
| 69 |
+
is_rms_norm=True,
|
| 70 |
+
)
|
| 71 |
+
self.k_norm = GroupNorm(
|
| 72 |
+
num_groups=self.num_kv_heads,
|
| 73 |
+
hidden_size=self.kv_dim,
|
| 74 |
+
is_rms_norm=True,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
def forward(
|
| 78 |
+
self,
|
| 79 |
+
hidden_states: torch.Tensor,
|
| 80 |
+
attention_mask: torch.LongTensor | None = None,
|
| 81 |
+
past_key_values: Cache | None = None,
|
| 82 |
+
output_attentions: bool = False,
|
| 83 |
+
use_cache: bool = False,
|
| 84 |
+
**kwargs,
|
| 85 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 86 |
+
if attention_mask is not None:
|
| 87 |
+
assert len(attention_mask.shape) == 2, (
|
| 88 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 89 |
+
"for padding purposes (0 indicating padding). "
|
| 90 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 94 |
+
|
| 95 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 96 |
+
f = F.logsigmoid(self.f_proj(hidden_states).float())
|
| 97 |
+
if self.qk_norm:
|
| 98 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 99 |
+
|
| 100 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 101 |
+
if past_key_values is not None:
|
| 102 |
+
assert cu_seqlens is None, "cu_seqlens should not be provided when past_key_values is not None"
|
| 103 |
+
state = past_key_values.update(
|
| 104 |
+
attn_state=(k, v, f),
|
| 105 |
+
layer_idx=self.layer_idx,
|
| 106 |
+
offset=q_len,
|
| 107 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 108 |
+
)
|
| 109 |
+
k, v, f = state['attn_state']
|
| 110 |
+
|
| 111 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 112 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 113 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 114 |
+
|
| 115 |
+
if attention_mask is not None:
|
| 116 |
+
q, (k, v, f), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v, f), attention_mask, q_len, keepdim=True)
|
| 117 |
+
_, cu_seqlens_k = cu_seqlens
|
| 118 |
+
cu_seqlens = cu_seqlens_k
|
| 119 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 120 |
+
if max_seqlen_q != max_seqlen_k:
|
| 121 |
+
assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
|
| 122 |
+
o = attn_decoding_one_step(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 123 |
+
else:
|
| 124 |
+
o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 125 |
+
else:
|
| 126 |
+
o = parallel_forgetting_attn(q, k, v, f, cu_seqlens=cu_seqlens)
|
| 127 |
+
if attention_mask is not None:
|
| 128 |
+
o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
|
| 129 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 130 |
+
if self.use_output_gate:
|
| 131 |
+
o = self.g_proj(hidden_states).sigmoid() * o
|
| 132 |
+
o = self.o_proj(o)
|
| 133 |
+
return o, None, past_key_values
|
fla/layers/gated_deltanet.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@torch.compile
|
| 25 |
+
def elu_p1(x):
|
| 26 |
+
return (F.elu(x, 1., False) + 1.).to(x)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@torch.compile
|
| 30 |
+
def sum_norm(x):
|
| 31 |
+
return (x / x.sum(-1, keepdim=True)).to(x)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class GatedDeltaNet(nn.Module):
|
| 35 |
+
"""
|
| 36 |
+
The layer implementaion for [Gated Delta Networks: Improving Mamba2 with Delta Rule](https://arxiv.org/abs/2412.06464). # noqa
|
| 37 |
+
|
| 38 |
+
Similar to Mamba2, each layer contains around 6*hidden_size*hidden_size parameters.
|
| 39 |
+
|
| 40 |
+
Parameter alloation when use_gate=True:
|
| 41 |
+
- 0.75 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 42 |
+
- 1.5 * hidden_size * hidden_size for the v_proj, g_proj and o_proj each
|
| 43 |
+
- Others are ignorably small.
|
| 44 |
+
- In total = 0.75 * 2 + 1.5 * 3 = 6 * hidden_size * hidden_size
|
| 45 |
+
NOTE: num_heads * head_dim = 0.75 * hidden_size, please make sure to set the correct num_heads and head_dim.
|
| 46 |
+
|
| 47 |
+
Parameter allocation when use_gate=False:
|
| 48 |
+
- 1 * hidden_size * hidden_size for the q_proj and k_proj each
|
| 49 |
+
- 2 * hidden_size * hidden_size for the v_proj and o_proj each
|
| 50 |
+
- Others are ignorably small.
|
| 51 |
+
- In total = 1 * 2 + 2 * 2 = 6 * hidden_size * hidden_size
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
hidden_size (int, Optional):
|
| 55 |
+
The hidden size of the input. Default: 2048.
|
| 56 |
+
expand_v (float, Optional):
|
| 57 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 58 |
+
head_dim (int, Optional):
|
| 59 |
+
The dimension of each head. Default: 256.
|
| 60 |
+
num_heads (int, Optional):
|
| 61 |
+
The number of heads. Default: 4.
|
| 62 |
+
num_v_heads (int, Optional):
|
| 63 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 64 |
+
GVA is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 65 |
+
mode (str, Optional):
|
| 66 |
+
Which Gated DeltaNet kernel to use.
|
| 67 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 68 |
+
Default: `chunk`.
|
| 69 |
+
use_beta (bool, Optional):
|
| 70 |
+
Whether to use beta. Default: `True`.
|
| 71 |
+
use_gate (bool, Optional):
|
| 72 |
+
Whether to use output gate. Default: `True`.
|
| 73 |
+
use_short_conv (bool, Optional):
|
| 74 |
+
Whether to use short convolutions. Default: `True`.
|
| 75 |
+
allow_neg_eigval (bool, Optional):
|
| 76 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 77 |
+
See reference: [Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 78 |
+
conv_size (int, Optional):
|
| 79 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 80 |
+
conv_bias (bool, Optional):
|
| 81 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 82 |
+
layer_idx (int, Optional):
|
| 83 |
+
The index of the layer. Default: None.
|
| 84 |
+
norm_eps (float, Optional):
|
| 85 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
def __init__(
|
| 89 |
+
self,
|
| 90 |
+
hidden_size: int = 2048,
|
| 91 |
+
expand_v: float = 2,
|
| 92 |
+
head_dim: int = 256,
|
| 93 |
+
num_heads: int = 6,
|
| 94 |
+
num_v_heads: int = None,
|
| 95 |
+
mode: str = 'chunk',
|
| 96 |
+
use_gate: bool = True,
|
| 97 |
+
use_short_conv: bool = True,
|
| 98 |
+
allow_neg_eigval: bool = False,
|
| 99 |
+
conv_size: int = 4,
|
| 100 |
+
conv_bias: bool = False,
|
| 101 |
+
layer_idx: int = None,
|
| 102 |
+
norm_eps: float = 1e-5,
|
| 103 |
+
**kwargs,
|
| 104 |
+
) -> GatedDeltaNet:
|
| 105 |
+
super().__init__()
|
| 106 |
+
|
| 107 |
+
self.mode = mode
|
| 108 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 109 |
+
self.hidden_size = hidden_size
|
| 110 |
+
self.expand_v = expand_v
|
| 111 |
+
|
| 112 |
+
self.use_gate = use_gate
|
| 113 |
+
self.use_short_conv = use_short_conv
|
| 114 |
+
self.conv_size = conv_size
|
| 115 |
+
self.conv_bias = conv_bias
|
| 116 |
+
|
| 117 |
+
self.head_dim = head_dim
|
| 118 |
+
self.num_heads = num_heads
|
| 119 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 120 |
+
|
| 121 |
+
self.head_k_dim = head_dim
|
| 122 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 123 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 124 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 125 |
+
self.layer_idx = layer_idx
|
| 126 |
+
|
| 127 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 128 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 129 |
+
raise ValueError(
|
| 130 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 131 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 132 |
+
)
|
| 133 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 134 |
+
raise ValueError(
|
| 135 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 139 |
+
raise ValueError(
|
| 140 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 141 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 142 |
+
)
|
| 143 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 144 |
+
|
| 145 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 146 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 147 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 148 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 149 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 150 |
+
|
| 151 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 152 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 153 |
+
self.A_log._no_weight_decay = True
|
| 154 |
+
# hard coded for now
|
| 155 |
+
dt_min = 0.001
|
| 156 |
+
dt_max = 0.1
|
| 157 |
+
dt_init_floor = 1e-4
|
| 158 |
+
dt = torch.exp(
|
| 159 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 160 |
+
+ math.log(dt_min),
|
| 161 |
+
)
|
| 162 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 163 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 164 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 165 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 166 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 167 |
+
# name.endswith("bias") in param_grouping.py
|
| 168 |
+
self.dt_bias._no_weight_decay = True
|
| 169 |
+
|
| 170 |
+
if use_short_conv:
|
| 171 |
+
self.conv_size = conv_size
|
| 172 |
+
self.q_conv1d = ShortConvolution(
|
| 173 |
+
hidden_size=self.key_dim,
|
| 174 |
+
kernel_size=conv_size,
|
| 175 |
+
bias=conv_bias,
|
| 176 |
+
activation='silu',
|
| 177 |
+
)
|
| 178 |
+
self.k_conv1d = ShortConvolution(
|
| 179 |
+
hidden_size=self.key_dim,
|
| 180 |
+
kernel_size=conv_size,
|
| 181 |
+
bias=conv_bias,
|
| 182 |
+
activation='silu',
|
| 183 |
+
)
|
| 184 |
+
self.v_conv1d = ShortConvolution(
|
| 185 |
+
hidden_size=self.value_dim,
|
| 186 |
+
kernel_size=conv_size,
|
| 187 |
+
bias=conv_bias,
|
| 188 |
+
activation='silu',
|
| 189 |
+
)
|
| 190 |
+
else:
|
| 191 |
+
warnings.warn(
|
| 192 |
+
"ShortConvolution is crucial to the performance. "
|
| 193 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 194 |
+
)
|
| 195 |
+
if use_gate:
|
| 196 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 197 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 198 |
+
else:
|
| 199 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 200 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 201 |
+
|
| 202 |
+
def forward(
|
| 203 |
+
self,
|
| 204 |
+
hidden_states: torch.Tensor,
|
| 205 |
+
attention_mask: torch.Tensor | None = None,
|
| 206 |
+
past_key_values: Cache | None = None,
|
| 207 |
+
use_cache: bool | None = False,
|
| 208 |
+
output_attentions: bool | None = False,
|
| 209 |
+
**kwargs: Unpack[dict],
|
| 210 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 211 |
+
if attention_mask is not None:
|
| 212 |
+
assert len(attention_mask.shape) == 2, (
|
| 213 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 214 |
+
"for padding purposes (0 indicating padding). "
|
| 215 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 219 |
+
# change to inference mode.
|
| 220 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 221 |
+
if self.training:
|
| 222 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 223 |
+
|
| 224 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 225 |
+
|
| 226 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 227 |
+
if attention_mask is not None:
|
| 228 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 229 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 230 |
+
|
| 231 |
+
if self.use_short_conv:
|
| 232 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 233 |
+
if last_state is not None:
|
| 234 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 235 |
+
q, conv_state_q = self.q_conv1d(
|
| 236 |
+
x=self.q_proj(hidden_states),
|
| 237 |
+
cache=conv_state_q,
|
| 238 |
+
output_final_state=use_cache,
|
| 239 |
+
cu_seqlens=cu_seqlens,
|
| 240 |
+
)
|
| 241 |
+
k, conv_state_k = self.k_conv1d(
|
| 242 |
+
x=self.k_proj(hidden_states),
|
| 243 |
+
cache=conv_state_k,
|
| 244 |
+
output_final_state=use_cache,
|
| 245 |
+
cu_seqlens=cu_seqlens,
|
| 246 |
+
)
|
| 247 |
+
v, conv_state_v = self.v_conv1d(
|
| 248 |
+
x=self.v_proj(hidden_states),
|
| 249 |
+
cache=conv_state_v,
|
| 250 |
+
output_final_state=use_cache,
|
| 251 |
+
cu_seqlens=cu_seqlens,
|
| 252 |
+
)
|
| 253 |
+
else:
|
| 254 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 255 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 256 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 257 |
+
|
| 258 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 259 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 260 |
+
|
| 261 |
+
if self.num_v_heads > self.num_heads:
|
| 262 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 263 |
+
|
| 264 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 265 |
+
if self.allow_neg_eigval:
|
| 266 |
+
beta = beta * 2.
|
| 267 |
+
|
| 268 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 269 |
+
|
| 270 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 271 |
+
if mode == 'chunk':
|
| 272 |
+
o, recurrent_state = chunk_gated_delta_rule(
|
| 273 |
+
q=q,
|
| 274 |
+
k=k,
|
| 275 |
+
v=v,
|
| 276 |
+
g=g,
|
| 277 |
+
beta=beta,
|
| 278 |
+
initial_state=recurrent_state,
|
| 279 |
+
output_final_state=use_cache,
|
| 280 |
+
cu_seqlens=cu_seqlens,
|
| 281 |
+
use_qk_l2norm_in_kernel=True,
|
| 282 |
+
)
|
| 283 |
+
elif mode == 'fused_recurrent':
|
| 284 |
+
o, recurrent_state = fused_recurrent_gated_delta_rule(
|
| 285 |
+
q=q,
|
| 286 |
+
k=k,
|
| 287 |
+
v=v,
|
| 288 |
+
g=g,
|
| 289 |
+
beta=beta,
|
| 290 |
+
initial_state=recurrent_state,
|
| 291 |
+
output_final_state=use_cache,
|
| 292 |
+
cu_seqlens=cu_seqlens,
|
| 293 |
+
use_qk_l2norm_in_kernel=True,
|
| 294 |
+
)
|
| 295 |
+
else:
|
| 296 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 297 |
+
|
| 298 |
+
update_layer_cache(
|
| 299 |
+
self,
|
| 300 |
+
past_key_values,
|
| 301 |
+
recurrent_state=recurrent_state,
|
| 302 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 303 |
+
offset=q_len,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
if self.use_gate:
|
| 307 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 308 |
+
o = self.o_norm(o, g)
|
| 309 |
+
else:
|
| 310 |
+
o = self.o_norm(o)
|
| 311 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 312 |
+
o = self.o_proj(o)
|
| 313 |
+
if attention_mask is not None:
|
| 314 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 315 |
+
|
| 316 |
+
return o, None, past_key_values
|
fla/layers/gated_deltaproduct.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 16 |
+
from fla.ops.gated_delta_product import chunk_gated_delta_product
|
| 17 |
+
from fla.ops.gated_delta_rule import fused_recurrent_gated_delta_rule
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class GatedDeltaProduct(nn.Module):
|
| 26 |
+
"""
|
| 27 |
+
Generalized version of GatedDoubleDeltaNet that supports arbitrary number of householder transformations.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
hidden_size: int = 2048,
|
| 33 |
+
expand_v: float = 2,
|
| 34 |
+
head_dim: int = 256,
|
| 35 |
+
num_heads: int = 6,
|
| 36 |
+
num_v_heads: int = None,
|
| 37 |
+
mode: str = 'chunk',
|
| 38 |
+
use_output_gate: bool = True,
|
| 39 |
+
use_short_conv: bool = True,
|
| 40 |
+
conv_size: int = 4,
|
| 41 |
+
conv_bias: bool = False,
|
| 42 |
+
layer_idx: int = None,
|
| 43 |
+
norm_eps: float = 1e-5,
|
| 44 |
+
use_forget_gate: bool = True,
|
| 45 |
+
allow_neg_eigval: bool = True,
|
| 46 |
+
num_householder: int = 2,
|
| 47 |
+
**kwargs,
|
| 48 |
+
) -> GatedDeltaProduct:
|
| 49 |
+
super().__init__()
|
| 50 |
+
|
| 51 |
+
self.mode = mode
|
| 52 |
+
|
| 53 |
+
self.hidden_size = hidden_size
|
| 54 |
+
self.expand_v = expand_v
|
| 55 |
+
|
| 56 |
+
self.use_forget_gate = use_forget_gate
|
| 57 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 58 |
+
self.num_householder = num_householder
|
| 59 |
+
self.use_output_gate = use_output_gate
|
| 60 |
+
self.use_short_conv = use_short_conv
|
| 61 |
+
self.conv_size = conv_size
|
| 62 |
+
self.conv_bias = conv_bias
|
| 63 |
+
|
| 64 |
+
self.head_dim = head_dim
|
| 65 |
+
self.num_heads = num_heads
|
| 66 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 67 |
+
|
| 68 |
+
self.head_k_dim = head_dim
|
| 69 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 70 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 71 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 72 |
+
self.layer_idx = layer_idx
|
| 73 |
+
|
| 74 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 75 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 76 |
+
raise ValueError(
|
| 77 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 78 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 79 |
+
)
|
| 80 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 81 |
+
raise ValueError(
|
| 82 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 86 |
+
raise ValueError(
|
| 87 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 88 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 89 |
+
)
|
| 90 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 91 |
+
|
| 92 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 93 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim * num_householder, bias=False)
|
| 94 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim * num_householder, bias=False)
|
| 95 |
+
self.b_proj = nn.Linear(hidden_size, self.num_v_heads * num_householder, bias=False)
|
| 96 |
+
|
| 97 |
+
if self.use_forget_gate:
|
| 98 |
+
self.a_proj = nn.Linear(hidden_size, self.num_v_heads, bias=False)
|
| 99 |
+
A = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(0, 16)
|
| 100 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 101 |
+
self.A_log._no_weight_decay = True
|
| 102 |
+
# hard coded for now
|
| 103 |
+
dt_min = 0.001
|
| 104 |
+
dt_max = 0.1
|
| 105 |
+
dt_init_floor = 1e-4
|
| 106 |
+
dt = torch.exp(
|
| 107 |
+
torch.rand(self.num_v_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 108 |
+
+ math.log(dt_min),
|
| 109 |
+
)
|
| 110 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 111 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 112 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 113 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 114 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 115 |
+
# name.endswith("bias") in param_grouping.py
|
| 116 |
+
self.dt_bias._no_weight_decay = True
|
| 117 |
+
|
| 118 |
+
if use_short_conv:
|
| 119 |
+
self.conv_size = conv_size
|
| 120 |
+
self.q_conv1d = ShortConvolution(
|
| 121 |
+
hidden_size=self.key_dim,
|
| 122 |
+
kernel_size=conv_size,
|
| 123 |
+
bias=conv_bias,
|
| 124 |
+
activation='silu',
|
| 125 |
+
)
|
| 126 |
+
self.k_conv1d = ShortConvolution(
|
| 127 |
+
hidden_size=self.key_dim * num_householder,
|
| 128 |
+
kernel_size=conv_size,
|
| 129 |
+
bias=conv_bias,
|
| 130 |
+
activation='silu',
|
| 131 |
+
)
|
| 132 |
+
self.v_conv1d = ShortConvolution(
|
| 133 |
+
hidden_size=self.value_dim * num_householder,
|
| 134 |
+
kernel_size=conv_size,
|
| 135 |
+
bias=conv_bias,
|
| 136 |
+
activation='silu',
|
| 137 |
+
)
|
| 138 |
+
else:
|
| 139 |
+
warnings.warn(
|
| 140 |
+
"ShortConvolution is crucial to the performance. "
|
| 141 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 142 |
+
)
|
| 143 |
+
if use_output_gate:
|
| 144 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 145 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 146 |
+
else:
|
| 147 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 148 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 149 |
+
|
| 150 |
+
def _initialize_weights(self, module: nn.Module):
|
| 151 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 152 |
+
return
|
| 153 |
+
if isinstance(module, nn.Linear):
|
| 154 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 155 |
+
if module.bias is not None:
|
| 156 |
+
nn.init.zeros_(module.bias)
|
| 157 |
+
module._is_hf_initialized = True
|
| 158 |
+
|
| 159 |
+
def forward(
|
| 160 |
+
self,
|
| 161 |
+
hidden_states: torch.Tensor,
|
| 162 |
+
attention_mask: torch.Tensor | None = None,
|
| 163 |
+
past_key_values: Cache | None = None,
|
| 164 |
+
use_cache: bool | None = False,
|
| 165 |
+
output_attentions: bool | None = False,
|
| 166 |
+
**kwargs: Unpack[dict],
|
| 167 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 168 |
+
if attention_mask is not None:
|
| 169 |
+
assert len(attention_mask.shape) == 2, (
|
| 170 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 171 |
+
"for padding purposes (0 indicating padding). "
|
| 172 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 176 |
+
# change to inference mode.
|
| 177 |
+
mode = 'fused_recurrent' if (q_len <= 64 and not self.training) else self.mode
|
| 178 |
+
if self.training:
|
| 179 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 180 |
+
|
| 181 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 182 |
+
|
| 183 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 184 |
+
if attention_mask is not None:
|
| 185 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 186 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 187 |
+
|
| 188 |
+
if self.use_short_conv:
|
| 189 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 190 |
+
if last_state is not None:
|
| 191 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 192 |
+
q, conv_state_q = self.q_conv1d(
|
| 193 |
+
x=self.q_proj(hidden_states),
|
| 194 |
+
cache=conv_state_q,
|
| 195 |
+
output_final_state=use_cache,
|
| 196 |
+
cu_seqlens=cu_seqlens,
|
| 197 |
+
)
|
| 198 |
+
k, conv_state_k = self.k_conv1d(
|
| 199 |
+
x=self.k_proj(hidden_states),
|
| 200 |
+
cache=conv_state_k,
|
| 201 |
+
output_final_state=use_cache,
|
| 202 |
+
cu_seqlens=cu_seqlens,
|
| 203 |
+
)
|
| 204 |
+
v, conv_state_v = self.v_conv1d(
|
| 205 |
+
x=self.v_proj(hidden_states),
|
| 206 |
+
cache=conv_state_v,
|
| 207 |
+
output_final_state=use_cache,
|
| 208 |
+
cu_seqlens=cu_seqlens,
|
| 209 |
+
)
|
| 210 |
+
else:
|
| 211 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 212 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 213 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 214 |
+
|
| 215 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 216 |
+
k = rearrange(k, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_k_dim)
|
| 217 |
+
v = rearrange(v, '... t (n h d) -> ... (t n) h d', n=self.num_householder, d=self.head_v_dim)
|
| 218 |
+
|
| 219 |
+
if self.num_v_heads > self.num_heads:
|
| 220 |
+
q, k = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_v_heads // self.num_heads), (q, k))
|
| 221 |
+
|
| 222 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 223 |
+
if self.allow_neg_eigval:
|
| 224 |
+
beta = beta * 2.
|
| 225 |
+
|
| 226 |
+
beta = rearrange(beta, '... t (n h) -> ... (t n) h', n=self.num_householder)
|
| 227 |
+
if self.use_forget_gate:
|
| 228 |
+
g = -self.A_log.float().exp() * F.softplus(self.a_proj(hidden_states).float() + self.dt_bias)
|
| 229 |
+
else:
|
| 230 |
+
g = None
|
| 231 |
+
|
| 232 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 233 |
+
if mode == 'chunk':
|
| 234 |
+
o, recurrent_state = chunk_gated_delta_product(
|
| 235 |
+
q=q,
|
| 236 |
+
k=k,
|
| 237 |
+
v=v,
|
| 238 |
+
g=g,
|
| 239 |
+
beta=beta,
|
| 240 |
+
initial_state=recurrent_state,
|
| 241 |
+
output_final_state=use_cache,
|
| 242 |
+
cu_seqlens=cu_seqlens,
|
| 243 |
+
num_householder=self.num_householder,
|
| 244 |
+
use_qk_l2norm_in_kernel=True,
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
elif mode == 'fused_recurrent':
|
| 248 |
+
if self.use_forget_gate:
|
| 249 |
+
g_new = g.new_zeros(g.shape[0], g.shape[1], self.num_householder, g.shape[2])
|
| 250 |
+
g_new[:, :, 0] = g
|
| 251 |
+
g = rearrange(g_new, '... t n h -> ... (t n) h')
|
| 252 |
+
|
| 253 |
+
q_new = q.new_zeros(q.shape[0], q.shape[1], self.num_householder, q.shape[2], q.shape[3])
|
| 254 |
+
q_new[:, :, -1] = q
|
| 255 |
+
q = rearrange(q_new, '... t n h d-> ... (t n) h d')
|
| 256 |
+
o, recurrent_state = fused_recurrent_gated_delta_rule(
|
| 257 |
+
q=q,
|
| 258 |
+
k=k,
|
| 259 |
+
v=v,
|
| 260 |
+
g=g,
|
| 261 |
+
beta=beta,
|
| 262 |
+
initial_state=recurrent_state,
|
| 263 |
+
output_final_state=use_cache,
|
| 264 |
+
cu_seqlens=cu_seqlens * self.num_householder if cu_seqlens is not None else None,
|
| 265 |
+
use_qk_l2norm_in_kernel=True,
|
| 266 |
+
)
|
| 267 |
+
o = rearrange(o, '... (t n) h d -> ... t n h d', n=self.num_householder)[..., -1, :, :].contiguous()
|
| 268 |
+
|
| 269 |
+
update_layer_cache(
|
| 270 |
+
self,
|
| 271 |
+
past_key_values,
|
| 272 |
+
recurrent_state=recurrent_state,
|
| 273 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 274 |
+
offset=q_len,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
if self.use_output_gate:
|
| 278 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 279 |
+
o = self.o_norm(o, g)
|
| 280 |
+
else:
|
| 281 |
+
o = self.o_norm(o)
|
| 282 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 283 |
+
o = self.o_proj(o)
|
| 284 |
+
if attention_mask is not None:
|
| 285 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 286 |
+
|
| 287 |
+
return o, None, past_key_values
|
fla/layers/gla.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.activations import ACT2FN
|
| 16 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class GatedLinearAttention(nn.Module):
|
| 25 |
+
r"""
|
| 26 |
+
The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
mode (str, Optional):
|
| 30 |
+
Which GLA kernel to use.
|
| 31 |
+
Currently available: `chunk`, `fused_recurrent`, and `fused_chunk`.
|
| 32 |
+
Default: `chunk`.
|
| 33 |
+
hidden_size (int, Optional):
|
| 34 |
+
The hidden size of the input. Default: 1024.
|
| 35 |
+
expand_k (float, Optional):
|
| 36 |
+
The expansion ratio for the key dim. Default: 0.5.
|
| 37 |
+
expand_v (float, Optional):
|
| 38 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 39 |
+
num_heads (int, Optional):
|
| 40 |
+
The number of heads. Default: 4.
|
| 41 |
+
num_kv_heads (int, Optional):
|
| 42 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 43 |
+
feature_map (str, Optional):
|
| 44 |
+
Feature map function applied to queries/keys. Default: None.
|
| 45 |
+
use_short_conv (bool, Optional):
|
| 46 |
+
Whether to use short convolutions. Default: `False`.
|
| 47 |
+
conv_size (int, Optional):
|
| 48 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 49 |
+
conv_bias (bool, Optional):
|
| 50 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 51 |
+
use_output_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `True`.
|
| 53 |
+
gate_fn (str, Optional):
|
| 54 |
+
The activation function for the output gate. Default: `swish`.
|
| 55 |
+
elementwise_affine (bool, Optional):
|
| 56 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 57 |
+
norm_eps (float, Optional):
|
| 58 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 59 |
+
gate_logit_normalizer (int, Optional):
|
| 60 |
+
The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
|
| 61 |
+
gate_low_rank_dim (int, Optional):
|
| 62 |
+
The low rank dim for the gate projection. Default: 16.
|
| 63 |
+
clamp_min (float, Optional):
|
| 64 |
+
The minimum value for the gate logits. Default: None.
|
| 65 |
+
fuse_norm (bool, Optional):
|
| 66 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 67 |
+
layer_idx (int, Optional):
|
| 68 |
+
The index of the layer. Default: None.
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
def __init__(
|
| 72 |
+
self,
|
| 73 |
+
mode: str = 'chunk',
|
| 74 |
+
hidden_size: int = 1024,
|
| 75 |
+
expand_k: float = 0.5,
|
| 76 |
+
expand_v: float = 1.0,
|
| 77 |
+
num_heads: int = 4,
|
| 78 |
+
num_kv_heads: int | None = None,
|
| 79 |
+
feature_map: str | None = None,
|
| 80 |
+
use_short_conv: bool = False,
|
| 81 |
+
conv_size: int = 4,
|
| 82 |
+
conv_bias: bool = False,
|
| 83 |
+
use_output_gate: bool = True,
|
| 84 |
+
gate_fn: str = 'swish',
|
| 85 |
+
elementwise_affine: bool | None = True,
|
| 86 |
+
norm_eps: float = 1e-5,
|
| 87 |
+
gate_logit_normalizer: int = 16,
|
| 88 |
+
gate_low_rank_dim: int = 16,
|
| 89 |
+
clamp_min: float | None = None,
|
| 90 |
+
fuse_norm: bool = True,
|
| 91 |
+
layer_idx: int = None,
|
| 92 |
+
) -> GatedLinearAttention:
|
| 93 |
+
super().__init__()
|
| 94 |
+
|
| 95 |
+
self.mode = mode
|
| 96 |
+
self.hidden_size = hidden_size
|
| 97 |
+
self.expand_k = expand_k
|
| 98 |
+
self.expand_v = expand_v
|
| 99 |
+
self.num_heads = num_heads
|
| 100 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 101 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 102 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 103 |
+
|
| 104 |
+
self.use_short_conv = use_short_conv
|
| 105 |
+
self.conv_size = conv_size
|
| 106 |
+
self.conv_bias = conv_bias
|
| 107 |
+
self.use_output_gate = use_output_gate
|
| 108 |
+
|
| 109 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 110 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 111 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 112 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 113 |
+
self.clamp_min = clamp_min
|
| 114 |
+
self.layer_idx = layer_idx
|
| 115 |
+
|
| 116 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 117 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 118 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 119 |
+
|
| 120 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 121 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 122 |
+
|
| 123 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 124 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 125 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 126 |
+
if self.use_output_gate:
|
| 127 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 128 |
+
|
| 129 |
+
if use_short_conv:
|
| 130 |
+
self.conv_size = conv_size
|
| 131 |
+
self.q_conv1d = ShortConvolution(
|
| 132 |
+
hidden_size=self.key_dim,
|
| 133 |
+
kernel_size=conv_size,
|
| 134 |
+
bias=conv_bias,
|
| 135 |
+
activation='silu',
|
| 136 |
+
)
|
| 137 |
+
self.k_conv1d = ShortConvolution(
|
| 138 |
+
hidden_size=self.key_dim_per_group,
|
| 139 |
+
kernel_size=conv_size,
|
| 140 |
+
bias=conv_bias,
|
| 141 |
+
activation='silu',
|
| 142 |
+
)
|
| 143 |
+
self.v_conv1d = ShortConvolution(
|
| 144 |
+
hidden_size=self.value_dim_per_group,
|
| 145 |
+
kernel_size=conv_size,
|
| 146 |
+
bias=conv_bias,
|
| 147 |
+
activation='silu',
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
self.gk_proj = nn.Sequential(nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
|
| 151 |
+
nn.Linear(gate_low_rank_dim, self.key_dim_per_group, bias=True))
|
| 152 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 153 |
+
|
| 154 |
+
if gate_fn == 'swish' and fuse_norm and use_output_gate:
|
| 155 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 156 |
+
hidden_size=self.head_v_dim,
|
| 157 |
+
elementwise_affine=elementwise_affine,
|
| 158 |
+
eps=norm_eps,
|
| 159 |
+
)
|
| 160 |
+
self.fuse_norm_and_gate = True
|
| 161 |
+
else:
|
| 162 |
+
self.fuse_norm_and_gate = False
|
| 163 |
+
self.g_norm = RMSNorm(
|
| 164 |
+
hidden_size=self.head_v_dim,
|
| 165 |
+
elementwise_affine=elementwise_affine,
|
| 166 |
+
eps=norm_eps,
|
| 167 |
+
dtype=torch.float32
|
| 168 |
+
)
|
| 169 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 170 |
+
|
| 171 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 172 |
+
|
| 173 |
+
def reset_parameters(self) -> None:
|
| 174 |
+
for module in self.children():
|
| 175 |
+
reset = getattr(module, "reset_parameters", None)
|
| 176 |
+
if callable(reset):
|
| 177 |
+
reset()
|
| 178 |
+
|
| 179 |
+
def forward(
|
| 180 |
+
self,
|
| 181 |
+
hidden_states: torch.Tensor,
|
| 182 |
+
attention_mask: torch.Tensor | None = None,
|
| 183 |
+
past_key_values: Cache | None = None,
|
| 184 |
+
use_cache: bool | None = False,
|
| 185 |
+
output_attentions: bool | None = False,
|
| 186 |
+
**kwargs: Unpack[dict],
|
| 187 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
assert len(attention_mask.shape) == 2, (
|
| 190 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 191 |
+
"for padding purposes (0 indicating padding). "
|
| 192 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 196 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 197 |
+
|
| 198 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 199 |
+
|
| 200 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 201 |
+
if attention_mask is not None:
|
| 202 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 203 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 204 |
+
|
| 205 |
+
if self.use_short_conv:
|
| 206 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 207 |
+
if last_state is not None:
|
| 208 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 209 |
+
q, conv_state_q = self.q_conv1d(
|
| 210 |
+
x=self.q_proj(hidden_states),
|
| 211 |
+
cache=conv_state_q,
|
| 212 |
+
output_final_state=use_cache,
|
| 213 |
+
cu_seqlens=cu_seqlens,
|
| 214 |
+
)
|
| 215 |
+
k, conv_state_k = self.k_conv1d(
|
| 216 |
+
x=self.k_proj(hidden_states),
|
| 217 |
+
cache=conv_state_k,
|
| 218 |
+
output_final_state=use_cache,
|
| 219 |
+
cu_seqlens=cu_seqlens,
|
| 220 |
+
)
|
| 221 |
+
v, conv_state_v = self.v_conv1d(
|
| 222 |
+
x=self.v_proj(hidden_states),
|
| 223 |
+
cache=conv_state_v,
|
| 224 |
+
output_final_state=use_cache,
|
| 225 |
+
cu_seqlens=cu_seqlens,
|
| 226 |
+
)
|
| 227 |
+
else:
|
| 228 |
+
q = self.q_proj(hidden_states)
|
| 229 |
+
k = self.k_proj(hidden_states)
|
| 230 |
+
v = self.v_proj(hidden_states)
|
| 231 |
+
gk = self.gk_proj(hidden_states)
|
| 232 |
+
|
| 233 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 234 |
+
if self.num_kv_groups > 1:
|
| 235 |
+
k, gk = (repeat(x, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_k_dim) for x in (k, gk))
|
| 236 |
+
v = repeat(v, '... (h d) -> ... (h g) d', g=self.num_kv_groups, d=self.head_v_dim)
|
| 237 |
+
else:
|
| 238 |
+
k, gk = (rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim) for x in (k, gk))
|
| 239 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 240 |
+
|
| 241 |
+
gk = F.logsigmoid(gk) / self.gate_logit_normalizer
|
| 242 |
+
if self.clamp_min is not None:
|
| 243 |
+
gk = torch.clamp_min(gk, self.clamp_min)
|
| 244 |
+
|
| 245 |
+
if self.feature_map_fn is not None:
|
| 246 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 247 |
+
|
| 248 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 249 |
+
if mode == 'fused_recurrent':
|
| 250 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 251 |
+
q=q,
|
| 252 |
+
k=k,
|
| 253 |
+
v=v,
|
| 254 |
+
gk=gk,
|
| 255 |
+
initial_state=recurrent_state,
|
| 256 |
+
output_final_state=use_cache,
|
| 257 |
+
cu_seqlens=cu_seqlens,
|
| 258 |
+
)
|
| 259 |
+
elif mode == 'fused_chunk':
|
| 260 |
+
o, recurrent_state = fused_chunk_gla(
|
| 261 |
+
q=q,
|
| 262 |
+
k=k,
|
| 263 |
+
v=v,
|
| 264 |
+
g=gk,
|
| 265 |
+
initial_state=recurrent_state,
|
| 266 |
+
output_final_state=use_cache,
|
| 267 |
+
)
|
| 268 |
+
elif mode == 'chunk':
|
| 269 |
+
o, recurrent_state = chunk_gla(
|
| 270 |
+
q=q,
|
| 271 |
+
k=k,
|
| 272 |
+
v=v,
|
| 273 |
+
g=gk,
|
| 274 |
+
initial_state=recurrent_state,
|
| 275 |
+
output_final_state=use_cache,
|
| 276 |
+
cu_seqlens=cu_seqlens,
|
| 277 |
+
)
|
| 278 |
+
else:
|
| 279 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 280 |
+
|
| 281 |
+
update_layer_cache(
|
| 282 |
+
self,
|
| 283 |
+
past_key_values,
|
| 284 |
+
recurrent_state=recurrent_state,
|
| 285 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 286 |
+
offset=q_len,
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
if self.use_output_gate:
|
| 290 |
+
g = self.g_proj(hidden_states)
|
| 291 |
+
if self.fuse_norm_and_gate:
|
| 292 |
+
g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 293 |
+
o = self.g_norm_swish_gate(o, g)
|
| 294 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 295 |
+
else:
|
| 296 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 297 |
+
o = o * self.gate_fn(g)
|
| 298 |
+
else:
|
| 299 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 300 |
+
o = self.o_proj(o)
|
| 301 |
+
if attention_mask is not None:
|
| 302 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 303 |
+
|
| 304 |
+
return o, None, past_key_values
|
fla/layers/gsa.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from einops import rearrange, repeat
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.feature_map import ReLUFeatureMap, SwishFeatureMap, T2RFeatureMap
|
| 16 |
+
from fla.modules.layernorm import rms_norm_linear
|
| 17 |
+
from fla.ops.gsa import chunk_gsa, fused_recurrent_gsa
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class GatedSlotAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
expand_k: float = 1.,
|
| 32 |
+
expand_v: float = 1.,
|
| 33 |
+
num_heads: int = 4,
|
| 34 |
+
num_kv_heads: int | None = None,
|
| 35 |
+
use_short_conv: bool = False,
|
| 36 |
+
conv_size: int = 4,
|
| 37 |
+
conv_bias: bool = False,
|
| 38 |
+
num_slots: int | None = None,
|
| 39 |
+
elementwise_affine: bool | None = True,
|
| 40 |
+
norm_eps: float = 1e-5,
|
| 41 |
+
gate_logit_normalizer: int = 8,
|
| 42 |
+
feature_map: str = 'swish',
|
| 43 |
+
use_output_gate: bool = False,
|
| 44 |
+
use_norm: bool = True,
|
| 45 |
+
layer_idx: int | None = None,
|
| 46 |
+
scale: float | None = 1.,
|
| 47 |
+
**kwargs,
|
| 48 |
+
) -> GatedSlotAttention:
|
| 49 |
+
super().__init__()
|
| 50 |
+
|
| 51 |
+
self.mode = mode
|
| 52 |
+
self.hidden_size = hidden_size
|
| 53 |
+
self.expand_k = expand_k
|
| 54 |
+
self.expand_v = expand_v
|
| 55 |
+
self.num_heads = num_heads
|
| 56 |
+
self.num_kv_heads = num_heads if num_kv_heads is None else num_kv_heads
|
| 57 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 58 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 59 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 60 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 61 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 62 |
+
self.head_k_dim = self.key_dim // self.num_heads
|
| 63 |
+
self.head_v_dim = self.value_dim // self.num_heads
|
| 64 |
+
|
| 65 |
+
self.use_short_conv = use_short_conv
|
| 66 |
+
self.conv_size = conv_size
|
| 67 |
+
self.conv_bias = conv_bias
|
| 68 |
+
|
| 69 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 70 |
+
|
| 71 |
+
self.use_output_gate = use_output_gate
|
| 72 |
+
self.use_norm = use_norm
|
| 73 |
+
self.scale = scale
|
| 74 |
+
|
| 75 |
+
if num_slots is None:
|
| 76 |
+
num_slots = self.head_k_dim
|
| 77 |
+
self.num_slots = num_slots
|
| 78 |
+
|
| 79 |
+
self.layer_idx = layer_idx
|
| 80 |
+
|
| 81 |
+
if layer_idx is None:
|
| 82 |
+
warnings.warn(
|
| 83 |
+
f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
|
| 84 |
+
"to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
|
| 85 |
+
"when creating this class.",
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
self.register_module('feature_map', None)
|
| 89 |
+
if feature_map == 'swish':
|
| 90 |
+
self.feature_map = SwishFeatureMap()
|
| 91 |
+
elif feature_map == 'relu':
|
| 92 |
+
self.feature_map = ReLUFeatureMap()
|
| 93 |
+
elif feature_map == 't2r':
|
| 94 |
+
self.feature_map = T2RFeatureMap(self.head_k_dim, self.head_k_dim)
|
| 95 |
+
else:
|
| 96 |
+
raise NotImplementedError(f"Feature map `{feature_map}` is not supported now.")
|
| 97 |
+
|
| 98 |
+
self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 99 |
+
self.k_proj = nn.Linear(self.hidden_size, self.key_dim_per_group, bias=False)
|
| 100 |
+
self.v_proj = nn.Linear(self.hidden_size, self.value_dim_per_group, bias=False)
|
| 101 |
+
self.f_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.num_slots, bias=False)
|
| 102 |
+
|
| 103 |
+
if use_short_conv:
|
| 104 |
+
self.conv_size = conv_size
|
| 105 |
+
self.q_conv1d = ShortConvolution(
|
| 106 |
+
hidden_size=self.key_dim,
|
| 107 |
+
kernel_size=conv_size,
|
| 108 |
+
bias=conv_bias,
|
| 109 |
+
activation='silu',
|
| 110 |
+
)
|
| 111 |
+
self.k_conv1d = ShortConvolution(
|
| 112 |
+
hidden_size=self.key_dim_per_group,
|
| 113 |
+
kernel_size=conv_size,
|
| 114 |
+
bias=conv_bias,
|
| 115 |
+
activation='silu',
|
| 116 |
+
)
|
| 117 |
+
self.v_conv1d = ShortConvolution(
|
| 118 |
+
hidden_size=self.value_dim_per_group,
|
| 119 |
+
kernel_size=conv_size,
|
| 120 |
+
bias=conv_bias,
|
| 121 |
+
activation='silu',
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
self.g_norm = RMSNorm(self.hidden_size, elementwise_affine, eps=norm_eps, dtype=torch.float32)
|
| 125 |
+
self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
|
| 126 |
+
|
| 127 |
+
def forward(
|
| 128 |
+
self,
|
| 129 |
+
hidden_states: torch.Tensor,
|
| 130 |
+
attention_mask: torch.Tensor | None = None,
|
| 131 |
+
past_key_values: Cache | None = None,
|
| 132 |
+
use_cache: bool | None = False,
|
| 133 |
+
output_attentions: bool | None = False,
|
| 134 |
+
**kwargs: Unpack[dict],
|
| 135 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 136 |
+
if attention_mask is not None:
|
| 137 |
+
assert len(attention_mask.shape) == 2, (
|
| 138 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 139 |
+
"for padding purposes (0 indicating padding). "
|
| 140 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 144 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 145 |
+
|
| 146 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 147 |
+
|
| 148 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 149 |
+
if attention_mask is not None:
|
| 150 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 151 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 152 |
+
|
| 153 |
+
if self.use_short_conv:
|
| 154 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 155 |
+
if last_state is not None:
|
| 156 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 157 |
+
q, conv_state_q = self.q_conv1d(
|
| 158 |
+
x=self.q_proj(hidden_states),
|
| 159 |
+
cache=conv_state_q,
|
| 160 |
+
output_final_state=use_cache,
|
| 161 |
+
cu_seqlens=cu_seqlens,
|
| 162 |
+
)
|
| 163 |
+
k, conv_state_k = self.k_conv1d(
|
| 164 |
+
x=self.k_proj(hidden_states),
|
| 165 |
+
cache=conv_state_k,
|
| 166 |
+
output_final_state=use_cache,
|
| 167 |
+
cu_seqlens=cu_seqlens,
|
| 168 |
+
)
|
| 169 |
+
v, conv_state_v = self.v_conv1d(
|
| 170 |
+
x=self.v_proj(hidden_states),
|
| 171 |
+
cache=conv_state_v,
|
| 172 |
+
output_final_state=use_cache,
|
| 173 |
+
cu_seqlens=cu_seqlens,
|
| 174 |
+
)
|
| 175 |
+
else:
|
| 176 |
+
q = self.q_proj(hidden_states)
|
| 177 |
+
k = self.k_proj(hidden_states)
|
| 178 |
+
v = self.v_proj(hidden_states)
|
| 179 |
+
f = self.f_proj(hidden_states)
|
| 180 |
+
|
| 181 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 182 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 183 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 184 |
+
f = rearrange(f, '... (h m) -> ... h m', m=self.num_slots)
|
| 185 |
+
|
| 186 |
+
if self.feature_map is not None:
|
| 187 |
+
q, k = map(lambda x: self.feature_map(x), (q, k))
|
| 188 |
+
v = F.silu(v)
|
| 189 |
+
|
| 190 |
+
f = F.logsigmoid(f) / self.gate_logit_normalizer
|
| 191 |
+
s = (1 - f.exp()).to(f.dtype)
|
| 192 |
+
|
| 193 |
+
if self.num_kv_groups > 1:
|
| 194 |
+
k, v, f, s = map(lambda x: repeat(x, '... h d -> ... (h g) d', g=self.num_kv_groups), (k, v, f, s))
|
| 195 |
+
|
| 196 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 197 |
+
if mode == 'fused_recurrent':
|
| 198 |
+
o, recurrent_state = fused_recurrent_gsa(
|
| 199 |
+
q=q,
|
| 200 |
+
k=k,
|
| 201 |
+
v=v,
|
| 202 |
+
s=s,
|
| 203 |
+
g=f,
|
| 204 |
+
initial_state=recurrent_state,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
scale=self.scale,
|
| 207 |
+
cu_seqlens=cu_seqlens,
|
| 208 |
+
)
|
| 209 |
+
elif mode == 'chunk':
|
| 210 |
+
o, recurrent_state = chunk_gsa(
|
| 211 |
+
q=q,
|
| 212 |
+
k=k,
|
| 213 |
+
v=v,
|
| 214 |
+
s=s,
|
| 215 |
+
g=f,
|
| 216 |
+
initial_state=recurrent_state,
|
| 217 |
+
output_final_state=use_cache,
|
| 218 |
+
scale=self.scale,
|
| 219 |
+
cu_seqlens=cu_seqlens,
|
| 220 |
+
)
|
| 221 |
+
else:
|
| 222 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 223 |
+
|
| 224 |
+
update_layer_cache(
|
| 225 |
+
self,
|
| 226 |
+
past_key_values,
|
| 227 |
+
recurrent_state=recurrent_state,
|
| 228 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 229 |
+
offset=q_len,
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 233 |
+
o = rms_norm_linear(F.silu(o), self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
|
| 234 |
+
if attention_mask is not None:
|
| 235 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 236 |
+
|
| 237 |
+
return o, None, past_key_values
|
fla/layers/hgrn.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "Hierarchically Gated Recurrent Neural Network for Sequence Modeling" [https://arxiv.org/abs/2311.04823]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 15 |
+
from fla.modules.activations import swiglu
|
| 16 |
+
from fla.ops.hgrn import chunk_hgrn, fused_recurrent_hgrn
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class HGRNAttention(nn.Module):
|
| 25 |
+
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
mode: str = 'chunk',
|
| 29 |
+
hidden_size: int = 1024,
|
| 30 |
+
expand_ratio: int | None = 1,
|
| 31 |
+
use_short_conv: bool = False,
|
| 32 |
+
conv_size: int = 4,
|
| 33 |
+
conv_bias: bool = False,
|
| 34 |
+
elementwise_affine: bool | None = True,
|
| 35 |
+
norm_eps: float = 1e-5,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
) -> HGRNAttention:
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.mode = mode
|
| 41 |
+
self.hidden_size = hidden_size
|
| 42 |
+
self.expand_ratio = expand_ratio
|
| 43 |
+
self.input_dim = int(hidden_size * expand_ratio)
|
| 44 |
+
|
| 45 |
+
self.use_short_conv = use_short_conv
|
| 46 |
+
self.conv_size = conv_size
|
| 47 |
+
self.conv_bias = conv_bias
|
| 48 |
+
|
| 49 |
+
self.layer_idx = layer_idx
|
| 50 |
+
|
| 51 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 52 |
+
|
| 53 |
+
self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 54 |
+
self.f_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 55 |
+
self.g_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 56 |
+
|
| 57 |
+
if use_short_conv:
|
| 58 |
+
self.conv_size = conv_size
|
| 59 |
+
self.f_conv1d = ShortConvolution(
|
| 60 |
+
hidden_size=self.input_dim,
|
| 61 |
+
kernel_size=conv_size,
|
| 62 |
+
bias=conv_bias,
|
| 63 |
+
activation=None,
|
| 64 |
+
)
|
| 65 |
+
self.i_conv1d = ShortConvolution(
|
| 66 |
+
hidden_size=self.input_dim,
|
| 67 |
+
kernel_size=conv_size,
|
| 68 |
+
bias=conv_bias,
|
| 69 |
+
activation=None,
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
self.g_norm = FusedRMSNormGated(
|
| 73 |
+
hidden_size=self.input_dim,
|
| 74 |
+
elementwise_affine=elementwise_affine,
|
| 75 |
+
eps=norm_eps,
|
| 76 |
+
)
|
| 77 |
+
self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
|
| 78 |
+
|
| 79 |
+
def forward(
|
| 80 |
+
self,
|
| 81 |
+
hidden_states: torch.Tensor,
|
| 82 |
+
attention_mask: torch.Tensor | None = None,
|
| 83 |
+
past_key_values: Cache | None = None,
|
| 84 |
+
use_cache: bool | None = False,
|
| 85 |
+
output_attentions: bool | None = False,
|
| 86 |
+
lower_bound: torch.Tensor | None = None,
|
| 87 |
+
**kwargs: Unpack[dict],
|
| 88 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 89 |
+
if attention_mask is not None:
|
| 90 |
+
assert len(attention_mask.shape) == 2, (
|
| 91 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 92 |
+
"for padding purposes (0 indicating padding). "
|
| 93 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
# launching the triton kernel for just one token will actually be slower
|
| 97 |
+
mode = 'fused_recurrent' if not self.training and hidden_states.shape[1] <= 64 else self.mode
|
| 98 |
+
|
| 99 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 100 |
+
|
| 101 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 102 |
+
if self.use_short_conv:
|
| 103 |
+
conv_state_i, conv_state_f = None, None
|
| 104 |
+
if last_state is not None:
|
| 105 |
+
conv_state_i, conv_state_f = last_state['conv_state']
|
| 106 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 107 |
+
i, conv_state_i = self.i_conv1d(
|
| 108 |
+
x=self.i_proj(hidden_states),
|
| 109 |
+
mask=conv_mask,
|
| 110 |
+
cache=conv_state_i,
|
| 111 |
+
output_final_state=use_cache,
|
| 112 |
+
cu_seqlens=cu_seqlens,
|
| 113 |
+
)
|
| 114 |
+
f, conv_state_f = self.f_conv1d(
|
| 115 |
+
x=self.f_proj(hidden_states),
|
| 116 |
+
mask=conv_mask,
|
| 117 |
+
cache=conv_state_f,
|
| 118 |
+
output_final_state=use_cache,
|
| 119 |
+
cu_seqlens=cu_seqlens,
|
| 120 |
+
)
|
| 121 |
+
else:
|
| 122 |
+
i = self.i_proj(hidden_states)
|
| 123 |
+
f = self.f_proj(hidden_states)
|
| 124 |
+
|
| 125 |
+
f = F.logsigmoid(f)
|
| 126 |
+
# the lower bound for the first layer is zero
|
| 127 |
+
if lower_bound is not None and self.layer_idx > 0:
|
| 128 |
+
f = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + f).to(f)
|
| 129 |
+
i = swiglu(i, 1 - f.exp())
|
| 130 |
+
|
| 131 |
+
# dealing with left-padding
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
i = i.mul(attention_mask[:, -i.shape[-2]:, None])
|
| 134 |
+
|
| 135 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 136 |
+
if mode == 'chunk':
|
| 137 |
+
if cu_seqlens is not None:
|
| 138 |
+
raise NotImplementedError("Chunk mode does not support variable-length sequences.")
|
| 139 |
+
o, recurrent_state = chunk_hgrn(
|
| 140 |
+
x=i,
|
| 141 |
+
g=f,
|
| 142 |
+
initial_state=recurrent_state,
|
| 143 |
+
output_final_state=use_cache,
|
| 144 |
+
)
|
| 145 |
+
elif mode == 'fused_recurrent':
|
| 146 |
+
o, recurrent_state = fused_recurrent_hgrn(
|
| 147 |
+
x=i,
|
| 148 |
+
g=f,
|
| 149 |
+
initial_state=recurrent_state,
|
| 150 |
+
output_final_state=use_cache,
|
| 151 |
+
cu_seqlens=cu_seqlens,
|
| 152 |
+
)
|
| 153 |
+
else:
|
| 154 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 155 |
+
|
| 156 |
+
update_layer_cache(
|
| 157 |
+
self,
|
| 158 |
+
past_key_values,
|
| 159 |
+
recurrent_state=recurrent_state,
|
| 160 |
+
conv_state=(conv_state_i, conv_state_f) if self.use_short_conv else None,
|
| 161 |
+
offset=i.shape[1],
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
o = self.g_norm(o, self.g_proj(hidden_states))
|
| 165 |
+
o = self.o_proj(o)
|
| 166 |
+
|
| 167 |
+
return o, None, past_key_values
|
| 168 |
+
|
| 169 |
+
def state_size(self, **kwargs) -> int:
|
| 170 |
+
state_size = self.hidden_size
|
| 171 |
+
for module in self.children():
|
| 172 |
+
if isinstance(module, ShortConvolution):
|
| 173 |
+
state_size += module.state_size
|
| 174 |
+
return state_size
|
fla/layers/hgrn2.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "HGRN2: Gated Linear RNNs with State Expansion"[https://arxiv.org/abs/2404.07904]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 15 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 16 |
+
from fla.modules.activations import swish
|
| 17 |
+
from fla.modules.layernorm import rms_norm_linear
|
| 18 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 19 |
+
|
| 20 |
+
if TYPE_CHECKING:
|
| 21 |
+
from transformers.processing_utils import Unpack
|
| 22 |
+
|
| 23 |
+
from fla.models.utils import Cache
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class HGRN2Attention(nn.Module):
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
mode: str = 'chunk',
|
| 31 |
+
hidden_size: int = 1024,
|
| 32 |
+
num_heads: int | None = None,
|
| 33 |
+
expand_ratio: int | None = 128,
|
| 34 |
+
use_short_conv: bool = False,
|
| 35 |
+
conv_size: int = 4,
|
| 36 |
+
conv_bias: bool = False,
|
| 37 |
+
elementwise_affine: bool | None = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int = None,
|
| 40 |
+
) -> HGRN2Attention:
|
| 41 |
+
super().__init__()
|
| 42 |
+
|
| 43 |
+
self.mode = mode
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
|
| 46 |
+
if expand_ratio is not None:
|
| 47 |
+
num_heads = hidden_size // expand_ratio
|
| 48 |
+
elif expand_ratio is None and num_heads is not None:
|
| 49 |
+
expand_ratio = hidden_size // num_heads
|
| 50 |
+
elif expand_ratio is None and num_heads is None:
|
| 51 |
+
raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
|
| 52 |
+
self.num_heads = num_heads
|
| 53 |
+
self.expand_ratio = expand_ratio
|
| 54 |
+
|
| 55 |
+
self.use_short_conv = use_short_conv
|
| 56 |
+
self.conv_size = conv_size
|
| 57 |
+
self.conv_bias = conv_bias
|
| 58 |
+
|
| 59 |
+
self.forget_dim = int(self.num_heads * self.expand_ratio)
|
| 60 |
+
self.input_dim = hidden_size
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 64 |
+
assert self.forget_dim % num_heads == 0, f"forget dim must be divisible by num_heads of {num_heads}"
|
| 65 |
+
assert self.input_dim % num_heads == 0, f"input dim must be divisible by num_heads of {num_heads}"
|
| 66 |
+
|
| 67 |
+
self.head_f_dim = self.expand_ratio
|
| 68 |
+
self.head_i_dim = self.hidden_size // num_heads
|
| 69 |
+
|
| 70 |
+
self.q_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
|
| 71 |
+
self.f_proj = nn.Linear(hidden_size, self.forget_dim, bias=False)
|
| 72 |
+
self.i_proj = nn.Linear(hidden_size, self.input_dim, bias=False)
|
| 73 |
+
|
| 74 |
+
if use_short_conv:
|
| 75 |
+
self.conv_size = conv_size
|
| 76 |
+
self.q_conv1d = ShortConvolution(
|
| 77 |
+
hidden_size=self.forget_dim,
|
| 78 |
+
kernel_size=conv_size,
|
| 79 |
+
bias=conv_bias,
|
| 80 |
+
activation=None,
|
| 81 |
+
)
|
| 82 |
+
self.f_conv1d = ShortConvolution(
|
| 83 |
+
hidden_size=self.forget_dim,
|
| 84 |
+
kernel_size=conv_size,
|
| 85 |
+
bias=conv_bias,
|
| 86 |
+
activation=None,
|
| 87 |
+
)
|
| 88 |
+
self.i_conv1d = ShortConvolution(
|
| 89 |
+
hidden_size=self.input_dim,
|
| 90 |
+
kernel_size=conv_size,
|
| 91 |
+
bias=conv_bias,
|
| 92 |
+
activation=None,
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
self.g_norm = RMSNorm(hidden_size=self.hidden_size, elementwise_affine=elementwise_affine,
|
| 96 |
+
eps=norm_eps, dtype=torch.float32)
|
| 97 |
+
self.o_proj = nn.Linear(self.input_dim, hidden_size, bias=False)
|
| 98 |
+
|
| 99 |
+
def forward(
|
| 100 |
+
self,
|
| 101 |
+
hidden_states: torch.Tensor,
|
| 102 |
+
attention_mask: torch.Tensor | None = None,
|
| 103 |
+
past_key_values: Cache | None = None,
|
| 104 |
+
use_cache: bool | None = False,
|
| 105 |
+
output_attentions: bool | None = False,
|
| 106 |
+
lower_bound: torch.Tensor | None = None,
|
| 107 |
+
**kwargs: Unpack[dict],
|
| 108 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 109 |
+
if attention_mask is not None:
|
| 110 |
+
assert len(attention_mask.shape) == 2, (
|
| 111 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 112 |
+
"for padding purposes (0 indicating padding). "
|
| 113 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 117 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 118 |
+
|
| 119 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 120 |
+
|
| 121 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 122 |
+
if attention_mask is not None:
|
| 123 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 124 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 125 |
+
|
| 126 |
+
if self.use_short_conv:
|
| 127 |
+
conv_state_q, conv_state_f, conv_state_i = None, None, None
|
| 128 |
+
if last_state is not None:
|
| 129 |
+
conv_state_q, conv_state_f, conv_state_i = last_state['conv_state']
|
| 130 |
+
q, conv_state_q = self.q_conv1d(
|
| 131 |
+
x=self.q_proj(hidden_states),
|
| 132 |
+
cache=conv_state_q,
|
| 133 |
+
output_final_state=use_cache,
|
| 134 |
+
cu_seqlens=cu_seqlens,
|
| 135 |
+
)
|
| 136 |
+
f, conv_state_f = self.f_conv1d(
|
| 137 |
+
x=self.f_proj(hidden_states),
|
| 138 |
+
cache=conv_state_f,
|
| 139 |
+
output_final_state=use_cache,
|
| 140 |
+
cu_seqlens=cu_seqlens,
|
| 141 |
+
)
|
| 142 |
+
i, conv_state_i = self.i_conv1d(
|
| 143 |
+
x=self.i_proj(hidden_states),
|
| 144 |
+
cache=conv_state_i,
|
| 145 |
+
output_final_state=use_cache,
|
| 146 |
+
cu_seqlens=cu_seqlens,
|
| 147 |
+
)
|
| 148 |
+
else:
|
| 149 |
+
q = self.q_proj(hidden_states)
|
| 150 |
+
f = self.f_proj(hidden_states)
|
| 151 |
+
i = self.i_proj(hidden_states)
|
| 152 |
+
|
| 153 |
+
q = swish(q)
|
| 154 |
+
|
| 155 |
+
g = F.logsigmoid(f)
|
| 156 |
+
# the lower bound for the first layer is zero
|
| 157 |
+
if lower_bound is not None and self.layer_idx > 0:
|
| 158 |
+
g = torch.logaddexp(lower_bound.log(), torch.log1p(-lower_bound) + g)
|
| 159 |
+
k = 1 - g.exp()
|
| 160 |
+
|
| 161 |
+
q, k, g = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k.to(i), g))
|
| 162 |
+
i = rearrange(i, '... (h d) -> ... h d', d=self.head_i_dim)
|
| 163 |
+
|
| 164 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 165 |
+
if mode == 'fused_recurrent':
|
| 166 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 167 |
+
q=q,
|
| 168 |
+
k=k,
|
| 169 |
+
v=i,
|
| 170 |
+
gk=g,
|
| 171 |
+
initial_state=recurrent_state,
|
| 172 |
+
output_final_state=use_cache,
|
| 173 |
+
cu_seqlens=cu_seqlens,
|
| 174 |
+
)
|
| 175 |
+
elif mode == 'fused_chunk':
|
| 176 |
+
o, recurrent_state = fused_chunk_gla(
|
| 177 |
+
q=q,
|
| 178 |
+
k=k,
|
| 179 |
+
v=i,
|
| 180 |
+
g=g,
|
| 181 |
+
initial_state=recurrent_state,
|
| 182 |
+
output_final_state=use_cache,
|
| 183 |
+
)
|
| 184 |
+
elif mode == 'chunk':
|
| 185 |
+
o, recurrent_state = chunk_gla(
|
| 186 |
+
q=q,
|
| 187 |
+
k=k,
|
| 188 |
+
v=i,
|
| 189 |
+
g=g,
|
| 190 |
+
initial_state=recurrent_state,
|
| 191 |
+
output_final_state=use_cache,
|
| 192 |
+
cu_seqlens=cu_seqlens,
|
| 193 |
+
)
|
| 194 |
+
else:
|
| 195 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 196 |
+
|
| 197 |
+
update_layer_cache(
|
| 198 |
+
self,
|
| 199 |
+
past_key_values,
|
| 200 |
+
recurrent_state=recurrent_state,
|
| 201 |
+
conv_state=(conv_state_q, conv_state_f, conv_state_i) if self.use_short_conv else None,
|
| 202 |
+
offset=q_len,
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 206 |
+
o = rms_norm_linear(o, self.g_norm.weight, self.g_norm.bias, self.o_proj.weight, self.o_proj.bias)
|
| 207 |
+
if attention_mask is not None:
|
| 208 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 209 |
+
|
| 210 |
+
return o, None, past_key_values
|
fla/layers/kda.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 14 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 15 |
+
from fla.ops.kda import chunk_kda, fused_recurrent_kda
|
| 16 |
+
from fla.ops.kda.gate import fused_kda_gate
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class KimiDeltaAttention(nn.Module):
|
| 25 |
+
"""
|
| 26 |
+
Kimi Delta Attention (KDA) layer implementation.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
hidden_size (int, Optional):
|
| 30 |
+
The hidden size of the input. Default: 2048.
|
| 31 |
+
expand_v (float, Optional):
|
| 32 |
+
The expansion ratio for the value dimension. Default: 1.0.
|
| 33 |
+
head_dim (int, Optional):
|
| 34 |
+
The dimension of each head. Default: 128.
|
| 35 |
+
num_heads (int, Optional):
|
| 36 |
+
The number of heads. Default: 16.
|
| 37 |
+
num_v_heads (int, Optional):
|
| 38 |
+
The number of heads for the value projection, equal to `num_heads` if `None`.
|
| 39 |
+
GVA (Grouped Value Attention) is applied if `num_v_heads` > `num_heads`. Default: `None`.
|
| 40 |
+
mode (str, Optional):
|
| 41 |
+
Which Kimi Delta Attention kernel to use.
|
| 42 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 43 |
+
Default: `chunk`.
|
| 44 |
+
use_short_conv (bool, Optional):
|
| 45 |
+
Whether to use short convolutions. Default: `True`.
|
| 46 |
+
allow_neg_eigval (bool, Optional):
|
| 47 |
+
Allow negative eigenvalues. Default: `False`. If set to `True`, the beta will be multiplied by 2.
|
| 48 |
+
See reference:
|
| 49 |
+
[Unlocking State-Tracking in Linear RNNs Through Negative Eigenvalues](https://arxiv.org/abs/2411.12537)
|
| 50 |
+
conv_size (int, Optional):
|
| 51 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 52 |
+
conv_bias (bool, Optional):
|
| 53 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 54 |
+
layer_idx (int, Optional):
|
| 55 |
+
The index of the layer. Default: None.
|
| 56 |
+
norm_eps (float, Optional):
|
| 57 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(
|
| 61 |
+
self,
|
| 62 |
+
hidden_size: int = 2048,
|
| 63 |
+
expand_v: float = 1,
|
| 64 |
+
head_dim: int = 128,
|
| 65 |
+
num_heads: int = 16,
|
| 66 |
+
num_v_heads: int = None,
|
| 67 |
+
mode: str = "chunk",
|
| 68 |
+
use_short_conv: bool = True,
|
| 69 |
+
allow_neg_eigval: bool = False,
|
| 70 |
+
conv_size: int = 4,
|
| 71 |
+
conv_bias: bool = False,
|
| 72 |
+
layer_idx: int = None,
|
| 73 |
+
norm_eps: float = 1e-5,
|
| 74 |
+
**kwargs,
|
| 75 |
+
) -> KimiDeltaAttention:
|
| 76 |
+
super().__init__()
|
| 77 |
+
|
| 78 |
+
self.mode = mode
|
| 79 |
+
self.allow_neg_eigval = allow_neg_eigval
|
| 80 |
+
self.hidden_size = hidden_size
|
| 81 |
+
self.expand_v = expand_v
|
| 82 |
+
|
| 83 |
+
self.use_short_conv = use_short_conv
|
| 84 |
+
self.conv_size = conv_size
|
| 85 |
+
self.conv_bias = conv_bias
|
| 86 |
+
|
| 87 |
+
self.head_dim = head_dim
|
| 88 |
+
self.num_heads = num_heads
|
| 89 |
+
self.num_v_heads = num_v_heads if num_v_heads is not None else num_heads
|
| 90 |
+
|
| 91 |
+
self.head_k_dim = head_dim
|
| 92 |
+
self.head_v_dim = int(self.head_dim * self.expand_v)
|
| 93 |
+
self.key_dim = int(self.num_heads * self.head_k_dim)
|
| 94 |
+
self.value_dim = int(self.num_v_heads * self.head_v_dim)
|
| 95 |
+
self.layer_idx = layer_idx
|
| 96 |
+
|
| 97 |
+
# Consistency check: Ensure expand_v produces integer values
|
| 98 |
+
if not math.isclose(self.num_v_heads * self.head_dim * expand_v, self.value_dim, rel_tol=1e-5):
|
| 99 |
+
raise ValueError(
|
| 100 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by key_dim={self.key_dim}. "
|
| 101 |
+
f"Resulting value_dim would be {self.num_v_heads * self.head_dim * expand_v}, which is invalid for nn.Linear.",
|
| 102 |
+
)
|
| 103 |
+
if self.num_v_heads > self.num_heads and self.num_v_heads % self.num_heads != 0:
|
| 104 |
+
raise ValueError(
|
| 105 |
+
f"num_v_heads={self.num_v_heads} must be divisible by num_heads={self.num_heads}.",
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if not math.isclose(head_dim * expand_v, self.head_v_dim, rel_tol=1e-5):
|
| 109 |
+
raise ValueError(
|
| 110 |
+
f"expand_v={expand_v} does not produce an integer value when multiplied by head_dim={head_dim}. "
|
| 111 |
+
f"Resulting head_v_dim would be {head_dim * expand_v}, which is invalid for FusedRMSNormGated.",
|
| 112 |
+
)
|
| 113 |
+
assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 114 |
+
|
| 115 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 116 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 117 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 118 |
+
|
| 119 |
+
if use_short_conv:
|
| 120 |
+
self.q_conv1d = ShortConvolution(
|
| 121 |
+
hidden_size=self.key_dim,
|
| 122 |
+
kernel_size=conv_size,
|
| 123 |
+
bias=conv_bias,
|
| 124 |
+
activation="silu",
|
| 125 |
+
)
|
| 126 |
+
self.k_conv1d = ShortConvolution(
|
| 127 |
+
hidden_size=self.key_dim,
|
| 128 |
+
kernel_size=conv_size,
|
| 129 |
+
bias=conv_bias,
|
| 130 |
+
activation="silu",
|
| 131 |
+
)
|
| 132 |
+
self.v_conv1d = ShortConvolution(
|
| 133 |
+
hidden_size=self.value_dim,
|
| 134 |
+
kernel_size=conv_size,
|
| 135 |
+
bias=conv_bias,
|
| 136 |
+
activation="silu",
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
self.f_proj = nn.Sequential(
|
| 140 |
+
nn.Linear(hidden_size, self.head_v_dim, bias=False),
|
| 141 |
+
nn.Linear(self.head_v_dim, self.key_dim, bias=False),
|
| 142 |
+
)
|
| 143 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 144 |
+
|
| 145 |
+
self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
|
| 146 |
+
self.A_log._no_weight_decay = True
|
| 147 |
+
dt = torch.exp(
|
| 148 |
+
torch.rand(self.key_dim, dtype=torch.float32) * (math.log(0.1) - math.log(0.001)) + math.log(0.001)
|
| 149 |
+
).clamp(min=1e-4)
|
| 150 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 151 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 152 |
+
self.dt_bias._no_weight_decay = True
|
| 153 |
+
|
| 154 |
+
self.g_proj = nn.Sequential(
|
| 155 |
+
nn.Linear(hidden_size, self.head_v_dim, bias=False),
|
| 156 |
+
nn.Linear(self.head_v_dim, self.value_dim, bias=True),
|
| 157 |
+
)
|
| 158 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, activation="sigmoid", eps=norm_eps)
|
| 159 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 160 |
+
|
| 161 |
+
def forward(
|
| 162 |
+
self,
|
| 163 |
+
hidden_states: torch.Tensor,
|
| 164 |
+
attention_mask: torch.Tensor | None = None,
|
| 165 |
+
past_key_values: Cache | None = None,
|
| 166 |
+
use_cache: bool | None = False,
|
| 167 |
+
output_attentions: bool | None = False,
|
| 168 |
+
**kwargs: Unpack[dict],
|
| 169 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 170 |
+
if attention_mask is not None:
|
| 171 |
+
assert len(attention_mask.shape) == 2, (
|
| 172 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 173 |
+
"for padding purposes (0 indicating padding). "
|
| 174 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 178 |
+
# change to inference mode.
|
| 179 |
+
mode = "fused_recurrent" if (q_len <= 64 and not self.training) else self.mode
|
| 180 |
+
if self.training:
|
| 181 |
+
assert mode == "chunk", "Only chunk mode is supported in training."
|
| 182 |
+
|
| 183 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 184 |
+
|
| 185 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 186 |
+
if attention_mask is not None:
|
| 187 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 188 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 189 |
+
|
| 190 |
+
if self.use_short_conv:
|
| 191 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 192 |
+
if last_state is not None:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = last_state["conv_state"]
|
| 194 |
+
q, conv_state_q = self.q_conv1d(
|
| 195 |
+
x=self.q_proj(hidden_states),
|
| 196 |
+
cache=conv_state_q,
|
| 197 |
+
output_final_state=use_cache,
|
| 198 |
+
cu_seqlens=cu_seqlens,
|
| 199 |
+
)
|
| 200 |
+
k, conv_state_k = self.k_conv1d(
|
| 201 |
+
x=self.k_proj(hidden_states),
|
| 202 |
+
cache=conv_state_k,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
cu_seqlens=cu_seqlens,
|
| 205 |
+
)
|
| 206 |
+
v, conv_state_v = self.v_conv1d(
|
| 207 |
+
x=self.v_proj(hidden_states),
|
| 208 |
+
cache=conv_state_v,
|
| 209 |
+
output_final_state=use_cache,
|
| 210 |
+
cu_seqlens=cu_seqlens,
|
| 211 |
+
)
|
| 212 |
+
else:
|
| 213 |
+
q = F.silu(self.q_proj(hidden_states))
|
| 214 |
+
k = F.silu(self.k_proj(hidden_states))
|
| 215 |
+
v = F.silu(self.v_proj(hidden_states))
|
| 216 |
+
|
| 217 |
+
g = self.f_proj(hidden_states)
|
| 218 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 219 |
+
|
| 220 |
+
q, k, g = (rearrange(x, "... (h d) -> ... h d", d=self.head_k_dim) for x in (q, k, g))
|
| 221 |
+
v = rearrange(v, "... (h d) -> ... h d", d=self.head_v_dim)
|
| 222 |
+
|
| 223 |
+
# for multi-value attention, we repeat the inputs for simplicity.
|
| 224 |
+
if self.num_v_heads > self.num_heads:
|
| 225 |
+
q, k, g = (repeat(x, "... h d -> ... (h g) d", g=self.num_v_heads // self.num_heads) for x in (q, k, g))
|
| 226 |
+
beta = repeat(beta, "... h -> ... (h g)", g=self.num_v_heads // self.num_heads)
|
| 227 |
+
|
| 228 |
+
if self.allow_neg_eigval:
|
| 229 |
+
beta = beta * 2.0
|
| 230 |
+
|
| 231 |
+
recurrent_state = last_state["recurrent_state"] if last_state is not None else None
|
| 232 |
+
if mode == "chunk":
|
| 233 |
+
o, recurrent_state = chunk_kda(
|
| 234 |
+
q=q,
|
| 235 |
+
k=k,
|
| 236 |
+
v=v,
|
| 237 |
+
g=g,
|
| 238 |
+
beta=beta,
|
| 239 |
+
A_log=self.A_log,
|
| 240 |
+
dt_bias=self.dt_bias,
|
| 241 |
+
initial_state=recurrent_state,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
use_qk_l2norm_in_kernel=True,
|
| 244 |
+
use_gate_in_kernel=True,
|
| 245 |
+
cu_seqlens=cu_seqlens,
|
| 246 |
+
)
|
| 247 |
+
elif mode == "fused_recurrent":
|
| 248 |
+
g = fused_kda_gate(g=g, A_log=self.A_log, dt_bias=self.dt_bias)
|
| 249 |
+
o, recurrent_state = fused_recurrent_kda(
|
| 250 |
+
q=q,
|
| 251 |
+
k=k,
|
| 252 |
+
v=v,
|
| 253 |
+
g=g,
|
| 254 |
+
beta=beta,
|
| 255 |
+
initial_state=recurrent_state,
|
| 256 |
+
output_final_state=use_cache,
|
| 257 |
+
use_qk_l2norm_in_kernel=True,
|
| 258 |
+
cu_seqlens=cu_seqlens,
|
| 259 |
+
)
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 262 |
+
|
| 263 |
+
update_layer_cache(
|
| 264 |
+
self,
|
| 265 |
+
past_key_values,
|
| 266 |
+
recurrent_state=recurrent_state,
|
| 267 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 268 |
+
offset=q_len,
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
o = self.o_norm(o, rearrange(self.g_proj(hidden_states), "... (h d) -> ... h d", d=self.head_v_dim))
|
| 272 |
+
o = rearrange(o, "b t h d -> b t (h d)")
|
| 273 |
+
o = self.o_proj(o)
|
| 274 |
+
if attention_mask is not None:
|
| 275 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 276 |
+
|
| 277 |
+
return o, None, past_key_values
|
fla/layers/lightnet.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# ["You Only Scan Once: Efficient Multi-dimension Sequential Modeling with LightNet"](https://arxiv.org/abs/2405.21022)
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
|
| 14 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 15 |
+
from fla.modules import FusedRMSNormGated, ShortConvolution
|
| 16 |
+
from fla.modules.fused_norm_gate import rms_norm_swish_gate_linear
|
| 17 |
+
from fla.ops.gla import chunk_gla, fused_recurrent_gla
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from transformers.processing_utils import Unpack
|
| 21 |
+
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class LightNetAttention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
num_heads: int | None = None,
|
| 32 |
+
expand_ratio: int | None = 128,
|
| 33 |
+
use_short_conv: bool = False,
|
| 34 |
+
conv_size: int = 4,
|
| 35 |
+
conv_bias: bool = False,
|
| 36 |
+
gate_low_rank_dim: int = 128,
|
| 37 |
+
elementwise_affine: bool | None = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int = None,
|
| 40 |
+
) -> LightNetAttention:
|
| 41 |
+
super().__init__()
|
| 42 |
+
|
| 43 |
+
self.mode = mode
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
|
| 46 |
+
if expand_ratio is None and num_heads is not None:
|
| 47 |
+
expand_ratio = hidden_size // num_heads
|
| 48 |
+
elif expand_ratio is not None and num_heads is None:
|
| 49 |
+
num_heads = hidden_size // expand_ratio
|
| 50 |
+
elif expand_ratio is None and num_heads is None:
|
| 51 |
+
raise RuntimeError("One of `expand_ratio` or `num_heads` should be provided.")
|
| 52 |
+
self.num_heads = num_heads
|
| 53 |
+
self.expand_ratio = expand_ratio
|
| 54 |
+
|
| 55 |
+
self.use_short_conv = use_short_conv
|
| 56 |
+
self.conv_size = conv_size
|
| 57 |
+
self.conv_bias = conv_bias
|
| 58 |
+
|
| 59 |
+
self.key_dim = int(self.num_heads * self.expand_ratio)
|
| 60 |
+
self.value_dim = hidden_size
|
| 61 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 62 |
+
self.layer_idx = layer_idx
|
| 63 |
+
|
| 64 |
+
assert mode in ['chunk', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 65 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 66 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 67 |
+
|
| 68 |
+
self.head_f_dim = self.expand_ratio
|
| 69 |
+
self.head_i_dim = self.hidden_size // num_heads
|
| 70 |
+
|
| 71 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 72 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 73 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 74 |
+
|
| 75 |
+
if use_short_conv:
|
| 76 |
+
self.conv_size = conv_size
|
| 77 |
+
self.q_conv1d = ShortConvolution(
|
| 78 |
+
hidden_size=self.key_dim,
|
| 79 |
+
kernel_size=conv_size,
|
| 80 |
+
bias=conv_bias,
|
| 81 |
+
activation=None,
|
| 82 |
+
)
|
| 83 |
+
self.k_conv1d = ShortConvolution(
|
| 84 |
+
hidden_size=self.key_dim,
|
| 85 |
+
kernel_size=conv_size,
|
| 86 |
+
bias=conv_bias,
|
| 87 |
+
activation=None,
|
| 88 |
+
)
|
| 89 |
+
self.v_conv1d = ShortConvolution(
|
| 90 |
+
hidden_size=self.value_dim,
|
| 91 |
+
kernel_size=conv_size,
|
| 92 |
+
bias=conv_bias,
|
| 93 |
+
activation=None,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
self.g_proj = nn.Sequential(
|
| 97 |
+
nn.Linear(hidden_size, gate_low_rank_dim, bias=False),
|
| 98 |
+
nn.Linear(gate_low_rank_dim, hidden_size, bias=False),
|
| 99 |
+
)
|
| 100 |
+
self.g_norm = FusedRMSNormGated(
|
| 101 |
+
hidden_size=hidden_size,
|
| 102 |
+
elementwise_affine=elementwise_affine,
|
| 103 |
+
eps=norm_eps,
|
| 104 |
+
)
|
| 105 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 106 |
+
|
| 107 |
+
def forward(
|
| 108 |
+
self,
|
| 109 |
+
hidden_states: torch.Tensor,
|
| 110 |
+
attention_mask: torch.Tensor | None = None,
|
| 111 |
+
past_key_values: Cache | None = None,
|
| 112 |
+
use_cache: bool | None = False,
|
| 113 |
+
output_attentions: bool | None = False,
|
| 114 |
+
**kwargs: Unpack[dict],
|
| 115 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 116 |
+
if attention_mask is not None:
|
| 117 |
+
assert len(attention_mask.shape) == 2, (
|
| 118 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 119 |
+
"for padding purposes (0 indicating padding). "
|
| 120 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
# launching the triton kernel for just one token will actually be slower
|
| 124 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 125 |
+
|
| 126 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 127 |
+
|
| 128 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 129 |
+
if self.use_short_conv:
|
| 130 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 131 |
+
if last_state is not None:
|
| 132 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 133 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 134 |
+
q, conv_state_q = self.q_conv1d(
|
| 135 |
+
x=self.q_proj(hidden_states),
|
| 136 |
+
mask=conv_mask,
|
| 137 |
+
cache=conv_state_q,
|
| 138 |
+
output_final_state=use_cache,
|
| 139 |
+
cu_seqlens=cu_seqlens,
|
| 140 |
+
)
|
| 141 |
+
k, conv_state_k = self.k_conv1d(
|
| 142 |
+
x=self.k_proj(hidden_states),
|
| 143 |
+
mask=conv_mask,
|
| 144 |
+
cache=conv_state_k,
|
| 145 |
+
output_final_state=use_cache,
|
| 146 |
+
cu_seqlens=cu_seqlens,
|
| 147 |
+
)
|
| 148 |
+
v, conv_state_v = self.v_conv1d(
|
| 149 |
+
x=self.v_proj(hidden_states),
|
| 150 |
+
mask=conv_mask,
|
| 151 |
+
cache=conv_state_v,
|
| 152 |
+
output_final_state=use_cache,
|
| 153 |
+
cu_seqlens=cu_seqlens,
|
| 154 |
+
)
|
| 155 |
+
else:
|
| 156 |
+
q = self.q_proj(hidden_states)
|
| 157 |
+
k = self.k_proj(hidden_states)
|
| 158 |
+
v = self.v_proj(hidden_states)
|
| 159 |
+
|
| 160 |
+
# dealing with left-padding
|
| 161 |
+
if attention_mask is not None:
|
| 162 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 163 |
+
|
| 164 |
+
q = F.silu(q)
|
| 165 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_f_dim), (q, k))
|
| 166 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_i_dim)
|
| 167 |
+
# TODO: this 2 steps took huge amount of time, which should be optimized
|
| 168 |
+
last_z = last_state['ffn_state'] if last_state is not None and last_state.get('ffn_state') is not None else None
|
| 169 |
+
if last_z is not None:
|
| 170 |
+
# Decode path: continue logcumsumexp from cached state
|
| 171 |
+
z = torch.logaddexp(last_z, k.float())
|
| 172 |
+
k, g = torch.exp(k - z).to(k.dtype), (last_z - z).to(k.dtype)
|
| 173 |
+
else:
|
| 174 |
+
# Prefill path: mask padding positions to -inf so they don't affect logcumsumexp
|
| 175 |
+
if cu_seqlens is not None:
|
| 176 |
+
raise NotImplementedError("LightNet does not support variable-length sequences for now.")
|
| 177 |
+
k_float = k.float()
|
| 178 |
+
if attention_mask is not None:
|
| 179 |
+
pad_mask = attention_mask[:, -k.shape[1]:, None, None] # (B, T, 1, 1)
|
| 180 |
+
k_for_z = k_float.masked_fill(pad_mask == 0, float('-inf'))
|
| 181 |
+
else:
|
| 182 |
+
k_for_z = k_float
|
| 183 |
+
z = k_for_z.logcumsumexp(1)
|
| 184 |
+
k_new = torch.exp(k_float - z)
|
| 185 |
+
g_new = torch.cat((z[:, :1], z[:, :-1]), 1) - z
|
| 186 |
+
# NaN/inf arise at fully-masked positions (-inf - (-inf)), zero them out
|
| 187 |
+
k = torch.nan_to_num(k_new, nan=0.0, posinf=0.0).to(k.dtype)
|
| 188 |
+
g = torch.nan_to_num(g_new, nan=0.0, posinf=0.0, neginf=0.0).to(k.dtype)
|
| 189 |
+
|
| 190 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 191 |
+
if mode == 'fused_recurrent':
|
| 192 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 193 |
+
q=q,
|
| 194 |
+
k=k,
|
| 195 |
+
v=v,
|
| 196 |
+
gk=g,
|
| 197 |
+
initial_state=recurrent_state,
|
| 198 |
+
output_final_state=use_cache,
|
| 199 |
+
cu_seqlens=cu_seqlens,
|
| 200 |
+
)
|
| 201 |
+
elif mode == 'chunk':
|
| 202 |
+
o, recurrent_state = chunk_gla(
|
| 203 |
+
q=q,
|
| 204 |
+
k=k,
|
| 205 |
+
v=v,
|
| 206 |
+
g=g,
|
| 207 |
+
initial_state=recurrent_state,
|
| 208 |
+
output_final_state=use_cache,
|
| 209 |
+
cu_seqlens=cu_seqlens,
|
| 210 |
+
)
|
| 211 |
+
else:
|
| 212 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 213 |
+
|
| 214 |
+
update_layer_cache(
|
| 215 |
+
self,
|
| 216 |
+
past_key_values,
|
| 217 |
+
recurrent_state=recurrent_state,
|
| 218 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 219 |
+
ffn_state=z[:, -1:],
|
| 220 |
+
offset=q.shape[1],
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
o = rms_norm_swish_gate_linear(
|
| 224 |
+
rearrange(o, 'b t h d -> b t (h d)'),
|
| 225 |
+
self.g_proj(hidden_states),
|
| 226 |
+
self.g_norm.weight,
|
| 227 |
+
self.g_norm.bias,
|
| 228 |
+
self.o_proj.weight,
|
| 229 |
+
self.o_proj.bias,
|
| 230 |
+
)
|
| 231 |
+
return o, None, past_key_values
|
| 232 |
+
|
| 233 |
+
def state_size(self, **kwargs) -> int:
|
| 234 |
+
state_size = self.key_dim * self.head_i_dim
|
| 235 |
+
for module in self.children():
|
| 236 |
+
if isinstance(module, ShortConvolution):
|
| 237 |
+
state_size += module.state_size
|
| 238 |
+
return state_size
|
fla/layers/linear_attn.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import RMSNorm
|
| 14 |
+
from fla.modules.feature_map import DPFPFeatureMap, HadamardFeatureMap, HedgehogFeatureMap, T2RFeatureMap
|
| 15 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn, fused_recurrent_linear_attn
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class LinearAttention(nn.Module):
|
| 22 |
+
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
mode: str = 'chunk',
|
| 26 |
+
hidden_size: str = 1024,
|
| 27 |
+
expand_k: float = 1.0,
|
| 28 |
+
expand_v: float = 1.0,
|
| 29 |
+
num_heads: int = 8,
|
| 30 |
+
num_kv_heads: int | None = None,
|
| 31 |
+
feature_map: str = 'elementwise_product',
|
| 32 |
+
tie_feature_map_qk: bool = False,
|
| 33 |
+
output_norm: str = 'rmsnorm',
|
| 34 |
+
norm_q: bool = False,
|
| 35 |
+
norm_k: bool = False,
|
| 36 |
+
do_feature_map_norm: bool = False,
|
| 37 |
+
elementwise_affine: bool = True,
|
| 38 |
+
norm_eps: float = 1e-5,
|
| 39 |
+
layer_idx: int | None = None,
|
| 40 |
+
**kwargs,
|
| 41 |
+
):
|
| 42 |
+
super().__init__()
|
| 43 |
+
|
| 44 |
+
self.hidden_size = hidden_size
|
| 45 |
+
self.mode = mode
|
| 46 |
+
self.num_heads = num_heads
|
| 47 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 48 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 49 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 50 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 51 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 52 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 53 |
+
|
| 54 |
+
assert mode in ['chunk', 'fused_chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 55 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 56 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 57 |
+
|
| 58 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 59 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 60 |
+
self.do_feature_map_norm = do_feature_map_norm
|
| 61 |
+
self.layer_idx = layer_idx
|
| 62 |
+
|
| 63 |
+
if feature_map == 'hedgehog':
|
| 64 |
+
if tie_feature_map_qk:
|
| 65 |
+
self.feature_map_q = self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 66 |
+
else:
|
| 67 |
+
self.feature_map_q = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 68 |
+
self.feature_map_k = HedgehogFeatureMap(head_dim=self.head_k_dim)
|
| 69 |
+
|
| 70 |
+
elif feature_map == 't2r':
|
| 71 |
+
if tie_feature_map_qk:
|
| 72 |
+
self.feature_map_q = self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 73 |
+
else:
|
| 74 |
+
self.feature_map_q = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 75 |
+
self.feature_map_k = T2RFeatureMap(head_dim=self.head_k_dim)
|
| 76 |
+
|
| 77 |
+
elif feature_map == 'elementwise_product':
|
| 78 |
+
if tie_feature_map_qk:
|
| 79 |
+
self.feature_map_q = self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 80 |
+
else:
|
| 81 |
+
self.feature_map_q = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 82 |
+
self.feature_map_k = HadamardFeatureMap(head_dim=self.head_k_dim)
|
| 83 |
+
|
| 84 |
+
elif feature_map == 'dpfp':
|
| 85 |
+
self.feature_map_q = DPFPFeatureMap(head_dim=self.head_k_dim)
|
| 86 |
+
self.feature_map_k = DPFPFeatureMap(head_dim=self.head_k_dim)
|
| 87 |
+
|
| 88 |
+
elif feature_map == 'elu':
|
| 89 |
+
def elu(x):
|
| 90 |
+
return F.elu(x) + 1
|
| 91 |
+
self.feature_map_q = elu
|
| 92 |
+
self.feature_map_k = elu
|
| 93 |
+
|
| 94 |
+
elif feature_map == 'relu':
|
| 95 |
+
self.feature_map_q = nn.ReLU()
|
| 96 |
+
self.feature_map_k = nn.ReLU()
|
| 97 |
+
|
| 98 |
+
elif feature_map == 'identity':
|
| 99 |
+
self.feature_map_q = nn.Identity()
|
| 100 |
+
self.feature_map_k = nn.Identity()
|
| 101 |
+
else:
|
| 102 |
+
raise NotImplementedError(f"Not supported feature map `{feature_map}`.")
|
| 103 |
+
|
| 104 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 105 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 106 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 107 |
+
|
| 108 |
+
if output_norm == 'rmsnorm':
|
| 109 |
+
self.norm = RMSNorm(hidden_size=self.head_v_dim, elementwise_affine=elementwise_affine,
|
| 110 |
+
eps=norm_eps, dtype=torch.float32)
|
| 111 |
+
elif output_norm == 'identity':
|
| 112 |
+
self.norm = nn.Identity()
|
| 113 |
+
else:
|
| 114 |
+
raise NotImplementedError(f"Not supported output norm `{output_norm}`.")
|
| 115 |
+
|
| 116 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 117 |
+
|
| 118 |
+
self.norm_q = norm_q
|
| 119 |
+
self.norm_k = norm_k
|
| 120 |
+
|
| 121 |
+
def forward(
|
| 122 |
+
self,
|
| 123 |
+
hidden_states: torch.Tensor,
|
| 124 |
+
attention_mask: torch.Tensor | None = None,
|
| 125 |
+
past_key_values: Cache | None = None,
|
| 126 |
+
use_cache: bool | None = False,
|
| 127 |
+
output_attentions: bool | None = False,
|
| 128 |
+
**kwargs,
|
| 129 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 130 |
+
# Match other recurrent layers: use the recurrent kernel for decode/small chunks.
|
| 131 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 132 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 133 |
+
|
| 134 |
+
q = self.q_proj(hidden_states)
|
| 135 |
+
k = self.k_proj(hidden_states)
|
| 136 |
+
v = self.v_proj(hidden_states)
|
| 137 |
+
|
| 138 |
+
if attention_mask is not None:
|
| 139 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 140 |
+
|
| 141 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 142 |
+
if self.num_kv_groups > 1:
|
| 143 |
+
k = repeat(k, '... (h d) -> ... (h g) d', d=self.head_k_dim, g=self.num_kv_groups)
|
| 144 |
+
v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
|
| 145 |
+
else:
|
| 146 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 147 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 148 |
+
|
| 149 |
+
q = self.feature_map_q(q)
|
| 150 |
+
k = self.feature_map_k(k)
|
| 151 |
+
|
| 152 |
+
if self.norm_q:
|
| 153 |
+
q = q / (q.sum(-1, True) + 1e-4)
|
| 154 |
+
if self.norm_k:
|
| 155 |
+
k = k / (k.sum(-1, True) + 1e-4)
|
| 156 |
+
|
| 157 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 158 |
+
if mode == 'chunk':
|
| 159 |
+
o, final_state = chunk_linear_attn(
|
| 160 |
+
q=q,
|
| 161 |
+
k=k,
|
| 162 |
+
v=v,
|
| 163 |
+
initial_state=recurrent_state,
|
| 164 |
+
output_final_state=use_cache,
|
| 165 |
+
normalize=self.do_feature_map_norm,
|
| 166 |
+
)
|
| 167 |
+
elif mode == 'fused_chunk':
|
| 168 |
+
o, final_state = fused_chunk_linear_attn(
|
| 169 |
+
q=q,
|
| 170 |
+
k=k,
|
| 171 |
+
v=v,
|
| 172 |
+
initial_state=recurrent_state,
|
| 173 |
+
output_final_state=use_cache,
|
| 174 |
+
normalize=self.do_feature_map_norm,
|
| 175 |
+
)
|
| 176 |
+
elif mode == 'fused_recurrent':
|
| 177 |
+
o, final_state = fused_recurrent_linear_attn(
|
| 178 |
+
q=q,
|
| 179 |
+
k=k,
|
| 180 |
+
v=v,
|
| 181 |
+
initial_state=recurrent_state,
|
| 182 |
+
output_final_state=use_cache,
|
| 183 |
+
normalize=self.do_feature_map_norm,
|
| 184 |
+
)
|
| 185 |
+
else:
|
| 186 |
+
raise NotImplementedError
|
| 187 |
+
update_layer_cache(
|
| 188 |
+
self,
|
| 189 |
+
past_key_values,
|
| 190 |
+
recurrent_state=final_state,
|
| 191 |
+
offset=q.shape[1],
|
| 192 |
+
)
|
| 193 |
+
o = self.norm(o)
|
| 194 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 195 |
+
o = self.o_proj(o)
|
| 196 |
+
return o, None, past_key_values
|
fla/layers/log_linear_mamba2.py
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
from typing import TYPE_CHECKING
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
from einops import rearrange
|
| 9 |
+
from transformers.activations import ACT2FN
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.layers.mamba2 import apply_mask_to_padding_states, causal_conv1d_fn, causal_conv1d_update, is_fast_path_available
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules.layernorm_gated import RMSNormGated, rmsnorm_fn
|
| 15 |
+
from fla.ops.log_linear_attn.chunk import LogLinearAttentionState, chunk_log_linear_attn
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
logger = logging.get_logger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def ceil_log(x: int, b: int) -> int:
|
| 24 |
+
return math.ceil(math.log(x, b))
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def get_num_levels(length: int, base: int) -> int:
|
| 28 |
+
return ceil_log(length, base) + 1
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
MAX_SEQUENCE_LENGTH = 2048 * 8
|
| 32 |
+
LAMBDA_LEVEL_BASE = 2
|
| 33 |
+
MAX_NUM_LEVELS = get_num_levels(length=MAX_SEQUENCE_LENGTH, base=LAMBDA_LEVEL_BASE)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def hmamba_chunk_scan_combined(
|
| 37 |
+
x: torch.Tensor,
|
| 38 |
+
dt: torch.Tensor,
|
| 39 |
+
A: torch.Tensor,
|
| 40 |
+
B: torch.Tensor,
|
| 41 |
+
C: torch.Tensor,
|
| 42 |
+
dl: torch.Tensor,
|
| 43 |
+
L: torch.Tensor,
|
| 44 |
+
chunk_size: int,
|
| 45 |
+
D: torch.Tensor | None = None,
|
| 46 |
+
z: torch.Tensor | None = None,
|
| 47 |
+
dt_bias: torch.Tensor | None = None,
|
| 48 |
+
initial_states: LogLinearAttentionState | None = None,
|
| 49 |
+
seq_idx: torch.Tensor | None = None,
|
| 50 |
+
cu_seqlens: torch.Tensor | None = None,
|
| 51 |
+
dt_softplus: bool = False,
|
| 52 |
+
dt_limit: tuple[float, float] = (0.0, float("inf")),
|
| 53 |
+
return_final_states: bool = False,
|
| 54 |
+
):
|
| 55 |
+
if z is not None:
|
| 56 |
+
raise NotImplementedError
|
| 57 |
+
if seq_idx is not None:
|
| 58 |
+
raise NotImplementedError
|
| 59 |
+
if cu_seqlens is not None:
|
| 60 |
+
raise NotImplementedError
|
| 61 |
+
if dt_softplus is not True:
|
| 62 |
+
raise NotImplementedError
|
| 63 |
+
if tuple(dt_limit) != (0.0, float("inf")):
|
| 64 |
+
raise NotImplementedError
|
| 65 |
+
if chunk_size != 64:
|
| 66 |
+
raise NotImplementedError
|
| 67 |
+
if not B.shape == C.shape:
|
| 68 |
+
raise ValueError("B and C must have the same shape")
|
| 69 |
+
|
| 70 |
+
if D is not None:
|
| 71 |
+
if D.dim() != 1:
|
| 72 |
+
raise ValueError
|
| 73 |
+
D = rearrange(D, "h -> 1 1 h 1")
|
| 74 |
+
D_residual = x * D
|
| 75 |
+
|
| 76 |
+
if dt_bias is not None:
|
| 77 |
+
dt = dt + rearrange(dt_bias, "h -> 1 1 h")
|
| 78 |
+
if dt_softplus:
|
| 79 |
+
dt = torch.nn.functional.softplus(dt)
|
| 80 |
+
if dt_limit != (0.0, float("inf")):
|
| 81 |
+
dt = torch.clamp(dt, min=dt_limit[0], max=dt_limit[1])
|
| 82 |
+
x = (x * rearrange(dt, "b l h -> b l h 1")).to(x.dtype)
|
| 83 |
+
A = rearrange(A, "h -> 1 1 h") * dt
|
| 84 |
+
|
| 85 |
+
L = torch.nn.functional.softplus(rearrange(L, "h ell -> 1 1 h ell") * dl).to(L.dtype)
|
| 86 |
+
|
| 87 |
+
y, state = chunk_log_linear_attn(
|
| 88 |
+
q=C,
|
| 89 |
+
k=B,
|
| 90 |
+
v=x,
|
| 91 |
+
g=A,
|
| 92 |
+
level_scales=L,
|
| 93 |
+
initial_state=initial_states,
|
| 94 |
+
output_final_state=return_final_states,
|
| 95 |
+
cu_seqlens=cu_seqlens,
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
if D is not None:
|
| 99 |
+
y = y + D_residual
|
| 100 |
+
|
| 101 |
+
return y, state
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def hmamba_split_conv1d_scan_combined(
|
| 105 |
+
zxbcdtdl: torch.Tensor,
|
| 106 |
+
conv1d_weight: torch.Tensor,
|
| 107 |
+
conv1d_bias: torch.Tensor,
|
| 108 |
+
dt_bias: torch.Tensor,
|
| 109 |
+
A: torch.Tensor,
|
| 110 |
+
L: torch.Tensor,
|
| 111 |
+
D: torch.Tensor,
|
| 112 |
+
chunk_size: int,
|
| 113 |
+
initial_states: torch.Tensor | None = None,
|
| 114 |
+
seq_idx: torch.Tensor | None = None,
|
| 115 |
+
dt_limit: tuple[float, float] = (0.0, float("inf")),
|
| 116 |
+
return_final_states: bool = False,
|
| 117 |
+
activation: str = "silu",
|
| 118 |
+
rmsnorm_weight: torch.Tensor | None = None,
|
| 119 |
+
rmsnorm_eps: float = 1e-6,
|
| 120 |
+
outproj_weight: torch.Tensor | None = None,
|
| 121 |
+
outproj_bias: torch.Tensor | None = None,
|
| 122 |
+
headdim: int | None = None,
|
| 123 |
+
ngroups: int = 1,
|
| 124 |
+
norm_before_gate: bool = True,
|
| 125 |
+
conv1d_fn=None,
|
| 126 |
+
conv_backend: str = "cuda",
|
| 127 |
+
) -> torch.Tensor:
|
| 128 |
+
"""
|
| 129 |
+
Argument:
|
| 130 |
+
zxbcdtdl: (batch, seqlen, 2 * dim + 2 * ngroups * dstate + nheads) where dim == nheads * headdim
|
| 131 |
+
conv1d_weight: (dim + 2 * ngroups * dstate, width)
|
| 132 |
+
conv1d_bias: (dim + 2 * ngroups * dstate,)
|
| 133 |
+
dt_bias: (nheads,)
|
| 134 |
+
A: (nheads)
|
| 135 |
+
L: (nheads, nlevels)
|
| 136 |
+
D: (nheads, headdim) or (nheads,)
|
| 137 |
+
initial_states: (batch, nheads, headdim, dstate)
|
| 138 |
+
seq_idx: (batch, seqlen), int32
|
| 139 |
+
rmsnorm_weight: (dim,)
|
| 140 |
+
outproj_weight: (out_dim, dim)
|
| 141 |
+
outproj_bias: (out_dim,)
|
| 142 |
+
headdim: if D is 1D, headdim must be passed in
|
| 143 |
+
norm_before_gate: if True, we do RMSNorm(x) * F.silu(z). If False, we do RMSNorm(x * F.silu(z))
|
| 144 |
+
Return:
|
| 145 |
+
out: (batch, seqlen, dim)
|
| 146 |
+
"""
|
| 147 |
+
if initial_states is not None:
|
| 148 |
+
raise NotImplementedError
|
| 149 |
+
if seq_idx is not None:
|
| 150 |
+
raise NotImplementedError
|
| 151 |
+
if dt_limit != (0.0, float("inf")):
|
| 152 |
+
raise NotImplementedError
|
| 153 |
+
if return_final_states is not False:
|
| 154 |
+
raise NotImplementedError
|
| 155 |
+
if norm_before_gate is not False:
|
| 156 |
+
raise NotImplementedError
|
| 157 |
+
if rmsnorm_weight is None:
|
| 158 |
+
raise NotImplementedError
|
| 159 |
+
if activation not in ["silu", "swish"]:
|
| 160 |
+
raise NotImplementedError
|
| 161 |
+
|
| 162 |
+
batch, seqlen, _ = zxbcdtdl.shape
|
| 163 |
+
dlambda = L.shape[-1]
|
| 164 |
+
(nheads,) = D.shape
|
| 165 |
+
dim = nheads * headdim
|
| 166 |
+
dstate = (zxbcdtdl.shape[-1] - 2 * dim - nheads - nheads * dlambda) // ngroups // 2
|
| 167 |
+
|
| 168 |
+
if D.dim() != 1:
|
| 169 |
+
raise ValueError
|
| 170 |
+
if headdim is None:
|
| 171 |
+
raise ValueError
|
| 172 |
+
if nheads % ngroups != 0:
|
| 173 |
+
raise ValueError
|
| 174 |
+
if zxbcdtdl.shape != (
|
| 175 |
+
batch,
|
| 176 |
+
seqlen,
|
| 177 |
+
2 * dim + 2 * ngroups * dstate + nheads + nheads * dlambda,
|
| 178 |
+
):
|
| 179 |
+
raise ValueError
|
| 180 |
+
if dt_bias.shape != (nheads,):
|
| 181 |
+
raise ValueError
|
| 182 |
+
if A.shape != (nheads,):
|
| 183 |
+
raise ValueError
|
| 184 |
+
if L.shape != (nheads, dlambda):
|
| 185 |
+
raise ValueError
|
| 186 |
+
if D.shape != (nheads,):
|
| 187 |
+
raise ValueError
|
| 188 |
+
if rmsnorm_weight is None:
|
| 189 |
+
raise ValueError
|
| 190 |
+
|
| 191 |
+
zxBCdtl_splits = [dim, dim + 2 * ngroups * dstate, nheads, nheads * dlambda]
|
| 192 |
+
xBC_splits = [dim, ngroups * dstate, ngroups * dstate]
|
| 193 |
+
z, xBC, dt, dl = torch.split(zxbcdtdl, zxBCdtl_splits, dim=-1)
|
| 194 |
+
_conv_fn = conv1d_fn if conv1d_fn is not None else causal_conv1d_fn
|
| 195 |
+
_conv_out = _conv_fn(
|
| 196 |
+
rearrange(xBC, "b s d -> b d s"),
|
| 197 |
+
conv1d_weight,
|
| 198 |
+
bias=conv1d_bias,
|
| 199 |
+
activation=activation,
|
| 200 |
+
seq_idx=seq_idx,
|
| 201 |
+
)
|
| 202 |
+
if conv_backend == 'triton':
|
| 203 |
+
_conv_out = _conv_out[0]
|
| 204 |
+
xBC = rearrange(_conv_out, "b d s -> b s d")
|
| 205 |
+
x, B, C = torch.split(xBC, xBC_splits, dim=-1)
|
| 206 |
+
x = rearrange(x, "b l (h p) -> b l h p", h=nheads, p=headdim)
|
| 207 |
+
B = rearrange(B, "b l (g n) -> b l g n", g=ngroups, n=dstate)
|
| 208 |
+
C = rearrange(C, "b l (g n) -> b l g n", g=ngroups, n=dstate)
|
| 209 |
+
dl = rearrange(dl, "b l (h ell) -> b l h ell", h=nheads, ell=dlambda)
|
| 210 |
+
y, _ = hmamba_chunk_scan_combined(
|
| 211 |
+
x=x,
|
| 212 |
+
dt=dt,
|
| 213 |
+
A=A,
|
| 214 |
+
B=B,
|
| 215 |
+
C=C,
|
| 216 |
+
dl=dl,
|
| 217 |
+
L=L,
|
| 218 |
+
chunk_size=chunk_size,
|
| 219 |
+
D=D,
|
| 220 |
+
z=z if rmsnorm_weight is None else None,
|
| 221 |
+
dt_bias=dt_bias,
|
| 222 |
+
dt_softplus=True,
|
| 223 |
+
seq_idx=seq_idx,
|
| 224 |
+
cu_seqlens=None,
|
| 225 |
+
dt_limit=dt_limit,
|
| 226 |
+
return_final_states=return_final_states,
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
y = rearrange(y, "b l h p -> b l (h p)")
|
| 230 |
+
if rmsnorm_weight is not None:
|
| 231 |
+
y = rmsnorm_fn(
|
| 232 |
+
x=y,
|
| 233 |
+
weight=rmsnorm_weight,
|
| 234 |
+
bias=None,
|
| 235 |
+
z=z,
|
| 236 |
+
eps=rmsnorm_eps,
|
| 237 |
+
group_size=None,
|
| 238 |
+
norm_before_gate=False,
|
| 239 |
+
)
|
| 240 |
+
out = torch.nn.functional.linear(y, outproj_weight, outproj_bias)
|
| 241 |
+
return out
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
class LogLinearMamba2(nn.Module):
|
| 245 |
+
"""
|
| 246 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 247 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 248 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 249 |
+
and is why Mamba is called **selective** state spaces)
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
def __init__(
|
| 253 |
+
self,
|
| 254 |
+
num_heads: int,
|
| 255 |
+
head_dim: int = 64,
|
| 256 |
+
hidden_size: int = 2048,
|
| 257 |
+
state_size: int = 128,
|
| 258 |
+
expand: int = 2,
|
| 259 |
+
n_groups: int = 1,
|
| 260 |
+
conv_kernel: int = 4,
|
| 261 |
+
use_conv_bias: bool = False,
|
| 262 |
+
hidden_act: str = "silu",
|
| 263 |
+
rms_norm: bool = True,
|
| 264 |
+
chunk_size: int = 64,
|
| 265 |
+
time_step_rank: float = 256,
|
| 266 |
+
time_step_limit: tuple[float, float] = (0.0, float("inf")),
|
| 267 |
+
time_step_min: float = 0.001,
|
| 268 |
+
time_step_max: float = 0.1,
|
| 269 |
+
use_bias: bool = True,
|
| 270 |
+
norm_eps: float = 1e-5,
|
| 271 |
+
layer_idx: int = None,
|
| 272 |
+
backend: str = "cuda",
|
| 273 |
+
):
|
| 274 |
+
super().__init__()
|
| 275 |
+
self.num_heads = num_heads
|
| 276 |
+
self.hidden_size = hidden_size
|
| 277 |
+
self.ssm_state_size = state_size
|
| 278 |
+
self.conv_kernel_size = conv_kernel
|
| 279 |
+
self.intermediate_size = int(expand * self.hidden_size)
|
| 280 |
+
self.time_step_rank = int(time_step_rank)
|
| 281 |
+
self.layer_idx = layer_idx
|
| 282 |
+
self.use_conv_bias = use_conv_bias
|
| 283 |
+
self.activation = hidden_act
|
| 284 |
+
self.act = ACT2FN[hidden_act]
|
| 285 |
+
|
| 286 |
+
self.layer_norm_epsilon = norm_eps
|
| 287 |
+
self.rms_norm = rms_norm
|
| 288 |
+
|
| 289 |
+
self.n_groups = n_groups
|
| 290 |
+
self.head_dim = head_dim
|
| 291 |
+
self.chunk_size = chunk_size
|
| 292 |
+
|
| 293 |
+
self.time_step_limit = time_step_limit
|
| 294 |
+
self.time_step_min = time_step_min
|
| 295 |
+
self.time_step_max = time_step_max
|
| 296 |
+
|
| 297 |
+
self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
|
| 298 |
+
self.conv1d = nn.Conv1d(
|
| 299 |
+
in_channels=self.conv_dim,
|
| 300 |
+
out_channels=self.conv_dim,
|
| 301 |
+
bias=use_conv_bias,
|
| 302 |
+
kernel_size=conv_kernel,
|
| 303 |
+
groups=self.conv_dim,
|
| 304 |
+
padding=conv_kernel - 1,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
self.num_lambda_dims = MAX_NUM_LEVELS
|
| 308 |
+
self.lambda_level_module = None
|
| 309 |
+
|
| 310 |
+
# projection of the input hidden states
|
| 311 |
+
projection_size = (
|
| 312 |
+
self.intermediate_size
|
| 313 |
+
+ self.conv_dim
|
| 314 |
+
+ self.num_heads * (self.num_lambda_dims + 1)
|
| 315 |
+
)
|
| 316 |
+
self.in_proj = nn.Linear(
|
| 317 |
+
self.hidden_size,
|
| 318 |
+
projection_size,
|
| 319 |
+
bias=use_bias,
|
| 320 |
+
)
|
| 321 |
+
# selective projection used to make dt, B and C input dependant
|
| 322 |
+
|
| 323 |
+
# time step projection (discretization)
|
| 324 |
+
# instantiate once and copy inv_dt in init_weights of PretrainedModel
|
| 325 |
+
self.dt_bias = nn.Parameter(torch.ones(self.num_heads))
|
| 326 |
+
|
| 327 |
+
# S4D real initialization. These are not discretized!
|
| 328 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 329 |
+
A = torch.arange(1, self.num_heads + 1)
|
| 330 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 331 |
+
self.A_log._no_weight_decay = True
|
| 332 |
+
|
| 333 |
+
self.lambda_mode = "positive"
|
| 334 |
+
L = torch.ones(self.num_heads, self.num_lambda_dims)
|
| 335 |
+
self.L = nn.Parameter(L)
|
| 336 |
+
self.L._no_weight_decay = True
|
| 337 |
+
|
| 338 |
+
self.norm = RMSNormGated(
|
| 339 |
+
self.intermediate_size, eps=self.layer_norm_epsilon, norm_before_gate=False,
|
| 340 |
+
)
|
| 341 |
+
self.D = nn.Parameter(torch.ones(self.num_heads))
|
| 342 |
+
self.D._no_weight_decay = True
|
| 343 |
+
|
| 344 |
+
self.out_proj = nn.Linear(
|
| 345 |
+
self.intermediate_size, self.hidden_size, bias=use_bias,
|
| 346 |
+
)
|
| 347 |
+
self.use_bias = use_bias
|
| 348 |
+
|
| 349 |
+
if not is_fast_path_available:
|
| 350 |
+
logger.warning_once(
|
| 351 |
+
"The fast path is not available because one of "
|
| 352 |
+
"`(selective_state_update, causal_conv1d_fn, causal_conv1d_update)` is None. "
|
| 353 |
+
"Falling back to the naive implementation. "
|
| 354 |
+
"To install follow https://github.com/state-spaces/mamba/#installation and"
|
| 355 |
+
"https://github.com/Dao-AILab/causal-conv1d",
|
| 356 |
+
)
|
| 357 |
+
import os
|
| 358 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 359 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 360 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 361 |
+
logger.warning_once(
|
| 362 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 363 |
+
"Falling back to the Triton backend. "
|
| 364 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 365 |
+
)
|
| 366 |
+
backend = 'triton'
|
| 367 |
+
if backend == 'triton':
|
| 368 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 369 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 370 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 371 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 372 |
+
logger.warning(
|
| 373 |
+
"LogLinearMamba2 does not recommend using Triton's conv1d backend, "
|
| 374 |
+
"as it is untested and may contain bugs.",
|
| 375 |
+
)
|
| 376 |
+
else:
|
| 377 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 378 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 379 |
+
self.backend = backend
|
| 380 |
+
|
| 381 |
+
def cuda_kernels_forward(
|
| 382 |
+
self,
|
| 383 |
+
hidden_states: torch.Tensor,
|
| 384 |
+
last_state: dict | None = None,
|
| 385 |
+
use_cache: bool = False,
|
| 386 |
+
attention_mask: torch.Tensor | None = None,
|
| 387 |
+
):
|
| 388 |
+
if self.activation not in ["silu", "swish"]:
|
| 389 |
+
raise ValueError
|
| 390 |
+
|
| 391 |
+
# 1. Gated MLP's linear projection
|
| 392 |
+
# Only apply padding mask during prefill (last_state is None).
|
| 393 |
+
# During decode, attention_mask has shape (B, accumulated_len) which
|
| 394 |
+
# mismatches hidden_states (B, 1, D).
|
| 395 |
+
hidden_states = apply_mask_to_padding_states(
|
| 396 |
+
hidden_states=hidden_states,
|
| 397 |
+
attention_mask=attention_mask if last_state is None else None,
|
| 398 |
+
)
|
| 399 |
+
projected_states = self.in_proj(hidden_states)
|
| 400 |
+
|
| 401 |
+
# Set up dimensions for reshapes later
|
| 402 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 403 |
+
groups_time_state_size = self.n_groups * self.ssm_state_size
|
| 404 |
+
d_mlp = (
|
| 405 |
+
projected_states.shape[-1]
|
| 406 |
+
- 2 * self.intermediate_size
|
| 407 |
+
- 2 * self.n_groups * self.ssm_state_size
|
| 408 |
+
- self.num_heads * (self.num_lambda_dims + 1)
|
| 409 |
+
) // 2
|
| 410 |
+
if d_mlp != 0:
|
| 411 |
+
raise ValueError
|
| 412 |
+
|
| 413 |
+
# Single step calculations via cache
|
| 414 |
+
if last_state is not None:
|
| 415 |
+
if hidden_states.shape[1] != 1:
|
| 416 |
+
raise ValueError("LogLinearMamba2 cached decoding only supports a single new token per step.")
|
| 417 |
+
|
| 418 |
+
gate, xBC, dt, dl = torch.split(
|
| 419 |
+
projected_states.squeeze(1),
|
| 420 |
+
[
|
| 421 |
+
self.intermediate_size,
|
| 422 |
+
self.conv_dim,
|
| 423 |
+
self.num_heads,
|
| 424 |
+
self.num_heads * self.num_lambda_dims,
|
| 425 |
+
],
|
| 426 |
+
dim=-1,
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
# 2. Convolution sequence transformation
|
| 430 |
+
conv_state = last_state['conv_state']
|
| 431 |
+
xBC = self.causal_conv1d_update(
|
| 432 |
+
xBC,
|
| 433 |
+
conv_state,
|
| 434 |
+
rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 435 |
+
self.conv1d.bias,
|
| 436 |
+
self.activation,
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
x, B, C = torch.split(
|
| 440 |
+
xBC,
|
| 441 |
+
[
|
| 442 |
+
self.intermediate_size,
|
| 443 |
+
groups_time_state_size,
|
| 444 |
+
groups_time_state_size,
|
| 445 |
+
],
|
| 446 |
+
dim=-1,
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
# 3. SSM transformation
|
| 450 |
+
A = -torch.exp(self.A_log.float()) # (nheads,)
|
| 451 |
+
B = rearrange(
|
| 452 |
+
B,
|
| 453 |
+
"b (g n) -> b g n",
|
| 454 |
+
b=batch_size,
|
| 455 |
+
g=self.n_groups,
|
| 456 |
+
n=self.ssm_state_size,
|
| 457 |
+
)
|
| 458 |
+
C = rearrange(
|
| 459 |
+
C,
|
| 460 |
+
"b (g n) -> b g n",
|
| 461 |
+
b=batch_size,
|
| 462 |
+
g=self.n_groups,
|
| 463 |
+
n=self.ssm_state_size,
|
| 464 |
+
)
|
| 465 |
+
x_reshaped = rearrange(
|
| 466 |
+
x,
|
| 467 |
+
"b (h p) -> b h p",
|
| 468 |
+
b=batch_size,
|
| 469 |
+
h=self.num_heads,
|
| 470 |
+
p=self.head_dim,
|
| 471 |
+
)
|
| 472 |
+
dl_reshaped = rearrange(
|
| 473 |
+
dl,
|
| 474 |
+
"b (h ell) -> b h ell",
|
| 475 |
+
b=batch_size,
|
| 476 |
+
h=self.num_heads,
|
| 477 |
+
ell=self.num_lambda_dims,
|
| 478 |
+
)
|
| 479 |
+
y, hssm_state = hmamba_chunk_scan_combined(
|
| 480 |
+
x_reshaped,
|
| 481 |
+
dt=dt,
|
| 482 |
+
A=A,
|
| 483 |
+
B=B,
|
| 484 |
+
C=C,
|
| 485 |
+
dl=dl_reshaped,
|
| 486 |
+
L=self.L,
|
| 487 |
+
D=self.D,
|
| 488 |
+
z=None,
|
| 489 |
+
dt_bias=self.dt_bias,
|
| 490 |
+
dt_softplus=True,
|
| 491 |
+
initial_states=last_state['recurrent_state'],
|
| 492 |
+
return_final_states=True,
|
| 493 |
+
)
|
| 494 |
+
y = rearrange(
|
| 495 |
+
y,
|
| 496 |
+
"b h p -> b (h p)",
|
| 497 |
+
b=batch_size,
|
| 498 |
+
h=self.num_heads,
|
| 499 |
+
p=self.head_dim,
|
| 500 |
+
)
|
| 501 |
+
y = self.norm(y, gate)
|
| 502 |
+
|
| 503 |
+
# 4. Final linear projection
|
| 504 |
+
out = self.out_proj(y)[:, None, ...]
|
| 505 |
+
return out, conv_state, hssm_state
|
| 506 |
+
|
| 507 |
+
# Fused calculations or step by step if no initialized cache is found
|
| 508 |
+
else:
|
| 509 |
+
A = -torch.exp(
|
| 510 |
+
self.A_log.float(),
|
| 511 |
+
) # (num_heads) or (intermediate_size, state_size)
|
| 512 |
+
dt_limit_kwargs = (
|
| 513 |
+
{}
|
| 514 |
+
if self.time_step_limit == (0.0, float("inf"))
|
| 515 |
+
else {"dt_limit": self.time_step_limit}
|
| 516 |
+
)
|
| 517 |
+
|
| 518 |
+
# 2-4. Fused kernel for conv1d, SSM, and the final projection
|
| 519 |
+
if self.training and not use_cache:
|
| 520 |
+
out = torch.utils.checkpoint.checkpoint(
|
| 521 |
+
hmamba_split_conv1d_scan_combined,
|
| 522 |
+
use_reentrant=False,
|
| 523 |
+
# function arguments
|
| 524 |
+
zxbcdtdl=projected_states,
|
| 525 |
+
conv1d_weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 526 |
+
conv1d_bias=self.conv1d.bias,
|
| 527 |
+
dt_bias=self.dt_bias,
|
| 528 |
+
A=A,
|
| 529 |
+
L=self.L,
|
| 530 |
+
D=self.D,
|
| 531 |
+
chunk_size=self.chunk_size,
|
| 532 |
+
conv1d_fn=self.causal_conv1d_fn,
|
| 533 |
+
conv_backend=self.backend,
|
| 534 |
+
seq_idx=None, # was seq_idx
|
| 535 |
+
activation=self.activation,
|
| 536 |
+
rmsnorm_weight=self.norm.weight,
|
| 537 |
+
rmsnorm_eps=self.norm.eps,
|
| 538 |
+
outproj_weight=self.out_proj.weight,
|
| 539 |
+
outproj_bias=self.out_proj.bias,
|
| 540 |
+
headdim=self.head_dim,
|
| 541 |
+
ngroups=self.n_groups,
|
| 542 |
+
norm_before_gate=False,
|
| 543 |
+
return_final_states=False,
|
| 544 |
+
**dt_limit_kwargs,
|
| 545 |
+
)
|
| 546 |
+
return out, None, None
|
| 547 |
+
|
| 548 |
+
else:
|
| 549 |
+
gate, xBC, dt, dl = torch.split(
|
| 550 |
+
projected_states,
|
| 551 |
+
[
|
| 552 |
+
self.intermediate_size,
|
| 553 |
+
self.conv_dim,
|
| 554 |
+
self.num_heads,
|
| 555 |
+
self.num_heads * self.num_lambda_dims,
|
| 556 |
+
],
|
| 557 |
+
dim=-1,
|
| 558 |
+
)
|
| 559 |
+
|
| 560 |
+
# 2. Convolution sequence transformation
|
| 561 |
+
# Init cache
|
| 562 |
+
masked_xBC = apply_mask_to_padding_states(xBC, attention_mask)
|
| 563 |
+
new_conv_state = None
|
| 564 |
+
if use_cache:
|
| 565 |
+
xBC_t = rearrange(masked_xBC, "b l d -> b d l")
|
| 566 |
+
new_conv_state = torch.nn.functional.pad(
|
| 567 |
+
xBC_t,
|
| 568 |
+
(self.conv_kernel_size - xBC_t.shape[-1], 0),
|
| 569 |
+
)
|
| 570 |
+
|
| 571 |
+
_conv1d_output = self.causal_conv1d_fn(
|
| 572 |
+
x=xBC.transpose(1, 2),
|
| 573 |
+
weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
|
| 574 |
+
bias=self.conv1d.bias,
|
| 575 |
+
activation=self.activation,
|
| 576 |
+
)
|
| 577 |
+
if self.backend == 'cuda':
|
| 578 |
+
xBC = _conv1d_output.transpose(1, 2)
|
| 579 |
+
elif self.backend == 'triton':
|
| 580 |
+
xBC = _conv1d_output[0].transpose(1, 2).contiguous()
|
| 581 |
+
else:
|
| 582 |
+
raise ValueError(f"Unsupported backend: {self.backend}")
|
| 583 |
+
|
| 584 |
+
xBC = apply_mask_to_padding_states(
|
| 585 |
+
hidden_states=xBC,
|
| 586 |
+
attention_mask=attention_mask,
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
x, B, C = torch.split(
|
| 590 |
+
xBC,
|
| 591 |
+
[
|
| 592 |
+
self.intermediate_size,
|
| 593 |
+
groups_time_state_size,
|
| 594 |
+
groups_time_state_size,
|
| 595 |
+
],
|
| 596 |
+
dim=-1,
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
# 3. SSM transformation
|
| 600 |
+
y, hssm_state = hmamba_chunk_scan_combined(
|
| 601 |
+
rearrange(
|
| 602 |
+
x,
|
| 603 |
+
"b l (h p) -> b l h p",
|
| 604 |
+
b=batch_size,
|
| 605 |
+
l=seq_len,
|
| 606 |
+
p=self.head_dim,
|
| 607 |
+
),
|
| 608 |
+
dt=dt,
|
| 609 |
+
A=A,
|
| 610 |
+
B=rearrange(
|
| 611 |
+
B,
|
| 612 |
+
"b l (g n) -> b l g n",
|
| 613 |
+
b=batch_size,
|
| 614 |
+
l=seq_len,
|
| 615 |
+
g=self.n_groups,
|
| 616 |
+
),
|
| 617 |
+
C=rearrange(
|
| 618 |
+
C,
|
| 619 |
+
"b l (g n) -> b l g n",
|
| 620 |
+
b=batch_size,
|
| 621 |
+
l=seq_len,
|
| 622 |
+
g=self.n_groups,
|
| 623 |
+
),
|
| 624 |
+
dl=rearrange(
|
| 625 |
+
dl,
|
| 626 |
+
"b l (h ell) -> b l h ell",
|
| 627 |
+
b=batch_size,
|
| 628 |
+
h=self.num_heads,
|
| 629 |
+
ell=self.num_lambda_dims,
|
| 630 |
+
),
|
| 631 |
+
L=self.L,
|
| 632 |
+
chunk_size=self.chunk_size,
|
| 633 |
+
D=self.D,
|
| 634 |
+
z=None,
|
| 635 |
+
seq_idx=None,
|
| 636 |
+
return_final_states=True,
|
| 637 |
+
dt_bias=self.dt_bias,
|
| 638 |
+
dt_softplus=True,
|
| 639 |
+
**dt_limit_kwargs,
|
| 640 |
+
)
|
| 641 |
+
|
| 642 |
+
y = rearrange(
|
| 643 |
+
y,
|
| 644 |
+
"b l h p -> b l (h p)",
|
| 645 |
+
b=batch_size,
|
| 646 |
+
l=seq_len,
|
| 647 |
+
h=self.num_heads,
|
| 648 |
+
p=self.head_dim,
|
| 649 |
+
)
|
| 650 |
+
# Multiply "gate" branch and apply extra normalization layer
|
| 651 |
+
y = self.norm(y, gate)
|
| 652 |
+
|
| 653 |
+
# 4. Final linear projection
|
| 654 |
+
out = self.out_proj(y)
|
| 655 |
+
|
| 656 |
+
return out, new_conv_state, hssm_state
|
| 657 |
+
|
| 658 |
+
def forward(
|
| 659 |
+
self,
|
| 660 |
+
hidden_states: torch.Tensor,
|
| 661 |
+
attention_mask: torch.Tensor | None = None,
|
| 662 |
+
past_key_values: Cache | None = None,
|
| 663 |
+
use_cache: bool | None = False,
|
| 664 |
+
output_attentions: bool | None = False,
|
| 665 |
+
**kwargs,
|
| 666 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 667 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 668 |
+
|
| 669 |
+
if "cuda" in self.in_proj.weight.device.type:
|
| 670 |
+
output, conv_state, hssm_state = self.cuda_kernels_forward(
|
| 671 |
+
hidden_states, last_state, use_cache, attention_mask
|
| 672 |
+
)
|
| 673 |
+
else:
|
| 674 |
+
raise NotImplementedError
|
| 675 |
+
|
| 676 |
+
update_layer_cache(
|
| 677 |
+
self,
|
| 678 |
+
past_key_values,
|
| 679 |
+
recurrent_state=hssm_state,
|
| 680 |
+
conv_state=conv_state,
|
| 681 |
+
offset=hidden_states.shape[1],
|
| 682 |
+
)
|
| 683 |
+
|
| 684 |
+
return output, None, past_key_values
|
fla/layers/mamba.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules.activations import ACT2FN
|
| 14 |
+
|
| 15 |
+
with warnings.catch_warnings():
|
| 16 |
+
warnings.simplefilter('ignore')
|
| 17 |
+
try:
|
| 18 |
+
from mamba_ssm.ops.selective_scan_interface import mamba_inner_fn, selective_scan_fn
|
| 19 |
+
from mamba_ssm.ops.triton.selective_state_update import selective_state_update
|
| 20 |
+
except ImportError:
|
| 21 |
+
selective_state_update, selective_scan_fn, mamba_inner_fn = None, None, None
|
| 22 |
+
|
| 23 |
+
try:
|
| 24 |
+
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
|
| 25 |
+
except ImportError:
|
| 26 |
+
causal_conv1d_update, causal_conv1d_fn = None, None
|
| 27 |
+
is_fast_path_available = all((
|
| 28 |
+
selective_state_update,
|
| 29 |
+
selective_scan_fn,
|
| 30 |
+
mamba_inner_fn,
|
| 31 |
+
))
|
| 32 |
+
if TYPE_CHECKING:
|
| 33 |
+
from transformers.processing_utils import Unpack
|
| 34 |
+
|
| 35 |
+
from fla.models.utils import Cache
|
| 36 |
+
|
| 37 |
+
logger = logging.get_logger(__name__)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class Mamba(nn.Module):
|
| 41 |
+
"""
|
| 42 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 43 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 44 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 45 |
+
and is why Mamba is called **selective** state spaces)
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def __init__(
|
| 49 |
+
self,
|
| 50 |
+
hidden_size: int = 2048,
|
| 51 |
+
state_size: int = 16,
|
| 52 |
+
conv_kernel: int = 4,
|
| 53 |
+
use_conv_bias: bool = True,
|
| 54 |
+
intermediate_size: int = 2048,
|
| 55 |
+
time_step_rank: int = 256,
|
| 56 |
+
use_bias: bool = True,
|
| 57 |
+
hidden_act: str = "silu",
|
| 58 |
+
layer_idx: int = None,
|
| 59 |
+
backend: str = "cuda",
|
| 60 |
+
):
|
| 61 |
+
super().__init__()
|
| 62 |
+
|
| 63 |
+
self.hidden_size = hidden_size
|
| 64 |
+
self.ssm_state_size = state_size
|
| 65 |
+
self.conv_kernel_size = conv_kernel
|
| 66 |
+
self.use_conv_bias = use_conv_bias
|
| 67 |
+
self.intermediate_size = intermediate_size
|
| 68 |
+
self.time_step_rank = time_step_rank
|
| 69 |
+
self.use_bias = use_bias
|
| 70 |
+
|
| 71 |
+
self.conv1d = nn.Conv1d(
|
| 72 |
+
in_channels=self.intermediate_size,
|
| 73 |
+
out_channels=self.intermediate_size,
|
| 74 |
+
bias=use_conv_bias,
|
| 75 |
+
kernel_size=conv_kernel,
|
| 76 |
+
groups=self.intermediate_size,
|
| 77 |
+
padding=conv_kernel - 1,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
self.activation = hidden_act
|
| 81 |
+
self.act = ACT2FN[hidden_act]
|
| 82 |
+
|
| 83 |
+
self.layer_idx = layer_idx
|
| 84 |
+
|
| 85 |
+
# projection of the input hidden states
|
| 86 |
+
self.in_proj = nn.Linear(self.hidden_size, self.intermediate_size * 2, bias=use_bias)
|
| 87 |
+
# selective projection used to make dt, B and C input dependant
|
| 88 |
+
self.x_proj = nn.Linear(self.intermediate_size, self.time_step_rank + self.ssm_state_size * 2, bias=False)
|
| 89 |
+
# time step projection (discretization)
|
| 90 |
+
self.dt_proj = nn.Linear(self.time_step_rank, self.intermediate_size, bias=True)
|
| 91 |
+
|
| 92 |
+
# S4D real initialization. These are not discretized!
|
| 93 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 94 |
+
A = torch.arange(1, self.ssm_state_size + 1, dtype=torch.float32)[None, :]
|
| 95 |
+
A = A.expand(self.intermediate_size, -1).contiguous()
|
| 96 |
+
|
| 97 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 98 |
+
self.D = nn.Parameter(torch.ones(self.intermediate_size))
|
| 99 |
+
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
|
| 100 |
+
|
| 101 |
+
if not is_fast_path_available:
|
| 102 |
+
logger.warning_once(
|
| 103 |
+
"The fast path is not available because on of "
|
| 104 |
+
"`(selective_state_update, selective_scan_fn, causal_conv1d_fn, causal_conv1d_update, mamba_inner_fn)`"
|
| 105 |
+
" is None. Falling back to the naive implementation. "
|
| 106 |
+
"To install follow https://github.com/state-spaces/mamba/#installation and"
|
| 107 |
+
" https://github.com/Dao-AILab/causal-conv1d",
|
| 108 |
+
)
|
| 109 |
+
import os
|
| 110 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 111 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 112 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 113 |
+
logger.warning_once(
|
| 114 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 115 |
+
"Falling back to the Triton backend. "
|
| 116 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 117 |
+
)
|
| 118 |
+
backend = 'triton'
|
| 119 |
+
if backend == 'triton':
|
| 120 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 121 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 122 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 123 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 124 |
+
else:
|
| 125 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 126 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 127 |
+
self.backend = backend
|
| 128 |
+
|
| 129 |
+
def _to_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 130 |
+
return hidden_states.transpose(1, 2).contiguous()
|
| 131 |
+
|
| 132 |
+
def _from_causal_conv_layout(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 133 |
+
return hidden_states.transpose(1, 2).contiguous()
|
| 134 |
+
|
| 135 |
+
def _build_conv_state(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 136 |
+
seq_len = hidden_states.shape[-1]
|
| 137 |
+
if seq_len >= self.conv_kernel_size:
|
| 138 |
+
return hidden_states[..., -self.conv_kernel_size:].contiguous()
|
| 139 |
+
return nn.functional.pad(hidden_states, (self.conv_kernel_size - seq_len, 0)).contiguous()
|
| 140 |
+
|
| 141 |
+
def cuda_kernels_forward(
|
| 142 |
+
self,
|
| 143 |
+
hidden_states: torch.Tensor,
|
| 144 |
+
last_state: dict | None = None,
|
| 145 |
+
use_cache: bool | None = False,
|
| 146 |
+
attention_mask: torch.LongTensor | None = None,
|
| 147 |
+
**kwargs: Unpack[dict],
|
| 148 |
+
):
|
| 149 |
+
if last_state is not None and hidden_states.shape[1] != 1:
|
| 150 |
+
raise ValueError("Mamba cached decoding only supports a single new token per step.")
|
| 151 |
+
|
| 152 |
+
# 1. Gated MLP's linear projection
|
| 153 |
+
projected_states = self.in_proj(hidden_states).transpose(1, 2)
|
| 154 |
+
|
| 155 |
+
if self.training and not use_cache:
|
| 156 |
+
contextualized_states = mamba_inner_fn(
|
| 157 |
+
projected_states,
|
| 158 |
+
self.conv1d.weight,
|
| 159 |
+
self.conv1d.bias if self.use_conv_bias else None,
|
| 160 |
+
self.x_proj.weight,
|
| 161 |
+
self.dt_proj.weight,
|
| 162 |
+
self.out_proj.weight,
|
| 163 |
+
self.out_proj.bias.float() if self.use_bias else None,
|
| 164 |
+
-torch.exp(self.A_log.float()),
|
| 165 |
+
None, # input-dependent B
|
| 166 |
+
None, # input-dependent C
|
| 167 |
+
self.D.float(),
|
| 168 |
+
delta_bias=self.dt_proj.bias.float(),
|
| 169 |
+
delta_softplus=True,
|
| 170 |
+
)
|
| 171 |
+
return contextualized_states, None, None
|
| 172 |
+
|
| 173 |
+
hidden_states, gate = projected_states.chunk(2, dim=1)
|
| 174 |
+
|
| 175 |
+
if attention_mask is not None and last_state is None:
|
| 176 |
+
# Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
|
| 177 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 178 |
+
|
| 179 |
+
# 2. Convolution sequence transformation
|
| 180 |
+
conv_inputs = hidden_states
|
| 181 |
+
conv_weights = self.conv1d.weight.view(self.conv1d.weight.size(0), self.conv1d.weight.size(2))
|
| 182 |
+
if last_state is not None:
|
| 183 |
+
conv_state = last_state['conv_state']
|
| 184 |
+
ssm_state = last_state['recurrent_state']
|
| 185 |
+
|
| 186 |
+
if self.backend == 'triton':
|
| 187 |
+
hidden_states, conv_state = self.causal_conv1d_update(
|
| 188 |
+
x=self._to_causal_conv_layout(conv_inputs),
|
| 189 |
+
cache=conv_state,
|
| 190 |
+
weight=conv_weights,
|
| 191 |
+
bias=self.conv1d.bias,
|
| 192 |
+
activation=self.activation,
|
| 193 |
+
)
|
| 194 |
+
hidden_states = self._from_causal_conv_layout(hidden_states)
|
| 195 |
+
else:
|
| 196 |
+
hidden_states = self.causal_conv1d_update(
|
| 197 |
+
conv_inputs.squeeze(-1),
|
| 198 |
+
conv_state,
|
| 199 |
+
conv_weights,
|
| 200 |
+
self.conv1d.bias,
|
| 201 |
+
self.activation,
|
| 202 |
+
)
|
| 203 |
+
hidden_states = hidden_states.unsqueeze(-1)
|
| 204 |
+
else:
|
| 205 |
+
conv_state = None
|
| 206 |
+
ssm_state = None
|
| 207 |
+
if self.backend == 'triton':
|
| 208 |
+
hidden_states, conv_state = self.causal_conv1d_fn(
|
| 209 |
+
x=self._to_causal_conv_layout(conv_inputs),
|
| 210 |
+
weight=conv_weights,
|
| 211 |
+
bias=self.conv1d.bias,
|
| 212 |
+
activation=self.activation,
|
| 213 |
+
output_final_state=bool(use_cache),
|
| 214 |
+
)
|
| 215 |
+
hidden_states = self._from_causal_conv_layout(hidden_states)
|
| 216 |
+
else:
|
| 217 |
+
if use_cache:
|
| 218 |
+
conv_state = self._build_conv_state(conv_inputs)
|
| 219 |
+
hidden_states = self.causal_conv1d_fn(
|
| 220 |
+
conv_inputs, conv_weights, self.conv1d.bias, activation=self.activation,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
if attention_mask is not None and last_state is None:
|
| 224 |
+
# Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
|
| 225 |
+
# and those values would otherwise leak into x_proj and the SSM recurrence.
|
| 226 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 227 |
+
|
| 228 |
+
# 3. State Space Model sequence transformation
|
| 229 |
+
# 3.a. input varying initialization of time_step, B and C
|
| 230 |
+
ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
|
| 231 |
+
time_step, B, C = torch.split(
|
| 232 |
+
ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
|
| 233 |
+
)
|
| 234 |
+
discrete_time_step = self.dt_proj.weight @ time_step.transpose(1, 2)
|
| 235 |
+
|
| 236 |
+
A = -torch.exp(self.A_log.float())
|
| 237 |
+
# 3.c perform the recurrence y ← SSM(A, B, C)(x)
|
| 238 |
+
time_proj_bias = self.dt_proj.bias.float() if hasattr(self.dt_proj, "bias") else None
|
| 239 |
+
if last_state is not None:
|
| 240 |
+
scan_outputs = selective_state_update(
|
| 241 |
+
ssm_state,
|
| 242 |
+
hidden_states[..., 0],
|
| 243 |
+
discrete_time_step[..., 0],
|
| 244 |
+
A,
|
| 245 |
+
B[:, 0],
|
| 246 |
+
C[:, 0],
|
| 247 |
+
self.D,
|
| 248 |
+
gate[..., 0],
|
| 249 |
+
time_proj_bias,
|
| 250 |
+
dt_softplus=True,
|
| 251 |
+
).unsqueeze(-1)
|
| 252 |
+
else:
|
| 253 |
+
scan_outputs, ssm_state = selective_scan_fn(
|
| 254 |
+
hidden_states,
|
| 255 |
+
discrete_time_step,
|
| 256 |
+
A,
|
| 257 |
+
B.transpose(1, 2),
|
| 258 |
+
C.transpose(1, 2),
|
| 259 |
+
self.D.float(),
|
| 260 |
+
gate,
|
| 261 |
+
time_proj_bias,
|
| 262 |
+
delta_softplus=True,
|
| 263 |
+
return_last_state=True,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
# 4. Final linear projection
|
| 267 |
+
contextualized_states = self.out_proj(scan_outputs.transpose(1, 2))
|
| 268 |
+
return contextualized_states, conv_state, ssm_state
|
| 269 |
+
|
| 270 |
+
def slow_forward(
|
| 271 |
+
self,
|
| 272 |
+
input_states,
|
| 273 |
+
last_state: dict | None = None,
|
| 274 |
+
use_cache: bool | None = False,
|
| 275 |
+
attention_mask: torch.LongTensor | None = None,
|
| 276 |
+
**kwargs: Unpack[dict],
|
| 277 |
+
):
|
| 278 |
+
if last_state is not None and input_states.shape[1] != 1:
|
| 279 |
+
raise ValueError("Mamba cached decoding only supports a single new token per step.")
|
| 280 |
+
|
| 281 |
+
batch_size, seq_len, _ = input_states.shape
|
| 282 |
+
dtype = input_states.dtype
|
| 283 |
+
# 1. Gated MLP's linear projection
|
| 284 |
+
# [batch, 2 * intermediate_size, seq_len]
|
| 285 |
+
projected_states = self.in_proj(input_states).transpose(1, 2)
|
| 286 |
+
hidden_states, gate = projected_states.chunk(2, dim=1)
|
| 287 |
+
|
| 288 |
+
if attention_mask is not None and last_state is None:
|
| 289 |
+
# Mask before the depthwise conv so cached/prefill conv inputs do not keep pad tokens.
|
| 290 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 291 |
+
|
| 292 |
+
# 2. Convolution sequence transformation
|
| 293 |
+
if last_state is not None:
|
| 294 |
+
conv_state = last_state['conv_state']
|
| 295 |
+
ssm_state = last_state['recurrent_state'].clone().to(hidden_states.device)
|
| 296 |
+
|
| 297 |
+
# decode path: single token
|
| 298 |
+
conv_state = conv_state.roll(shifts=-1, dims=-1)
|
| 299 |
+
conv_state[:, :, -1] = hidden_states[:, :, 0].to(conv_state.device)
|
| 300 |
+
hidden_states = torch.sum(conv_state * self.conv1d.weight[:, 0, :], dim=-1)
|
| 301 |
+
if self.use_conv_bias:
|
| 302 |
+
hidden_states += self.conv1d.bias
|
| 303 |
+
# [batch, intermediate_size, 1] : decoding
|
| 304 |
+
hidden_states = self.act(hidden_states).to(dtype).unsqueeze(-1)
|
| 305 |
+
elif use_cache:
|
| 306 |
+
ssm_state = torch.zeros(
|
| 307 |
+
(batch_size, self.intermediate_size, self.ssm_state_size),
|
| 308 |
+
device=hidden_states.device, dtype=dtype,
|
| 309 |
+
)
|
| 310 |
+
conv_state = self._build_conv_state(hidden_states)
|
| 311 |
+
# [batch, intermediate_size, seq_len]
|
| 312 |
+
hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
|
| 313 |
+
else:
|
| 314 |
+
ssm_state = torch.zeros(
|
| 315 |
+
(batch_size, self.intermediate_size, self.ssm_state_size),
|
| 316 |
+
device=hidden_states.device, dtype=dtype,
|
| 317 |
+
)
|
| 318 |
+
conv_state = None
|
| 319 |
+
# [batch, intermediate_size, seq_len]
|
| 320 |
+
hidden_states = self.act(self.conv1d(hidden_states)[..., :seq_len])
|
| 321 |
+
|
| 322 |
+
if attention_mask is not None and last_state is None:
|
| 323 |
+
# Re-mask after the conv: causal kernels can regenerate non-zero values at masked positions,
|
| 324 |
+
# and those values would otherwise leak into x_proj and the SSM recurrence.
|
| 325 |
+
hidden_states = hidden_states * attention_mask.unsqueeze(1)
|
| 326 |
+
|
| 327 |
+
# 3. State Space Model sequence transformation
|
| 328 |
+
# 3.a. Selection: [batch, seq_len, self.time_step_rank + self.ssm_state_size * 2]
|
| 329 |
+
ssm_parameters = self.x_proj(hidden_states.transpose(1, 2))
|
| 330 |
+
time_step, B, C = torch.split(
|
| 331 |
+
ssm_parameters, [self.time_step_rank, self.ssm_state_size, self.ssm_state_size], dim=-1,
|
| 332 |
+
)
|
| 333 |
+
# [batch, seq_len, intermediate_size]
|
| 334 |
+
discrete_time_step = self.dt_proj(time_step)
|
| 335 |
+
# [batch, intermediate_size, seq_len]
|
| 336 |
+
discrete_time_step = nn.functional.softplus(discrete_time_step).transpose(1, 2)
|
| 337 |
+
|
| 338 |
+
# 3.b. Discretization: B and C to [batch, seq_len, intermediate_size, ssm_state_size] (SRAM)
|
| 339 |
+
# [intermediate_size, ssm_state_size]
|
| 340 |
+
A = -torch.exp(self.A_log.float())
|
| 341 |
+
# [batch, intermediate_size, seq_len, ssm_state_size]
|
| 342 |
+
discrete_A = torch.exp(A[None, :, None, :] * discrete_time_step[:, :, :, None])
|
| 343 |
+
# [batch, intermediate_size, seq_len, ssm_state_size]
|
| 344 |
+
discrete_B = discrete_time_step[:, :, :, None] * B[:, None, :, :].float()
|
| 345 |
+
deltaB_u = discrete_B * hidden_states[:, :, :, None].float()
|
| 346 |
+
|
| 347 |
+
# 3.c perform the recurrence y ← SSM(A, B, C)(x)
|
| 348 |
+
scan_outputs = []
|
| 349 |
+
for i in range(hidden_states.shape[-1]):
|
| 350 |
+
# [batch, intermediade_size, ssm_state]
|
| 351 |
+
ssm_state = discrete_A[:, :, i, :] * ssm_state + deltaB_u[:, :, i, :]
|
| 352 |
+
# [batch, intermediade_size, 1]
|
| 353 |
+
scan_output = torch.matmul(ssm_state.to(dtype), C[:, i, :].unsqueeze(-1))
|
| 354 |
+
scan_outputs.append(scan_output[:, :, 0])
|
| 355 |
+
# [batch, seq_len, intermediade_size]
|
| 356 |
+
scan_output = torch.stack(scan_outputs, dim=-1)
|
| 357 |
+
scan_output = scan_output + (hidden_states * self.D[None, :, None])
|
| 358 |
+
scan_output = (scan_output * self.act(gate))
|
| 359 |
+
|
| 360 |
+
# 4. Final linear projection
|
| 361 |
+
# [batch, seq_len, hidden_size]
|
| 362 |
+
contextualized_states = self.out_proj(scan_output.transpose(1, 2))
|
| 363 |
+
return contextualized_states, conv_state, ssm_state
|
| 364 |
+
# fmt: on
|
| 365 |
+
|
| 366 |
+
def forward(
|
| 367 |
+
self,
|
| 368 |
+
hidden_states: torch.Tensor,
|
| 369 |
+
attention_mask: torch.LongTensor | None = None,
|
| 370 |
+
past_key_values: Cache | None = None,
|
| 371 |
+
use_cache: bool | None = False,
|
| 372 |
+
output_attentions: bool | None = False,
|
| 373 |
+
**kwargs: Unpack[dict],
|
| 374 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 375 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 376 |
+
|
| 377 |
+
if is_fast_path_available and "cuda" in self.x_proj.weight.device.type:
|
| 378 |
+
output, conv_state, ssm_state = self.cuda_kernels_forward(
|
| 379 |
+
hidden_states, last_state, use_cache, attention_mask, **kwargs
|
| 380 |
+
)
|
| 381 |
+
else:
|
| 382 |
+
output, conv_state, ssm_state = self.slow_forward(
|
| 383 |
+
hidden_states, last_state, use_cache, attention_mask, **kwargs
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
if use_cache and past_key_values is not None:
|
| 387 |
+
update_layer_cache(
|
| 388 |
+
self,
|
| 389 |
+
past_key_values,
|
| 390 |
+
recurrent_state=ssm_state,
|
| 391 |
+
conv_state=conv_state,
|
| 392 |
+
offset=hidden_states.shape[1],
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
return output, None, past_key_values
|
fla/layers/mamba2.py
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import warnings
|
| 7 |
+
from typing import TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 14 |
+
from fla.modules.activations import ACT2FN
|
| 15 |
+
from fla.modules.layernorm_gated import RMSNormGated
|
| 16 |
+
|
| 17 |
+
with warnings.catch_warnings():
|
| 18 |
+
warnings.simplefilter('ignore')
|
| 19 |
+
try:
|
| 20 |
+
from mamba_ssm.ops.triton.selective_state_update import selective_state_update
|
| 21 |
+
from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined
|
| 22 |
+
except ImportError:
|
| 23 |
+
selective_state_update, mamba_chunk_scan_combined, mamba_split_conv1d_scan_combined = None, None, None
|
| 24 |
+
try:
|
| 25 |
+
from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
|
| 26 |
+
except ImportError:
|
| 27 |
+
causal_conv1d_update, causal_conv1d_fn = None, None
|
| 28 |
+
is_fast_path_available = selective_state_update is not None
|
| 29 |
+
|
| 30 |
+
if TYPE_CHECKING:
|
| 31 |
+
from fla.models.utils import Cache
|
| 32 |
+
|
| 33 |
+
logger = logging.get_logger(__name__)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def apply_mask_to_padding_states(hidden_states, attention_mask):
|
| 37 |
+
"""
|
| 38 |
+
Tunes out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66
|
| 39 |
+
"""
|
| 40 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
|
| 41 |
+
dtype = hidden_states.dtype
|
| 42 |
+
hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
|
| 43 |
+
|
| 44 |
+
return hidden_states
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def pad_tensor_by_size(input_tensor: torch.Tensor, pad_size: int):
|
| 48 |
+
"""
|
| 49 |
+
Padding x tensor with `pad_size` on the seq_len dim (dim=1)
|
| 50 |
+
|
| 51 |
+
Assumes that we only have tensors of either size 4 or 3
|
| 52 |
+
"""
|
| 53 |
+
pad_shape = (0, 0, 0, 0, 0, pad_size, 0, 0) if len(input_tensor.shape) == 4 else (0, 0, 0, pad_size, 0, 0)
|
| 54 |
+
|
| 55 |
+
return torch.nn.functional.pad(input_tensor, pad_shape, mode="constant", value=0)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def reshape_into_chunks(input_tensor, pad_size, chunk_size):
|
| 59 |
+
"""
|
| 60 |
+
Padding input_tensor with `pad_size` on the seq_len dim (dim=1) and
|
| 61 |
+
simultaneously splitting it into chunk sequences.
|
| 62 |
+
|
| 63 |
+
Assumes that we only have tensors of either size 4 or 3
|
| 64 |
+
"""
|
| 65 |
+
# [bsz, seq_len, ...] -> [bsz, seq_len multiple of chunk_size, ...]
|
| 66 |
+
input_tensor = pad_tensor_by_size(input_tensor, pad_size)
|
| 67 |
+
|
| 68 |
+
if len(input_tensor.shape) == 3:
|
| 69 |
+
# [bsz, seq_len multiple of chunk_size, num_heads] -> [bsz, -1, chunk_size, num_heads]
|
| 70 |
+
return input_tensor.reshape(input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2])
|
| 71 |
+
else:
|
| 72 |
+
# [bsz, seq_len multiple of chunk_size, num_heads, head_dim or state_size] ->
|
| 73 |
+
# [bsz, -1, chunk_size, num_heads, head_dim or state_size]
|
| 74 |
+
return input_tensor.reshape(
|
| 75 |
+
input_tensor.shape[0], -1, chunk_size, input_tensor.shape[2], input_tensor.shape[3],
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def segment_sum(input_tensor):
|
| 80 |
+
"""
|
| 81 |
+
More stable segment sum calculation. Uses cumulative sums and masking instead of direct subtractions.
|
| 82 |
+
"""
|
| 83 |
+
chunk_size = input_tensor.size(-1)
|
| 84 |
+
# 1. expand input tensor to have an additional dimension and repeat along that dimension
|
| 85 |
+
# [..., chunk_size] -> [..., chunk_size, chunk_size]
|
| 86 |
+
input_tensor = input_tensor[..., None].expand(*input_tensor.size(), chunk_size)
|
| 87 |
+
# 2. create a lower triangular mask with the diagonal set to 0 to 0 out elements above diag
|
| 88 |
+
mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=-1)
|
| 89 |
+
input_tensor = input_tensor.masked_fill(~mask, 0)
|
| 90 |
+
# 3. compute actual cumsum
|
| 91 |
+
tensor_segsum = torch.cumsum(input_tensor, dim=-2)
|
| 92 |
+
|
| 93 |
+
# 4. apply mask to keep only the lower triangular part of the cumulative sum result (incl diagonal this time)
|
| 94 |
+
mask = torch.tril(torch.ones(chunk_size, chunk_size, device=input_tensor.device, dtype=torch.bool), diagonal=0)
|
| 95 |
+
tensor_segsum = tensor_segsum.masked_fill(~mask, -torch.inf)
|
| 96 |
+
return tensor_segsum
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class Mamba2(nn.Module):
|
| 100 |
+
"""
|
| 101 |
+
Compute ∆, A, B, C, and D the state space parameters and compute the `contextualized_states`.
|
| 102 |
+
A, D are input independent (see Mamba paper [1] Section 3.5.2 "Interpretation of A" for why A isn't selective)
|
| 103 |
+
∆, B, C are input-dependent (this is a key difference between Mamba and the linear time invariant S4,
|
| 104 |
+
and is why Mamba is called **selective** state spaces)
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
def __init__(
|
| 108 |
+
self,
|
| 109 |
+
num_heads: int,
|
| 110 |
+
head_dim: int = 64,
|
| 111 |
+
hidden_size: int = 2048,
|
| 112 |
+
state_size: int = 128,
|
| 113 |
+
expand: int = 2,
|
| 114 |
+
n_groups: int = 1,
|
| 115 |
+
conv_kernel: int = 4,
|
| 116 |
+
use_conv_bias: bool = False,
|
| 117 |
+
hidden_act: str = "silu",
|
| 118 |
+
rms_norm: bool = True,
|
| 119 |
+
chunk_size: int = 256,
|
| 120 |
+
time_step_rank: float = 256,
|
| 121 |
+
time_step_limit: tuple[float, float] = (0.0, float("inf")),
|
| 122 |
+
time_step_min: float = 0.001,
|
| 123 |
+
time_step_max: float = 0.1,
|
| 124 |
+
use_bias: bool = True,
|
| 125 |
+
norm_eps: float = 1e-5,
|
| 126 |
+
layer_idx: int = None,
|
| 127 |
+
backend: str = "cuda",
|
| 128 |
+
) -> Mamba2:
|
| 129 |
+
super().__init__()
|
| 130 |
+
|
| 131 |
+
self.num_heads = num_heads
|
| 132 |
+
self.head_dim = head_dim
|
| 133 |
+
self.hidden_size = hidden_size
|
| 134 |
+
self.ssm_state_size = state_size
|
| 135 |
+
self.expand = expand
|
| 136 |
+
self.intermediate_size = int(expand * hidden_size)
|
| 137 |
+
self.n_groups = n_groups
|
| 138 |
+
|
| 139 |
+
self.conv_kernel_size = conv_kernel
|
| 140 |
+
self.use_conv_bias = use_conv_bias
|
| 141 |
+
self.activation = hidden_act
|
| 142 |
+
self.act = ACT2FN[hidden_act]
|
| 143 |
+
|
| 144 |
+
self.rms_norm = rms_norm
|
| 145 |
+
self.norm_eps = norm_eps
|
| 146 |
+
|
| 147 |
+
self.chunk_size = chunk_size
|
| 148 |
+
|
| 149 |
+
self.time_step_rank = int(time_step_rank)
|
| 150 |
+
self.time_step_limit = time_step_limit
|
| 151 |
+
self.time_step_min = time_step_min
|
| 152 |
+
self.time_step_max = time_step_max
|
| 153 |
+
|
| 154 |
+
self.conv_dim = self.intermediate_size + 2 * self.n_groups * self.ssm_state_size
|
| 155 |
+
self.conv1d = nn.Conv1d(
|
| 156 |
+
in_channels=self.conv_dim,
|
| 157 |
+
out_channels=self.conv_dim,
|
| 158 |
+
bias=use_conv_bias,
|
| 159 |
+
kernel_size=conv_kernel,
|
| 160 |
+
groups=self.conv_dim,
|
| 161 |
+
padding=conv_kernel - 1,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
# projection of the input hidden states
|
| 165 |
+
projection_size = self.intermediate_size + self.conv_dim + self.num_heads
|
| 166 |
+
self.in_proj = nn.Linear(
|
| 167 |
+
self.hidden_size,
|
| 168 |
+
projection_size,
|
| 169 |
+
bias=use_bias,
|
| 170 |
+
)
|
| 171 |
+
# selective projection used to make dt, B and C input dependant
|
| 172 |
+
|
| 173 |
+
# time step projection (discretization)
|
| 174 |
+
# instantiate once and copy inv_dt in init_weights of PretrainedModel
|
| 175 |
+
# hard coded for now
|
| 176 |
+
dt_init_floor = 1e-4
|
| 177 |
+
dt = torch.exp(
|
| 178 |
+
torch.rand(self.num_heads) * (
|
| 179 |
+
math.log(self.time_step_max) - math.log(self.time_step_min)
|
| 180 |
+
) + math.log(self.time_step_min)
|
| 181 |
+
)
|
| 182 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 183 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 184 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 185 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 186 |
+
|
| 187 |
+
# S4D real initialization. These are not discretized!
|
| 188 |
+
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
|
| 189 |
+
A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
|
| 190 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 191 |
+
self.A_log._no_weight_decay = True
|
| 192 |
+
self.norm = RMSNormGated(
|
| 193 |
+
self.intermediate_size, eps=self.norm_eps, norm_before_gate=False,
|
| 194 |
+
)
|
| 195 |
+
self.D = nn.Parameter(torch.ones(self.num_heads))
|
| 196 |
+
self.D._no_weight_decay = True
|
| 197 |
+
|
| 198 |
+
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=use_bias)
|
| 199 |
+
self.use_bias = use_bias
|
| 200 |
+
|
| 201 |
+
self.layer_idx = layer_idx
|
| 202 |
+
|
| 203 |
+
if not is_fast_path_available:
|
| 204 |
+
logger.warning_once(
|
| 205 |
+
"The fast path is not available because one of "
|
| 206 |
+
"`(selective_state_update)` is None. "
|
| 207 |
+
"Falling back to the naive implementation. "
|
| 208 |
+
"To install follow https://github.com/state-spaces/mamba/#installation",
|
| 209 |
+
)
|
| 210 |
+
import os
|
| 211 |
+
backend = os.environ.get('FLA_CONV_BACKEND', backend)
|
| 212 |
+
assert backend in ['cuda', 'triton'], f"Unsupported backend: {backend}"
|
| 213 |
+
if backend == 'cuda' and causal_conv1d_fn is None:
|
| 214 |
+
logger.warning_once(
|
| 215 |
+
"The CUDA backend is not available because `causal_conv1d` is None. "
|
| 216 |
+
"Falling back to the Triton backend. "
|
| 217 |
+
"To install follow https://github.com/Dao-AILab/causal-conv1d",
|
| 218 |
+
)
|
| 219 |
+
backend = 'triton'
|
| 220 |
+
if backend == 'triton':
|
| 221 |
+
from fla.modules.convolution import causal_conv1d as causal_conv1d_triton
|
| 222 |
+
from fla.modules.convolution import causal_conv1d_update as causal_conv1d_update_triton
|
| 223 |
+
self.causal_conv1d_fn = causal_conv1d_triton
|
| 224 |
+
self.causal_conv1d_update = causal_conv1d_update_triton
|
| 225 |
+
logger.warning(
|
| 226 |
+
"Mamba2 does not recommend using Triton's conv1d backend, "
|
| 227 |
+
"as it is untested and may contain bugs.",
|
| 228 |
+
)
|
| 229 |
+
else:
|
| 230 |
+
self.causal_conv1d_fn = causal_conv1d_fn
|
| 231 |
+
self.causal_conv1d_update = causal_conv1d_update
|
| 232 |
+
self.backend = backend
|
| 233 |
+
|
| 234 |
+
def cuda_kernels_forward(
|
| 235 |
+
self,
|
| 236 |
+
hidden_states: torch.Tensor,
|
| 237 |
+
last_state: dict | None = None,
|
| 238 |
+
use_cache: bool = False,
|
| 239 |
+
attention_mask: torch.Tensor | None = None,
|
| 240 |
+
):
|
| 241 |
+
# 1. Gated MLP's linear projection
|
| 242 |
+
projected_states = self.in_proj(hidden_states)
|
| 243 |
+
|
| 244 |
+
# Set up dimensions for reshapes later
|
| 245 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 246 |
+
groups_time_state_size = self.n_groups * self.ssm_state_size
|
| 247 |
+
d_mlp = (
|
| 248 |
+
projected_states.shape[-1]
|
| 249 |
+
- 2 * self.intermediate_size
|
| 250 |
+
- 2 * self.n_groups * self.ssm_state_size
|
| 251 |
+
- self.num_heads
|
| 252 |
+
) // 2
|
| 253 |
+
|
| 254 |
+
# Single step calculations via cache (decode)
|
| 255 |
+
if last_state is not None:
|
| 256 |
+
if hidden_states.shape[1] != 1:
|
| 257 |
+
raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
|
| 258 |
+
conv_state = last_state['conv_state']
|
| 259 |
+
ssm_state = last_state['recurrent_state']
|
| 260 |
+
|
| 261 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.squeeze(1).split(
|
| 262 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
# 2. Convolution sequence transformation
|
| 266 |
+
hidden_states_B_C = self.causal_conv1d_update(
|
| 267 |
+
hidden_states_B_C.contiguous(),
|
| 268 |
+
conv_state,
|
| 269 |
+
self.conv1d.weight.squeeze(1),
|
| 270 |
+
self.conv1d.bias,
|
| 271 |
+
self.activation,
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
hidden_states, B, C = torch.split(
|
| 275 |
+
hidden_states_B_C,
|
| 276 |
+
[
|
| 277 |
+
self.intermediate_size,
|
| 278 |
+
groups_time_state_size,
|
| 279 |
+
groups_time_state_size,
|
| 280 |
+
],
|
| 281 |
+
dim=-1,
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
# 3. SSM transformation
|
| 285 |
+
A = -torch.exp(self.A_log.float()) # (nheads,)
|
| 286 |
+
A = A[:, None, ...][:, :, None].expand(-1, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
|
| 287 |
+
dt = dt[:, :, None].expand(-1, -1, self.head_dim)
|
| 288 |
+
dt_bias = self.dt_bias[:, None, ...].expand(-1, self.head_dim)
|
| 289 |
+
D = self.D[:, None, ...].expand(-1, self.head_dim)
|
| 290 |
+
B = B.view(batch_size, self.n_groups, B.shape[1] // self.n_groups)
|
| 291 |
+
C = C.view(batch_size, self.n_groups, C.shape[1] // self.n_groups)
|
| 292 |
+
hidden_states_reshaped = hidden_states.view(batch_size, self.num_heads, self.head_dim)
|
| 293 |
+
|
| 294 |
+
hidden_states = selective_state_update(
|
| 295 |
+
ssm_state,
|
| 296 |
+
hidden_states_reshaped,
|
| 297 |
+
dt,
|
| 298 |
+
A,
|
| 299 |
+
B,
|
| 300 |
+
C,
|
| 301 |
+
D,
|
| 302 |
+
z=None,
|
| 303 |
+
dt_bias=dt_bias,
|
| 304 |
+
dt_softplus=True,
|
| 305 |
+
)
|
| 306 |
+
hidden_states = hidden_states.view(batch_size, self.num_heads * self.head_dim)
|
| 307 |
+
hidden_states = self.norm(hidden_states, gate)
|
| 308 |
+
|
| 309 |
+
# 4. Final linear projection
|
| 310 |
+
out = self.out_proj(hidden_states)[:, None, ...]
|
| 311 |
+
|
| 312 |
+
# conv_state is updated in-place by causal_conv1d_update
|
| 313 |
+
# ssm_state is updated in-place by selective_state_update
|
| 314 |
+
return out, conv_state, ssm_state
|
| 315 |
+
|
| 316 |
+
# Fused calculations or step by step if no initialized cache is found (prefill)
|
| 317 |
+
else:
|
| 318 |
+
A = -torch.exp(self.A_log.float()) # (num_heads) or (intermediate_size, state_size)
|
| 319 |
+
dt_limit_kwargs = {} if self.time_step_limit == (0.0, float("inf")) else {"dt_limit": self.time_step_limit}
|
| 320 |
+
|
| 321 |
+
# 2-4. Fused kernel for conv1d, SSM, and the final projection
|
| 322 |
+
if self.training and not use_cache:
|
| 323 |
+
out = mamba_split_conv1d_scan_combined(
|
| 324 |
+
projected_states,
|
| 325 |
+
self.conv1d.weight.squeeze(1),
|
| 326 |
+
self.conv1d.bias,
|
| 327 |
+
self.dt_bias,
|
| 328 |
+
A,
|
| 329 |
+
D=self.D,
|
| 330 |
+
chunk_size=self.chunk_size,
|
| 331 |
+
seq_idx=None, # was seq_idx
|
| 332 |
+
activation=self.activation,
|
| 333 |
+
rmsnorm_weight=self.norm.weight,
|
| 334 |
+
rmsnorm_eps=self.norm.eps,
|
| 335 |
+
outproj_weight=self.out_proj.weight,
|
| 336 |
+
outproj_bias=self.out_proj.bias,
|
| 337 |
+
headdim=self.head_dim,
|
| 338 |
+
ngroups=self.n_groups,
|
| 339 |
+
norm_before_gate=False,
|
| 340 |
+
return_final_states=False,
|
| 341 |
+
**dt_limit_kwargs,
|
| 342 |
+
)
|
| 343 |
+
return out, None, None
|
| 344 |
+
|
| 345 |
+
else:
|
| 346 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.split(
|
| 347 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
# 2. Convolution sequence transformation
|
| 351 |
+
hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
|
| 352 |
+
# Compute conv_state for cache
|
| 353 |
+
new_conv_state = None
|
| 354 |
+
if use_cache:
|
| 355 |
+
hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
|
| 356 |
+
new_conv_state = nn.functional.pad(
|
| 357 |
+
hidden_states_B_C_transposed,
|
| 358 |
+
(self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
if self.activation not in ["silu", "swish"]:
|
| 362 |
+
hidden_states_B_C = self.act(
|
| 363 |
+
self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2),
|
| 364 |
+
)
|
| 365 |
+
else:
|
| 366 |
+
_conv1d_output = self.causal_conv1d_fn(
|
| 367 |
+
x=hidden_states_B_C.transpose(1, 2).contiguous(),
|
| 368 |
+
weight=self.conv1d.weight.squeeze(1),
|
| 369 |
+
bias=self.conv1d.bias,
|
| 370 |
+
activation=self.activation,
|
| 371 |
+
)
|
| 372 |
+
if self.backend == 'cuda':
|
| 373 |
+
hidden_states_B_C = _conv1d_output
|
| 374 |
+
hidden_states_B_C = hidden_states_B_C.transpose(1, 2)
|
| 375 |
+
elif self.backend == 'triton':
|
| 376 |
+
hidden_states_B_C, _ = _conv1d_output
|
| 377 |
+
hidden_states_B_C = hidden_states_B_C.transpose(1, 2).contiguous()
|
| 378 |
+
else:
|
| 379 |
+
raise ValueError(f"Unsupported backend: {self.backend}")
|
| 380 |
+
|
| 381 |
+
hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
|
| 382 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
|
| 383 |
+
else hidden_states_B_C
|
| 384 |
+
hidden_states, B, C = torch.split(
|
| 385 |
+
hidden_states_B_C,
|
| 386 |
+
[self.intermediate_size, groups_time_state_size, groups_time_state_size],
|
| 387 |
+
dim=-1,
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
# 3. SSM transformation
|
| 391 |
+
scan_output, ssm_state = mamba_chunk_scan_combined(
|
| 392 |
+
hidden_states.view(batch_size, seq_len, -1, self.head_dim),
|
| 393 |
+
dt,
|
| 394 |
+
A,
|
| 395 |
+
B.view(batch_size, seq_len, self.n_groups, -1),
|
| 396 |
+
C.view(batch_size, seq_len, self.n_groups, -1),
|
| 397 |
+
chunk_size=self.chunk_size,
|
| 398 |
+
D=self.D,
|
| 399 |
+
z=None,
|
| 400 |
+
seq_idx=None,
|
| 401 |
+
return_final_states=True,
|
| 402 |
+
dt_bias=self.dt_bias,
|
| 403 |
+
dt_softplus=True,
|
| 404 |
+
**dt_limit_kwargs,
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
scan_output = scan_output.view(batch_size, seq_len, -1)
|
| 408 |
+
# Multiply "gate" branch and apply extra normalization layer
|
| 409 |
+
scan_output = self.norm(scan_output, gate)
|
| 410 |
+
|
| 411 |
+
# 4. Final linear projection
|
| 412 |
+
out = self.out_proj(scan_output)
|
| 413 |
+
|
| 414 |
+
return out, new_conv_state, ssm_state
|
| 415 |
+
|
| 416 |
+
# fmt: off
|
| 417 |
+
def torch_forward(
|
| 418 |
+
self,
|
| 419 |
+
input_states,
|
| 420 |
+
last_state: dict | None = None,
|
| 421 |
+
use_cache: bool = False,
|
| 422 |
+
attention_mask: torch.Tensor | None = None,
|
| 423 |
+
):
|
| 424 |
+
batch_size, seq_len, _ = input_states.shape
|
| 425 |
+
dtype = input_states.dtype
|
| 426 |
+
|
| 427 |
+
# 1. Gated MLP's linear projection
|
| 428 |
+
projected_states = self.in_proj(input_states)
|
| 429 |
+
d_mlp = (projected_states.shape[-1] - 2 * self.intermediate_size -
|
| 430 |
+
2 * self.n_groups * self.ssm_state_size - self.num_heads) // 2
|
| 431 |
+
_, _, gate, hidden_states_B_C, dt = projected_states.split(
|
| 432 |
+
[d_mlp, d_mlp, self.intermediate_size, self.conv_dim, self.num_heads], dim=-1,
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
# 2. Convolution sequence transformation
|
| 436 |
+
if last_state is not None:
|
| 437 |
+
if input_states.shape[1] != 1:
|
| 438 |
+
raise ValueError("Mamba2 cached decoding only supports a single new token per step.")
|
| 439 |
+
# Decode path: single-step update
|
| 440 |
+
conv_state = last_state['conv_state']
|
| 441 |
+
ssm_state = last_state['recurrent_state']
|
| 442 |
+
|
| 443 |
+
conv_state = conv_state.roll(shifts=-1, dims=-1)
|
| 444 |
+
conv_state[:, :, -1] = hidden_states_B_C[:, 0, :].to(conv_state.device)
|
| 445 |
+
|
| 446 |
+
# We need to guarantee that anything regarding the cache is on the same device
|
| 447 |
+
conv_states_for_compute = conv_state.to(device=self.conv1d.weight.device)
|
| 448 |
+
|
| 449 |
+
hidden_states_B_C = torch.sum(
|
| 450 |
+
conv_states_for_compute * self.conv1d.weight.squeeze(1), dim=-1,
|
| 451 |
+
)
|
| 452 |
+
if self.use_conv_bias:
|
| 453 |
+
hidden_states_B_C = hidden_states_B_C + self.conv1d.bias
|
| 454 |
+
hidden_states_B_C = self.act(hidden_states_B_C)
|
| 455 |
+
else:
|
| 456 |
+
# Prefill path
|
| 457 |
+
hidden_states_B_C = apply_mask_to_padding_states(hidden_states_B_C, attention_mask)
|
| 458 |
+
new_conv_state = None
|
| 459 |
+
if use_cache:
|
| 460 |
+
hidden_states_B_C_transposed = hidden_states_B_C.transpose(1, 2)
|
| 461 |
+
new_conv_state = nn.functional.pad(
|
| 462 |
+
hidden_states_B_C_transposed, (self.conv_kernel_size - hidden_states_B_C_transposed.shape[-1], 0),
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
hidden_states_B_C = self.act(self.conv1d(hidden_states_B_C.transpose(1, 2))[..., :seq_len].transpose(1, 2))
|
| 466 |
+
|
| 467 |
+
if last_state is None:
|
| 468 |
+
hidden_states_B_C = (hidden_states_B_C * attention_mask[:, :, None]).to(hidden_states_B_C.dtype) \
|
| 469 |
+
if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1 \
|
| 470 |
+
else hidden_states_B_C
|
| 471 |
+
hidden_states, B, C = torch.split(
|
| 472 |
+
hidden_states_B_C,
|
| 473 |
+
[self.intermediate_size, self.n_groups * self.ssm_state_size, self.n_groups * self.ssm_state_size],
|
| 474 |
+
dim=-1,
|
| 475 |
+
)
|
| 476 |
+
|
| 477 |
+
# 3. SSM transformation
|
| 478 |
+
A = -torch.exp(self.A_log.float()) # [num_heads]
|
| 479 |
+
if last_state is not None:
|
| 480 |
+
# Decode path
|
| 481 |
+
cache_device = ssm_state.device
|
| 482 |
+
|
| 483 |
+
# Note: there is no need to pad parameter matrices here, as there is just one new token
|
| 484 |
+
# for batched generation
|
| 485 |
+
dt = dt[:, 0, :][:, None, ...]
|
| 486 |
+
dt = dt.transpose(1, 2).expand(batch_size, dt.shape[-1], self.head_dim)
|
| 487 |
+
# [num_heads] -> [num_heads, head_dim]
|
| 488 |
+
dt_bias = self.dt_bias[..., None].expand(self.dt_bias.shape[0], self.head_dim)
|
| 489 |
+
|
| 490 |
+
dt = torch.nn.functional.softplus(dt + dt_bias.to(dt.dtype))
|
| 491 |
+
dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
|
| 492 |
+
A = A[..., None, None].expand(self.num_heads, self.head_dim, self.ssm_state_size).to(dtype=torch.float32)
|
| 493 |
+
# [bsz, num_heads, head_dim, state_size]
|
| 494 |
+
dA = (torch.exp(dt[..., None] * A)).to(device=cache_device)
|
| 495 |
+
|
| 496 |
+
# Discretize B
|
| 497 |
+
# [bsz, n_groups * state_size] -> [bsz, n_groups, 1, state_size] ->
|
| 498 |
+
# -> [bsz, n_groups, group to head repetition factor, state_size] -> [bsz, num_heads, state_size]
|
| 499 |
+
B = B.reshape(batch_size, self.n_groups, -1)[..., None, :]
|
| 500 |
+
B = B.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, B.shape[-1]).contiguous()
|
| 501 |
+
B = B.reshape(batch_size, -1, B.shape[-1])
|
| 502 |
+
# [bsz, num_heads, head_dim, state_size]
|
| 503 |
+
dB = dt[..., None] * B[..., None, :]
|
| 504 |
+
|
| 505 |
+
# Discretize x into dB
|
| 506 |
+
# [bsz, intermediate_size] -> [bsz, num_heads, head_dim]
|
| 507 |
+
hidden_states = hidden_states.reshape(batch_size, -1, self.head_dim)
|
| 508 |
+
dBx = (dB * hidden_states[..., None]).to(device=cache_device)
|
| 509 |
+
|
| 510 |
+
# State calculation
|
| 511 |
+
ssm_state = ssm_state * dA + dBx
|
| 512 |
+
|
| 513 |
+
# Subsequent output
|
| 514 |
+
# [bsz, n_groups * state_size] -> [bsz, num_heads, state_size]
|
| 515 |
+
C = C.reshape(batch_size, self.n_groups, -1)[..., None, :]
|
| 516 |
+
C = C.expand(batch_size, self.n_groups, self.num_heads // self.n_groups, C.shape[-1]).contiguous()
|
| 517 |
+
C = C.reshape(batch_size, -1, C.shape[-1])
|
| 518 |
+
# [bsz, num_heads, head_dim]
|
| 519 |
+
|
| 520 |
+
ssm_states_for_compute = ssm_state.to(device=C.device, dtype=C.dtype) # Shape: [b, h, d, n]
|
| 521 |
+
# Reshape ssm_states to merge the first two dimensions
|
| 522 |
+
# Shape: [b*h, d, n]
|
| 523 |
+
ssm_states_reshaped = ssm_states_for_compute.view(batch_size * self.num_heads, self.head_dim, self.ssm_state_size)
|
| 524 |
+
C_reshaped = C.view(batch_size * self.num_heads, self.ssm_state_size, 1) # Shape: [b*h, n, 1]
|
| 525 |
+
y = torch.bmm(ssm_states_reshaped, C_reshaped)
|
| 526 |
+
y = y.view(batch_size, self.num_heads, self.head_dim)
|
| 527 |
+
|
| 528 |
+
# D skip connection
|
| 529 |
+
# [num_heads] -> [num_heads, head_dim]
|
| 530 |
+
D = self.D[..., None].expand(self.D.shape[0], self.head_dim)
|
| 531 |
+
y = (y + hidden_states * D).to(y.dtype)
|
| 532 |
+
|
| 533 |
+
# [bsz, num_heads, head_dim] -> [bsz, 1, intermediate_size]
|
| 534 |
+
y = y.reshape(batch_size, -1)[:, None, ...]
|
| 535 |
+
|
| 536 |
+
scan_output = self.norm(y, gate)
|
| 537 |
+
contextualized_states = self.out_proj(scan_output.to(dtype))
|
| 538 |
+
return contextualized_states, conv_state, ssm_state
|
| 539 |
+
else:
|
| 540 |
+
# Prefill path
|
| 541 |
+
# begin ssd naive implementation without einsums
|
| 542 |
+
dt = nn.functional.softplus(dt + self.dt_bias)
|
| 543 |
+
dt = torch.clamp(dt, self.time_step_limit[0], self.time_step_limit[1])
|
| 544 |
+
hidden_states = hidden_states.reshape(batch_size, seq_len, -1, self.head_dim).float()
|
| 545 |
+
B = B.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
|
| 546 |
+
C = C.reshape(batch_size, seq_len, -1, self.ssm_state_size).float()
|
| 547 |
+
B = B.repeat(1, 1, self.num_heads // self.n_groups, 1)
|
| 548 |
+
C = C.repeat(1, 1, self.num_heads // self.n_groups, 1)
|
| 549 |
+
pad_size = (self.chunk_size - seq_len % self.chunk_size) % self.chunk_size
|
| 550 |
+
|
| 551 |
+
D_residual = self.D[..., None] * pad_tensor_by_size(hidden_states, pad_size)
|
| 552 |
+
|
| 553 |
+
# Discretize x and A
|
| 554 |
+
hidden_states = hidden_states * dt[..., None]
|
| 555 |
+
A = A.to(hidden_states.dtype) * dt
|
| 556 |
+
|
| 557 |
+
# Rearrange into blocks/chunks
|
| 558 |
+
hidden_states, A, B, C = [reshape_into_chunks(t, pad_size, self.chunk_size) for t in (hidden_states, A, B, C)]
|
| 559 |
+
|
| 560 |
+
# [bsz, -1, chunk_size, num_heads] -> [bsz, num_heads, -1, chunk_size]
|
| 561 |
+
A = A.permute(0, 3, 1, 2)
|
| 562 |
+
A_cumsum = torch.cumsum(A, dim=-1)
|
| 563 |
+
|
| 564 |
+
# 1. Compute the output for each intra-chunk (diagonal blocks)
|
| 565 |
+
# This is the analog of a causal mask
|
| 566 |
+
L = torch.exp(segment_sum(A))
|
| 567 |
+
|
| 568 |
+
# Contraction of C and B to get G (attention-weights like)
|
| 569 |
+
# shape: (b, c, l, s, h, n)
|
| 570 |
+
G_intermediate = C[:, :, :, None, :, :] * B[:, :, None, :, :, :]
|
| 571 |
+
G = G_intermediate.sum(dim=-1) # shape: (b, c, l, s, h)
|
| 572 |
+
|
| 573 |
+
# Compute M, equivalent to applying attention mask to weights
|
| 574 |
+
M_intermediate = G[..., None] * L.permute(0, 2, 3, 4, 1)[..., None]
|
| 575 |
+
M = M_intermediate.sum(dim=-1)
|
| 576 |
+
|
| 577 |
+
# Compute Y_diag (apply to values)
|
| 578 |
+
Y_diag = (M[..., None] * hidden_states[:, :, None]).sum(dim=3)
|
| 579 |
+
|
| 580 |
+
# 2. Compute the state for each intra-chunk
|
| 581 |
+
# (right term of low-rank factorization of off-diagonal blocks; B terms)
|
| 582 |
+
decay_states = torch.exp(A_cumsum[:, :, :, -1:] - A_cumsum)
|
| 583 |
+
B_decay = B * decay_states.permute(0, -2, -1, 1)[..., None]
|
| 584 |
+
states = (B_decay[..., None, :] * hidden_states[..., None]).sum(dim=2)
|
| 585 |
+
|
| 586 |
+
# 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
|
| 587 |
+
# (middle term of factorization of off-diag blocks; A terms)
|
| 588 |
+
previous_states = torch.zeros_like(states[:, :1])
|
| 589 |
+
states = torch.cat([previous_states, states], dim=1)
|
| 590 |
+
decay_chunk = torch.exp(segment_sum(nn.functional.pad(A_cumsum[:, :, :, -1], (1, 0))))
|
| 591 |
+
decay_chunk = decay_chunk.transpose(1, 3)
|
| 592 |
+
new_states = (decay_chunk[..., None, None] * states[:, :, None, ...]).sum(dim=1)
|
| 593 |
+
states, ssm_state = new_states[:, :-1], new_states[:, -1]
|
| 594 |
+
|
| 595 |
+
# 4. Compute state -> output conversion per chunk
|
| 596 |
+
# (left term of low-rank factorization of off-diagonal blocks; C terms)
|
| 597 |
+
state_decay_out = torch.exp(A_cumsum)
|
| 598 |
+
C_times_states = (C[..., None, :] * states[:, :, None, ...])
|
| 599 |
+
state_decay_out_permuted = state_decay_out.permute(0, 2, 3, 1)
|
| 600 |
+
Y_off = (C_times_states.sum(-1) * state_decay_out_permuted[..., None])
|
| 601 |
+
|
| 602 |
+
# Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
|
| 603 |
+
y = Y_diag + Y_off
|
| 604 |
+
# [bsz, -1, self.chunk_size, num_heads, head_dim] -> [bsz, (padded) seq_len, num_heads, head_dim]
|
| 605 |
+
y = y.reshape(batch_size, -1, self.num_heads, self.head_dim)
|
| 606 |
+
|
| 607 |
+
y = y + D_residual
|
| 608 |
+
# Cutting off padded chunks
|
| 609 |
+
if pad_size > 0:
|
| 610 |
+
y = y[:, :seq_len, :, :]
|
| 611 |
+
y = y.reshape(batch_size, seq_len, -1)
|
| 612 |
+
|
| 613 |
+
scan_output = self.norm(y, gate)
|
| 614 |
+
|
| 615 |
+
# end ssd naive
|
| 616 |
+
|
| 617 |
+
# 4. Final linear projection
|
| 618 |
+
contextualized_states = self.out_proj(scan_output.to(dtype)) # [batch, seq_len, hidden_size]
|
| 619 |
+
return contextualized_states, new_conv_state if use_cache else None, ssm_state
|
| 620 |
+
# fmt: on
|
| 621 |
+
|
| 622 |
+
def forward(
|
| 623 |
+
self,
|
| 624 |
+
hidden_states: torch.Tensor,
|
| 625 |
+
attention_mask: torch.Tensor | None = None,
|
| 626 |
+
past_key_values: Cache | None = None,
|
| 627 |
+
use_cache: bool | None = False,
|
| 628 |
+
output_attentions: bool | None = False,
|
| 629 |
+
**kwargs,
|
| 630 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 631 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 632 |
+
|
| 633 |
+
if is_fast_path_available and "cuda" in self.in_proj.weight.device.type:
|
| 634 |
+
output, conv_state, ssm_state = self.cuda_kernels_forward(hidden_states, last_state, use_cache, attention_mask)
|
| 635 |
+
else:
|
| 636 |
+
dtype = hidden_states.dtype
|
| 637 |
+
if last_state is None and attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
|
| 638 |
+
hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
|
| 639 |
+
output, conv_state, ssm_state = self.torch_forward(hidden_states, last_state, use_cache, attention_mask)
|
| 640 |
+
|
| 641 |
+
update_layer_cache(
|
| 642 |
+
self,
|
| 643 |
+
past_key_values,
|
| 644 |
+
recurrent_state=ssm_state,
|
| 645 |
+
conv_state=conv_state,
|
| 646 |
+
offset=hidden_states.shape[1],
|
| 647 |
+
)
|
| 648 |
+
|
| 649 |
+
return output, None, past_key_values
|
fla/layers/mesa_net.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from torch.nn import functional as F
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.l2norm import l2_norm
|
| 15 |
+
from fla.ops.mesa_net import chunk_mesa_net, mesa_net_decoding_one_step
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from transformers.processing_utils import Unpack
|
| 19 |
+
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class MesaNet(nn.Module):
|
| 24 |
+
"""
|
| 25 |
+
The layer implementaion for [MesaNet: Sequence Modeling by Locally Optimal Test-Time Training]. # noqa
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
hidden_size (int, Optional):
|
| 29 |
+
The hidden size of the input. Default: 2048.
|
| 30 |
+
expand_v (float, Optional):
|
| 31 |
+
The expansion ratio for the value dim. Default: 1.
|
| 32 |
+
num_heads (int, Optional):
|
| 33 |
+
The number of heads. Default: 16.
|
| 34 |
+
mode (str, Optional):
|
| 35 |
+
Which MesaNet kernel to use.
|
| 36 |
+
Currently available: `chunk`.
|
| 37 |
+
Default: `chunk`.
|
| 38 |
+
use_output_gate (bool, Optional):
|
| 39 |
+
Whether to use output gate. Default: `False`.
|
| 40 |
+
conv_size (int):
|
| 41 |
+
The kernel size of the short convolution. Default: 4.
|
| 42 |
+
layer_idx (int, Optional):
|
| 43 |
+
The index of the layer. Default: None.
|
| 44 |
+
norm_eps (float, Optional):
|
| 45 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 46 |
+
lambda_lower_bound (float):
|
| 47 |
+
The lower bound for the lambda parameter. Default: 0.25.
|
| 48 |
+
max_cg_step_training (int):
|
| 49 |
+
The maximum number of CG steps for training. Default: 30.
|
| 50 |
+
max_cg_step_decoding (int):
|
| 51 |
+
The maximum number of CG steps for decoding. Default: 30.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
def __init__(
|
| 55 |
+
self,
|
| 56 |
+
hidden_size: int = 2048,
|
| 57 |
+
num_heads: int = 16,
|
| 58 |
+
head_dim: int = 128,
|
| 59 |
+
mode: str = 'chunk',
|
| 60 |
+
use_output_gate: bool = False,
|
| 61 |
+
use_short_conv: bool = True,
|
| 62 |
+
conv_size: int = 4,
|
| 63 |
+
conv_bias: bool = False,
|
| 64 |
+
layer_idx: int = None,
|
| 65 |
+
norm_eps: float = 1e-5,
|
| 66 |
+
lambda_lower_bound: float = 0.25,
|
| 67 |
+
max_cg_step_training: int = 30,
|
| 68 |
+
max_cg_step_decoding: int = 30,
|
| 69 |
+
**kwargs,
|
| 70 |
+
) -> MesaNet:
|
| 71 |
+
super().__init__()
|
| 72 |
+
|
| 73 |
+
self.mode = mode
|
| 74 |
+
self.hidden_size = hidden_size
|
| 75 |
+
self.use_output_gate = use_output_gate
|
| 76 |
+
self.use_short_conv = use_short_conv
|
| 77 |
+
self.conv_size = conv_size
|
| 78 |
+
self.conv_bias = conv_bias
|
| 79 |
+
self.num_heads = num_heads
|
| 80 |
+
self.head_dim = head_dim
|
| 81 |
+
self.key_dim = self.num_heads * self.head_dim
|
| 82 |
+
self.value_dim = self.key_dim
|
| 83 |
+
self.head_k_dim = self.head_dim
|
| 84 |
+
self.head_v_dim = self.head_dim
|
| 85 |
+
self.layer_idx = layer_idx
|
| 86 |
+
self.lambda_lower_bound = lambda_lower_bound
|
| 87 |
+
self.max_cg_step_training = max_cg_step_training
|
| 88 |
+
self.max_cg_step_decoding = max_cg_step_decoding
|
| 89 |
+
|
| 90 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 91 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 92 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 93 |
+
self.a_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
|
| 94 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
|
| 95 |
+
|
| 96 |
+
lambda_initial_value = 1.0
|
| 97 |
+
init_lamb_value = torch.log(torch.exp(torch.tensor(lambda_initial_value - lambda_lower_bound)) - 1.0)
|
| 98 |
+
init_lamb_params = torch.empty(self.key_dim, dtype=torch.float32).fill_(init_lamb_value)
|
| 99 |
+
|
| 100 |
+
self.lambda_params = nn.Parameter(init_lamb_params)
|
| 101 |
+
self.lambda_params._no_weight_decay = True
|
| 102 |
+
|
| 103 |
+
self.conv_size = conv_size
|
| 104 |
+
self.q_conv1d = ShortConvolution(
|
| 105 |
+
hidden_size=self.key_dim,
|
| 106 |
+
kernel_size=conv_size,
|
| 107 |
+
bias=self.conv_bias,
|
| 108 |
+
activation='silu',
|
| 109 |
+
)
|
| 110 |
+
self.k_conv1d = ShortConvolution(
|
| 111 |
+
hidden_size=self.key_dim,
|
| 112 |
+
kernel_size=conv_size,
|
| 113 |
+
bias=self.conv_bias,
|
| 114 |
+
activation='silu',
|
| 115 |
+
)
|
| 116 |
+
if use_output_gate:
|
| 117 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 118 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 119 |
+
else:
|
| 120 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 121 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 122 |
+
|
| 123 |
+
def forward(
|
| 124 |
+
self,
|
| 125 |
+
hidden_states: torch.Tensor,
|
| 126 |
+
attention_mask: torch.Tensor | None = None,
|
| 127 |
+
past_key_values: Cache | None = None,
|
| 128 |
+
use_cache: bool | None = False,
|
| 129 |
+
output_attentions: bool | None = False,
|
| 130 |
+
**kwargs: Unpack[dict],
|
| 131 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
assert len(attention_mask.shape) == 2, (
|
| 134 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 135 |
+
"for padding purposes (0 indicating padding). "
|
| 136 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 140 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 141 |
+
|
| 142 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 143 |
+
if attention_mask is not None:
|
| 144 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 145 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 146 |
+
|
| 147 |
+
conv_state_q, conv_state_k = None, None
|
| 148 |
+
if last_state is not None:
|
| 149 |
+
conv_state_q, conv_state_k = last_state['conv_state']
|
| 150 |
+
q, conv_state_q = self.q_conv1d(
|
| 151 |
+
x=self.q_proj(hidden_states),
|
| 152 |
+
cache=conv_state_q,
|
| 153 |
+
output_final_state=use_cache,
|
| 154 |
+
cu_seqlens=cu_seqlens,
|
| 155 |
+
)
|
| 156 |
+
k, conv_state_k = self.k_conv1d(
|
| 157 |
+
x=self.k_proj(hidden_states),
|
| 158 |
+
cache=conv_state_k,
|
| 159 |
+
output_final_state=use_cache,
|
| 160 |
+
cu_seqlens=cu_seqlens,
|
| 161 |
+
)
|
| 162 |
+
v = self.v_proj(hidden_states)
|
| 163 |
+
|
| 164 |
+
q, k = map(lambda x: rearrange(x, '... (h d) -> ... h d', d=self.head_k_dim), (q, k))
|
| 165 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 166 |
+
beta = self.b_proj(hidden_states).float().sigmoid()
|
| 167 |
+
g = F.logsigmoid(self.a_proj(hidden_states).float())
|
| 168 |
+
lamb = F.softplus(self.lambda_params.float()) + self.lambda_lower_bound
|
| 169 |
+
lamb = lamb.reshape(self.num_heads, -1)
|
| 170 |
+
|
| 171 |
+
last_h_kk, last_h_kv = last_state['recurrent_state'] if last_state is not None else (None, None)
|
| 172 |
+
|
| 173 |
+
# prefilling or training
|
| 174 |
+
# Note that QK will be normalized inside the kernel to avoid saving the activations, thereby reducing the memory usage.
|
| 175 |
+
if last_state is None:
|
| 176 |
+
o, h_kk, h_kv = chunk_mesa_net(
|
| 177 |
+
q=q,
|
| 178 |
+
k=k,
|
| 179 |
+
v=v,
|
| 180 |
+
g=g,
|
| 181 |
+
beta=beta,
|
| 182 |
+
lamb=lamb,
|
| 183 |
+
output_final_state=use_cache,
|
| 184 |
+
max_CG_iteration=self.max_cg_step_training,
|
| 185 |
+
use_qk_l2norm_in_kernel=True,
|
| 186 |
+
cu_seqlens=cu_seqlens,
|
| 187 |
+
)
|
| 188 |
+
# decoding
|
| 189 |
+
else:
|
| 190 |
+
q = l2_norm(q)
|
| 191 |
+
k = l2_norm(k)
|
| 192 |
+
o, h_kk, h_kv = mesa_net_decoding_one_step(
|
| 193 |
+
q=q.squeeze(0),
|
| 194 |
+
k=k.squeeze(0),
|
| 195 |
+
v=v.squeeze(0),
|
| 196 |
+
g=g.squeeze(0),
|
| 197 |
+
beta=beta.squeeze(0),
|
| 198 |
+
lamb=lamb,
|
| 199 |
+
prev_h_kk=last_h_kk,
|
| 200 |
+
prev_h_kv=last_h_kv,
|
| 201 |
+
max_CG_iteration=self.max_cg_step_decoding,
|
| 202 |
+
)
|
| 203 |
+
o = o.unsqueeze(0).to(q)
|
| 204 |
+
|
| 205 |
+
update_layer_cache(
|
| 206 |
+
self,
|
| 207 |
+
past_key_values,
|
| 208 |
+
recurrent_state=(h_kk, h_kv),
|
| 209 |
+
conv_state=(conv_state_q, conv_state_k),
|
| 210 |
+
offset=q_len,
|
| 211 |
+
)
|
| 212 |
+
if self.use_output_gate:
|
| 213 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 214 |
+
o = self.o_norm(o, g)
|
| 215 |
+
else:
|
| 216 |
+
o = self.o_norm(o)
|
| 217 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 218 |
+
o = self.o_proj(o)
|
| 219 |
+
if attention_mask is not None:
|
| 220 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 221 |
+
return o, None, past_key_values
|
fla/layers/mla.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
""" Implementing the Deepseek Multi Latent Attention (MLA) module. Reference:
|
| 4 |
+
|
| 5 |
+
https://github.com/huggingface/transformers/blob/main/src/transformers/models/deepseek_v3/modeling_deepseek_v3.py#L328
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import math
|
| 11 |
+
import warnings
|
| 12 |
+
from typing import TYPE_CHECKING
|
| 13 |
+
|
| 14 |
+
import torch
|
| 15 |
+
import torch.nn as nn
|
| 16 |
+
import torch.nn.functional as F
|
| 17 |
+
from einops import rearrange, repeat
|
| 18 |
+
from transformers.utils import logging
|
| 19 |
+
|
| 20 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 21 |
+
from fla.modules import RMSNorm, RotaryEmbedding
|
| 22 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 23 |
+
|
| 24 |
+
if TYPE_CHECKING:
|
| 25 |
+
from fla.models.utils import Cache
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 29 |
+
except ImportError:
|
| 30 |
+
warnings.warn(
|
| 31 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 32 |
+
category=ImportWarning,
|
| 33 |
+
)
|
| 34 |
+
flash_attn_func = None
|
| 35 |
+
|
| 36 |
+
logger = logging.get_logger(__name__)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def yarn_get_mscale(scale=1, mscale=1):
|
| 40 |
+
if scale <= 1:
|
| 41 |
+
return 1.0
|
| 42 |
+
return 0.1 * mscale * math.log(scale) + 1.0
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class MultiheadLatentAttention(nn.Module):
|
| 46 |
+
r"""
|
| 47 |
+
Multi-headed attention from [Deepseek V2](https://arxiv.org/abs/2405.04434)
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
hidden_size: int = 2048,
|
| 53 |
+
num_heads: int = 16,
|
| 54 |
+
q_lora_rank: int | None = 1536, # q lora rank is optional, None indicates no q lora
|
| 55 |
+
qk_rope_head_dim: int = 64,
|
| 56 |
+
kv_lora_rank: int = 512, # following the original Deepseek paper
|
| 57 |
+
v_head_dim: int = 128,
|
| 58 |
+
qk_nope_head_dim: int = 128,
|
| 59 |
+
qk_head_dim: int | None = 192, # qk_nope_head_dim + qk_rope_head_dim
|
| 60 |
+
window_size: int | None = None,
|
| 61 |
+
rope_theta: float = 10000.,
|
| 62 |
+
max_position_embeddings: int | None = None,
|
| 63 |
+
rope_scaling: dict | None = None,
|
| 64 |
+
layer_idx: int = None,
|
| 65 |
+
) -> MultiheadLatentAttention:
|
| 66 |
+
super().__init__()
|
| 67 |
+
|
| 68 |
+
# sanity check
|
| 69 |
+
if qk_head_dim is not None:
|
| 70 |
+
assert qk_head_dim == qk_nope_head_dim + qk_rope_head_dim, \
|
| 71 |
+
f"qk_head_dim {qk_head_dim} != qk_nope_head_dim {qk_nope_head_dim} + qk_rope_head_dim {qk_rope_head_dim}"
|
| 72 |
+
else:
|
| 73 |
+
qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
|
| 74 |
+
|
| 75 |
+
# attention params info
|
| 76 |
+
self.hidden_size = hidden_size
|
| 77 |
+
self.num_heads = num_heads
|
| 78 |
+
self.q_lora_rank = q_lora_rank
|
| 79 |
+
self.qk_rope_head_dim = qk_rope_head_dim
|
| 80 |
+
self.kv_lora_rank = kv_lora_rank
|
| 81 |
+
self.v_head_dim = v_head_dim
|
| 82 |
+
self.qk_nope_head_dim = qk_nope_head_dim
|
| 83 |
+
self.qk_head_dim = qk_head_dim
|
| 84 |
+
|
| 85 |
+
self.window_size = window_size
|
| 86 |
+
self.rope_theta = rope_theta
|
| 87 |
+
self.max_position_embeddings = max_position_embeddings
|
| 88 |
+
self.layer_idx = layer_idx
|
| 89 |
+
|
| 90 |
+
if flash_attn_func is None:
|
| 91 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 92 |
+
|
| 93 |
+
if q_lora_rank is not None:
|
| 94 |
+
self.q_proj = nn.Sequential(
|
| 95 |
+
nn.Linear(hidden_size, q_lora_rank, bias=False),
|
| 96 |
+
RMSNorm(q_lora_rank, dtype=torch.float32),
|
| 97 |
+
nn.Linear(q_lora_rank, self.num_heads * self.qk_head_dim, bias=False),
|
| 98 |
+
)
|
| 99 |
+
else:
|
| 100 |
+
self.q_proj = nn.Linear(hidden_size, self.num_heads * self.qk_head_dim, bias=False)
|
| 101 |
+
|
| 102 |
+
self.k_rope = nn.Linear(hidden_size, self.qk_rope_head_dim, bias=False)
|
| 103 |
+
self.kv_proj = nn.Sequential(
|
| 104 |
+
nn.Linear(hidden_size, self.kv_lora_rank, bias=False),
|
| 105 |
+
RMSNorm(self.kv_lora_rank, dtype=torch.float32),
|
| 106 |
+
nn.Linear(self.kv_lora_rank, self.num_heads * (self.qk_nope_head_dim + self.v_head_dim), bias=False),
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
self.o_proj = nn.Linear(self.num_heads * self.v_head_dim, hidden_size, bias=False)
|
| 110 |
+
|
| 111 |
+
self.scaling = self.qk_head_dim ** (-0.5)
|
| 112 |
+
if rope_scaling is not None and rope_scaling.get("rope_type", "default") != "default":
|
| 113 |
+
mscale_all_dim = rope_scaling.get("mscale_all_dim", 0)
|
| 114 |
+
scaling_factor = rope_scaling["factor"]
|
| 115 |
+
if mscale_all_dim:
|
| 116 |
+
mscale = yarn_get_mscale(scaling_factor, mscale_all_dim)
|
| 117 |
+
self.scaling = self.scaling * mscale * mscale
|
| 118 |
+
|
| 119 |
+
self.rotary = RotaryEmbedding(dim=self.qk_rope_head_dim, base=self.rope_theta)
|
| 120 |
+
|
| 121 |
+
def forward(
|
| 122 |
+
self,
|
| 123 |
+
hidden_states: torch.Tensor,
|
| 124 |
+
attention_mask: torch.Tensor | None,
|
| 125 |
+
past_key_values: Cache | None = None,
|
| 126 |
+
output_attentions: bool = False,
|
| 127 |
+
use_cache: bool = False,
|
| 128 |
+
**kwargs,
|
| 129 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 130 |
+
# if attention_mask is not None, this is doing inference
|
| 131 |
+
if attention_mask is not None:
|
| 132 |
+
assert len(attention_mask.shape) == 2, (
|
| 133 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 134 |
+
"for padding purposes (0 indicating padding). "
|
| 135 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# prepare q, k, v
|
| 139 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 140 |
+
|
| 141 |
+
q_states = self.q_proj(hidden_states)
|
| 142 |
+
q_states = rearrange(q_states, '... (h d) -> ... h d', d=self.qk_head_dim)
|
| 143 |
+
q_pass, q_rot = torch.split(q_states, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
|
| 144 |
+
k_pass, k_rot = self.kv_proj(hidden_states), self.k_rope(hidden_states)
|
| 145 |
+
|
| 146 |
+
k_rot = rearrange(k_rot, 'b t d -> b t 1 d')
|
| 147 |
+
k_pass = rearrange(k_pass, '... (h d) -> ... h d', d=self.qk_nope_head_dim + self.v_head_dim)
|
| 148 |
+
k_pass, v = torch.split(k_pass, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
|
| 149 |
+
|
| 150 |
+
# apply rotary position embedding
|
| 151 |
+
seqlen_offset, max_seqlen = 0, q_len
|
| 152 |
+
if past_key_values is not None:
|
| 153 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 154 |
+
max_seqlen = q_len + seqlen_offset
|
| 155 |
+
|
| 156 |
+
if attention_mask is not None:
|
| 157 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 158 |
+
max_seqlen = q_len + max(seqlen_offset)
|
| 159 |
+
|
| 160 |
+
if self.max_position_embeddings is not None:
|
| 161 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 162 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 163 |
+
q_rot, k_rot = self.rotary(
|
| 164 |
+
q_rot, k_rot, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens,
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
k_rot = repeat(k_rot, 'b t 1 d -> b t h d', h=self.num_heads)
|
| 168 |
+
q = torch.cat((q_pass, q_rot), dim=-1)
|
| 169 |
+
k = torch.cat((k_pass, k_rot), dim=-1)
|
| 170 |
+
|
| 171 |
+
# TODO: instead of caching the full k, v, we can actually only cache the compressed_kv and k_rot
|
| 172 |
+
# and recover the full k, v from compressed_kv and k_rot
|
| 173 |
+
if past_key_values is not None:
|
| 174 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 175 |
+
k_cached, v_cached = past_key_values.update(
|
| 176 |
+
attn_state=(k, v),
|
| 177 |
+
layer_idx=self.layer_idx,
|
| 178 |
+
offset=q_len,
|
| 179 |
+
)['attn_state']
|
| 180 |
+
if cache_has_content:
|
| 181 |
+
k, v = k_cached, v_cached
|
| 182 |
+
|
| 183 |
+
# Head dim match to use flash-attn
|
| 184 |
+
if self.qk_head_dim != self.v_head_dim:
|
| 185 |
+
v = F.pad(v, [0, self.qk_head_dim - self.v_head_dim])
|
| 186 |
+
|
| 187 |
+
# Contains at least one padding token in the sequence
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
if q.shape[1] == 1 and self.window_size is not None:
|
| 190 |
+
attention_mask = attention_mask[:, -self.window_size:]
|
| 191 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(q, (k, v), attention_mask, q_len)
|
| 192 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 193 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 194 |
+
o = flash_attn_varlen_func(
|
| 195 |
+
q, k, v,
|
| 196 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 197 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 198 |
+
max_seqlen_q=max_seqlen_q,
|
| 199 |
+
max_seqlen_k=max_seqlen_k,
|
| 200 |
+
causal=True,
|
| 201 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 202 |
+
)
|
| 203 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 204 |
+
elif cu_seqlens is not None:
|
| 205 |
+
o = flash_attn_varlen_func(
|
| 206 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 207 |
+
cu_seqlens_q=cu_seqlens,
|
| 208 |
+
cu_seqlens_k=cu_seqlens,
|
| 209 |
+
max_seqlen_q=max_seqlen,
|
| 210 |
+
max_seqlen_k=max_seqlen,
|
| 211 |
+
causal=True,
|
| 212 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 213 |
+
).unsqueeze(0)
|
| 214 |
+
else:
|
| 215 |
+
o = flash_attn_func(
|
| 216 |
+
q, k, v,
|
| 217 |
+
causal=True,
|
| 218 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
if self.qk_head_dim != self.v_head_dim:
|
| 222 |
+
o = o[:, :, :, :self.v_head_dim]
|
| 223 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 224 |
+
o = self.o_proj(o)
|
| 225 |
+
return o, None, past_key_values
|
fla/layers/mom.py
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import math
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from torch.nn import functional as F
|
| 11 |
+
|
| 12 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 13 |
+
from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule
|
| 14 |
+
|
| 15 |
+
if TYPE_CHECKING:
|
| 16 |
+
from transformers.processing_utils import Unpack
|
| 17 |
+
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, unpad_input, update_layer_cache
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _upad_input(
|
| 24 |
+
query_layer: torch.Tensor,
|
| 25 |
+
key_layer: torch.Tensor,
|
| 26 |
+
value_layer: torch.Tensor,
|
| 27 |
+
gate_layer: torch.Tensor,
|
| 28 |
+
beta_layer: torch.Tensor,
|
| 29 |
+
attention_mask: torch.Tensor,
|
| 30 |
+
):
|
| 31 |
+
"""
|
| 32 |
+
Unpads query, key, and values tensors, using a single dimension for all tokens even though they belong to
|
| 33 |
+
different batches.
|
| 34 |
+
|
| 35 |
+
This function is used instead of `flash_attn.bert_padding.unpad_input` in order to avoid the recomputation
|
| 36 |
+
of the same intermediary
|
| 37 |
+
tensors for query, key, value tensors.
|
| 38 |
+
|
| 39 |
+
Arguments:
|
| 40 |
+
query_layer (`torch.Tensor`):
|
| 41 |
+
Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
|
| 42 |
+
key_layer (`torch.Tensor`):
|
| 43 |
+
Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 44 |
+
value_layer (`torch.Tensor`):
|
| 45 |
+
Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 46 |
+
attention_mask (`torch.Tensor`):
|
| 47 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 48 |
+
query_length (`int`):
|
| 49 |
+
Target length.
|
| 50 |
+
|
| 51 |
+
Return:
|
| 52 |
+
query_layer (`torch.Tensor`):
|
| 53 |
+
Query state without padding. Shape: (total_target_length, num_heads, head_dim).
|
| 54 |
+
key_layer (`torch.Tensor`):
|
| 55 |
+
Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 56 |
+
value_layer (`torch.Tensor`):
|
| 57 |
+
Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 58 |
+
indices_q (`torch.Tensor`):
|
| 59 |
+
The indices of non-masked tokens from the flattened input target sequence.
|
| 60 |
+
(cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
|
| 61 |
+
The cumulative sequence lengths for the target (query) and source (key, value), used to index
|
| 62 |
+
into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 63 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
|
| 64 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query,
|
| 65 |
+
`max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 66 |
+
"""
|
| 67 |
+
query_length = query_layer.shape[1]
|
| 68 |
+
indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
|
| 69 |
+
batch_size, kv_seq_len, dim = key_layer.shape
|
| 70 |
+
v_dim = value_layer.shape[-1]
|
| 71 |
+
|
| 72 |
+
key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
|
| 73 |
+
value_layer = index_first_axis(
|
| 74 |
+
value_layer.reshape(batch_size * kv_seq_len, v_dim), indices_k,
|
| 75 |
+
)
|
| 76 |
+
gate_layer = index_first_axis(gate_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
|
| 77 |
+
beta_layer = index_first_axis(beta_layer.reshape(batch_size * kv_seq_len, -1), indices_k)
|
| 78 |
+
if query_length == kv_seq_len:
|
| 79 |
+
query_layer = index_first_axis(query_layer.reshape(batch_size * kv_seq_len, dim), indices_k)
|
| 80 |
+
cu_seqlens_q = cu_seqlens_k
|
| 81 |
+
max_seqlen_in_batch_q = max_seqlen_in_batch_k
|
| 82 |
+
indices_q = indices_k
|
| 83 |
+
elif query_length == 1:
|
| 84 |
+
max_seqlen_in_batch_q = 1
|
| 85 |
+
cu_seqlens_q = torch.arange(
|
| 86 |
+
batch_size + 1, dtype=torch.int32, device=query_layer.device,
|
| 87 |
+
) # There is a memcpy here, that is very bad.
|
| 88 |
+
indices_q = cu_seqlens_q[:-1]
|
| 89 |
+
query_layer = query_layer.squeeze(1)
|
| 90 |
+
else:
|
| 91 |
+
# The -q_len: slice assumes left padding.
|
| 92 |
+
attention_mask = attention_mask[:, -query_length:]
|
| 93 |
+
query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask)
|
| 94 |
+
|
| 95 |
+
return (
|
| 96 |
+
query_layer,
|
| 97 |
+
key_layer,
|
| 98 |
+
value_layer,
|
| 99 |
+
gate_layer,
|
| 100 |
+
beta_layer,
|
| 101 |
+
indices_q,
|
| 102 |
+
(cu_seqlens_q, cu_seqlens_k),
|
| 103 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k),
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def transform(
|
| 108 |
+
x: torch.Tensor,
|
| 109 |
+
routing_mask: torch.Tensor,
|
| 110 |
+
num_memories: int,
|
| 111 |
+
selected_memories: torch.Tensor,
|
| 112 |
+
attention_mask: torch.Tensor,
|
| 113 |
+
):
|
| 114 |
+
"""
|
| 115 |
+
Reorganize token embeddings into memory-aligned chunks.
|
| 116 |
+
|
| 117 |
+
Steps:
|
| 118 |
+
- Expand for top-k routing if needed.
|
| 119 |
+
- Mask out padded tokens via `attention_mask`.
|
| 120 |
+
- Sort tokens by (batch, memory).
|
| 121 |
+
- Gather and pad tokens per memory slot.
|
| 122 |
+
|
| 123 |
+
Args:
|
| 124 |
+
x: (batch, seq, hidden) input embeddings.
|
| 125 |
+
routing_mask: (batch, seq, num_memories) binary routing mask.
|
| 126 |
+
num_memories: number of memory slots.
|
| 127 |
+
selected_memories: memory indices per token,
|
| 128 |
+
(batch, seq) if k=1 else (batch, seq, topk).
|
| 129 |
+
attention_mask: (batch, seq) valid-token mask.
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
transformed_x: (num_memories, batch, max_len, hidden) reorganized tokens.
|
| 133 |
+
truncation_indices: (batch*num_memories, max_len) gather indices.
|
| 134 |
+
sorted_indices: (batch*seq*topk,) global sort order.
|
| 135 |
+
max_len: int, max tokens per memory.
|
| 136 |
+
mask: (batch*num_memories, max_len) validity mask.
|
| 137 |
+
mask_2: (num_memories, batch, max_len) validity mask reshaped.
|
| 138 |
+
"""
|
| 139 |
+
if selected_memories.dim() == 3:
|
| 140 |
+
# (batch, seq, topk)
|
| 141 |
+
topk = selected_memories.shape[2]
|
| 142 |
+
# x (batch, seq, hidden)
|
| 143 |
+
x = x.repeat_interleave(topk, dim=1)
|
| 144 |
+
# x (batch, seq * topk, hidden)
|
| 145 |
+
# (batch, seq, topk)
|
| 146 |
+
selected_memories = selected_memories.reshape(selected_memories.shape[0], -1)
|
| 147 |
+
# (batch, seq * topk)
|
| 148 |
+
|
| 149 |
+
if attention_mask is not None:
|
| 150 |
+
attention_mask = attention_mask[:, -routing_mask.shape[1]:]
|
| 151 |
+
# mask out the masked tokens
|
| 152 |
+
routing_mask[attention_mask.bitwise_not().unsqueeze(-1).expand(-1, -1, num_memories)] = 0
|
| 153 |
+
|
| 154 |
+
b, s, d = x.shape
|
| 155 |
+
x_flat = x.reshape(b * s, d) # [b*s, d]
|
| 156 |
+
|
| 157 |
+
with torch.no_grad():
|
| 158 |
+
batch_indices = torch.arange(b, device=x.device).unsqueeze(-1)
|
| 159 |
+
batch_indices = batch_indices.repeat(1, s).reshape(-1)
|
| 160 |
+
if attention_mask is not None:
|
| 161 |
+
# sort the masked tokens to the end
|
| 162 |
+
batch_indices[attention_mask.repeat_interleave(topk, dim=1).bitwise_not().flatten()] = b
|
| 163 |
+
# (b * s)
|
| 164 |
+
memories_flat = selected_memories.reshape(-1) # [b*s]
|
| 165 |
+
|
| 166 |
+
combined = batch_indices * (memories_flat.max() + 1) + memories_flat
|
| 167 |
+
sorted_indices = combined.argsort()
|
| 168 |
+
|
| 169 |
+
x_sorted = x_flat[sorted_indices] # [b*s, d]
|
| 170 |
+
# (b*s, hidden) -> (b, s, hidd)
|
| 171 |
+
with torch.no_grad():
|
| 172 |
+
# routing_mask (b, s, num_memories)
|
| 173 |
+
batch_memory_tokens = routing_mask.sum(dim=1)
|
| 174 |
+
# (b, num_memories)
|
| 175 |
+
flatten_offset = batch_memory_tokens.flatten().cumsum(dim=0)
|
| 176 |
+
max_len = batch_memory_tokens.max()
|
| 177 |
+
indices = (
|
| 178 |
+
torch.arange(max_len, device=flatten_offset.device).unsqueeze(0).expand(b * num_memories, -1)
|
| 179 |
+
+ torch.cat([torch.tensor([0], device=flatten_offset.device), flatten_offset[:-1]], dim=0).unsqueeze(1)
|
| 180 |
+
)
|
| 181 |
+
mask = indices < flatten_offset.unsqueeze(-1)
|
| 182 |
+
truncation_indices = torch.where(mask, indices, torch.zeros_like(indices))
|
| 183 |
+
|
| 184 |
+
gathered_x = torch.gather(x_sorted, 0, truncation_indices.reshape(-1).unsqueeze(-1).expand(-1, d))
|
| 185 |
+
transformed_x = gathered_x.reshape(b * num_memories, -1, d).reshape((b, num_memories, max_len, d)).transpose(0, 1)
|
| 186 |
+
# transformed_x = transformed_x * mask.unsqueeze(-1).expand_as(transformed_x)
|
| 187 |
+
# pad_x = torch.zeros((b * num_memories, capacity_len-max_len, d), dtype=transformed_x.dtype, device=transformed_x.device)
|
| 188 |
+
# pad_mask = torch.zeros((b * num_memories, capacity_len-max_len), dtype=transformed_x.dtype, device=transformed_x.device)
|
| 189 |
+
# left pad
|
| 190 |
+
# transformed_x = torch.cat((pad_x, transformed_x), dim=1).reshape((b, num_memories, capacity_len, d)).transpose(0, 1)
|
| 191 |
+
mask_2 = mask.reshape((b, num_memories, max_len)).transpose(0, 1)
|
| 192 |
+
# truncation_indices += capacity_len-max_len
|
| 193 |
+
# if attention_mask is not None:
|
| 194 |
+
# mask_2
|
| 195 |
+
|
| 196 |
+
return transformed_x, truncation_indices, sorted_indices, max_len, mask, mask_2
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def reconstruct(
|
| 200 |
+
transformed_x,
|
| 201 |
+
indices: torch.Tensor,
|
| 202 |
+
sorted_indices: torch.Tensor,
|
| 203 |
+
batch_size: int,
|
| 204 |
+
seq_len: int,
|
| 205 |
+
topk: int,
|
| 206 |
+
routing_weights: torch.Tensor,
|
| 207 |
+
mask: torch.Tensor,
|
| 208 |
+
):
|
| 209 |
+
'''
|
| 210 |
+
Reconstruct and mix transformed outputs back into the original input sequence shape.
|
| 211 |
+
|
| 212 |
+
Key operations:
|
| 213 |
+
1. Reshapes and transposes `transformed_x` to prepare for scattering.
|
| 214 |
+
2. Applies the `mask` to zero out invalid positions.
|
| 215 |
+
3. Uses `torch.scatter_add_` to scatter and sum the transformed outputs back to their original positions
|
| 216 |
+
based on `indices`.
|
| 217 |
+
4. Rearranges the scattered outputs using `sorted_indices` to ensure correct ordering.
|
| 218 |
+
5. Applies the `routing_weights` to weight the outputs.
|
| 219 |
+
6. Sums over the `topk` dimension to produce the final reconstructed output.
|
| 220 |
+
|
| 221 |
+
Args:
|
| 222 |
+
transformed_x (torch.Tensor):
|
| 223 |
+
The transformed output tensor from memory units or experts.
|
| 224 |
+
Shape: (num_memories, batch_size, capacity_len, hidden_size)
|
| 225 |
+
indices (torch.Tensor):
|
| 226 |
+
Indices used for scattering the transformed outputs back to their corresponding positions.
|
| 227 |
+
Shape: (batch*num_memories, max_len)
|
| 228 |
+
sorted_indices (torch.Tensor):
|
| 229 |
+
Sorting indices used to rearrange the scattered outputs back into the original sequence order.
|
| 230 |
+
Shape: (batch_size*seq_len*topk)
|
| 231 |
+
batch_size (int):
|
| 232 |
+
The size of the batch.
|
| 233 |
+
seq_len (int):
|
| 234 |
+
The length of the input sequence.
|
| 235 |
+
topk (int):
|
| 236 |
+
The number of top elements selected (`topk`) per token during the selection process.
|
| 237 |
+
routing_weights (torch.Tensor):
|
| 238 |
+
Routing weights assigned to the top-k selected outputs when reconstructing the final output.
|
| 239 |
+
Shape: (batch_size, seq_len, topk)
|
| 240 |
+
mask (torch.Tensor):
|
| 241 |
+
Boolean mask indicating valid positions in the sequence.
|
| 242 |
+
Shape: (batch*num_memories, max_len)
|
| 243 |
+
|
| 244 |
+
Returns:
|
| 245 |
+
restored_x (torch.Tensor):
|
| 246 |
+
The reconstructed output tensor in the original input sequence shape.
|
| 247 |
+
Shape: (batch_size, seq_len, hidden_size)
|
| 248 |
+
'''
|
| 249 |
+
transformed_x = transformed_x.transpose(0, 1).reshape(
|
| 250 |
+
(-1, transformed_x.shape[2], transformed_x.shape[3]))
|
| 251 |
+
b, s, k, d = batch_size, seq_len, topk, transformed_x.shape[2]
|
| 252 |
+
gathered_x = transformed_x.reshape(
|
| 253 |
+
(transformed_x.shape[0] * transformed_x.shape[1], transformed_x.shape[2]))
|
| 254 |
+
mask_expanded = mask.reshape(-1).unsqueeze(-1).expand_as(gathered_x)
|
| 255 |
+
gathered_x = gathered_x * mask_expanded
|
| 256 |
+
|
| 257 |
+
assert (indices >= 0).all(), "Indices should be non-negative"
|
| 258 |
+
|
| 259 |
+
resortd_x = torch.zeros((b * s * k, d), device=gathered_x.device, dtype=gathered_x.dtype).scatter_add_(
|
| 260 |
+
0,
|
| 261 |
+
indices.reshape(-1).unsqueeze(-1).expand(-1, d),
|
| 262 |
+
gathered_x,
|
| 263 |
+
)
|
| 264 |
+
assert (indices < resortd_x.size(0)).all(), "Indices should be less than resortd_x size"
|
| 265 |
+
|
| 266 |
+
inverse_indices = sorted_indices.argsort()
|
| 267 |
+
rearranged_x_flat = resortd_x[inverse_indices]
|
| 268 |
+
restored_x = rearranged_x_flat.reshape((b, s * k, d))
|
| 269 |
+
restored_x = restored_x.reshape(b, s, k, d) * routing_weights.reshape(b, s, k).unsqueeze(-1)
|
| 270 |
+
restored_x = restored_x.sum(dim=2)
|
| 271 |
+
return restored_x
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
class MomAttention(nn.Module):
|
| 275 |
+
"""
|
| 276 |
+
The layer implementaion for [MoM: Linear Sequence Modeling with Mixture-of-Memories](https://arxiv.org/abs/2502.13685).
|
| 277 |
+
"""
|
| 278 |
+
|
| 279 |
+
def __init__(
|
| 280 |
+
self,
|
| 281 |
+
hidden_size: int = 2048,
|
| 282 |
+
head_dim: int = 256,
|
| 283 |
+
num_heads: int = 4,
|
| 284 |
+
expand_v: float = 2,
|
| 285 |
+
mode: str = 'chunk',
|
| 286 |
+
use_output_gate: bool = True,
|
| 287 |
+
use_short_conv: bool = True,
|
| 288 |
+
conv_size: int = 4,
|
| 289 |
+
conv_bias: bool = False,
|
| 290 |
+
layer_idx: int = None,
|
| 291 |
+
norm_eps: float = 1e-5,
|
| 292 |
+
num_memories: int = 8,
|
| 293 |
+
topk: int = 2,
|
| 294 |
+
capacity: float = 1.0,
|
| 295 |
+
shared_mem: bool = False,
|
| 296 |
+
single_kv_proj: bool = False,
|
| 297 |
+
**kwargs,
|
| 298 |
+
) -> MomAttention:
|
| 299 |
+
super().__init__()
|
| 300 |
+
self.num_memories = num_memories
|
| 301 |
+
self.topk = topk
|
| 302 |
+
self.capacity = capacity
|
| 303 |
+
self.shared_mem = shared_mem
|
| 304 |
+
self.single_kv_proj = single_kv_proj
|
| 305 |
+
|
| 306 |
+
self.mode = mode
|
| 307 |
+
|
| 308 |
+
self.hidden_size = hidden_size
|
| 309 |
+
self.expand_v = expand_v
|
| 310 |
+
|
| 311 |
+
self.use_output_gate = use_output_gate
|
| 312 |
+
self.use_short_conv = use_short_conv
|
| 313 |
+
self.conv_size = conv_size
|
| 314 |
+
self.conv_bias = conv_bias
|
| 315 |
+
|
| 316 |
+
self.head_dim = head_dim
|
| 317 |
+
self.num_heads = num_heads
|
| 318 |
+
|
| 319 |
+
self.key_dim = int(self.num_heads * self.head_dim)
|
| 320 |
+
self.value_dim = int(self.key_dim * self.expand_v)
|
| 321 |
+
self.head_qk_dim = head_dim
|
| 322 |
+
self.head_v_dim = int(head_dim * self.expand_v)
|
| 323 |
+
self.layer_idx = layer_idx
|
| 324 |
+
self.silu = nn.SiLU()
|
| 325 |
+
|
| 326 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not suppoerted mode `{mode}`."
|
| 327 |
+
|
| 328 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 329 |
+
self.gate = nn.Linear(self.hidden_size, self.num_memories, bias=False)
|
| 330 |
+
if self.single_kv_proj:
|
| 331 |
+
self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 332 |
+
self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 333 |
+
self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 334 |
+
self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 335 |
+
else:
|
| 336 |
+
self.k_proj = nn.ModuleList([
|
| 337 |
+
nn.Linear(self.hidden_size, self.key_dim, bias=False)
|
| 338 |
+
for _ in range(self.num_memories)
|
| 339 |
+
])
|
| 340 |
+
self.v_proj = nn.ModuleList([
|
| 341 |
+
nn.Linear(self.hidden_size, self.value_dim, bias=False)
|
| 342 |
+
for _ in range(self.num_memories)
|
| 343 |
+
])
|
| 344 |
+
self.b_proj = nn.ModuleList([
|
| 345 |
+
nn.Linear(self.hidden_size, self.num_heads, bias=False)
|
| 346 |
+
for _ in range(self.num_memories)
|
| 347 |
+
])
|
| 348 |
+
self.a_proj = nn.ModuleList([
|
| 349 |
+
nn.Linear(self.hidden_size, self.num_heads, bias=False)
|
| 350 |
+
for _ in range(self.num_memories)
|
| 351 |
+
])
|
| 352 |
+
if self.shared_mem:
|
| 353 |
+
self.shared_k = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 354 |
+
self.shared_v = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 355 |
+
self.shared_b = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 356 |
+
self.shared_a = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 357 |
+
|
| 358 |
+
A = torch.empty(self.num_heads, dtype=torch.float32).uniform_(0, 16)
|
| 359 |
+
self.A_log = nn.Parameter(torch.log(A))
|
| 360 |
+
self.A_log._no_weight_decay = True
|
| 361 |
+
# hard coded for now
|
| 362 |
+
dt_min = 0.001
|
| 363 |
+
dt_max = 0.1
|
| 364 |
+
dt_init_floor = 1e-4
|
| 365 |
+
dt = torch.exp(
|
| 366 |
+
torch.rand(self.num_heads) * (math.log(dt_max) - math.log(dt_min))
|
| 367 |
+
+ math.log(dt_min),
|
| 368 |
+
)
|
| 369 |
+
dt = torch.clamp(dt, min=dt_init_floor)
|
| 370 |
+
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
|
| 371 |
+
inv_dt = dt + torch.log(-torch.expm1(-dt))
|
| 372 |
+
self.dt_bias = nn.Parameter(inv_dt)
|
| 373 |
+
# Just to be explicit. Without this we already don't put wd on dt_bias because of the check
|
| 374 |
+
# name.endswith("bias") in param_grouping.py
|
| 375 |
+
self.dt_bias._no_weight_decay = True
|
| 376 |
+
|
| 377 |
+
if use_short_conv:
|
| 378 |
+
self.conv_size = conv_size
|
| 379 |
+
self.q_conv1d = ShortConvolution(
|
| 380 |
+
hidden_size=self.key_dim,
|
| 381 |
+
kernel_size=conv_size,
|
| 382 |
+
bias=conv_bias,
|
| 383 |
+
activation='silu',
|
| 384 |
+
)
|
| 385 |
+
self.k_conv1d = ShortConvolution(
|
| 386 |
+
hidden_size=self.key_dim,
|
| 387 |
+
kernel_size=conv_size,
|
| 388 |
+
bias=conv_bias,
|
| 389 |
+
activation='silu',
|
| 390 |
+
)
|
| 391 |
+
self.v_conv1d = ShortConvolution(
|
| 392 |
+
hidden_size=self.value_dim,
|
| 393 |
+
kernel_size=conv_size,
|
| 394 |
+
bias=conv_bias,
|
| 395 |
+
activation='silu',
|
| 396 |
+
)
|
| 397 |
+
else:
|
| 398 |
+
raise UserWarning(
|
| 399 |
+
"ShortConvolution is crucial to the performance. "
|
| 400 |
+
"Do not turn it off, i.e., setting `use_short_conv=False` unless you know what you are doing.",
|
| 401 |
+
)
|
| 402 |
+
if use_output_gate:
|
| 403 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 404 |
+
self.o_norm = FusedRMSNormGated(self.head_v_dim, eps=norm_eps)
|
| 405 |
+
else:
|
| 406 |
+
self.o_norm = RMSNorm(self.head_v_dim, eps=norm_eps, dtype=torch.float32)
|
| 407 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 408 |
+
self.apply(self._initialize_weights)
|
| 409 |
+
|
| 410 |
+
def _initialize_weights(self, module: nn.Module):
|
| 411 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 412 |
+
return
|
| 413 |
+
if isinstance(module, nn.Linear):
|
| 414 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 415 |
+
if module.bias is not None:
|
| 416 |
+
nn.init.zeros_(module.bias)
|
| 417 |
+
module._is_hf_initialized = True
|
| 418 |
+
|
| 419 |
+
def forward(
|
| 420 |
+
self,
|
| 421 |
+
hidden_states: torch.Tensor,
|
| 422 |
+
attention_mask: torch.Tensor | None = None,
|
| 423 |
+
past_key_values: Cache | None = None,
|
| 424 |
+
use_cache: bool | None = False,
|
| 425 |
+
output_attentions: bool | None = False,
|
| 426 |
+
**kwargs: Unpack[dict],
|
| 427 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 428 |
+
if attention_mask is not None:
|
| 429 |
+
attention_mask = (attention_mask == 1)
|
| 430 |
+
assert len(attention_mask.shape) == 2, (
|
| 431 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 432 |
+
"for padding purposes (0 indicating padding). "
|
| 433 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
origin_cu_seqlens = kwargs.get('cu_seqlens')
|
| 437 |
+
if origin_cu_seqlens is not None:
|
| 438 |
+
hidden_states, attention_mask = self.cu2pad(hidden_states, origin_cu_seqlens)
|
| 439 |
+
|
| 440 |
+
mode = 'fused_recurrent' if (hidden_states.shape[1] <= 64 and not self.training) else self.mode
|
| 441 |
+
if self.training:
|
| 442 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 443 |
+
|
| 444 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 445 |
+
# _, q_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 446 |
+
|
| 447 |
+
# 🔍 topk gating
|
| 448 |
+
router_logits = self.gate(hidden_states) # (bsz, q_len, num_memories)
|
| 449 |
+
scores = F.softmax(router_logits, dim=2, dtype=torch.float)
|
| 450 |
+
routing_weights, selected_memories = torch.topk(scores, self.topk, dim=-1) # (bsz, seq, topk)
|
| 451 |
+
routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
|
| 452 |
+
routing_weights = routing_weights.to(hidden_states.dtype) # we cast back to the input dtype
|
| 453 |
+
routing_weights_full = torch.zeros(
|
| 454 |
+
routing_weights.shape[0],
|
| 455 |
+
routing_weights.shape[1],
|
| 456 |
+
self.num_memories,
|
| 457 |
+
dtype=routing_weights.dtype,
|
| 458 |
+
device=routing_weights.device,
|
| 459 |
+
).scatter(-1, selected_memories, routing_weights)
|
| 460 |
+
routing_mask = routing_weights_full.bool().int()
|
| 461 |
+
|
| 462 |
+
# if self.use_output_gate:
|
| 463 |
+
# o_g = self.g_proj(hidden_states)
|
| 464 |
+
|
| 465 |
+
batch_size, seq_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 466 |
+
|
| 467 |
+
shared_hidden_states = hidden_states
|
| 468 |
+
hidden_states, indices, sorted_indices, max_len, mask, mask_2 = transform(
|
| 469 |
+
hidden_states, routing_mask, self.num_memories, selected_memories, attention_mask)
|
| 470 |
+
|
| 471 |
+
q = self.q_proj(hidden_states)
|
| 472 |
+
if self.single_kv_proj:
|
| 473 |
+
k = self.shared_k(hidden_states)
|
| 474 |
+
v = self.shared_v(hidden_states)
|
| 475 |
+
beta = self.shared_b(hidden_states).sigmoid()
|
| 476 |
+
g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
|
| 477 |
+
else:
|
| 478 |
+
k = torch.stack([k_expert(hidden_states[i]) for i, k_expert in enumerate(self.k_proj)], dim=0)
|
| 479 |
+
v = torch.stack([v_expert(hidden_states[i]) for i, v_expert in enumerate(self.v_proj)], dim=0)
|
| 480 |
+
beta = torch.stack([b_expert(hidden_states[i]).sigmoid() for i, b_expert in enumerate(self.b_proj)], dim=0)
|
| 481 |
+
g = torch.stack([-self.A_log.float().exp() * F.softplus(a_expert(hidden_states[i]).float() + self.dt_bias)
|
| 482 |
+
for i, a_expert in enumerate(self.a_proj)], dim=0)
|
| 483 |
+
|
| 484 |
+
q, k, v, g, beta, mask_2 = (rearrange(x, 'e b l ... -> (e b) l ...') for x in (q, k, v, g, beta, mask_2))
|
| 485 |
+
cu_q, cu_k, cu_v, cu_g, cu_beta, indices_q, cu_seqlen_all, max_seq_lens = _upad_input(q, k, v, g, beta, mask_2)
|
| 486 |
+
cu_seqlens, reverse_indices = cu_seqlen_all[0].to(torch.long).unique(return_inverse=True)
|
| 487 |
+
cu_q, cu_k, cu_v, cu_g, cu_beta = (x.unsqueeze(0).contiguous() for x in (cu_q, cu_k, cu_v, cu_g, cu_beta))
|
| 488 |
+
|
| 489 |
+
if self.use_short_conv:
|
| 490 |
+
conv_state_q, conv_state_k, conv_state_v = [None, None], [None, None], [None, None]
|
| 491 |
+
if last_state is not None:
|
| 492 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 493 |
+
|
| 494 |
+
conv_cu_seqlens = cu_seqlens
|
| 495 |
+
padded = False
|
| 496 |
+
if self.training:
|
| 497 |
+
conv_cu_seqlens = None
|
| 498 |
+
elif seq_len != 1 and (cu_seqlens[1:] - cu_seqlens[:-1]).min().item() < self.conv_size:
|
| 499 |
+
padded = True
|
| 500 |
+
conv_cu_seqlens, cu_q, cu_k, cu_v, pad_lengths = self.pad_for_conv(cu_seqlens, cu_q, cu_k, cu_v)
|
| 501 |
+
|
| 502 |
+
conv_q = self.prepare_recurrent_state(
|
| 503 |
+
conv_state_q[0],
|
| 504 |
+
conv_cu_seqlens,
|
| 505 |
+
cu_seqlen_all[0],
|
| 506 |
+
reverse_indices,
|
| 507 |
+
batch_size,
|
| 508 |
+
)
|
| 509 |
+
cu_q, conv_q_new = self.q_conv1d(
|
| 510 |
+
x=cu_q,
|
| 511 |
+
cache=conv_q,
|
| 512 |
+
output_final_state=use_cache,
|
| 513 |
+
cu_seqlens=conv_cu_seqlens,
|
| 514 |
+
)
|
| 515 |
+
conv_state_q[0] = self.handle_recurrent_state(
|
| 516 |
+
conv_state_q[0],
|
| 517 |
+
conv_q_new,
|
| 518 |
+
conv_cu_seqlens,
|
| 519 |
+
cu_seqlen_all[0],
|
| 520 |
+
reverse_indices,
|
| 521 |
+
)
|
| 522 |
+
conv_k = self.prepare_recurrent_state(
|
| 523 |
+
conv_state_k[0],
|
| 524 |
+
conv_cu_seqlens,
|
| 525 |
+
cu_seqlen_all[0],
|
| 526 |
+
reverse_indices,
|
| 527 |
+
batch_size,
|
| 528 |
+
)
|
| 529 |
+
cu_k, conv_k_new = self.k_conv1d(
|
| 530 |
+
x=cu_k,
|
| 531 |
+
cache=conv_k,
|
| 532 |
+
output_final_state=use_cache,
|
| 533 |
+
cu_seqlens=conv_cu_seqlens,
|
| 534 |
+
)
|
| 535 |
+
conv_state_k[0] = self.handle_recurrent_state(
|
| 536 |
+
conv_state_k[0],
|
| 537 |
+
conv_k_new,
|
| 538 |
+
conv_cu_seqlens,
|
| 539 |
+
cu_seqlen_all[0],
|
| 540 |
+
reverse_indices,
|
| 541 |
+
)
|
| 542 |
+
conv_v = self.prepare_recurrent_state(
|
| 543 |
+
conv_state_v[0],
|
| 544 |
+
conv_cu_seqlens,
|
| 545 |
+
cu_seqlen_all[0],
|
| 546 |
+
reverse_indices,
|
| 547 |
+
batch_size,
|
| 548 |
+
)
|
| 549 |
+
cu_v, conv_v_new = self.v_conv1d(
|
| 550 |
+
x=cu_v,
|
| 551 |
+
cache=conv_v,
|
| 552 |
+
output_final_state=use_cache,
|
| 553 |
+
cu_seqlens=conv_cu_seqlens,
|
| 554 |
+
)
|
| 555 |
+
conv_state_v[0] = self.handle_recurrent_state(
|
| 556 |
+
conv_state_v[0],
|
| 557 |
+
conv_v_new, conv_cu_seqlens,
|
| 558 |
+
cu_seqlen_all[0],
|
| 559 |
+
reverse_indices,
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
if padded:
|
| 563 |
+
cu_q, cu_k, cu_v = self.unpad_after_conv(conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths)
|
| 564 |
+
|
| 565 |
+
else:
|
| 566 |
+
q, k, v = self.silu(q), self.silu(k), self.silu(v)
|
| 567 |
+
|
| 568 |
+
cu_q, cu_k, cu_v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (cu_q, cu_k, cu_v))
|
| 569 |
+
|
| 570 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else [
|
| 571 |
+
None for _ in range(1 + self.shared_mem)]
|
| 572 |
+
if mode == 'chunk':
|
| 573 |
+
o, recurrent_state_ = chunk_gated_delta_rule(
|
| 574 |
+
q=cu_q,
|
| 575 |
+
k=cu_k,
|
| 576 |
+
v=cu_v,
|
| 577 |
+
g=cu_g,
|
| 578 |
+
beta=cu_beta,
|
| 579 |
+
initial_state=recurrent_state[0],
|
| 580 |
+
output_final_state=use_cache,
|
| 581 |
+
use_qk_l2norm_in_kernel=True,
|
| 582 |
+
cu_seqlens=cu_seqlens,
|
| 583 |
+
)
|
| 584 |
+
recurrent_state[0] = self.handle_recurrent_state(
|
| 585 |
+
recurrent_state[0],
|
| 586 |
+
recurrent_state_,
|
| 587 |
+
cu_seqlens,
|
| 588 |
+
cu_seqlen_all[0],
|
| 589 |
+
reverse_indices,
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
elif mode == 'fused_recurrent':
|
| 593 |
+
memories = self.prepare_recurrent_state(
|
| 594 |
+
recurrent_state[0],
|
| 595 |
+
cu_seqlens, cu_seqlen_all[0],
|
| 596 |
+
reverse_indices, batch_size,
|
| 597 |
+
)
|
| 598 |
+
o, recurrent_state_ = fused_recurrent_gated_delta_rule(
|
| 599 |
+
q=cu_q,
|
| 600 |
+
k=cu_k,
|
| 601 |
+
v=cu_v,
|
| 602 |
+
g=cu_g,
|
| 603 |
+
beta=cu_beta,
|
| 604 |
+
initial_state=memories,
|
| 605 |
+
output_final_state=use_cache,
|
| 606 |
+
use_qk_l2norm_in_kernel=True,
|
| 607 |
+
cu_seqlens=cu_seqlens,
|
| 608 |
+
)
|
| 609 |
+
recurrent_state[0] = self.handle_recurrent_state(
|
| 610 |
+
recurrent_state[0],
|
| 611 |
+
recurrent_state_,
|
| 612 |
+
cu_seqlens,
|
| 613 |
+
cu_seqlen_all[0],
|
| 614 |
+
reverse_indices,
|
| 615 |
+
)
|
| 616 |
+
|
| 617 |
+
o = o.squeeze(0).contiguous()
|
| 618 |
+
o = pad_input(o, indices_q, batch_size*self.num_memories, max_len)
|
| 619 |
+
o = rearrange(o, '(e b) l h d -> e b l (h d)', b=batch_size)
|
| 620 |
+
o = reconstruct(o, indices=indices, sorted_indices=sorted_indices, batch_size=batch_size,
|
| 621 |
+
seq_len=seq_len, topk=self.topk, routing_weights=routing_weights, mask=mask)
|
| 622 |
+
o = rearrange(o, 'b l (h d) -> b l h d', h=self.num_heads)
|
| 623 |
+
|
| 624 |
+
if self.shared_mem:
|
| 625 |
+
shared_o = self.shared_o(shared_hidden_states, attention_mask, recurrent_state,
|
| 626 |
+
use_cache, conv_state_q, conv_state_k, conv_state_v)
|
| 627 |
+
o += shared_o
|
| 628 |
+
|
| 629 |
+
update_layer_cache(
|
| 630 |
+
self,
|
| 631 |
+
past_key_values,
|
| 632 |
+
recurrent_state=recurrent_state,
|
| 633 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 634 |
+
offset=q.shape[2],
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
if self.use_output_gate:
|
| 638 |
+
g = rearrange(self.g_proj(shared_hidden_states), '... (h d) -> ... h d', d=self.head_v_dim)
|
| 639 |
+
o = self.o_norm(o, g)
|
| 640 |
+
else:
|
| 641 |
+
o = self.o_norm(o)
|
| 642 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 643 |
+
o = self.o_proj(o)
|
| 644 |
+
|
| 645 |
+
if origin_cu_seqlens is not None:
|
| 646 |
+
indices, _, _ = get_unpad_data(attention_mask[:, -seq_len:])
|
| 647 |
+
o = index_first_axis(rearrange(o, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 648 |
+
|
| 649 |
+
return o, None, past_key_values, router_logits.view(-1, self.num_memories)
|
| 650 |
+
|
| 651 |
+
def shared_o(
|
| 652 |
+
self,
|
| 653 |
+
hidden_states: torch.Tensor,
|
| 654 |
+
attention_mask: torch.Tensor | None = None,
|
| 655 |
+
recurrent_state=None,
|
| 656 |
+
use_cache: bool | None = False,
|
| 657 |
+
conv_state_q=[None, None],
|
| 658 |
+
conv_state_k=[None, None],
|
| 659 |
+
conv_state_v=[None, None],
|
| 660 |
+
**kwargs,
|
| 661 |
+
) -> torch.Tensor:
|
| 662 |
+
if attention_mask is not None:
|
| 663 |
+
assert len(attention_mask.shape) == 2, (
|
| 664 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 665 |
+
"for padding purposes (0 indicating padding). "
|
| 666 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 670 |
+
if self.training:
|
| 671 |
+
assert mode == 'chunk', "Only chunk mode is supported in training."
|
| 672 |
+
|
| 673 |
+
cu_seqlens = None
|
| 674 |
+
if attention_mask is not None:
|
| 675 |
+
batch_size, q_len = hidden_states.shape[0], hidden_states.shape[1]
|
| 676 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 677 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 678 |
+
|
| 679 |
+
if self.use_short_conv:
|
| 680 |
+
q, conv_state_q[1] = self.q_conv1d(
|
| 681 |
+
x=self.q_proj(hidden_states),
|
| 682 |
+
cache=conv_state_q[1],
|
| 683 |
+
output_final_state=use_cache,
|
| 684 |
+
cu_seqlens=cu_seqlens,
|
| 685 |
+
)
|
| 686 |
+
k, conv_state_k[1] = self.k_conv1d(
|
| 687 |
+
x=self.shared_k(hidden_states),
|
| 688 |
+
cache=conv_state_k[1],
|
| 689 |
+
output_final_state=use_cache,
|
| 690 |
+
cu_seqlens=cu_seqlens,
|
| 691 |
+
)
|
| 692 |
+
v, conv_state_v[1] = self.v_conv1d(
|
| 693 |
+
x=self.shared_v(hidden_states),
|
| 694 |
+
cache=conv_state_v[1],
|
| 695 |
+
output_final_state=use_cache,
|
| 696 |
+
cu_seqlens=cu_seqlens,
|
| 697 |
+
)
|
| 698 |
+
else:
|
| 699 |
+
q = self.silu(self.q_proj(hidden_states))
|
| 700 |
+
k = self.silu(self.shared_k(hidden_states))
|
| 701 |
+
v = self.silu(self.shared_v(hidden_states))
|
| 702 |
+
|
| 703 |
+
q, k, v = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', h=self.num_heads), (q, k, v))
|
| 704 |
+
beta = self.shared_b(hidden_states).sigmoid()
|
| 705 |
+
g = -self.A_log.float().exp() * F.softplus(self.shared_a(hidden_states).float() + self.dt_bias)
|
| 706 |
+
|
| 707 |
+
if mode == 'chunk':
|
| 708 |
+
o, recurrent_state[-1] = chunk_gated_delta_rule(
|
| 709 |
+
q=q,
|
| 710 |
+
k=k,
|
| 711 |
+
v=v,
|
| 712 |
+
g=g,
|
| 713 |
+
beta=beta,
|
| 714 |
+
initial_state=recurrent_state[-1],
|
| 715 |
+
output_final_state=use_cache,
|
| 716 |
+
cu_seqlens=cu_seqlens,
|
| 717 |
+
use_qk_l2norm_in_kernel=True,
|
| 718 |
+
)
|
| 719 |
+
elif mode == 'fused_recurrent':
|
| 720 |
+
o, recurrent_state[-1] = fused_recurrent_gated_delta_rule(
|
| 721 |
+
q=q,
|
| 722 |
+
k=k,
|
| 723 |
+
v=v,
|
| 724 |
+
g=g,
|
| 725 |
+
beta=beta,
|
| 726 |
+
initial_state=recurrent_state[-1],
|
| 727 |
+
output_final_state=use_cache,
|
| 728 |
+
cu_seqlens=cu_seqlens,
|
| 729 |
+
use_qk_l2norm_in_kernel=True,
|
| 730 |
+
)
|
| 731 |
+
else:
|
| 732 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 733 |
+
|
| 734 |
+
if attention_mask is not None:
|
| 735 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 736 |
+
return o
|
| 737 |
+
|
| 738 |
+
def cu2pad(self, x, cu_seqlens):
|
| 739 |
+
batch_size = cu_seqlens.shape[0] - 1
|
| 740 |
+
max_len = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item()
|
| 741 |
+
indices = torch.tensor([], dtype=torch.long, device=x.device)
|
| 742 |
+
attention_mask = torch.ones((batch_size, max_len), dtype=torch.bool, device=x.device)
|
| 743 |
+
for i in range(batch_size):
|
| 744 |
+
seq_len = cu_seqlens[i+1] - cu_seqlens[i]
|
| 745 |
+
pad_len = max_len - seq_len
|
| 746 |
+
batch_indices = torch.arange(pad_len, max_len, device=x.device)
|
| 747 |
+
batch_indices = batch_indices + i * max_len
|
| 748 |
+
indices = torch.cat([indices, batch_indices])
|
| 749 |
+
attention_mask[i, :pad_len] = False
|
| 750 |
+
x = pad_input(x.squeeze(0), indices, batch_size, max_len)
|
| 751 |
+
return x, attention_mask
|
| 752 |
+
|
| 753 |
+
def pad_for_conv(self, cu_seqlens, cu_q, cu_k, cu_v):
|
| 754 |
+
lengths = cu_seqlens[1:] - cu_seqlens[:-1]
|
| 755 |
+
pad_lengths = torch.clamp(self.conv_size - lengths, min=0)
|
| 756 |
+
new_lengths = lengths + pad_lengths
|
| 757 |
+
new_cu_seqlens = torch.cat([
|
| 758 |
+
torch.tensor([0], device=cu_seqlens.device, dtype=cu_seqlens.dtype),
|
| 759 |
+
torch.cumsum(new_lengths, dim=0),
|
| 760 |
+
])
|
| 761 |
+
final_total_len = new_cu_seqlens[-1].item()
|
| 762 |
+
new_q = torch.zeros((1, final_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
|
| 763 |
+
new_k = torch.zeros((1, final_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
|
| 764 |
+
new_v = torch.zeros((1, final_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
|
| 765 |
+
num_sequences = len(lengths)
|
| 766 |
+
for i in range(num_sequences):
|
| 767 |
+
src_start = cu_seqlens[i]
|
| 768 |
+
src_end = cu_seqlens[i+1]
|
| 769 |
+
dest_start = new_cu_seqlens[i] + pad_lengths[i]
|
| 770 |
+
dest_end = new_cu_seqlens[i+1]
|
| 771 |
+
new_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
|
| 772 |
+
new_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
|
| 773 |
+
new_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
|
| 774 |
+
|
| 775 |
+
return new_cu_seqlens, new_q, new_k, new_v, pad_lengths
|
| 776 |
+
|
| 777 |
+
def unpad_after_conv(self, conv_cu_seqlens, cu_seqlens, cu_q, cu_k, cu_v, pad_lengths):
|
| 778 |
+
original_total_len = cu_seqlens[-1].item()
|
| 779 |
+
orig_q = torch.empty((1, original_total_len, cu_q.shape[-1]), dtype=cu_q.dtype, device=cu_q.device)
|
| 780 |
+
orig_k = torch.empty((1, original_total_len, cu_k.shape[-1]), dtype=cu_k.dtype, device=cu_k.device)
|
| 781 |
+
orig_v = torch.empty((1, original_total_len, cu_v.shape[-1]), dtype=cu_v.dtype, device=cu_v.device)
|
| 782 |
+
|
| 783 |
+
num_sequences = len(pad_lengths)
|
| 784 |
+
for i in range(num_sequences):
|
| 785 |
+
dest_start = cu_seqlens[i]
|
| 786 |
+
dest_end = cu_seqlens[i+1]
|
| 787 |
+
src_start = conv_cu_seqlens[i] + pad_lengths[i]
|
| 788 |
+
src_end = conv_cu_seqlens[i+1]
|
| 789 |
+
|
| 790 |
+
orig_q[:, dest_start:dest_end, ...] = cu_q[:, src_start:src_end, ...]
|
| 791 |
+
orig_k[:, dest_start:dest_end, ...] = cu_k[:, src_start:src_end, ...]
|
| 792 |
+
orig_v[:, dest_start:dest_end, ...] = cu_v[:, src_start:src_end, ...]
|
| 793 |
+
return orig_q, orig_k, orig_v
|
| 794 |
+
|
| 795 |
+
def prepare_recurrent_state(self, recurrent_state, cu_seqlens, cu_seqlen_all, reverse_indices, batch_size):
|
| 796 |
+
if recurrent_state is None:
|
| 797 |
+
return None
|
| 798 |
+
|
| 799 |
+
if cu_seqlens is None:
|
| 800 |
+
return recurrent_state
|
| 801 |
+
|
| 802 |
+
total_len = len(cu_seqlen_all)
|
| 803 |
+
if len(cu_seqlens) != total_len:
|
| 804 |
+
# select memories that are activated
|
| 805 |
+
memories = torch.zeros_like(recurrent_state[:self.topk*batch_size])
|
| 806 |
+
mem_id = 0
|
| 807 |
+
for i in range(total_len-1):
|
| 808 |
+
if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
|
| 809 |
+
memories[mem_id] = recurrent_state[i]
|
| 810 |
+
mem_id += 1
|
| 811 |
+
assert mem_id == self.topk * batch_size, f"The number of memories {mem_id} is not correct."
|
| 812 |
+
else:
|
| 813 |
+
memories = recurrent_state
|
| 814 |
+
|
| 815 |
+
return memories
|
| 816 |
+
|
| 817 |
+
def handle_recurrent_state(self, recurrent_state, recurrent_state_new, cu_seqlens, cu_seqlen_all, reverse_indices):
|
| 818 |
+
if recurrent_state_new is None:
|
| 819 |
+
return None
|
| 820 |
+
if cu_seqlens is None:
|
| 821 |
+
return recurrent_state_new
|
| 822 |
+
if recurrent_state is None:
|
| 823 |
+
recurrent_state = torch.zeros_like(recurrent_state_new[reverse_indices[1:]-1])
|
| 824 |
+
total_len = len(cu_seqlen_all)
|
| 825 |
+
if len(cu_seqlens) != total_len:
|
| 826 |
+
for i in range(total_len-1):
|
| 827 |
+
if cu_seqlen_all[i] != cu_seqlen_all[i+1]:
|
| 828 |
+
recurrent_state[i] = recurrent_state_new[reverse_indices[i+1]-1]
|
| 829 |
+
else:
|
| 830 |
+
recurrent_state = recurrent_state_new
|
| 831 |
+
return recurrent_state
|
fla/layers/multiscale_retention.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange, repeat
|
| 10 |
+
from transformers.activations import ACT2FN
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, get_unpad_data, index_first_axis, pad_input, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.rotary import RotaryEmbedding
|
| 15 |
+
from fla.ops.retention import chunk_retention, fused_chunk_retention, fused_recurrent_retention, parallel_retention
|
| 16 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from transformers.processing_utils import Unpack
|
| 20 |
+
|
| 21 |
+
from fla.models.utils import Cache
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class MultiScaleRetention(nn.Module):
|
| 25 |
+
r"""
|
| 26 |
+
The layer implementaion for [Retentive Network: A Successor to Transformer for Large Language Models](https://arxiv.org/pdf/2307.08621.pdf). # noqa
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
mode (str, Optional):
|
| 30 |
+
Which Retention kernel to use.
|
| 31 |
+
Currently available: `chunk`, `fused_recurrent`, `parallel`, and `fused_chunk`.
|
| 32 |
+
Default: `chunk`.
|
| 33 |
+
hidden_size (int, Optional):
|
| 34 |
+
The hidden size of the input. Default: 1024.
|
| 35 |
+
expand_k (float, Optional):
|
| 36 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 37 |
+
expand_v (float, Optional):
|
| 38 |
+
The expansion ratio for the value dim. Default: 2.0.
|
| 39 |
+
num_heads (int, Optional):
|
| 40 |
+
The number of heads. Default: 8.
|
| 41 |
+
num_kv_heads (int, Optional):
|
| 42 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 43 |
+
feature_map (str, Optional):
|
| 44 |
+
Feature map function applied to queries/keys. Default: None.
|
| 45 |
+
use_short_conv (bool, Optional):
|
| 46 |
+
Whether to use short convolutions. Default: `False`.
|
| 47 |
+
conv_size (int, Optional):
|
| 48 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 49 |
+
conv_bias (bool, Optional):
|
| 50 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 51 |
+
use_output_gate (bool, Optional):
|
| 52 |
+
Whether to use output gate. Default: `True`.
|
| 53 |
+
gate_fn (str, Optional):
|
| 54 |
+
The activation function for the output gate. Default: `swish`.
|
| 55 |
+
elementwise_affine (bool, Optional):
|
| 56 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 57 |
+
norm_eps (float, Optional):
|
| 58 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 59 |
+
fuse_norm (bool, Optional):
|
| 60 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 61 |
+
layer_idx (int, Optional):
|
| 62 |
+
The index of the layer. Default: None.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
def __init__(
|
| 66 |
+
self,
|
| 67 |
+
mode: str = 'chunk',
|
| 68 |
+
hidden_size: int = 1024,
|
| 69 |
+
expand_k: float = 1.0,
|
| 70 |
+
expand_v: float = 2.0,
|
| 71 |
+
num_heads: int = 8,
|
| 72 |
+
num_kv_heads: int | None = None,
|
| 73 |
+
feature_map: str | None = None,
|
| 74 |
+
use_short_conv: bool = False,
|
| 75 |
+
conv_size: int = 4,
|
| 76 |
+
conv_bias: bool = False,
|
| 77 |
+
use_output_gate: bool = True,
|
| 78 |
+
gate_fn: str = 'swish',
|
| 79 |
+
elementwise_affine: bool | None = True,
|
| 80 |
+
norm_eps: float = 1e-5,
|
| 81 |
+
fuse_norm: bool = True,
|
| 82 |
+
layer_idx: int = None,
|
| 83 |
+
**kwargs,
|
| 84 |
+
) -> MultiScaleRetention:
|
| 85 |
+
super().__init__()
|
| 86 |
+
|
| 87 |
+
self.mode = mode
|
| 88 |
+
self.hidden_size = hidden_size
|
| 89 |
+
self.expand_k = expand_k
|
| 90 |
+
self.expand_v = expand_v
|
| 91 |
+
self.num_heads = num_heads
|
| 92 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 93 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 94 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 95 |
+
|
| 96 |
+
self.use_short_conv = use_short_conv
|
| 97 |
+
self.conv_size = conv_size
|
| 98 |
+
self.conv_bias = conv_bias
|
| 99 |
+
self.use_output_gate = use_output_gate
|
| 100 |
+
|
| 101 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 102 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 103 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 104 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 105 |
+
self.layer_idx = layer_idx
|
| 106 |
+
|
| 107 |
+
assert mode in ['chunk', 'fused_chunk', 'parallel', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 108 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 109 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 110 |
+
|
| 111 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 112 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 113 |
+
|
| 114 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 115 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 116 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 117 |
+
if self.use_output_gate:
|
| 118 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 119 |
+
|
| 120 |
+
if use_short_conv:
|
| 121 |
+
self.conv_size = conv_size
|
| 122 |
+
self.q_conv1d = ShortConvolution(
|
| 123 |
+
hidden_size=self.key_dim,
|
| 124 |
+
kernel_size=conv_size,
|
| 125 |
+
bias=conv_bias,
|
| 126 |
+
activation='silu',
|
| 127 |
+
)
|
| 128 |
+
self.k_conv1d = ShortConvolution(
|
| 129 |
+
hidden_size=self.key_dim_per_group,
|
| 130 |
+
kernel_size=conv_size,
|
| 131 |
+
bias=conv_bias,
|
| 132 |
+
activation='silu',
|
| 133 |
+
)
|
| 134 |
+
self.v_conv1d = ShortConvolution(
|
| 135 |
+
hidden_size=self.value_dim_per_group,
|
| 136 |
+
kernel_size=conv_size,
|
| 137 |
+
bias=conv_bias,
|
| 138 |
+
activation='silu',
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 142 |
+
|
| 143 |
+
if gate_fn == 'swish' and fuse_norm and use_output_gate:
|
| 144 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 145 |
+
hidden_size=self.head_v_dim,
|
| 146 |
+
elementwise_affine=elementwise_affine,
|
| 147 |
+
eps=norm_eps,
|
| 148 |
+
)
|
| 149 |
+
self.fuse_norm_and_gate = True
|
| 150 |
+
else:
|
| 151 |
+
self.fuse_norm_and_gate = False
|
| 152 |
+
self.g_norm = RMSNorm(
|
| 153 |
+
hidden_size=self.head_v_dim,
|
| 154 |
+
elementwise_affine=elementwise_affine,
|
| 155 |
+
eps=norm_eps,
|
| 156 |
+
dtype=torch.float32
|
| 157 |
+
)
|
| 158 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 159 |
+
|
| 160 |
+
# TODO: fix this issue
|
| 161 |
+
# https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/ops/triton/rotary.py#L180
|
| 162 |
+
# Ideally, we would want to support arbitrary d_head_qk
|
| 163 |
+
assert self.head_k_dim <= 256, "head_k_dim must be less than or equal to 256"
|
| 164 |
+
self.rotary = RotaryEmbedding(dim=self.head_k_dim)
|
| 165 |
+
|
| 166 |
+
def forward(
|
| 167 |
+
self,
|
| 168 |
+
hidden_states: torch.Tensor,
|
| 169 |
+
attention_mask: torch.Tensor | None = None,
|
| 170 |
+
past_key_values: Cache | None = None,
|
| 171 |
+
use_cache: bool | None = False,
|
| 172 |
+
output_attentions: bool | None = False,
|
| 173 |
+
**kwargs: Unpack[dict],
|
| 174 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 175 |
+
if attention_mask is not None:
|
| 176 |
+
assert len(attention_mask.shape) == 2, (
|
| 177 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 178 |
+
"for padding purposes (0 indicating padding). "
|
| 179 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 183 |
+
mode = 'fused_recurrent' if q_len <= 64 else self.mode
|
| 184 |
+
|
| 185 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 186 |
+
|
| 187 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 190 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 191 |
+
|
| 192 |
+
if self.use_short_conv:
|
| 193 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 194 |
+
if last_state is not None:
|
| 195 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 196 |
+
q, conv_state_q = self.q_conv1d(
|
| 197 |
+
x=self.q_proj(hidden_states),
|
| 198 |
+
cache=conv_state_q,
|
| 199 |
+
output_final_state=use_cache,
|
| 200 |
+
cu_seqlens=cu_seqlens,
|
| 201 |
+
)
|
| 202 |
+
k, conv_state_k = self.k_conv1d(
|
| 203 |
+
x=self.k_proj(hidden_states),
|
| 204 |
+
cache=conv_state_k,
|
| 205 |
+
output_final_state=use_cache,
|
| 206 |
+
cu_seqlens=cu_seqlens,
|
| 207 |
+
)
|
| 208 |
+
v, conv_state_v = self.v_conv1d(
|
| 209 |
+
x=self.v_proj(hidden_states),
|
| 210 |
+
cache=conv_state_v,
|
| 211 |
+
output_final_state=use_cache,
|
| 212 |
+
cu_seqlens=cu_seqlens,
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
q = self.q_proj(hidden_states)
|
| 216 |
+
k = self.k_proj(hidden_states)
|
| 217 |
+
v = self.v_proj(hidden_states)
|
| 218 |
+
|
| 219 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 220 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_k_dim)
|
| 221 |
+
if self.feature_map_fn is not None:
|
| 222 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 223 |
+
|
| 224 |
+
seqlen_offset, max_seqlen = 0, q.shape[1]
|
| 225 |
+
if past_key_values is not None:
|
| 226 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 227 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 228 |
+
|
| 229 |
+
if attention_mask is not None and seqlen_offset > 0:
|
| 230 |
+
# to deliminate the offsets of padding tokens
|
| 231 |
+
seqlen_offset = prepare_lens_from_mask(attention_mask) - q_len
|
| 232 |
+
max_seqlen = q.shape[1] + seqlen_offset.max().item()
|
| 233 |
+
|
| 234 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 235 |
+
|
| 236 |
+
if self.num_kv_groups > 1:
|
| 237 |
+
k = repeat(k, '... h d -> ... (h g) d', g=self.num_kv_groups)
|
| 238 |
+
v = repeat(v, '... (h d) -> ... (h g) d', d=self.head_v_dim, g=self.num_kv_groups)
|
| 239 |
+
else:
|
| 240 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 241 |
+
|
| 242 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 243 |
+
if mode == 'chunk':
|
| 244 |
+
o, recurrent_state = chunk_retention(
|
| 245 |
+
q=q,
|
| 246 |
+
k=k,
|
| 247 |
+
v=v,
|
| 248 |
+
initial_state=recurrent_state,
|
| 249 |
+
output_final_state=use_cache,
|
| 250 |
+
cu_seqlens=cu_seqlens,
|
| 251 |
+
)
|
| 252 |
+
elif mode == 'fused_chunk':
|
| 253 |
+
o, recurrent_state = fused_chunk_retention(
|
| 254 |
+
q=q,
|
| 255 |
+
k=k,
|
| 256 |
+
v=v,
|
| 257 |
+
initial_state=recurrent_state,
|
| 258 |
+
output_final_state=use_cache,
|
| 259 |
+
cu_seqlens=cu_seqlens,
|
| 260 |
+
)
|
| 261 |
+
elif mode == 'parallel':
|
| 262 |
+
o, recurrent_state = parallel_retention(
|
| 263 |
+
q=q,
|
| 264 |
+
k=k,
|
| 265 |
+
v=v,
|
| 266 |
+
cu_seqlens=cu_seqlens,
|
| 267 |
+
)
|
| 268 |
+
elif mode == 'fused_recurrent':
|
| 269 |
+
o, recurrent_state = fused_recurrent_retention(
|
| 270 |
+
q=q,
|
| 271 |
+
k=k,
|
| 272 |
+
v=v,
|
| 273 |
+
initial_state=recurrent_state,
|
| 274 |
+
output_final_state=use_cache,
|
| 275 |
+
cu_seqlens=cu_seqlens,
|
| 276 |
+
)
|
| 277 |
+
else:
|
| 278 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 279 |
+
|
| 280 |
+
update_layer_cache(
|
| 281 |
+
self,
|
| 282 |
+
past_key_values,
|
| 283 |
+
recurrent_state=recurrent_state,
|
| 284 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 285 |
+
offset=q_len,
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
if self.use_output_gate:
|
| 289 |
+
g = self.g_proj(hidden_states)
|
| 290 |
+
if self.fuse_norm_and_gate:
|
| 291 |
+
g = rearrange(g, '... (h d) -> ... h d', d=self.head_v_dim)
|
| 292 |
+
o = self.g_norm_swish_gate(o, g)
|
| 293 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 294 |
+
else:
|
| 295 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 296 |
+
o = o * self.gate_fn(g)
|
| 297 |
+
else:
|
| 298 |
+
o = rearrange(self.g_norm(o), '... h d -> ... (h d)')
|
| 299 |
+
o = self.o_proj(o)
|
| 300 |
+
if attention_mask is not None:
|
| 301 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 302 |
+
|
| 303 |
+
return o, None, past_key_values
|
fla/layers/nsa.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
from transformers.utils import logging
|
| 11 |
+
|
| 12 |
+
from fla.modules import RotaryEmbedding
|
| 13 |
+
from fla.ops.nsa.parallel import parallel_nsa
|
| 14 |
+
from fla.ops.utils.index import prepare_lens_from_mask
|
| 15 |
+
|
| 16 |
+
if TYPE_CHECKING:
|
| 17 |
+
from fla.models.utils import Cache
|
| 18 |
+
|
| 19 |
+
logger = logging.get_logger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class NativeSparseAttention(nn.Module):
|
| 23 |
+
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
hidden_size: int = 2048,
|
| 27 |
+
num_heads: int = 64,
|
| 28 |
+
num_kv_heads: int | None = 4,
|
| 29 |
+
head_dim: int = 64,
|
| 30 |
+
qkv_bias: bool = False,
|
| 31 |
+
block_size: int | None = 64,
|
| 32 |
+
block_counts: torch.LongTensor | int | None = 16,
|
| 33 |
+
window_size: int | None = 512,
|
| 34 |
+
rope_theta: float | None = 10000.,
|
| 35 |
+
max_position_embeddings: int | None = None,
|
| 36 |
+
layer_idx: int = None,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
|
| 40 |
+
self.hidden_size = hidden_size
|
| 41 |
+
self.num_heads = num_heads
|
| 42 |
+
if num_kv_heads is None:
|
| 43 |
+
self.num_kv_heads = self.num_heads
|
| 44 |
+
else:
|
| 45 |
+
self.num_kv_heads = num_kv_heads
|
| 46 |
+
self.num_kv_groups = num_heads // self.num_kv_heads
|
| 47 |
+
self.head_dim = head_dim
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
self.qkv_bias = qkv_bias
|
| 50 |
+
|
| 51 |
+
self.block_size = block_size
|
| 52 |
+
self.block_counts = block_counts
|
| 53 |
+
self.window_size = window_size
|
| 54 |
+
self.rope_theta = rope_theta
|
| 55 |
+
self.max_position_embeddings = max_position_embeddings
|
| 56 |
+
self.layer_idx = layer_idx
|
| 57 |
+
|
| 58 |
+
self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=self.qkv_bias)
|
| 59 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 60 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=self.qkv_bias)
|
| 61 |
+
self.g_proj = nn.Linear(self.hidden_size, self.num_heads * 3, bias=False)
|
| 62 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 63 |
+
|
| 64 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 65 |
+
|
| 66 |
+
def forward(
|
| 67 |
+
self,
|
| 68 |
+
hidden_states: torch.Tensor,
|
| 69 |
+
attention_mask: torch.LongTensor | None = None,
|
| 70 |
+
past_key_values: Cache | None = None,
|
| 71 |
+
output_attentions: bool = False,
|
| 72 |
+
use_cache: bool = False,
|
| 73 |
+
**kwargs,
|
| 74 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 75 |
+
if attention_mask is not None:
|
| 76 |
+
assert len(attention_mask.shape) == 2, (
|
| 77 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 78 |
+
"for padding purposes (0 indicating padding). "
|
| 79 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
batch_size, seq_len, _ = hidden_states.size()
|
| 83 |
+
|
| 84 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 85 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 86 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 87 |
+
g = rearrange(self.g_proj(hidden_states), '... (h d) -> ... h d', d=3)
|
| 88 |
+
g_cmp, g_slc, g_swa = g.sigmoid().unbind(-1)
|
| 89 |
+
|
| 90 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 91 |
+
|
| 92 |
+
seqlen_offset, max_seqlen = 0, seq_len
|
| 93 |
+
if past_key_values is not None:
|
| 94 |
+
seqlen_offset = past_key_values.get_seq_length(self.layer_idx)
|
| 95 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 96 |
+
|
| 97 |
+
if attention_mask is not None:
|
| 98 |
+
# to deliminate the offsets of padding tokens
|
| 99 |
+
seqlen_offset = seqlen_offset + prepare_lens_from_mask(attention_mask) - attention_mask.shape[-1]
|
| 100 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 101 |
+
|
| 102 |
+
if self.max_position_embeddings is not None:
|
| 103 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 104 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 105 |
+
|
| 106 |
+
if past_key_values is not None:
|
| 107 |
+
cache_has_content = past_key_values.get_seq_length(self.layer_idx) > 0
|
| 108 |
+
k_cached, v_cached = past_key_values.update(
|
| 109 |
+
attn_state=(k.flatten(-2, -1), v.flatten(-2, -1)),
|
| 110 |
+
layer_idx=self.layer_idx,
|
| 111 |
+
offset=seq_len,
|
| 112 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 113 |
+
)['attn_state']
|
| 114 |
+
if cache_has_content:
|
| 115 |
+
k, v = k_cached, v_cached
|
| 116 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 117 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 118 |
+
|
| 119 |
+
o = parallel_nsa(
|
| 120 |
+
q=q,
|
| 121 |
+
k=k,
|
| 122 |
+
v=v,
|
| 123 |
+
g_cmp=g_cmp,
|
| 124 |
+
g_slc=g_slc,
|
| 125 |
+
g_swa=g_swa,
|
| 126 |
+
block_size=self.block_size,
|
| 127 |
+
block_counts=self.block_counts,
|
| 128 |
+
window_size=self.window_size,
|
| 129 |
+
cu_seqlens=cu_seqlens,
|
| 130 |
+
)
|
| 131 |
+
o = o.reshape(batch_size, seq_len, -1)
|
| 132 |
+
o = self.o_proj(o)
|
| 133 |
+
|
| 134 |
+
if not output_attentions:
|
| 135 |
+
attentions = None
|
| 136 |
+
|
| 137 |
+
return o, attentions, past_key_values
|
fla/layers/path_attn.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
|
| 13 |
+
from fla.layers.utils import pad_input, unpad_input
|
| 14 |
+
from fla.modules import RMSNorm, ShortConvolution
|
| 15 |
+
from fla.modules.l2norm import l2_norm
|
| 16 |
+
from fla.ops.attn.decoding import attn_decoding_one_step
|
| 17 |
+
from fla.ops.path_attn.parallel import parallel_path_attn
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from fla.models.utils import Cache
|
| 21 |
+
|
| 22 |
+
logger = logging.get_logger(__name__)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class PaTHAttention(nn.Module):
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
hidden_size: int = 2048,
|
| 29 |
+
num_heads: int = 32,
|
| 30 |
+
num_kv_heads: int | None = None,
|
| 31 |
+
use_forget_gate: bool = False,
|
| 32 |
+
use_qk_norm: bool = False,
|
| 33 |
+
layer_idx: int = None,
|
| 34 |
+
use_low_rank_w: bool = True,
|
| 35 |
+
use_w_shortconv: bool = True,
|
| 36 |
+
conv_size: int = 3,
|
| 37 |
+
conv_bias: bool = False,
|
| 38 |
+
):
|
| 39 |
+
super().__init__()
|
| 40 |
+
|
| 41 |
+
self.hidden_size = hidden_size
|
| 42 |
+
self.num_heads = num_heads
|
| 43 |
+
if num_kv_heads is None:
|
| 44 |
+
self.num_kv_heads = self.num_heads
|
| 45 |
+
else:
|
| 46 |
+
self.num_kv_heads = num_kv_heads
|
| 47 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 48 |
+
self.kv_dim = self.num_kv_heads * self.head_dim
|
| 49 |
+
|
| 50 |
+
self.layer_idx = layer_idx
|
| 51 |
+
|
| 52 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 53 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 54 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 55 |
+
|
| 56 |
+
# We use low-rank parameterization for the w_proj to reduce parameters in MHA settings.
|
| 57 |
+
if use_low_rank_w:
|
| 58 |
+
self.w_proj = nn.Sequential(
|
| 59 |
+
nn.Linear(self.hidden_size, 32, bias=False),
|
| 60 |
+
nn.Linear(32, self.kv_dim, bias=False),
|
| 61 |
+
)
|
| 62 |
+
# In MQA/GQA settings, key/value heads are shared, so we use a standard linear projection
|
| 63 |
+
# which doesn't introduce too many parameters
|
| 64 |
+
else:
|
| 65 |
+
self.w_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 66 |
+
|
| 67 |
+
# per head norm
|
| 68 |
+
if use_qk_norm:
|
| 69 |
+
self.maybe_q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 70 |
+
self.maybe_k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 71 |
+
else:
|
| 72 |
+
self.maybe_q_norm = nn.Identity()
|
| 73 |
+
self.maybe_k_norm = nn.Identity()
|
| 74 |
+
|
| 75 |
+
if use_w_shortconv:
|
| 76 |
+
self.w_conv1d = ShortConvolution(hidden_size=self.kv_dim, kernel_size=conv_size, bias=conv_bias, activation='silu')
|
| 77 |
+
self.use_w_shortconv = use_w_shortconv
|
| 78 |
+
self.bt_proj = nn.Linear(self.hidden_size, self.num_kv_heads, bias=True)
|
| 79 |
+
self.use_forget_gate = use_forget_gate
|
| 80 |
+
if use_forget_gate:
|
| 81 |
+
self.g_proj = nn.Linear(self.hidden_size, self.num_heads, bias=True)
|
| 82 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 83 |
+
|
| 84 |
+
def forward(
|
| 85 |
+
self,
|
| 86 |
+
hidden_states: torch.Tensor,
|
| 87 |
+
attention_mask: torch.LongTensor | None = None,
|
| 88 |
+
past_key_values: Cache | None = None,
|
| 89 |
+
output_attentions: bool = False,
|
| 90 |
+
use_cache: bool = False,
|
| 91 |
+
**kwargs,
|
| 92 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 93 |
+
if use_cache:
|
| 94 |
+
assert past_key_values is not None, "past_key_values must be provided when use_cache is True"
|
| 95 |
+
if attention_mask is not None:
|
| 96 |
+
assert len(attention_mask.shape) == 2, (
|
| 97 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 98 |
+
"for padding purposes (0 indicating padding). "
|
| 99 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 100 |
+
)
|
| 101 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 102 |
+
q = self.q_proj(hidden_states)
|
| 103 |
+
k = self.k_proj(hidden_states)
|
| 104 |
+
v = self.v_proj(hidden_states)
|
| 105 |
+
w = self.w_proj(hidden_states)
|
| 106 |
+
beta = self.bt_proj(hidden_states).float().sigmoid() * 2 # allowing negative eigenvalues
|
| 107 |
+
g = F.logsigmoid(self.g_proj(hidden_states).float()) if self.use_forget_gate else None
|
| 108 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 109 |
+
assert not (cu_seqlens is not None and attention_mask is not None), (
|
| 110 |
+
"cu_seqlens should not be provided when attention_mask is not None"
|
| 111 |
+
)
|
| 112 |
+
# Training
|
| 113 |
+
if attention_mask is None:
|
| 114 |
+
assert use_cache is False, "use_cache should be False in training"
|
| 115 |
+
if self.use_w_shortconv:
|
| 116 |
+
w, _ = self.w_conv1d(w, cache=None, output_final_state=False, cu_seqlens=cu_seqlens)
|
| 117 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 118 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 119 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 120 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 121 |
+
q, k = self.maybe_q_norm(q), self.maybe_k_norm(k)
|
| 122 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 123 |
+
o, _ = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g, cu_seqlens=cu_seqlens)
|
| 124 |
+
|
| 125 |
+
# Prefilling or decoding
|
| 126 |
+
else:
|
| 127 |
+
assert self.training is False, "attention mask is not supported in training. Please use variable length input."
|
| 128 |
+
try:
|
| 129 |
+
last_state = past_key_values[self.layer_idx]
|
| 130 |
+
except KeyError:
|
| 131 |
+
last_state = None
|
| 132 |
+
# Decoding
|
| 133 |
+
if last_state is not None:
|
| 134 |
+
if g is not None:
|
| 135 |
+
past_k, past_v, past_g = last_state['attn_state']
|
| 136 |
+
else:
|
| 137 |
+
past_k, past_v = last_state['attn_state']
|
| 138 |
+
past_g = None
|
| 139 |
+
w_conv_state = last_state['conv_state']
|
| 140 |
+
past_k = rearrange(past_k, '... (h d) -> ... h d', d=self.head_dim)
|
| 141 |
+
if self.use_w_shortconv:
|
| 142 |
+
w, w_conv_state = self.w_conv1d(w, cache=w_conv_state, output_final_state=use_cache, cu_seqlens=cu_seqlens)
|
| 143 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 144 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 145 |
+
|
| 146 |
+
@torch.compile
|
| 147 |
+
def rank_one_update(k, w, beta):
|
| 148 |
+
original_dtype = k.dtype
|
| 149 |
+
k = k.float()
|
| 150 |
+
w = w.float()
|
| 151 |
+
beta = beta.float()
|
| 152 |
+
k = k - beta[..., None].float() * (k * w).sum(-1, keepdim=True) * w
|
| 153 |
+
return k.to(original_dtype)
|
| 154 |
+
|
| 155 |
+
past_k = rank_one_update(past_k, w, beta)
|
| 156 |
+
past_k = rearrange(past_k, '... h d -> ... (h d)')
|
| 157 |
+
k = torch.cat([past_k, k], dim=1)
|
| 158 |
+
v = torch.cat([past_v, v], dim=1)
|
| 159 |
+
g = torch.cat([past_g, g], dim=1) if g is not None else None
|
| 160 |
+
past_key_values[self.layer_idx]['attn_state'] = (k, v, g) if g is not None else (k, v)
|
| 161 |
+
past_key_values.update(
|
| 162 |
+
conv_state=w_conv_state,
|
| 163 |
+
layer_idx=self.layer_idx,
|
| 164 |
+
offset=q_len,
|
| 165 |
+
)
|
| 166 |
+
if g is not None:
|
| 167 |
+
q, (k, v, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 168 |
+
q, (k, v, g), attention_mask, q_len, keepdim=True)
|
| 169 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 170 |
+
else:
|
| 171 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 172 |
+
q, (k, v), attention_mask, q_len, keepdim=True)
|
| 173 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 174 |
+
_, cu_seqlens = cu_seqlens
|
| 175 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 176 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 177 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 178 |
+
assert max_seqlen_q == 1, "only support q_len == 1 for decoding"
|
| 179 |
+
o = attn_decoding_one_step(q, k, v, g, cu_seqlens=cu_seqlens, do_gate_scale=True) # reduced to fox's decoding
|
| 180 |
+
# Prefilling
|
| 181 |
+
else:
|
| 182 |
+
v_cache = v.clone()
|
| 183 |
+
g_cache = g.clone() if g is not None else None
|
| 184 |
+
if g is None:
|
| 185 |
+
q, (k, v, w, beta), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 186 |
+
q, (k, v, w, beta), attention_mask, q_len, keepdim=True)
|
| 187 |
+
else:
|
| 188 |
+
q, (k, v, w, beta, g), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 189 |
+
q, (k, v, w, beta, g), attention_mask, q_len, keepdim=True)
|
| 190 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 191 |
+
assert max_seqlen_q == max_seqlen_k, "max_seqlen_q should be equal to max_seqlen_k in prefilling"
|
| 192 |
+
_, cu_seqlens = cu_seqlens
|
| 193 |
+
if self.use_w_shortconv:
|
| 194 |
+
w, w_conv_state = self.w_conv1d(w, cache=None, output_final_state=use_cache, cu_seqlens=cu_seqlens)
|
| 195 |
+
else:
|
| 196 |
+
w_conv_state = None
|
| 197 |
+
q = rearrange(q, '... (h d) -> ... h d', d=self.head_dim)
|
| 198 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 199 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 200 |
+
w = rearrange(w, '... (h d) -> ... h d', d=self.head_dim)
|
| 201 |
+
w = l2_norm(w, output_dtype=torch.float32)
|
| 202 |
+
o, k_cache = parallel_path_attn(q=q, k=k, v=v, w=w, beta=beta, g=g,
|
| 203 |
+
cu_seqlens=cu_seqlens, use_cache=use_cache)
|
| 204 |
+
if use_cache:
|
| 205 |
+
k_cache = pad_input(k_cache.squeeze(0), indices_q, batch_size, q_len)
|
| 206 |
+
k_cache = rearrange(k_cache, '... h d -> ... (h d)')
|
| 207 |
+
past_key_values.update(
|
| 208 |
+
attn_state=(k_cache, v_cache, g_cache) if g_cache is not None else (k_cache, v_cache),
|
| 209 |
+
conv_state=w_conv_state,
|
| 210 |
+
layer_idx=self.layer_idx,
|
| 211 |
+
offset=q_len,
|
| 212 |
+
)
|
| 213 |
+
o = pad_input(o.squeeze(0), indices_q, batch_size, q_len)
|
| 214 |
+
o = rearrange(o, '... h d -> ... (h d)')
|
| 215 |
+
o = self.o_proj(o)
|
| 216 |
+
return o, None, past_key_values
|
fla/layers/quasar.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
# Modified for QuasarAttention
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import contextlib
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
from typing import TYPE_CHECKING
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from einops import rearrange, repeat
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
|
| 16 |
+
from fla.layers.utils import get_unpad_data, index_first_axis, pad_input
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _quasar_debug_tensor(name: str, tensor: torch.Tensor, layer_idx: int | None) -> None:
|
| 20 |
+
if os.environ.get("QUASAR_DEBUG_FINITE", "0") != "1":
|
| 21 |
+
return
|
| 22 |
+
if tensor is None or torch.isfinite(tensor).all():
|
| 23 |
+
return
|
| 24 |
+
with torch.no_grad():
|
| 25 |
+
t = torch.nan_to_num(tensor.detach().float(), nan=0.0, posinf=0.0, neginf=0.0)
|
| 26 |
+
nonfinite = int((~torch.isfinite(tensor)).sum().item())
|
| 27 |
+
print(
|
| 28 |
+
f"[QUASAR DEBUG] layer={layer_idx} stage={name} nonfinite={nonfinite} "
|
| 29 |
+
f"min={float(t.min())} max={float(t.max())} mean={float(t.mean())}",
|
| 30 |
+
flush=True,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class _TorchRMSNormGated(nn.Module):
|
| 35 |
+
def __init__(self, hidden_size: int, activation: str = "sigmoid", eps: float = 1e-5):
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 38 |
+
self.activation = activation
|
| 39 |
+
self.eps = eps
|
| 40 |
+
|
| 41 |
+
def reset_parameters(self) -> None:
|
| 42 |
+
self.weight.data.fill_(1.0)
|
| 43 |
+
|
| 44 |
+
def forward(self, x: torch.Tensor, gate: torch.Tensor) -> torch.Tensor:
|
| 45 |
+
dtype = x.dtype
|
| 46 |
+
y = torch.nan_to_num(
|
| 47 |
+
x.float(),
|
| 48 |
+
nan=0.0,
|
| 49 |
+
posinf=1e4,
|
| 50 |
+
neginf=-1e4,
|
| 51 |
+
).clamp_(min=-1e4, max=1e4)
|
| 52 |
+
y = y * torch.rsqrt(y.square().mean(dim=-1, keepdim=True) + self.eps)
|
| 53 |
+
weight = torch.nan_to_num(
|
| 54 |
+
self.weight.float(),
|
| 55 |
+
nan=1.0,
|
| 56 |
+
posinf=1.0,
|
| 57 |
+
neginf=1.0,
|
| 58 |
+
).clamp_(min=0.0, max=4.0)
|
| 59 |
+
y = y.to(dtype) * weight.to(dtype=dtype, device=x.device)
|
| 60 |
+
gate = torch.nan_to_num(
|
| 61 |
+
gate.float(),
|
| 62 |
+
nan=0.0,
|
| 63 |
+
posinf=30.0,
|
| 64 |
+
neginf=-30.0,
|
| 65 |
+
).clamp_(min=-30.0, max=30.0)
|
| 66 |
+
if self.activation in {"swish", "silu"}:
|
| 67 |
+
gate = gate * torch.sigmoid(gate)
|
| 68 |
+
elif self.activation == "sigmoid":
|
| 69 |
+
gate = torch.sigmoid(gate)
|
| 70 |
+
return y * gate.to(dtype=dtype, device=x.device)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def rotate_half(x):
|
| 74 |
+
"""Rotates half the hidden dims of the input."""
|
| 75 |
+
x1 = x[..., : x.shape[-1] // 2]
|
| 76 |
+
x2 = x[..., x.shape[-1] // 2 :]
|
| 77 |
+
return torch.cat((-x2, x1), dim=-1)
|
| 78 |
+
|
| 79 |
+
def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None):
|
| 80 |
+
"""Applies Rotary Position Embedding to the query and key tensors."""
|
| 81 |
+
# cos, sin: [1, 1, seq_len, rotary_dim]
|
| 82 |
+
# q, k: [batch_size, seq_len, n_heads, head_dim]
|
| 83 |
+
rotary_dim = cos.shape[-1]
|
| 84 |
+
q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
|
| 85 |
+
k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
|
| 86 |
+
cos = cos.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
|
| 87 |
+
sin = sin.transpose(1, 2) # [1, seq_len, 1, rotary_dim]
|
| 88 |
+
q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
|
| 89 |
+
k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
|
| 90 |
+
return torch.cat([q_embed, q_pass], dim=-1), torch.cat([k_embed, k_pass], dim=-1)
|
| 91 |
+
|
| 92 |
+
if TYPE_CHECKING:
|
| 93 |
+
from transformers.processing_utils import Unpack
|
| 94 |
+
|
| 95 |
+
from fla.models.utils import Cache
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class QuasarAttention(nn.Module):
|
| 99 |
+
"""
|
| 100 |
+
QuasarAttention layer implementation.
|
| 101 |
+
|
| 102 |
+
Args:
|
| 103 |
+
hidden_size (int, Optional):
|
| 104 |
+
The hidden size of the input. Default: 2048.
|
| 105 |
+
head_dim (int, Optional):
|
| 106 |
+
The dimension of each head. Default: 128.
|
| 107 |
+
num_heads (int, Optional):
|
| 108 |
+
The number of heads. Default: 16.
|
| 109 |
+
mode (str, Optional):
|
| 110 |
+
Which QuasarAttention kernel to use.
|
| 111 |
+
Currently available: `chunk` and `fused_recurrent`.
|
| 112 |
+
Default: `chunk`.
|
| 113 |
+
use_short_conv (bool, Optional):
|
| 114 |
+
Whether to use short convolutions. Default: `True`.
|
| 115 |
+
conv_size (int, Optional):
|
| 116 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 117 |
+
conv_bias (bool, Optional):
|
| 118 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 119 |
+
layer_idx (int, Optional):
|
| 120 |
+
The index of the layer. Default: None.
|
| 121 |
+
norm_eps (float, Optional):
|
| 122 |
+
The epsilon value for the normalization layer. Default: 1e-5.
|
| 123 |
+
"""
|
| 124 |
+
|
| 125 |
+
def __init__(
|
| 126 |
+
self,
|
| 127 |
+
hidden_size: int = 2048,
|
| 128 |
+
head_dim: int = 128,
|
| 129 |
+
num_heads: int = 16,
|
| 130 |
+
mode: str = "chunk",
|
| 131 |
+
use_short_conv: bool = True,
|
| 132 |
+
conv_size: int = 4,
|
| 133 |
+
conv_bias: bool = False,
|
| 134 |
+
layer_idx: int = None,
|
| 135 |
+
norm_eps: float = 1e-5,
|
| 136 |
+
**kwargs,
|
| 137 |
+
) -> QuasarAttention:
|
| 138 |
+
super().__init__()
|
| 139 |
+
|
| 140 |
+
self.mode = mode
|
| 141 |
+
self.hidden_size = hidden_size
|
| 142 |
+
|
| 143 |
+
self.use_short_conv = use_short_conv
|
| 144 |
+
self.conv_size = conv_size
|
| 145 |
+
self.conv_bias = conv_bias
|
| 146 |
+
|
| 147 |
+
self.head_dim = head_dim
|
| 148 |
+
self.num_heads = num_heads
|
| 149 |
+
self.key_dim = int(self.num_heads * self.head_dim)
|
| 150 |
+
self.value_dim = int(self.num_heads * self.head_dim)
|
| 151 |
+
self.layer_idx = layer_idx
|
| 152 |
+
|
| 153 |
+
assert mode in ["chunk", "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 154 |
+
|
| 155 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 156 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 157 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 158 |
+
|
| 159 |
+
# KDA matching: Use SiLU on q, k, v for better learning if not using short conv
|
| 160 |
+
# (Short conv already has its own activation)
|
| 161 |
+
self.q_act = nn.SiLU()
|
| 162 |
+
self.k_act = nn.SiLU()
|
| 163 |
+
self.v_act = nn.SiLU()
|
| 164 |
+
|
| 165 |
+
if use_short_conv:
|
| 166 |
+
from fla.modules.convolution import ShortConvolution
|
| 167 |
+
|
| 168 |
+
self.q_conv1d = ShortConvolution(
|
| 169 |
+
hidden_size=self.key_dim,
|
| 170 |
+
kernel_size=conv_size,
|
| 171 |
+
bias=conv_bias,
|
| 172 |
+
activation="silu",
|
| 173 |
+
)
|
| 174 |
+
self.k_conv1d = ShortConvolution(
|
| 175 |
+
hidden_size=self.key_dim,
|
| 176 |
+
kernel_size=conv_size,
|
| 177 |
+
bias=conv_bias,
|
| 178 |
+
activation="silu",
|
| 179 |
+
)
|
| 180 |
+
self.v_conv1d = ShortConvolution(
|
| 181 |
+
hidden_size=self.value_dim,
|
| 182 |
+
kernel_size=conv_size,
|
| 183 |
+
bias=conv_bias,
|
| 184 |
+
activation="silu",
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
# Data-dependent Beta (Adaptive Decay)
|
| 188 |
+
# Instead of a static per-head parameter, we use a linear projection
|
| 189 |
+
# to allow the model to learn contextual importance (read/write sharpness).
|
| 190 |
+
self.b_proj = nn.Linear(hidden_size, self.num_heads, bias=False)
|
| 191 |
+
|
| 192 |
+
# Learnable state decay (like KDA/Mamba A matrix)
|
| 193 |
+
self.A_log = nn.Parameter(torch.log(torch.empty(self.num_heads, dtype=torch.float32).uniform_(1, 16)))
|
| 194 |
+
self.A_log._no_weight_decay = True
|
| 195 |
+
self.dt_bias = nn.Parameter(torch.zeros(self.key_dim, dtype=torch.float32))
|
| 196 |
+
self.dt_bias._no_weight_decay = True
|
| 197 |
+
|
| 198 |
+
# KIMI matches: separate f_proj for kernel and g_proj for final output gating
|
| 199 |
+
self.f_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 200 |
+
self.g_proj = nn.Sequential(
|
| 201 |
+
nn.Linear(hidden_size, self.head_dim, bias=False),
|
| 202 |
+
nn.Linear(self.head_dim, self.value_dim, bias=True),
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
self.o_norm = _TorchRMSNormGated(self.head_dim, activation="sigmoid", eps=norm_eps)
|
| 206 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 207 |
+
|
| 208 |
+
def reset_parameters(self) -> None:
|
| 209 |
+
for module in self.children():
|
| 210 |
+
reset = getattr(module, "reset_parameters", None)
|
| 211 |
+
if callable(reset):
|
| 212 |
+
reset()
|
| 213 |
+
|
| 214 |
+
def forward(
|
| 215 |
+
self,
|
| 216 |
+
hidden_states: torch.Tensor,
|
| 217 |
+
attention_mask: torch.Tensor | None = None,
|
| 218 |
+
past_key_values: Cache | None = None,
|
| 219 |
+
use_cache: bool | None = False,
|
| 220 |
+
output_attentions: bool | None = False,
|
| 221 |
+
**kwargs: Unpack[dict],
|
| 222 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 223 |
+
if attention_mask is not None:
|
| 224 |
+
assert len(attention_mask.shape) == 2, (
|
| 225 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 226 |
+
"for padding purposes (0 indicating padding). "
|
| 227 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 231 |
+
mode = self.mode
|
| 232 |
+
if self.training and mode == "fused_recurrent":
|
| 233 |
+
# The fused recurrent Quasar path is forward-only in this tree.
|
| 234 |
+
# Training must use the chunk kernel until its backward exists.
|
| 235 |
+
mode = "chunk"
|
| 236 |
+
|
| 237 |
+
# Bailing hidden states can be very large after MoE/FSDP checkpoint
|
| 238 |
+
# restore. Quasar's delta-rule triangular solve is much more sensitive
|
| 239 |
+
# to projection scale than GQA/GLA, so sanitize and RMS-normalize only
|
| 240 |
+
# the Quasar branch input. The residual model path remains untouched.
|
| 241 |
+
input_dtype = hidden_states.dtype
|
| 242 |
+
hidden_states = torch.nan_to_num(
|
| 243 |
+
hidden_states.float(),
|
| 244 |
+
nan=0.0,
|
| 245 |
+
posinf=60.0,
|
| 246 |
+
neginf=-60.0,
|
| 247 |
+
).clamp_(min=-60.0, max=60.0)
|
| 248 |
+
hidden_states = hidden_states * torch.rsqrt(
|
| 249 |
+
hidden_states.square().mean(dim=-1, keepdim=True) + 1e-6
|
| 250 |
+
)
|
| 251 |
+
hidden_states = hidden_states.to(dtype=input_dtype)
|
| 252 |
+
_quasar_debug_tensor("input_normed", hidden_states, self.layer_idx)
|
| 253 |
+
|
| 254 |
+
last_state = None
|
| 255 |
+
recurrent_state = None
|
| 256 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 257 |
+
|
| 258 |
+
if past_key_values is not None and self.layer_idx is not None:
|
| 259 |
+
if hasattr(past_key_values, "recurrent_states") and self.layer_idx in past_key_values.recurrent_states:
|
| 260 |
+
recurrent_state = past_key_values.recurrent_states[self.layer_idx]
|
| 261 |
+
if hasattr(past_key_values, "conv_states") and self.layer_idx in past_key_values.conv_states:
|
| 262 |
+
conv_state_q, conv_state_k, conv_state_v = past_key_values.conv_states[self.layer_idx]
|
| 263 |
+
else:
|
| 264 |
+
try:
|
| 265 |
+
# Standard list/tuple cache (FLA style fallback)
|
| 266 |
+
if len(past_key_values) > self.layer_idx:
|
| 267 |
+
last_state = past_key_values[self.layer_idx]
|
| 268 |
+
if isinstance(last_state, dict):
|
| 269 |
+
recurrent_state = last_state.get("recurrent_state", None)
|
| 270 |
+
convs = last_state.get("conv_state", None)
|
| 271 |
+
if convs is not None:
|
| 272 |
+
conv_state_q, conv_state_k, conv_state_v = convs
|
| 273 |
+
except TypeError:
|
| 274 |
+
pass
|
| 275 |
+
|
| 276 |
+
cu_seqlens = kwargs.get("cu_seqlens")
|
| 277 |
+
if attention_mask is not None:
|
| 278 |
+
# Optimization: Skip unpadding if all tokens are valid (common in packed distillation)
|
| 279 |
+
if attention_mask.all():
|
| 280 |
+
indices, cu_seqlens = None, None
|
| 281 |
+
else:
|
| 282 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 283 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 284 |
+
else:
|
| 285 |
+
indices = None
|
| 286 |
+
|
| 287 |
+
if self.use_short_conv:
|
| 288 |
+
q, conv_state_q = self.q_conv1d(
|
| 289 |
+
x=self.q_proj(hidden_states),
|
| 290 |
+
cache=conv_state_q,
|
| 291 |
+
output_final_state=use_cache,
|
| 292 |
+
cu_seqlens=cu_seqlens,
|
| 293 |
+
)
|
| 294 |
+
k, conv_state_k = self.k_conv1d(
|
| 295 |
+
x=self.k_proj(hidden_states),
|
| 296 |
+
cache=conv_state_k,
|
| 297 |
+
output_final_state=use_cache,
|
| 298 |
+
cu_seqlens=cu_seqlens,
|
| 299 |
+
)
|
| 300 |
+
v, conv_state_v = self.v_conv1d(
|
| 301 |
+
x=self.v_proj(hidden_states),
|
| 302 |
+
cache=conv_state_v,
|
| 303 |
+
output_final_state=use_cache,
|
| 304 |
+
cu_seqlens=cu_seqlens,
|
| 305 |
+
)
|
| 306 |
+
else:
|
| 307 |
+
q = self.q_act(self.q_proj(hidden_states))
|
| 308 |
+
k = self.k_act(self.k_proj(hidden_states))
|
| 309 |
+
v = self.v_act(self.v_proj(hidden_states))
|
| 310 |
+
_quasar_debug_tensor("q_proj", q, self.layer_idx)
|
| 311 |
+
_quasar_debug_tensor("k_proj", k, self.layer_idx)
|
| 312 |
+
_quasar_debug_tensor("v_proj", v, self.layer_idx)
|
| 313 |
+
|
| 314 |
+
q = rearrange(q, "... (h d) -> ... h d", d=self.head_dim)
|
| 315 |
+
k = rearrange(k, "... (h d) -> ... h d", d=self.head_dim)
|
| 316 |
+
v = rearrange(v, "... (h d) -> ... h d", d=self.head_dim)
|
| 317 |
+
|
| 318 |
+
# Apply RoPE if provided
|
| 319 |
+
cos = kwargs.get("cos")
|
| 320 |
+
sin = kwargs.get("sin")
|
| 321 |
+
if cos is not None and sin is not None:
|
| 322 |
+
if attention_mask is not None:
|
| 323 |
+
# Unpad cos/sin using the same indices
|
| 324 |
+
# cos/sin shape is [1, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
|
| 325 |
+
if cos.shape[0] == 1 and cos.shape[1] == 1:
|
| 326 |
+
# Broadcastable/Shared RoPE [1, 1, seq_len, head_dim]
|
| 327 |
+
# We need to expand to [batch_size, seq_len, head_dim] before unpadding
|
| 328 |
+
cos_expanded = cos.squeeze(1).expand(batch_size, -1, -1)
|
| 329 |
+
sin_expanded = sin.squeeze(1).expand(batch_size, -1, -1)
|
| 330 |
+
cos = index_first_axis(rearrange(cos_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 331 |
+
sin = index_first_axis(rearrange(sin_expanded, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 332 |
+
else:
|
| 333 |
+
# Already [batch_size, 1, seq_len, head_dim] or [batch_size, seq_len, head_dim]
|
| 334 |
+
if cos.dim() == 4:
|
| 335 |
+
cos = cos.squeeze(1)
|
| 336 |
+
sin = sin.squeeze(1)
|
| 337 |
+
cos = index_first_axis(rearrange(cos, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 338 |
+
sin = index_first_axis(rearrange(sin, "b s d -> (b s) d"), indices).unsqueeze(0).unsqueeze(1)
|
| 339 |
+
|
| 340 |
+
q, k = apply_rotary_pos_emb(q, k, cos, sin)
|
| 341 |
+
|
| 342 |
+
# QK Normalization AFTER RoPE — ensures kernel receives unit-norm vectors
|
| 343 |
+
# regardless of any precision drift introduced by the rotation
|
| 344 |
+
q = F.normalize(q, p=2, dim=-1)
|
| 345 |
+
k = F.normalize(k, p=2, dim=-1)
|
| 346 |
+
_quasar_debug_tensor("q_norm", q, self.layer_idx)
|
| 347 |
+
_quasar_debug_tensor("k_norm", k, self.layer_idx)
|
| 348 |
+
|
| 349 |
+
# Adaptive Beta: Sigmoid(b_proj(x)) is bounded to (0, 1) to prevent explosions.
|
| 350 |
+
beta = self.b_proj(hidden_states).sigmoid()
|
| 351 |
+
_quasar_debug_tensor("beta", beta, self.layer_idx)
|
| 352 |
+
|
| 353 |
+
if mode == "chunk":
|
| 354 |
+
from fla.ops.quasar.chunk import chunk_quasar
|
| 355 |
+
|
| 356 |
+
o, recurrent_state = chunk_quasar(
|
| 357 |
+
q=q,
|
| 358 |
+
k=k,
|
| 359 |
+
v=v,
|
| 360 |
+
beta=beta,
|
| 361 |
+
A_log=self.A_log,
|
| 362 |
+
dt_bias=self.dt_bias,
|
| 363 |
+
initial_state=recurrent_state,
|
| 364 |
+
output_final_state=use_cache,
|
| 365 |
+
cu_seqlens=cu_seqlens,
|
| 366 |
+
use_qk_l2norm_in_kernel=True,
|
| 367 |
+
)
|
| 368 |
+
_quasar_debug_tensor("chunk_kernel_o", o, self.layer_idx)
|
| 369 |
+
elif mode == "fused_recurrent":
|
| 370 |
+
from fla.ops.quasar.fused_recurrent import fused_recurrent_quasar
|
| 371 |
+
|
| 372 |
+
# Use f_proj for kernel gate in fused mode
|
| 373 |
+
f_gate = self.f_proj(hidden_states)
|
| 374 |
+
f_gate = rearrange(f_gate, "... (h d) -> ... h d", d=self.head_dim)
|
| 375 |
+
o, recurrent_state = fused_recurrent_quasar(
|
| 376 |
+
q=q,
|
| 377 |
+
k=k,
|
| 378 |
+
v=v,
|
| 379 |
+
g=f_gate,
|
| 380 |
+
beta=beta,
|
| 381 |
+
A_log=self.A_log,
|
| 382 |
+
dt_bias=self.dt_bias,
|
| 383 |
+
initial_state=recurrent_state,
|
| 384 |
+
output_final_state=use_cache,
|
| 385 |
+
use_qk_l2norm_in_kernel=True,
|
| 386 |
+
)
|
| 387 |
+
_quasar_debug_tensor("fused_kernel_o", o, self.layer_idx)
|
| 388 |
+
else:
|
| 389 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 390 |
+
|
| 391 |
+
o = torch.nan_to_num(
|
| 392 |
+
o.float(),
|
| 393 |
+
nan=0.0,
|
| 394 |
+
posinf=1e4,
|
| 395 |
+
neginf=-1e4,
|
| 396 |
+
).clamp_(min=-1e4, max=1e4).to(dtype=v.dtype)
|
| 397 |
+
_quasar_debug_tensor("kernel_o_clamped", o, self.layer_idx)
|
| 398 |
+
|
| 399 |
+
if past_key_values is not None:
|
| 400 |
+
if hasattr(past_key_values, "update_quasar_state"):
|
| 401 |
+
past_key_values.update_quasar_state(
|
| 402 |
+
self.layer_idx,
|
| 403 |
+
recurrent_state,
|
| 404 |
+
(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None
|
| 405 |
+
)
|
| 406 |
+
else:
|
| 407 |
+
with contextlib.suppress(TypeError):
|
| 408 |
+
past_key_values.update(
|
| 409 |
+
recurrent_state=recurrent_state,
|
| 410 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 411 |
+
layer_idx=self.layer_idx,
|
| 412 |
+
offset=q_len,
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
# Final output gating using g_proj
|
| 416 |
+
# Handle flattened inputs (unpadded) from FSDP/Flash-Linear-Attention
|
| 417 |
+
if hidden_states.dim() == 2:
|
| 418 |
+
# (N, D) -> (N, H, D/H)
|
| 419 |
+
g = self.g_proj(hidden_states)
|
| 420 |
+
g = rearrange(g, "n (h d) -> n h d", d=self.head_dim)
|
| 421 |
+
_quasar_debug_tensor("output_gate", g, self.layer_idx)
|
| 422 |
+
o = self.o_norm(o, g)
|
| 423 |
+
o = rearrange(o, "n h d -> n (h d)")
|
| 424 |
+
else:
|
| 425 |
+
# (B, S, D) -> (B, S, H, D/H)
|
| 426 |
+
g = self.g_proj(hidden_states)
|
| 427 |
+
g = rearrange(g, "b s (h d) -> b s h d", d=self.head_dim)
|
| 428 |
+
_quasar_debug_tensor("output_gate", g, self.layer_idx)
|
| 429 |
+
o = self.o_norm(o, g)
|
| 430 |
+
o = rearrange(o, "b s h d -> b s (h d)")
|
| 431 |
+
_quasar_debug_tensor("post_norm_gate", o, self.layer_idx)
|
| 432 |
+
|
| 433 |
+
o = self.o_proj(o)
|
| 434 |
+
_quasar_debug_tensor("o_proj", o, self.layer_idx)
|
| 435 |
+
if attention_mask is not None:
|
| 436 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 437 |
+
|
| 438 |
+
# LFM2 expects 2 return values (hidden_states, _)
|
| 439 |
+
return o, None
|
fla/layers/rebased.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
https://github.com/corl-team/rebased/blob/main/flash_linear_attention/fla/layers/rebased_fast.py
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
from einops import rearrange
|
| 12 |
+
|
| 13 |
+
from fla.modules.feature_map import RebasedFeatureMap
|
| 14 |
+
from fla.ops.linear_attn import chunk_linear_attn, fused_chunk_linear_attn
|
| 15 |
+
from fla.ops.rebased import parallel_rebased
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ReBasedLinearAttention(nn.Module):
|
| 19 |
+
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
hidden_size: int,
|
| 23 |
+
l_max: int = 2048,
|
| 24 |
+
feature_dim: int = 16,
|
| 25 |
+
num_key_value_heads: int = 16,
|
| 26 |
+
num_heads: int = 16,
|
| 27 |
+
use_gamma: bool | None = True,
|
| 28 |
+
use_beta: bool | None = True,
|
| 29 |
+
normalize: bool | None = True,
|
| 30 |
+
causal: bool = True,
|
| 31 |
+
eps: float = 1e-5,
|
| 32 |
+
mode: str = "parallel",
|
| 33 |
+
layer_idx: int | None = None,
|
| 34 |
+
**kwargs,
|
| 35 |
+
) -> ReBasedLinearAttention:
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.hidden_size = hidden_size
|
| 38 |
+
self.l_max = l_max
|
| 39 |
+
self.mode = mode
|
| 40 |
+
assert self.mode in ["fused_chunk", "parallel", 'chunk']
|
| 41 |
+
|
| 42 |
+
self.feature_dim = feature_dim
|
| 43 |
+
self.num_key_value_heads = num_key_value_heads
|
| 44 |
+
self.num_heads = num_heads
|
| 45 |
+
self.head_dim = self.hidden_size // self.num_key_value_heads
|
| 46 |
+
self.use_gamma = use_gamma
|
| 47 |
+
self.use_beta = use_beta
|
| 48 |
+
self.normalize = normalize
|
| 49 |
+
self.causal = causal
|
| 50 |
+
self.eps = eps
|
| 51 |
+
self.mode = mode
|
| 52 |
+
self.layer_idx = layer_idx
|
| 53 |
+
|
| 54 |
+
self.feature_map = RebasedFeatureMap(self.feature_dim, use_gamma, use_beta, normalize)
|
| 55 |
+
self.q_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 56 |
+
self.k_proj = nn.Linear(self.hidden_size, self.feature_dim * self.num_heads, bias=False)
|
| 57 |
+
self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
|
| 58 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
|
| 59 |
+
self.dropout = nn.Identity()
|
| 60 |
+
|
| 61 |
+
def forward(self, hidden_states: torch.Tensor, **kwargs):
|
| 62 |
+
mode = self.mode
|
| 63 |
+
q = rearrange(
|
| 64 |
+
self.q_proj(hidden_states),
|
| 65 |
+
"... (h d) -> ... h d",
|
| 66 |
+
h=self.num_heads,
|
| 67 |
+
d=self.feature_dim,
|
| 68 |
+
)
|
| 69 |
+
k = rearrange(
|
| 70 |
+
self.k_proj(hidden_states),
|
| 71 |
+
"... (h d) -> ... h d",
|
| 72 |
+
h=self.num_heads,
|
| 73 |
+
d=self.feature_dim,
|
| 74 |
+
)
|
| 75 |
+
v = rearrange(
|
| 76 |
+
self.v_proj(hidden_states),
|
| 77 |
+
"... (h d) -> ... h d",
|
| 78 |
+
h=self.num_key_value_heads,
|
| 79 |
+
d=self.head_dim,
|
| 80 |
+
)
|
| 81 |
+
q, k = self.feature_map(q, flatten=(mode != 'parallel')), self.feature_map(k, flatten=(mode != 'parallel'))
|
| 82 |
+
if mode == "fused_chunk":
|
| 83 |
+
o = fused_chunk_linear_attn(
|
| 84 |
+
q=q,
|
| 85 |
+
k=k,
|
| 86 |
+
v=v,
|
| 87 |
+
normalize=True,
|
| 88 |
+
scale=1,
|
| 89 |
+
)
|
| 90 |
+
elif mode == 'chunk':
|
| 91 |
+
o = chunk_linear_attn(
|
| 92 |
+
q=q,
|
| 93 |
+
k=k,
|
| 94 |
+
v=v,
|
| 95 |
+
normalize=True,
|
| 96 |
+
scale=1,
|
| 97 |
+
)
|
| 98 |
+
elif mode == 'parallel':
|
| 99 |
+
assert q.shape[-1] <= 128
|
| 100 |
+
o = parallel_rebased(
|
| 101 |
+
q=q,
|
| 102 |
+
k=k,
|
| 103 |
+
v=v,
|
| 104 |
+
eps=self.eps,
|
| 105 |
+
use_scale=True,
|
| 106 |
+
use_normalize=True,
|
| 107 |
+
)
|
| 108 |
+
o = rearrange(o, "... h d -> ... (h d)")
|
| 109 |
+
o = self.o_proj(o)
|
| 110 |
+
o = self.dropout(o)
|
| 111 |
+
return o
|
| 112 |
+
|
| 113 |
+
# https://github.com/HazyResearch/zoology/blob/main/zoology/mixers/based.py#L119
|
| 114 |
+
def forward_reference(
|
| 115 |
+
self,
|
| 116 |
+
hidden_states: torch.Tensor,
|
| 117 |
+
filters: torch.Tensor = None,
|
| 118 |
+
*args,
|
| 119 |
+
**kwargs,
|
| 120 |
+
):
|
| 121 |
+
"""
|
| 122 |
+
x (torch.Tensor): tensor of shape (b, d, t)
|
| 123 |
+
y (torch.Tensor): tensor of shape (b, d, t)
|
| 124 |
+
"""
|
| 125 |
+
b, t, _ = hidden_states.size()
|
| 126 |
+
q, k, v = self.q_proj(hidden_states), self.k_proj(hidden_states), self.v_proj(hidden_states)
|
| 127 |
+
|
| 128 |
+
q = q.view(b, t, -1, self.feature_dim).transpose(1, 2)
|
| 129 |
+
k = k.view(b, t, -1, self.feature_dim).transpose(1, 2)
|
| 130 |
+
v = v.view(b, t, -1, self.head_dim).transpose(1, 2)
|
| 131 |
+
|
| 132 |
+
# Linear attention
|
| 133 |
+
q, k = self.feature_map(q), self.feature_map(k)
|
| 134 |
+
q, k, v = q.unsqueeze(-2), k.unsqueeze(-2), v.unsqueeze(-1)
|
| 135 |
+
|
| 136 |
+
# Compute attention
|
| 137 |
+
if self.causal:
|
| 138 |
+
y = ((q * (k * v).cumsum(2)).sum(-1) / ((q * k.cumsum(2)).sum(-1) + self.eps))
|
| 139 |
+
else:
|
| 140 |
+
y = ((q * (k * v).sum(2, True)).sum(-1) / ((q * k.sum(2, True)).sum(-1) + self.eps))
|
| 141 |
+
y = rearrange(y, 'b h t d -> b t (h d)')
|
| 142 |
+
y = self.o_proj(y.to(hidden_states.dtype))
|
| 143 |
+
y = self.dropout(y)
|
| 144 |
+
return y.to(hidden_states.dtype)
|
fla/layers/rodimus.py
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import torch.utils.checkpoint
|
| 12 |
+
from einops import rearrange, repeat
|
| 13 |
+
from transformers.utils import logging
|
| 14 |
+
|
| 15 |
+
from fla.layers.utils import (
|
| 16 |
+
get_layer_cache,
|
| 17 |
+
get_unpad_data,
|
| 18 |
+
index_first_axis,
|
| 19 |
+
pad_input,
|
| 20 |
+
require_cache_layer_idx,
|
| 21 |
+
unpad_input,
|
| 22 |
+
update_layer_cache,
|
| 23 |
+
)
|
| 24 |
+
from fla.modules import RMSNorm, RotaryEmbedding, ShortConvolution
|
| 25 |
+
from fla.modules.layernorm_gated import RMSNormGated
|
| 26 |
+
from fla.ops.gla import chunk_gla, fused_chunk_gla, fused_recurrent_gla
|
| 27 |
+
|
| 28 |
+
if TYPE_CHECKING:
|
| 29 |
+
from transformers.processing_utils import Unpack
|
| 30 |
+
|
| 31 |
+
from fla.models.utils import Cache
|
| 32 |
+
|
| 33 |
+
try:
|
| 34 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 35 |
+
except ImportError:
|
| 36 |
+
warnings.warn(
|
| 37 |
+
"Flash Attention is not installed. Please install it via `pip install flash-attn --no-build-isolation`",
|
| 38 |
+
category=ImportWarning,
|
| 39 |
+
)
|
| 40 |
+
flash_attn_func = None
|
| 41 |
+
|
| 42 |
+
logger = logging.get_logger(__name__)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def align_multiple(value, multiple_size=8):
|
| 46 |
+
if value % multiple_size != 0:
|
| 47 |
+
value += multiple_size - (value % multiple_size)
|
| 48 |
+
return value
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def autocast_to_fp16(x):
|
| 52 |
+
if x.dtype not in {torch.float16, torch.bfloat16}:
|
| 53 |
+
return x.to(dtype=torch.bfloat16)
|
| 54 |
+
else:
|
| 55 |
+
return x
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class RodimusAttention(nn.Module):
|
| 59 |
+
def __init__(
|
| 60 |
+
self,
|
| 61 |
+
block_type: str = 'rodimus',
|
| 62 |
+
mode: str = 'chunk',
|
| 63 |
+
hidden_size: int = 1024,
|
| 64 |
+
input_gate_low_rank: float | str | None = 'auto',
|
| 65 |
+
expand_ratio: int = 64,
|
| 66 |
+
use_short_conv: bool = True,
|
| 67 |
+
conv_size: int = 4,
|
| 68 |
+
conv_bias: bool = True,
|
| 69 |
+
norm_eps: float = 1e-5,
|
| 70 |
+
k_norm_eps: float | None = None,
|
| 71 |
+
residual_in_fp32: bool = True,
|
| 72 |
+
layer_idx: int = None,
|
| 73 |
+
):
|
| 74 |
+
super().__init__()
|
| 75 |
+
|
| 76 |
+
self.block_type = block_type
|
| 77 |
+
self.mode = mode
|
| 78 |
+
self.hidden_size = hidden_size
|
| 79 |
+
self.d_inner = align_multiple(int(self.hidden_size * 2), 8)
|
| 80 |
+
|
| 81 |
+
self.expand_ratio = expand_ratio
|
| 82 |
+
self.input_gate_low_rank = max(self.hidden_size // 64, 16) if input_gate_low_rank == "auto" else input_gate_low_rank
|
| 83 |
+
|
| 84 |
+
self.use_short_conv = use_short_conv
|
| 85 |
+
self.conv_size = conv_size
|
| 86 |
+
self.conv_bias = conv_bias
|
| 87 |
+
|
| 88 |
+
self.norm_eps = norm_eps
|
| 89 |
+
self.k_norm_eps = k_norm_eps if k_norm_eps is not None else 1e-12
|
| 90 |
+
self.mem_size = expand_ratio
|
| 91 |
+
|
| 92 |
+
self.residual_in_fp32 = residual_in_fp32
|
| 93 |
+
self.layer_idx = layer_idx
|
| 94 |
+
|
| 95 |
+
assert mode in ['chunk', 'fused_recurrent', 'fused_chunk'], f"Not supported mode `{mode}`."
|
| 96 |
+
|
| 97 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
|
| 98 |
+
self.up_proj = nn.Linear(self.hidden_size, self.d_inner, bias=False)
|
| 99 |
+
self.activation_norm = RMSNormGated(hidden_size=self.d_inner, eps=norm_eps, norm_before_gate=False)
|
| 100 |
+
self.down_proj = nn.Linear(self.d_inner, self.hidden_size, bias=False)
|
| 101 |
+
|
| 102 |
+
if use_short_conv:
|
| 103 |
+
self.short_conv = ShortConvolution(
|
| 104 |
+
hidden_size=self.d_inner,
|
| 105 |
+
kernel_size=conv_size,
|
| 106 |
+
bias=conv_bias,
|
| 107 |
+
activation='silu',
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
self.residual_weight = nn.Parameter(torch.ones(
|
| 111 |
+
(self.d_inner, ), dtype=torch.float32 if self.residual_in_fp32 else None), requires_grad=True)
|
| 112 |
+
|
| 113 |
+
self.k_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
|
| 114 |
+
self.q_proj = nn.Linear(self.d_inner, self.mem_size, bias=False)
|
| 115 |
+
|
| 116 |
+
self.g_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
|
| 117 |
+
self.tau_gate_proj = nn.Linear(self.d_inner, self.mem_size, bias=True)
|
| 118 |
+
self.i_gate_proj = nn.Sequential(
|
| 119 |
+
nn.Linear(self.d_inner, self.input_gate_low_rank, bias=False),
|
| 120 |
+
nn.Linear(self.input_gate_low_rank, self.d_inner, bias=True),
|
| 121 |
+
nn.Sigmoid(),
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def forward(
|
| 125 |
+
self,
|
| 126 |
+
hidden_states: torch.Tensor,
|
| 127 |
+
attention_mask: torch.Tensor | None = None,
|
| 128 |
+
past_key_values: Cache | None = None,
|
| 129 |
+
use_cache: bool | None = False,
|
| 130 |
+
output_attentions: bool | None = False,
|
| 131 |
+
**kwargs: Unpack[dict],
|
| 132 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 133 |
+
if attention_mask is not None:
|
| 134 |
+
assert len(attention_mask.shape) == 2, (
|
| 135 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 136 |
+
"for padding purposes (0 indicating padding). "
|
| 137 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
batch_size, q_len, _ = hidden_states.shape
|
| 141 |
+
# mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 142 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] == 1 else self.mode
|
| 143 |
+
|
| 144 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 145 |
+
|
| 146 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 147 |
+
if attention_mask is not None:
|
| 148 |
+
indices, cu_seqlens, _ = get_unpad_data(attention_mask[:, -q_len:])
|
| 149 |
+
hidden_states = index_first_axis(rearrange(hidden_states, "b s ... -> (b s) ..."), indices).unsqueeze(0)
|
| 150 |
+
|
| 151 |
+
hidden_states, final_gate = self.up_proj(hidden_states), self.gate_proj(hidden_states)
|
| 152 |
+
|
| 153 |
+
if self.use_short_conv:
|
| 154 |
+
conv_state = None
|
| 155 |
+
if last_state is not None:
|
| 156 |
+
conv_state = last_state['conv_state']
|
| 157 |
+
shift_hidden_states, conv_state = self.short_conv(
|
| 158 |
+
x=hidden_states,
|
| 159 |
+
cache=conv_state,
|
| 160 |
+
output_final_state=use_cache,
|
| 161 |
+
cu_seqlens=cu_seqlens,
|
| 162 |
+
)
|
| 163 |
+
else:
|
| 164 |
+
shift_hidden_states = hidden_states
|
| 165 |
+
|
| 166 |
+
q = self.q_proj(shift_hidden_states)
|
| 167 |
+
k = self.k_proj(shift_hidden_states)
|
| 168 |
+
v = self.i_gate_proj(hidden_states) * hidden_states
|
| 169 |
+
|
| 170 |
+
g_gate = F.linear(shift_hidden_states, self.g_gate_proj.weight) + self.g_gate_proj.bias.float()
|
| 171 |
+
tau_gate = F.linear(shift_hidden_states, self.tau_gate_proj.weight) + self.tau_gate_proj.bias.float()
|
| 172 |
+
|
| 173 |
+
g_gate = F.softplus(g_gate)
|
| 174 |
+
it_gate = g_gate
|
| 175 |
+
rt_gate_log = -g_gate
|
| 176 |
+
|
| 177 |
+
tau_gate = F.sigmoid(tau_gate)
|
| 178 |
+
it_gate = it_gate ** tau_gate
|
| 179 |
+
rt_gate_log = rt_gate_log * tau_gate
|
| 180 |
+
|
| 181 |
+
k = F.normalize(k.float(), dim=-1, eps=self.k_norm_eps) * it_gate
|
| 182 |
+
q, k, v, rt_gate_log = map(lambda x: x.unsqueeze(1).transpose(1, 2), (q, k, v, rt_gate_log))
|
| 183 |
+
|
| 184 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 185 |
+
if mode == 'fused_recurrent':
|
| 186 |
+
o, recurrent_state = fused_recurrent_gla(
|
| 187 |
+
q=q,
|
| 188 |
+
k=k,
|
| 189 |
+
v=v,
|
| 190 |
+
gk=rt_gate_log,
|
| 191 |
+
initial_state=recurrent_state,
|
| 192 |
+
output_final_state=use_cache,
|
| 193 |
+
cu_seqlens=cu_seqlens,
|
| 194 |
+
head_first=False,
|
| 195 |
+
)
|
| 196 |
+
elif mode == 'fused_chunk':
|
| 197 |
+
o, recurrent_state = fused_chunk_gla(
|
| 198 |
+
q=q,
|
| 199 |
+
k=k,
|
| 200 |
+
v=v,
|
| 201 |
+
g=rt_gate_log,
|
| 202 |
+
initial_state=recurrent_state,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
head_first=False,
|
| 205 |
+
)
|
| 206 |
+
elif mode == 'chunk':
|
| 207 |
+
q, k, rt_gate_log = map(lambda x: x.to(v.dtype), (q, k, rt_gate_log))
|
| 208 |
+
o, recurrent_state = chunk_gla(
|
| 209 |
+
q=q,
|
| 210 |
+
k=k,
|
| 211 |
+
v=v,
|
| 212 |
+
g=rt_gate_log,
|
| 213 |
+
initial_state=recurrent_state,
|
| 214 |
+
output_final_state=use_cache,
|
| 215 |
+
cu_seqlens=cu_seqlens,
|
| 216 |
+
head_first=False,
|
| 217 |
+
)
|
| 218 |
+
else:
|
| 219 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 220 |
+
|
| 221 |
+
rodimus_caches = None
|
| 222 |
+
if past_key_values is not None:
|
| 223 |
+
if self.block_type == 'rodimus':
|
| 224 |
+
update_layer_cache(
|
| 225 |
+
self,
|
| 226 |
+
past_key_values,
|
| 227 |
+
recurrent_state=recurrent_state,
|
| 228 |
+
conv_state=conv_state if self.use_short_conv else None,
|
| 229 |
+
offset=q_len,
|
| 230 |
+
)
|
| 231 |
+
else:
|
| 232 |
+
rodimus_caches = (recurrent_state, conv_state if self.use_short_conv else None)
|
| 233 |
+
|
| 234 |
+
o = (o.transpose(1, 2).squeeze(1) + (shift_hidden_states.float()
|
| 235 |
+
if self.residual_in_fp32 else shift_hidden_states) * self.residual_weight).to(o.dtype)
|
| 236 |
+
|
| 237 |
+
o = self.activation_norm(o, final_gate)
|
| 238 |
+
o = self.down_proj(o)
|
| 239 |
+
|
| 240 |
+
if attention_mask is not None:
|
| 241 |
+
o = pad_input(o.squeeze(0), indices, batch_size, q_len)
|
| 242 |
+
|
| 243 |
+
if self.block_type == 'rodimus':
|
| 244 |
+
return o, None, past_key_values
|
| 245 |
+
else:
|
| 246 |
+
return o, None, (past_key_values, rodimus_caches)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class SlidingWindowSharedKeyAttention(nn.Module):
|
| 250 |
+
def __init__(
|
| 251 |
+
self,
|
| 252 |
+
hidden_size: int = 2048,
|
| 253 |
+
num_heads: int = 32,
|
| 254 |
+
qkv_bias: bool = False,
|
| 255 |
+
qk_norm: bool = False,
|
| 256 |
+
window_size: int = 2048,
|
| 257 |
+
rope_theta: float | None = 10000.,
|
| 258 |
+
max_position_embeddings: int | None = None,
|
| 259 |
+
layer_idx: int = None,
|
| 260 |
+
):
|
| 261 |
+
super().__init__()
|
| 262 |
+
|
| 263 |
+
self.hidden_size = hidden_size
|
| 264 |
+
self.num_heads = num_heads
|
| 265 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 266 |
+
self.qkv_bias = qkv_bias
|
| 267 |
+
self.qk_norm = qk_norm
|
| 268 |
+
|
| 269 |
+
self.window_size = window_size
|
| 270 |
+
self.rope_theta = rope_theta
|
| 271 |
+
self.max_position_embeddings = max_position_embeddings
|
| 272 |
+
self.layer_idx = layer_idx
|
| 273 |
+
|
| 274 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 275 |
+
self.k_proj = nn.Linear(self.hidden_size, self.head_dim, bias=self.qkv_bias)
|
| 276 |
+
self.v_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=self.qkv_bias)
|
| 277 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 278 |
+
|
| 279 |
+
if qk_norm:
|
| 280 |
+
self.q_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 281 |
+
self.k_norm = RMSNorm(self.head_dim, dtype=torch.float32)
|
| 282 |
+
|
| 283 |
+
self.rotary = RotaryEmbedding(dim=self.head_dim, base=self.rope_theta)
|
| 284 |
+
|
| 285 |
+
def forward(
|
| 286 |
+
self,
|
| 287 |
+
hidden_states: torch.Tensor,
|
| 288 |
+
attention_mask: torch.LongTensor | None = None,
|
| 289 |
+
past_key_values: Cache | None = None,
|
| 290 |
+
output_attentions: bool = False,
|
| 291 |
+
use_cache: bool = False,
|
| 292 |
+
**kwargs,
|
| 293 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
|
| 294 |
+
rodimus_caches = kwargs.get('rodimus_caches')
|
| 295 |
+
|
| 296 |
+
if attention_mask is not None:
|
| 297 |
+
assert len(attention_mask.shape) == 2, (
|
| 298 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 299 |
+
"for padding purposes (0 indicating padding). "
|
| 300 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
batch_size, q_len, _ = hidden_states.size()
|
| 304 |
+
|
| 305 |
+
q = rearrange(self.q_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 306 |
+
k = rearrange(self.k_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 307 |
+
v = rearrange(self.v_proj(hidden_states), '... (h d) -> ... h d', d=self.head_dim)
|
| 308 |
+
|
| 309 |
+
if self.qk_norm:
|
| 310 |
+
q, k = self.q_norm(q), self.k_norm(k)
|
| 311 |
+
|
| 312 |
+
# equivalent to cu_seqlens in `flash_attn`
|
| 313 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 314 |
+
|
| 315 |
+
layer_idx = require_cache_layer_idx(self, past_key_values)
|
| 316 |
+
seqlen_offset, max_seqlen = 0, q.shape[1]
|
| 317 |
+
if past_key_values is not None:
|
| 318 |
+
seqlen_offset = past_key_values.get_seq_length(layer_idx)
|
| 319 |
+
max_seqlen = q.shape[1] + seqlen_offset
|
| 320 |
+
|
| 321 |
+
if attention_mask is not None:
|
| 322 |
+
# to deliminate the offsets of padding tokens
|
| 323 |
+
seqlen_offset = seqlen_offset + attention_mask.sum(-1) - attention_mask.shape[-1]
|
| 324 |
+
max_seqlen = q.shape[1] + max(seqlen_offset)
|
| 325 |
+
|
| 326 |
+
if self.max_position_embeddings is not None:
|
| 327 |
+
max_seqlen = max(max_seqlen, self.max_position_embeddings)
|
| 328 |
+
q, k = self.rotary(q, k, seqlen_offset=seqlen_offset, max_seqlen=max_seqlen, cu_seqlens=cu_seqlens)
|
| 329 |
+
|
| 330 |
+
if past_key_values is not None:
|
| 331 |
+
if rodimus_caches is not None:
|
| 332 |
+
recurrent_state, conv_state = rodimus_caches
|
| 333 |
+
else:
|
| 334 |
+
recurrent_state, conv_state = None, None
|
| 335 |
+
|
| 336 |
+
cache_has_content = past_key_values.get_seq_length(layer_idx) > 0
|
| 337 |
+
k_cached, v_cached = past_key_values.update(
|
| 338 |
+
recurrent_state=recurrent_state,
|
| 339 |
+
conv_state=conv_state,
|
| 340 |
+
attn_state=[k.flatten(-2, -1), v.flatten(-2, -1)],
|
| 341 |
+
layer_idx=layer_idx,
|
| 342 |
+
offset=q_len,
|
| 343 |
+
cache_kwargs=dict(window_size=self.window_size),
|
| 344 |
+
)['attn_state']
|
| 345 |
+
if cache_has_content:
|
| 346 |
+
k, v = k_cached, v_cached
|
| 347 |
+
k = rearrange(k, '... (h d) -> ... h d', d=self.head_dim)
|
| 348 |
+
v = rearrange(v, '... (h d) -> ... h d', d=self.head_dim)
|
| 349 |
+
|
| 350 |
+
if flash_attn_func is None:
|
| 351 |
+
raise ImportError("Please install Flash Attention via `pip install flash-attn --no-build-isolation` first")
|
| 352 |
+
|
| 353 |
+
q, k, v = map(autocast_to_fp16, (q, k, v))
|
| 354 |
+
k = repeat(k, "... h d -> ... (n h) d", n=self.num_heads)
|
| 355 |
+
# Contains at least one padding token in the sequence
|
| 356 |
+
if attention_mask is not None:
|
| 357 |
+
q, (k, v), indices_q, cu_seqlens, max_seq_lens = unpad_input(
|
| 358 |
+
q=q,
|
| 359 |
+
states=(k, v),
|
| 360 |
+
attention_mask=attention_mask[:, -max(self.window_size, q_len):],
|
| 361 |
+
q_len=q_len,
|
| 362 |
+
)
|
| 363 |
+
cu_seqlens_q, cu_seqlens_k = cu_seqlens
|
| 364 |
+
max_seqlen_q, max_seqlen_k = max_seq_lens
|
| 365 |
+
o = flash_attn_varlen_func(
|
| 366 |
+
q, k, v,
|
| 367 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 368 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 369 |
+
max_seqlen_q=max_seqlen_q,
|
| 370 |
+
max_seqlen_k=max_seqlen_k,
|
| 371 |
+
causal=True,
|
| 372 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 373 |
+
)
|
| 374 |
+
o = pad_input(o, indices_q, batch_size, q_len)
|
| 375 |
+
elif cu_seqlens is not None:
|
| 376 |
+
o = flash_attn_varlen_func(
|
| 377 |
+
q.squeeze(0), k.squeeze(0), v.squeeze(0),
|
| 378 |
+
cu_seqlens_q=cu_seqlens,
|
| 379 |
+
cu_seqlens_k=cu_seqlens,
|
| 380 |
+
max_seqlen_q=max_seqlen,
|
| 381 |
+
max_seqlen_k=max_seqlen,
|
| 382 |
+
causal=True,
|
| 383 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 384 |
+
).unsqueeze(0)
|
| 385 |
+
else:
|
| 386 |
+
o = flash_attn_func(
|
| 387 |
+
q, k, v,
|
| 388 |
+
causal=True,
|
| 389 |
+
window_size=(-1, -1) if self.window_size is None else (self.window_size-1, 0),
|
| 390 |
+
)
|
| 391 |
+
o = o.reshape(batch_size, q_len, -1)
|
| 392 |
+
o = self.o_proj(o.to(dtype=self.o_proj.weight.dtype))
|
| 393 |
+
|
| 394 |
+
if not output_attentions:
|
| 395 |
+
attentions = None
|
| 396 |
+
|
| 397 |
+
return o, attentions, past_key_values
|
fla/layers/rwkv6.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# "Eagle and Finch: RWKV with Matrix-Valued States and Dynamic Recurrence"[https://arxiv.org/abs/2404.05892]
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import math
|
| 8 |
+
import warnings
|
| 9 |
+
from typing import TYPE_CHECKING
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from einops import rearrange
|
| 14 |
+
|
| 15 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 16 |
+
from fla.modules import GroupNorm
|
| 17 |
+
from fla.modules.activations import ACT2FN
|
| 18 |
+
from fla.modules.token_shift import token_shift
|
| 19 |
+
from fla.ops.rwkv6 import chunk_rwkv6, fused_recurrent_rwkv6
|
| 20 |
+
|
| 21 |
+
if TYPE_CHECKING:
|
| 22 |
+
from fla.models.utils import Cache
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class RWKV6Attention(nn.Module):
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
mode: str = 'chunk',
|
| 30 |
+
hidden_size: int = 1024,
|
| 31 |
+
expand_k: float = 0.5,
|
| 32 |
+
expand_v: float = 1.0,
|
| 33 |
+
num_heads: int = 4,
|
| 34 |
+
gate_fn: str = 'swish',
|
| 35 |
+
proj_low_rank_dim: int = 32,
|
| 36 |
+
gate_low_rank_dim: int = 64,
|
| 37 |
+
fuse_norm: bool = True,
|
| 38 |
+
elementwise_affine: bool | None = True,
|
| 39 |
+
norm_eps: float = 1e-5,
|
| 40 |
+
layer_idx: int = None,
|
| 41 |
+
**kwargs,
|
| 42 |
+
) -> RWKV6Attention:
|
| 43 |
+
super().__init__()
|
| 44 |
+
|
| 45 |
+
self.mode = mode
|
| 46 |
+
self.hidden_size = hidden_size
|
| 47 |
+
self.expand_k = expand_k
|
| 48 |
+
self.expand_v = expand_v
|
| 49 |
+
self.num_heads = num_heads
|
| 50 |
+
self.proj_low_rank_dim = proj_low_rank_dim
|
| 51 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 52 |
+
|
| 53 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 54 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 55 |
+
self.layer_idx = layer_idx
|
| 56 |
+
|
| 57 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 58 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 59 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 60 |
+
|
| 61 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 62 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 63 |
+
|
| 64 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 65 |
+
self.x_proj = nn.Sequential(
|
| 66 |
+
LerpLinear(hidden_size, proj_low_rank_dim * 5),
|
| 67 |
+
nn.Tanh(),
|
| 68 |
+
nn.Linear(proj_low_rank_dim * 5, hidden_size, bias=False),
|
| 69 |
+
)
|
| 70 |
+
self.x_bias = nn.Parameter(torch.zeros(5, hidden_size))
|
| 71 |
+
|
| 72 |
+
self.r_proj = DDLerpLinear(hidden_size, self.key_dim)
|
| 73 |
+
self.w_proj = DDLerpLinear(hidden_size, self.key_dim, low_rank_dim=gate_low_rank_dim)
|
| 74 |
+
self.k_proj = DDLerpLinear(hidden_size, self.key_dim)
|
| 75 |
+
self.v_proj = DDLerpLinear(hidden_size, self.value_dim)
|
| 76 |
+
self.g_proj = DDLerpLinear(hidden_size, self.value_dim)
|
| 77 |
+
self.bonus = nn.Parameter(torch.zeros(num_heads, self.head_k_dim))
|
| 78 |
+
|
| 79 |
+
# TODO: fuse GroupNorm and output gate
|
| 80 |
+
self.g_norm = GroupNorm(self.num_heads, self.value_dim, elementwise_affine=elementwise_affine, bias=True, eps=norm_eps)
|
| 81 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 82 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 83 |
+
|
| 84 |
+
try:
|
| 85 |
+
from transformers.modeling_utils import _init_weights
|
| 86 |
+
except ImportError:
|
| 87 |
+
_init_weights = True
|
| 88 |
+
if _init_weights:
|
| 89 |
+
self.apply(self._initialize_weights)
|
| 90 |
+
|
| 91 |
+
warnings.warn(
|
| 92 |
+
"According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
|
| 93 |
+
"If you plan to report any numbers based on this implementation, we strongly recommend "
|
| 94 |
+
"cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
|
| 95 |
+
"Bo may disagree with results reported from this version.",
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
def _initialize_weights(self, module: nn.Module):
|
| 99 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 100 |
+
return
|
| 101 |
+
if isinstance(module, nn.Linear):
|
| 102 |
+
nn.init.xavier_uniform_(module.weight, gain=2 ** -2.5)
|
| 103 |
+
if module.bias is not None:
|
| 104 |
+
nn.init.zeros_(module.bias)
|
| 105 |
+
if isinstance(module, nn.Parameter):
|
| 106 |
+
nn.init.xavier_uniform_(module, gain=2 ** -2.5)
|
| 107 |
+
module._is_hf_initialized = True
|
| 108 |
+
|
| 109 |
+
def forward(
|
| 110 |
+
self,
|
| 111 |
+
hidden_states: torch.Tensor,
|
| 112 |
+
attention_mask: torch.Tensor | None = None,
|
| 113 |
+
past_key_values: Cache | None = None,
|
| 114 |
+
use_cache: bool | None = False,
|
| 115 |
+
output_attentions: bool | None = False,
|
| 116 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 117 |
+
**kwargs,
|
| 118 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 119 |
+
if attention_mask is not None:
|
| 120 |
+
assert len(attention_mask.shape) == 2, (
|
| 121 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 122 |
+
"for padding purposes (0 indicating padding). "
|
| 123 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
batch_size, seq_len, hidden_size = hidden_states.shape
|
| 127 |
+
# launching the triton kernel for just one token will actually be slower
|
| 128 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 129 |
+
|
| 130 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 131 |
+
|
| 132 |
+
if attention_mask is not None:
|
| 133 |
+
hidden_states = hidden_states.mul(attention_mask[:, -hidden_states.shape[-2]:, None])
|
| 134 |
+
|
| 135 |
+
if hidden_states.shape[1] == 1 and last_state is not None:
|
| 136 |
+
shifted = last_state['conv_state'].unsqueeze(1)
|
| 137 |
+
delta = shifted - hidden_states
|
| 138 |
+
elif last_state is None:
|
| 139 |
+
delta = token_shift(hidden_states, cu_seqlens)
|
| 140 |
+
else:
|
| 141 |
+
shifted = self.time_shift(hidden_states)
|
| 142 |
+
shifted[:, 0] = last_state['conv_state']
|
| 143 |
+
delta = shifted - hidden_states
|
| 144 |
+
|
| 145 |
+
x = self.x_proj[0](hidden_states, delta, cu_seqlens).view(batch_size, seq_len, -1, self.proj_low_rank_dim)
|
| 146 |
+
x = torch.einsum('b t n r, h n r-> b t n h', self.x_proj[1](x), self.x_proj[2].weight.view(hidden_size, 5, -1))
|
| 147 |
+
|
| 148 |
+
r, w, k, v, g = x.add_(self.x_bias).unbind(-2)
|
| 149 |
+
r = self.r_proj(hidden_states, r, delta, cu_seqlens)
|
| 150 |
+
w = self.w_proj(hidden_states, w, delta, cu_seqlens)
|
| 151 |
+
k = self.k_proj(hidden_states, k, delta, cu_seqlens)
|
| 152 |
+
v = self.v_proj(hidden_states, v, delta, cu_seqlens)
|
| 153 |
+
g = self.g_proj(hidden_states, g, delta, cu_seqlens)
|
| 154 |
+
|
| 155 |
+
# dealing with left-padding
|
| 156 |
+
if attention_mask is not None:
|
| 157 |
+
v = v.mul(attention_mask[:, -v.shape[-2]:, None])
|
| 158 |
+
r, w, k = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_k_dim), (r, w, k))
|
| 159 |
+
v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
|
| 160 |
+
w = -torch.exp(w)
|
| 161 |
+
u = self.bonus
|
| 162 |
+
|
| 163 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 164 |
+
|
| 165 |
+
if mode == 'fused_recurrent':
|
| 166 |
+
o, recurrent_state = fused_recurrent_rwkv6(
|
| 167 |
+
r=r,
|
| 168 |
+
k=k,
|
| 169 |
+
v=v,
|
| 170 |
+
w=w,
|
| 171 |
+
u=u,
|
| 172 |
+
scale=1.,
|
| 173 |
+
initial_state=recurrent_state,
|
| 174 |
+
output_final_state=use_cache,
|
| 175 |
+
cu_seqlens=cu_seqlens,
|
| 176 |
+
)
|
| 177 |
+
elif mode == 'chunk':
|
| 178 |
+
o, recurrent_state = chunk_rwkv6(
|
| 179 |
+
r=r,
|
| 180 |
+
k=k,
|
| 181 |
+
v=v,
|
| 182 |
+
w=w,
|
| 183 |
+
u=u,
|
| 184 |
+
scale=1.,
|
| 185 |
+
initial_state=recurrent_state,
|
| 186 |
+
output_final_state=use_cache,
|
| 187 |
+
cu_seqlens=cu_seqlens,
|
| 188 |
+
)
|
| 189 |
+
else:
|
| 190 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 191 |
+
|
| 192 |
+
update_layer_cache(
|
| 193 |
+
self,
|
| 194 |
+
past_key_values,
|
| 195 |
+
recurrent_state=recurrent_state,
|
| 196 |
+
conv_state=hidden_states[:, -1],
|
| 197 |
+
offset=seq_len,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
o = self.g_norm(rearrange(o, '... h d -> ... (h d)')) * self.gate_fn(g)
|
| 201 |
+
o = self.o_proj(o)
|
| 202 |
+
|
| 203 |
+
return o, None, past_key_values
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class LoRA(nn.Module):
|
| 207 |
+
|
| 208 |
+
def __init__(
|
| 209 |
+
self,
|
| 210 |
+
input_dim: int,
|
| 211 |
+
output_dim: int,
|
| 212 |
+
low_rank_dim: int,
|
| 213 |
+
bias: bool | None = True,
|
| 214 |
+
activation: str | None = 'tanh',
|
| 215 |
+
):
|
| 216 |
+
super().__init__()
|
| 217 |
+
|
| 218 |
+
self.input_dim = input_dim
|
| 219 |
+
self.output_dim = output_dim
|
| 220 |
+
self.low_rank_dim = low_rank_dim
|
| 221 |
+
self.bias = bias
|
| 222 |
+
|
| 223 |
+
if activation is None:
|
| 224 |
+
self.activation = nn.Identity()
|
| 225 |
+
elif activation == 'sigmoid':
|
| 226 |
+
self.activation = nn.Sigmoid()
|
| 227 |
+
elif activation == 'tanh':
|
| 228 |
+
self.activation = nn.Tanh()
|
| 229 |
+
elif activation == 'relu':
|
| 230 |
+
self.activation = nn.ReLU()
|
| 231 |
+
else:
|
| 232 |
+
raise ValueError(f"Not supported activation `{activation}`.")
|
| 233 |
+
|
| 234 |
+
self.lora = nn.Sequential(
|
| 235 |
+
nn.Linear(input_dim, low_rank_dim, bias=False),
|
| 236 |
+
self.activation,
|
| 237 |
+
nn.Linear(low_rank_dim, output_dim, bias=bias),
|
| 238 |
+
)
|
| 239 |
+
try:
|
| 240 |
+
from transformers.modeling_utils import _init_weights
|
| 241 |
+
except ImportError:
|
| 242 |
+
_init_weights = True
|
| 243 |
+
if _init_weights:
|
| 244 |
+
self.apply(self._initialize_weights)
|
| 245 |
+
|
| 246 |
+
def __repr__(self) -> str:
|
| 247 |
+
s = f"{self.__class__.__name__}("
|
| 248 |
+
s += f"input_dim={self.input_dim}, low_rank_dim={self.low_rank_dim}, output_dim={self.output_dim}"
|
| 249 |
+
if not self.bias:
|
| 250 |
+
s += f", bias={self.bias}"
|
| 251 |
+
s += ")"
|
| 252 |
+
return s
|
| 253 |
+
|
| 254 |
+
def _initialize_weights(self, module: nn.Module):
|
| 255 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 256 |
+
return
|
| 257 |
+
|
| 258 |
+
# Initialize weights to zero as in original code
|
| 259 |
+
nn.init.zeros_(self.lora[0].weight)
|
| 260 |
+
original_dtype = self.lora[2].weight.dtype
|
| 261 |
+
shape = self.lora[2].weight.shape
|
| 262 |
+
# Convert to float32 for numerical stability in orthogonal init
|
| 263 |
+
weight_fp32 = self.lora[2].weight.float()
|
| 264 |
+
|
| 265 |
+
# Calculate gain based on dimensions
|
| 266 |
+
gain = math.sqrt(shape[1] / shape[0]) if shape[1] > shape[0] else 1
|
| 267 |
+
|
| 268 |
+
# Apply orthogonal initialization with scaling factor 0.1
|
| 269 |
+
nn.init.orthogonal_(weight_fp32, gain=gain * 0.1)
|
| 270 |
+
|
| 271 |
+
# Convert back to original dtype
|
| 272 |
+
self.lora[2].weight.data.copy_(weight_fp32.to(original_dtype))
|
| 273 |
+
# Set Lora[2] bias to zero
|
| 274 |
+
if self.lora[2].bias is not None:
|
| 275 |
+
nn.init.zeros_(self.lora[2].bias)
|
| 276 |
+
|
| 277 |
+
module._is_hf_initialized = True
|
| 278 |
+
|
| 279 |
+
def set_bias_value(self, value):
|
| 280 |
+
"""Set bias to a specific value (for v0, w0 etc.)"""
|
| 281 |
+
if self.bias and self.lora[2].bias is not None:
|
| 282 |
+
if isinstance(value, torch.Tensor):
|
| 283 |
+
# Handle tensor values
|
| 284 |
+
self.lora[2].bias.data.copy_(value.to(self.lora[2].bias.dtype))
|
| 285 |
+
else:
|
| 286 |
+
# Handle scalar values
|
| 287 |
+
nn.init.constant_(self.lora[2].bias, value)
|
| 288 |
+
|
| 289 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 290 |
+
return self.lora(x)
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
class LerpLinear(nn.Module):
|
| 294 |
+
|
| 295 |
+
def __init__(
|
| 296 |
+
self,
|
| 297 |
+
input_dim: int,
|
| 298 |
+
output_dim: int,
|
| 299 |
+
low_rank_dim: int | None = None,
|
| 300 |
+
):
|
| 301 |
+
super().__init__()
|
| 302 |
+
|
| 303 |
+
self.input_dim = input_dim
|
| 304 |
+
self.output_dim = output_dim
|
| 305 |
+
self.low_rank_dim = low_rank_dim
|
| 306 |
+
|
| 307 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 308 |
+
if low_rank_dim is None:
|
| 309 |
+
self.linear = nn.Linear(input_dim, output_dim, bias=False)
|
| 310 |
+
else:
|
| 311 |
+
self.linear = LoRA(input_dim, output_dim, low_rank_dim)
|
| 312 |
+
self.mu = nn.Parameter(torch.zeros(input_dim))
|
| 313 |
+
|
| 314 |
+
def __repr__(self) -> str:
|
| 315 |
+
s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
|
| 316 |
+
if self.low_rank_dim is not None:
|
| 317 |
+
s += f", low_rank_dim={self.low_rank_dim}"
|
| 318 |
+
s += ")"
|
| 319 |
+
return s
|
| 320 |
+
|
| 321 |
+
def forward(self, x: torch.Tensor, delta: torch.Tensor | None = None,
|
| 322 |
+
cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
|
| 323 |
+
if delta is None:
|
| 324 |
+
delta = token_shift(x, cu_seqlens)
|
| 325 |
+
return self.linear(x + delta * self.mu)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
class DDLerpLinear(nn.Module):
|
| 329 |
+
|
| 330 |
+
def __init__(
|
| 331 |
+
self,
|
| 332 |
+
input_dim: int,
|
| 333 |
+
output_dim: int,
|
| 334 |
+
low_rank_dim: int | None = None,
|
| 335 |
+
):
|
| 336 |
+
super().__init__()
|
| 337 |
+
|
| 338 |
+
self.input_dim = input_dim
|
| 339 |
+
self.output_dim = output_dim
|
| 340 |
+
self.low_rank_dim = low_rank_dim
|
| 341 |
+
|
| 342 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 343 |
+
if low_rank_dim is None:
|
| 344 |
+
self.linear = nn.Linear(input_dim, output_dim, bias=False)
|
| 345 |
+
else:
|
| 346 |
+
self.linear = LoRA(input_dim, output_dim, low_rank_dim)
|
| 347 |
+
|
| 348 |
+
def __repr__(self) -> str:
|
| 349 |
+
s = f"{self.__class__.__name__}({self.input_dim}, {self.output_dim}"
|
| 350 |
+
if self.low_rank_dim is not None:
|
| 351 |
+
s += f", low_rank_dim={self.low_rank_dim}"
|
| 352 |
+
s += ")"
|
| 353 |
+
return s
|
| 354 |
+
|
| 355 |
+
def forward(self, x: torch.Tensor, mu: torch.Tensor,
|
| 356 |
+
delta: torch.Tensor | None = None,
|
| 357 |
+
cu_seqlens: torch.LongTensor | None = None) -> torch.Tensor:
|
| 358 |
+
if delta is None:
|
| 359 |
+
delta = token_shift(x, cu_seqlens)
|
| 360 |
+
return self.linear(x + delta * mu)
|
fla/layers/rwkv7.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import TYPE_CHECKING
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from einops import rearrange
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
from fla.layers.rwkv6 import LoRA
|
| 14 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 15 |
+
from fla.modules import GroupNorm
|
| 16 |
+
from fla.modules.l2norm import l2_norm
|
| 17 |
+
from fla.modules.token_shift import token_shift
|
| 18 |
+
from fla.ops.rwkv7 import chunk_rwkv7, fused_mul_recurrent_rwkv7
|
| 19 |
+
from fla.ops.rwkv7.fused_addcmul import fused_addcmul_rwkv7
|
| 20 |
+
from fla.ops.rwkv7.fused_k_update import fused_k_rwkv7
|
| 21 |
+
from fla.ops.rwkv7.gate_output_correction import gate_output_correction
|
| 22 |
+
|
| 23 |
+
if TYPE_CHECKING:
|
| 24 |
+
from fla.models.utils import Cache
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class RWKV7Attention(nn.Module):
|
| 28 |
+
|
| 29 |
+
def __init__(
|
| 30 |
+
self,
|
| 31 |
+
mode: str = 'chunk',
|
| 32 |
+
hidden_size: int = 1024,
|
| 33 |
+
head_dim: int | None = 64,
|
| 34 |
+
num_heads: int | None = None,
|
| 35 |
+
decay_low_rank_dim: int | None = None,
|
| 36 |
+
gate_low_rank_dim: int | None = None,
|
| 37 |
+
a_low_rank_dim: int | None = None,
|
| 38 |
+
v_low_rank_dim: int | None = None,
|
| 39 |
+
elementwise_affine: bool | None = True,
|
| 40 |
+
norm_eps: float = 1e-5,
|
| 41 |
+
layer_idx: int = None,
|
| 42 |
+
fuse_norm: bool = False,
|
| 43 |
+
value_dim: int = None,
|
| 44 |
+
num_hidden_layers: int = None,
|
| 45 |
+
**kwargs,
|
| 46 |
+
) -> RWKV7Attention:
|
| 47 |
+
super().__init__()
|
| 48 |
+
|
| 49 |
+
self.mode = mode
|
| 50 |
+
assert mode in ['chunk', 'fused_recurrent'], f"Not supported mode `{mode}`."
|
| 51 |
+
self.hidden_size = hidden_size
|
| 52 |
+
|
| 53 |
+
self.key_dim = hidden_size
|
| 54 |
+
self.value_dim = value_dim if value_dim is not None else hidden_size
|
| 55 |
+
if head_dim is None and num_heads is None:
|
| 56 |
+
raise ValueError("Either `head_dim` or `num_heads` must be specified.")
|
| 57 |
+
elif head_dim is not None:
|
| 58 |
+
self.head_dim = head_dim
|
| 59 |
+
self.num_heads = int(hidden_size // head_dim)
|
| 60 |
+
elif num_heads is not None:
|
| 61 |
+
self.head_dim = int(hidden_size // num_heads)
|
| 62 |
+
self.num_heads = num_heads
|
| 63 |
+
self.head_v_dim = int(self.value_dim // self.num_heads)
|
| 64 |
+
|
| 65 |
+
# Increase lora dimension for headdim>64
|
| 66 |
+
factor = self.head_dim / 64
|
| 67 |
+
if decay_low_rank_dim is None:
|
| 68 |
+
decay_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 69 |
+
self.decay_low_rank_dim = decay_low_rank_dim
|
| 70 |
+
else:
|
| 71 |
+
self.decay_low_rank_dim = decay_low_rank_dim
|
| 72 |
+
|
| 73 |
+
if gate_low_rank_dim is None:
|
| 74 |
+
gate_low_rank_dim = max(32, int(round((5 * (hidden_size**0.5)) / 32) * 32))
|
| 75 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 76 |
+
else:
|
| 77 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 78 |
+
|
| 79 |
+
if a_low_rank_dim is None:
|
| 80 |
+
a_low_rank_dim = max(32, int(round((2.5 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 81 |
+
self.a_low_rank_dim = a_low_rank_dim
|
| 82 |
+
else:
|
| 83 |
+
self.a_low_rank_dim = a_low_rank_dim
|
| 84 |
+
|
| 85 |
+
if v_low_rank_dim is None:
|
| 86 |
+
v_low_rank_dim = max(32, int(round((1.7 * (hidden_size**0.5)) * factor / 32) * 32))
|
| 87 |
+
self.v_low_rank_dim = v_low_rank_dim
|
| 88 |
+
else:
|
| 89 |
+
self.v_low_rank_dim = v_low_rank_dim
|
| 90 |
+
|
| 91 |
+
self.layer_idx = layer_idx
|
| 92 |
+
self.num_hidden_layers = num_hidden_layers
|
| 93 |
+
self.fuse_norm = fuse_norm
|
| 94 |
+
|
| 95 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 96 |
+
self.x_r = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 97 |
+
self.x_w = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 98 |
+
self.x_k = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 99 |
+
self.x_v = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 100 |
+
self.x_a = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 101 |
+
self.x_g = nn.Parameter(torch.zeros(1, 1, hidden_size))
|
| 102 |
+
|
| 103 |
+
self.k_k = nn.Parameter(torch.zeros(self.key_dim))
|
| 104 |
+
self.k_a = nn.Parameter(torch.zeros(self.key_dim))
|
| 105 |
+
self.r_k = nn.Parameter(torch.zeros(self.num_heads, self.head_dim))
|
| 106 |
+
|
| 107 |
+
self.r_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 108 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 109 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 110 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 111 |
+
|
| 112 |
+
self.w_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=decay_low_rank_dim, activation='tanh')
|
| 113 |
+
if self.layer_idx != 0:
|
| 114 |
+
self.v_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=v_low_rank_dim, activation=None)
|
| 115 |
+
self.a_lora = LoRA(hidden_size, self.key_dim, low_rank_dim=a_low_rank_dim, activation=None)
|
| 116 |
+
self.g_lora = LoRA(hidden_size, self.value_dim, low_rank_dim=gate_low_rank_dim, activation='sigmoid', bias=False)
|
| 117 |
+
|
| 118 |
+
if self.fuse_norm:
|
| 119 |
+
self.g_norm = GroupNorm(
|
| 120 |
+
num_groups=self.num_heads,
|
| 121 |
+
hidden_size=self.value_dim,
|
| 122 |
+
elementwise_affine=elementwise_affine,
|
| 123 |
+
eps=self.head_dim*norm_eps,
|
| 124 |
+
bias=True,
|
| 125 |
+
)
|
| 126 |
+
else:
|
| 127 |
+
self.g_norm = nn.GroupNorm(
|
| 128 |
+
num_groups=self.num_heads,
|
| 129 |
+
num_channels=self.value_dim,
|
| 130 |
+
eps=self.head_dim*norm_eps,
|
| 131 |
+
affine=elementwise_affine,
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
try:
|
| 135 |
+
from transformers.modeling_utils import _init_weights
|
| 136 |
+
except ImportError:
|
| 137 |
+
_init_weights = True
|
| 138 |
+
if _init_weights:
|
| 139 |
+
self.apply(self._initialize_weights)
|
| 140 |
+
for name, module in self.named_modules():
|
| 141 |
+
module._in_rwkv_module = True
|
| 142 |
+
|
| 143 |
+
warnings.warn(
|
| 144 |
+
"According to Bo, you are using a potentially buggy FLA implementation of RWKV. "
|
| 145 |
+
"If you plan to report any numbers based on this implementation, we strongly recommend "
|
| 146 |
+
"cross-checking with the official repo: https://github.com/BlinkDL/RWKV-LM. "
|
| 147 |
+
"Bo may disagree with results reported from this version.",
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
@torch.no_grad()
|
| 151 |
+
@torch.compiler.disable
|
| 152 |
+
def _initialize_weights(self, module: nn.Module):
|
| 153 |
+
if getattr(module, "_is_hf_initialized", False):
|
| 154 |
+
return
|
| 155 |
+
|
| 156 |
+
# Initialize only when we're processing the RWKV7Attention module itself
|
| 157 |
+
if isinstance(module, RWKV7Attention) and self.layer_idx is not None:
|
| 158 |
+
ratio_0_to_1 = self.layer_idx / (self.num_hidden_layers - 1) # 0 to 1
|
| 159 |
+
ratio_1_to_almost0 = 1.0 - (self.layer_idx / self.num_hidden_layers) # 1 to ~0
|
| 160 |
+
|
| 161 |
+
# Create position-based initialization tensor
|
| 162 |
+
ddd = torch.ones(1, 1, self.hidden_size, device=self.x_r.device)
|
| 163 |
+
www = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 164 |
+
zigzag = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 165 |
+
linear = torch.zeros(self.hidden_size, device=self.x_r.device)
|
| 166 |
+
for n in range(self.hidden_size):
|
| 167 |
+
linear[n] = n / (self.hidden_size-1) - 0.5
|
| 168 |
+
zigzag[n] = ((n % self.head_dim) - ((self.head_dim-1) / 2)) / ((self.head_dim-1) / 2)
|
| 169 |
+
zigzag[n] = zigzag[n] * abs(zigzag[n])
|
| 170 |
+
www[n] = -6 + 6 * (n / (self.hidden_size - 1)) ** (1 + 1 * ratio_0_to_1 ** 0.3)
|
| 171 |
+
ddd[0, 0, n] = n / self.hidden_size
|
| 172 |
+
|
| 173 |
+
# Initialize x_* parameters directly
|
| 174 |
+
self.x_r.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_r.dtype)
|
| 175 |
+
self.x_w.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_w.dtype)
|
| 176 |
+
self.x_k.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_k.dtype)
|
| 177 |
+
self.x_v.data = (1.0 - torch.pow(ddd, 0.7 * ratio_1_to_almost0)).to(self.x_v.dtype)
|
| 178 |
+
self.x_a.data = (1.0 - torch.pow(ddd, 0.9 * ratio_1_to_almost0)).to(self.x_a.dtype)
|
| 179 |
+
self.x_g.data = (1.0 - torch.pow(ddd, 0.2 * ratio_1_to_almost0)).to(self.x_g.dtype)
|
| 180 |
+
|
| 181 |
+
# Initialize k_k, k_a, r_k
|
| 182 |
+
nn.init.constant_(self.k_a, 1.02)
|
| 183 |
+
nn.init.constant_(self.r_k, -0.04)
|
| 184 |
+
self.k_k.data.copy_((torch.zeros(self.hidden_size, device=self.k_k.device) +
|
| 185 |
+
0.71 - linear*0.1).to(self.k_k.dtype))
|
| 186 |
+
# Set specific bias values for LoRA modules
|
| 187 |
+
# 0.5 comes from F.softplus
|
| 188 |
+
self.w_lora.set_bias_value(www + 0.5 + zigzag*2.5)
|
| 189 |
+
self.a_lora.set_bias_value(-0.19 + zigzag*0.3 + linear*0.4)
|
| 190 |
+
|
| 191 |
+
# v0 initialization - ones (for non-first layers)
|
| 192 |
+
if self.layer_idx != 0:
|
| 193 |
+
self.v_lora._initialize_weights(self.v_lora)
|
| 194 |
+
self.v_lora.set_bias_value(0.73 - linear*0.4)
|
| 195 |
+
|
| 196 |
+
# Initialize GroupNorm
|
| 197 |
+
self.g_norm.weight.data[:] = ((self.layer_idx + 1) / self.num_hidden_layers) ** 0.7
|
| 198 |
+
|
| 199 |
+
# Initialize Linear projections
|
| 200 |
+
self._orthogonal_init(self.r_proj.weight)
|
| 201 |
+
self._orthogonal_init(self.k_proj.weight, gain=0.1)
|
| 202 |
+
self._orthogonal_init(self.v_proj.weight)
|
| 203 |
+
self.o_proj.weight.data.zero_()
|
| 204 |
+
|
| 205 |
+
# Clean up temporary tensors to free memory
|
| 206 |
+
del ddd, www, zigzag, linear
|
| 207 |
+
|
| 208 |
+
module._is_hf_initialized = True
|
| 209 |
+
|
| 210 |
+
@staticmethod
|
| 211 |
+
def _orthogonal_init(weight, gain=1.0):
|
| 212 |
+
oringinal_dtype = weight.dtype
|
| 213 |
+
weight = weight.float()
|
| 214 |
+
nn.init.orthogonal_(weight, gain=gain)
|
| 215 |
+
weight = weight.to(oringinal_dtype)
|
| 216 |
+
|
| 217 |
+
def forward(
|
| 218 |
+
self,
|
| 219 |
+
hidden_states: torch.Tensor,
|
| 220 |
+
attention_mask: torch.Tensor | None = None,
|
| 221 |
+
past_key_values: Cache | None = None,
|
| 222 |
+
use_cache: bool | None = False,
|
| 223 |
+
output_attentions: bool | None = False,
|
| 224 |
+
v_first: torch.Tensor = None,
|
| 225 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 226 |
+
**kwargs,
|
| 227 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 228 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 229 |
+
if attention_mask is not None:
|
| 230 |
+
assert len(attention_mask.shape) == 2, (
|
| 231 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 232 |
+
"for padding purposes (0 indicating padding). "
|
| 233 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 234 |
+
)
|
| 235 |
+
am = attention_mask.narrow(1, attention_mask.size(1) - seq_len, seq_len).unsqueeze(-1)
|
| 236 |
+
|
| 237 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 238 |
+
|
| 239 |
+
if attention_mask is not None:
|
| 240 |
+
hidden_states = hidden_states.mul(am)
|
| 241 |
+
|
| 242 |
+
# delta [batch_size, seq_len, hidden_size]
|
| 243 |
+
# conv_cache [N, D]
|
| 244 |
+
if last_state is None:
|
| 245 |
+
conv_cache = None
|
| 246 |
+
recurrent_state = None
|
| 247 |
+
else:
|
| 248 |
+
conv_cache = last_state['conv_state']
|
| 249 |
+
recurrent_state = last_state['recurrent_state']
|
| 250 |
+
|
| 251 |
+
delta, conv_state = token_shift(
|
| 252 |
+
hidden_states, cu_seqlens, output_cache=True, cache=conv_cache,
|
| 253 |
+
)
|
| 254 |
+
xr, xw, xk, xv, xa, xg = fused_addcmul_rwkv7(hidden_states, delta, self.x_r, self.x_w,
|
| 255 |
+
self.x_k, self.x_v, self.x_a, self.x_g)
|
| 256 |
+
|
| 257 |
+
r = self.r_proj(xr)
|
| 258 |
+
# Using bf16 for LoRA computation is numerically safe here because:
|
| 259 |
+
# 1. After sigmoid activation:
|
| 260 |
+
# - Max absolute error (vs float32): 0.003
|
| 261 |
+
# - Mean absolute error: 0.0004
|
| 262 |
+
# 2. Subsequent scaling by -0.6065 will further reduce relative error
|
| 263 |
+
# (error scales linearly with constant multiplication)
|
| 264 |
+
# 3. Final compounded error remains within acceptable bounds for bf16 precision
|
| 265 |
+
# Empirical observation confirms bf16 introduces no practical degradation
|
| 266 |
+
w = -0.6065306597126334 * self.w_lora(xw).sigmoid()
|
| 267 |
+
|
| 268 |
+
k = self.k_proj(xk)
|
| 269 |
+
v = self.v_proj(xv)
|
| 270 |
+
|
| 271 |
+
if self.layer_idx == 0:
|
| 272 |
+
v_first = v
|
| 273 |
+
else:
|
| 274 |
+
v = torch.lerp(v, v_first, self.v_lora(xv).sigmoid())
|
| 275 |
+
a = self.a_lora(xa).sigmoid()
|
| 276 |
+
g = self.g_lora(xg)
|
| 277 |
+
|
| 278 |
+
if self.fuse_norm:
|
| 279 |
+
kk = l2_norm(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim))
|
| 280 |
+
else:
|
| 281 |
+
kk = F.normalize(rearrange(k * self.k_k, 'b t (h d) -> b t h d', d=self.head_dim), dim=-1, p=2.0)
|
| 282 |
+
|
| 283 |
+
# Prefer addcmul over expanded form for numerical stability in bf16:
|
| 284 |
+
# 1. Fused Multiply-Add (FMA) in addcmul reduces intermediate rounding:
|
| 285 |
+
# - Single op vs original 3 ops (mul, sub, mul)
|
| 286 |
+
# - 1 less intermediate value storage (bf16 write->read overhead)
|
| 287 |
+
# 2. Mathematically equivalent to k*(1 + (a-1)*self.k_a)
|
| 288 |
+
# but with better precision preservation
|
| 289 |
+
# 3. Particularly crucial for bf16 where intermediate values easily lose precision
|
| 290 |
+
# 4. Pytorch method: k = k.addcmul(k * (a - 1), self.k_a)
|
| 291 |
+
k = fused_k_rwkv7(k, a, self.k_a)
|
| 292 |
+
|
| 293 |
+
# dealing with left-padding
|
| 294 |
+
if attention_mask is not None:
|
| 295 |
+
v = v * am
|
| 296 |
+
|
| 297 |
+
r, w, k, a = map(lambda x: rearrange(x, 'b t (h d) -> b t h d', d=self.head_dim), (r, w, k, a))
|
| 298 |
+
v = rearrange(v, 'b t (h d) -> b t h d', d=self.head_v_dim)
|
| 299 |
+
|
| 300 |
+
if self.training or seq_len >= 64:
|
| 301 |
+
# if training, use chunk mode no matter how short the sequence is
|
| 302 |
+
# launching the triton kernel for just one token will actually be slower
|
| 303 |
+
o, recurrent_state = chunk_rwkv7(
|
| 304 |
+
r=r,
|
| 305 |
+
w=w,
|
| 306 |
+
k=k,
|
| 307 |
+
v=v,
|
| 308 |
+
a=-kk,
|
| 309 |
+
b=kk * a,
|
| 310 |
+
scale=1.,
|
| 311 |
+
initial_state=recurrent_state,
|
| 312 |
+
output_final_state=use_cache,
|
| 313 |
+
cu_seqlens=cu_seqlens,
|
| 314 |
+
safe_gate=True,
|
| 315 |
+
chunk_size=64,
|
| 316 |
+
)
|
| 317 |
+
else:
|
| 318 |
+
o, recurrent_state = fused_mul_recurrent_rwkv7(
|
| 319 |
+
r=r,
|
| 320 |
+
w=w,
|
| 321 |
+
k=k,
|
| 322 |
+
v=v,
|
| 323 |
+
kk=kk,
|
| 324 |
+
a=a,
|
| 325 |
+
scale=1.,
|
| 326 |
+
initial_state=recurrent_state,
|
| 327 |
+
output_final_state=use_cache,
|
| 328 |
+
cu_seqlens=cu_seqlens,
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
update_layer_cache(
|
| 332 |
+
self,
|
| 333 |
+
past_key_values,
|
| 334 |
+
recurrent_state=recurrent_state,
|
| 335 |
+
conv_state=conv_state,
|
| 336 |
+
offset=r.shape[1],
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
if self.fuse_norm:
|
| 340 |
+
o = self.g_norm(rearrange(o, '... h d -> ... (h d)'))
|
| 341 |
+
else:
|
| 342 |
+
o = self.g_norm(rearrange(o, 'b t h d -> (b t) (h d)')).view(batch_size, seq_len, -1)
|
| 343 |
+
|
| 344 |
+
o = gate_output_correction(o, r, k, self.r_k, v, g)
|
| 345 |
+
o = self.o_proj(o)
|
| 346 |
+
|
| 347 |
+
return o, None, past_key_values, v_first
|
fla/layers/simple_gla.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
|
| 12 |
+
from fla.layers.utils import get_layer_cache, update_layer_cache
|
| 13 |
+
from fla.modules import FusedRMSNormGated, RMSNorm, ShortConvolution
|
| 14 |
+
from fla.modules.activations import ACT2FN
|
| 15 |
+
from fla.ops.simple_gla import chunk_simple_gla, fused_recurrent_simple_gla
|
| 16 |
+
|
| 17 |
+
if TYPE_CHECKING:
|
| 18 |
+
from fla.models.utils import Cache
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class SimpleGatedLinearAttention(nn.Module):
|
| 22 |
+
r"""
|
| 23 |
+
The layer implementaion for [Gated Linear Attention Transformers with Hardware-Efficient Training](https://arxiv.org/abs/2312.06635). # noqa
|
| 24 |
+
This layer calls the simplified GLA kernel in which the gating is head-wise instead of elementwise.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
mode (str, Optional):
|
| 28 |
+
Which GLA kernel to use.
|
| 29 |
+
Currently available: `chunk`.
|
| 30 |
+
Default: `chunk`.
|
| 31 |
+
hidden_size (int, Optional):
|
| 32 |
+
The hidden size of the input. Default: 1024.
|
| 33 |
+
expand_k (float, Optional):
|
| 34 |
+
The expansion ratio for the key dim. Default: 1.0.
|
| 35 |
+
expand_v (float, Optional):
|
| 36 |
+
The expansion ratio for the value dim. Default: 1.0.
|
| 37 |
+
num_heads (int, Optional):
|
| 38 |
+
The number of heads. Default: 4.
|
| 39 |
+
num_kv_heads (int, Optional):
|
| 40 |
+
The number of key/value heads, used for MQA. Default: None.
|
| 41 |
+
feature_map (str, Optional):
|
| 42 |
+
Feature map function applied to queries/keys. Default: None.
|
| 43 |
+
use_short_conv (bool, Optional):
|
| 44 |
+
Whether to use short convolutions. Default: `False`.
|
| 45 |
+
conv_size (int, Optional):
|
| 46 |
+
The kernel size of the short convolution, only used when `use_short_conv` is `True`. Default: 4.
|
| 47 |
+
conv_bias (bool, Optional):
|
| 48 |
+
Whether to use bias in the short convolution, only used when `use_short_conv` is `True`. Default: `False`.
|
| 49 |
+
gate_fn (str, Optional):
|
| 50 |
+
The activation function for the output gate. Default: `swish`.
|
| 51 |
+
elementwise_affine (bool, Optional):
|
| 52 |
+
If `True`, applies elementwise affine to LayerNorm with learnable parameters. Default: `True`.
|
| 53 |
+
norm_eps (float, Optional):
|
| 54 |
+
The epsilon value for the layernorm/rmsnorm layer. Default: 1e-5.
|
| 55 |
+
gate_logit_normalizer (int, Optional):
|
| 56 |
+
The normalizer for the gate logits, appied after `logsigmoid`. Default: 16.
|
| 57 |
+
fuse_norm (bool, Optional):
|
| 58 |
+
Whether to fuse the norm and the output gate for better memory footprint. Default: `True`.
|
| 59 |
+
layer_idx (int, Optional):
|
| 60 |
+
The index of the layer. Default: None.
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
def __init__(
|
| 64 |
+
self,
|
| 65 |
+
mode: str = 'chunk',
|
| 66 |
+
hidden_size: int = 1024,
|
| 67 |
+
expand_k: float = 1.,
|
| 68 |
+
expand_v: float = 1.,
|
| 69 |
+
num_heads: int = 4,
|
| 70 |
+
num_kv_heads: int | None = None,
|
| 71 |
+
feature_map: str | None = None,
|
| 72 |
+
use_short_conv: bool = True,
|
| 73 |
+
conv_size: int = 4,
|
| 74 |
+
conv_bias: bool = False,
|
| 75 |
+
gate_fn: str = 'swish',
|
| 76 |
+
elementwise_affine: bool | None = True,
|
| 77 |
+
norm_eps: float = 1e-5,
|
| 78 |
+
gate_logit_normalizer: int = 16,
|
| 79 |
+
fuse_norm: bool = True,
|
| 80 |
+
layer_idx: int = None,
|
| 81 |
+
) -> SimpleGatedLinearAttention:
|
| 82 |
+
super().__init__()
|
| 83 |
+
|
| 84 |
+
self.mode = mode
|
| 85 |
+
self.hidden_size = hidden_size
|
| 86 |
+
self.expand_k = expand_k
|
| 87 |
+
self.expand_v = expand_v
|
| 88 |
+
self.num_heads = num_heads
|
| 89 |
+
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
| 90 |
+
self.num_kv_groups = self.num_heads // self.num_kv_heads
|
| 91 |
+
self.feature_map_fn = ACT2FN[feature_map] if feature_map is not None else None
|
| 92 |
+
|
| 93 |
+
self.use_short_conv = use_short_conv
|
| 94 |
+
self.conv_size = conv_size
|
| 95 |
+
self.conv_bias = conv_bias
|
| 96 |
+
|
| 97 |
+
self.key_dim = int(hidden_size * expand_k)
|
| 98 |
+
self.value_dim = int(hidden_size * expand_v)
|
| 99 |
+
self.key_dim_per_group = self.key_dim // self.num_kv_groups
|
| 100 |
+
self.value_dim_per_group = self.value_dim // self.num_kv_groups
|
| 101 |
+
self.layer_idx = layer_idx
|
| 102 |
+
|
| 103 |
+
assert mode in ['chunk', "fused_recurrent"], f"Not supported mode `{mode}`."
|
| 104 |
+
assert self.key_dim % num_heads == 0, f"key dim must be divisible by num_heads of {num_heads}"
|
| 105 |
+
assert self.value_dim % num_heads == 0, f"value dim must be divisible by num_heads of {num_heads}"
|
| 106 |
+
|
| 107 |
+
self.head_k_dim = self.key_dim // num_heads
|
| 108 |
+
self.head_v_dim = self.value_dim // num_heads
|
| 109 |
+
|
| 110 |
+
self.q_proj = nn.Linear(hidden_size, self.key_dim, bias=False)
|
| 111 |
+
self.k_proj = nn.Linear(hidden_size, self.key_dim_per_group, bias=False)
|
| 112 |
+
self.v_proj = nn.Linear(hidden_size, self.value_dim_per_group, bias=False)
|
| 113 |
+
self.g_proj = nn.Linear(hidden_size, self.value_dim, bias=False)
|
| 114 |
+
|
| 115 |
+
if use_short_conv:
|
| 116 |
+
self.conv_size = conv_size
|
| 117 |
+
self.q_conv1d = ShortConvolution(
|
| 118 |
+
hidden_size=self.key_dim,
|
| 119 |
+
kernel_size=conv_size,
|
| 120 |
+
bias=conv_bias,
|
| 121 |
+
activation='silu',
|
| 122 |
+
)
|
| 123 |
+
self.k_conv1d = ShortConvolution(
|
| 124 |
+
hidden_size=self.key_dim_per_group,
|
| 125 |
+
kernel_size=conv_size,
|
| 126 |
+
bias=conv_bias,
|
| 127 |
+
activation='silu',
|
| 128 |
+
)
|
| 129 |
+
self.v_conv1d = ShortConvolution(
|
| 130 |
+
hidden_size=self.value_dim_per_group,
|
| 131 |
+
kernel_size=conv_size,
|
| 132 |
+
bias=conv_bias,
|
| 133 |
+
activation='silu',
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
self.gk_proj = nn.Linear(hidden_size, self.num_heads)
|
| 137 |
+
|
| 138 |
+
if gate_fn == 'swish' and fuse_norm:
|
| 139 |
+
self.g_norm_swish_gate = FusedRMSNormGated(
|
| 140 |
+
hidden_size=self.head_v_dim,
|
| 141 |
+
elementwise_affine=elementwise_affine,
|
| 142 |
+
eps=norm_eps,
|
| 143 |
+
)
|
| 144 |
+
self.fuse_norm_and_gate = True
|
| 145 |
+
else:
|
| 146 |
+
self.fuse_norm_and_gate = False
|
| 147 |
+
self.g_norm = RMSNorm(
|
| 148 |
+
hidden_size=self.head_v_dim,
|
| 149 |
+
elementwise_affine=elementwise_affine,
|
| 150 |
+
eps=norm_eps,
|
| 151 |
+
dtype=torch.float32
|
| 152 |
+
)
|
| 153 |
+
self.gate_fn = ACT2FN[gate_fn]
|
| 154 |
+
self.o_proj = nn.Linear(self.value_dim, hidden_size, bias=False)
|
| 155 |
+
|
| 156 |
+
self.gate_logit_normalizer = gate_logit_normalizer
|
| 157 |
+
|
| 158 |
+
def forward(
|
| 159 |
+
self,
|
| 160 |
+
hidden_states: torch.Tensor,
|
| 161 |
+
attention_mask: torch.Tensor | None = None,
|
| 162 |
+
past_key_values: Cache | None = None,
|
| 163 |
+
use_cache: bool | None = False,
|
| 164 |
+
output_attentions: bool | None = False,
|
| 165 |
+
**kwargs,
|
| 166 |
+
) -> tuple[torch.Tensor, torch.Tensor | None, Cache | None]:
|
| 167 |
+
if attention_mask is not None:
|
| 168 |
+
assert len(attention_mask.shape) == 2, (
|
| 169 |
+
"Expected attention_mask as a 0-1 matrix with shape [batch_size, seq_len] "
|
| 170 |
+
"for padding purposes (0 indicating padding). "
|
| 171 |
+
"Arbitrary attention masks of shape [batch_size, seq_len, seq_len] are not allowed."
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
# launching the triton kernel for just one token will actually be slower
|
| 175 |
+
mode = 'fused_recurrent' if hidden_states.shape[1] <= 64 else self.mode
|
| 176 |
+
|
| 177 |
+
last_state = get_layer_cache(self, past_key_values)
|
| 178 |
+
|
| 179 |
+
cu_seqlens = kwargs.get('cu_seqlens')
|
| 180 |
+
if self.use_short_conv:
|
| 181 |
+
conv_state_q, conv_state_k, conv_state_v = None, None, None
|
| 182 |
+
if last_state is not None:
|
| 183 |
+
conv_state_q, conv_state_k, conv_state_v = last_state['conv_state']
|
| 184 |
+
conv_mask = attention_mask[:, -hidden_states.shape[1]:] if attention_mask is not None else None
|
| 185 |
+
q, conv_state_q = self.q_conv1d(
|
| 186 |
+
x=self.q_proj(hidden_states),
|
| 187 |
+
mask=conv_mask,
|
| 188 |
+
cache=conv_state_q,
|
| 189 |
+
output_final_state=use_cache,
|
| 190 |
+
cu_seqlens=cu_seqlens,
|
| 191 |
+
)
|
| 192 |
+
k, conv_state_k = self.k_conv1d(
|
| 193 |
+
x=self.k_proj(hidden_states),
|
| 194 |
+
mask=conv_mask,
|
| 195 |
+
cache=conv_state_k,
|
| 196 |
+
output_final_state=use_cache,
|
| 197 |
+
cu_seqlens=cu_seqlens,
|
| 198 |
+
)
|
| 199 |
+
v, conv_state_v = self.v_conv1d(
|
| 200 |
+
x=self.v_proj(hidden_states),
|
| 201 |
+
mask=conv_mask,
|
| 202 |
+
cache=conv_state_v,
|
| 203 |
+
output_final_state=use_cache,
|
| 204 |
+
cu_seqlens=cu_seqlens,
|
| 205 |
+
)
|
| 206 |
+
else:
|
| 207 |
+
q = self.q_proj(hidden_states)
|
| 208 |
+
k = self.k_proj(hidden_states)
|
| 209 |
+
v = self.v_proj(hidden_states)
|
| 210 |
+
gk = self.gk_proj(hidden_states)
|
| 211 |
+
|
| 212 |
+
if self.feature_map_fn is not None:
|
| 213 |
+
q, k = map(self.feature_map_fn, (q, k))
|
| 214 |
+
# dealing with left-padding
|
| 215 |
+
if attention_mask is not None:
|
| 216 |
+
v = v.mul_(attention_mask[:, -v.shape[-2]:, None])
|
| 217 |
+
q = rearrange(q, '... (h d) -> ... h d', h=self.num_heads)
|
| 218 |
+
if self.num_kv_groups > 1:
|
| 219 |
+
k, v = (repeat(x, '... (h d) -> ... (h g) d', h=self.num_kv_heads, g=self.num_kv_groups) for x in (k, v))
|
| 220 |
+
else:
|
| 221 |
+
k, v = (rearrange(x, '... (h d) -> ... h d', h=self.num_kv_heads) for x in (k, v))
|
| 222 |
+
gk = F.logsigmoid(gk) / self.gate_logit_normalizer
|
| 223 |
+
|
| 224 |
+
recurrent_state = last_state['recurrent_state'] if last_state is not None else None
|
| 225 |
+
if mode == 'chunk':
|
| 226 |
+
o, recurrent_state = chunk_simple_gla(
|
| 227 |
+
q=q,
|
| 228 |
+
k=k,
|
| 229 |
+
v=v,
|
| 230 |
+
g=gk,
|
| 231 |
+
initial_state=recurrent_state,
|
| 232 |
+
output_final_state=use_cache,
|
| 233 |
+
cu_seqlens=cu_seqlens,
|
| 234 |
+
)
|
| 235 |
+
elif mode == 'fused_recurrent':
|
| 236 |
+
o, recurrent_state = fused_recurrent_simple_gla(
|
| 237 |
+
q=q,
|
| 238 |
+
k=k,
|
| 239 |
+
v=v,
|
| 240 |
+
g=gk,
|
| 241 |
+
initial_state=recurrent_state,
|
| 242 |
+
output_final_state=use_cache,
|
| 243 |
+
cu_seqlens=cu_seqlens,
|
| 244 |
+
)
|
| 245 |
+
else:
|
| 246 |
+
raise NotImplementedError(f"Not supported mode `{mode}`.")
|
| 247 |
+
|
| 248 |
+
update_layer_cache(
|
| 249 |
+
self,
|
| 250 |
+
past_key_values,
|
| 251 |
+
recurrent_state=recurrent_state,
|
| 252 |
+
conv_state=(conv_state_q, conv_state_k, conv_state_v) if self.use_short_conv else None,
|
| 253 |
+
offset=q.shape[1],
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
g = self.g_proj(hidden_states)
|
| 257 |
+
if self.fuse_norm_and_gate:
|
| 258 |
+
g = rearrange(g, 'b t (h d) -> b t h d', h=self.num_heads)
|
| 259 |
+
o = self.g_norm_swish_gate(o, g)
|
| 260 |
+
o = rearrange(o, 'b t h d -> b t (h d)')
|
| 261 |
+
else:
|
| 262 |
+
o = rearrange(self.g_norm(o), 'b t h d -> b t (h d)')
|
| 263 |
+
o = o * self.gate_fn(g)
|
| 264 |
+
o = self.o_proj(o)
|
| 265 |
+
|
| 266 |
+
return o, None, past_key_values
|
| 267 |
+
|
| 268 |
+
def state_size(self, **kwargs) -> int:
|
| 269 |
+
state_size = self.key_dim * self.head_v_dim
|
| 270 |
+
for module in self.children():
|
| 271 |
+
if isinstance(module, ShortConvolution):
|
| 272 |
+
state_size += module.state_size
|
| 273 |
+
return state_size
|
fla/layers/utils.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
# Code is adapted from flash-attn.bert_padding.py
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from einops import rearrange, repeat
|
| 8 |
+
|
| 9 |
+
from fla.ops.utils.index import prepare_cu_seqlens_from_mask, prepare_lens_from_mask
|
| 10 |
+
from fla.utils import tensor_cache
|
| 11 |
+
|
| 12 |
+
_LAYER_IDX_REQUIRED_MSG = "{cls} requires `layer_idx` when `past_key_values` is provided."
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class IndexFirstAxis(torch.autograd.Function):
|
| 16 |
+
|
| 17 |
+
@staticmethod
|
| 18 |
+
def forward(ctx, x, indices):
|
| 19 |
+
ctx.save_for_backward(indices)
|
| 20 |
+
assert x.ndim >= 2
|
| 21 |
+
ctx.first_axis_dim, other_shape = x.shape[0], x.shape[1:]
|
| 22 |
+
second_dim = other_shape.numel()
|
| 23 |
+
# TD [2022-03-04] For some reason torch.gather is a bit faster than indexing.
|
| 24 |
+
# return x[indices]
|
| 25 |
+
return torch.gather(
|
| 26 |
+
rearrange(x, "b ... -> b (...)"), 0, repeat(indices, "z -> z d", d=second_dim),
|
| 27 |
+
).reshape(-1, *other_shape)
|
| 28 |
+
|
| 29 |
+
@staticmethod
|
| 30 |
+
def backward(ctx, do):
|
| 31 |
+
(indices,) = ctx.saved_tensors
|
| 32 |
+
assert do.ndim >= 2
|
| 33 |
+
other_shape = do.shape[1:]
|
| 34 |
+
do = rearrange(do, "b ... -> b (...)")
|
| 35 |
+
dx = torch.zeros(
|
| 36 |
+
[ctx.first_axis_dim, do.shape[1]],
|
| 37 |
+
device=do.device,
|
| 38 |
+
dtype=do.dtype,
|
| 39 |
+
)
|
| 40 |
+
# TD [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
|
| 41 |
+
# dx[indices] = do
|
| 42 |
+
dx.scatter_(0, repeat(indices, "z -> z d", d=do.shape[1]), do)
|
| 43 |
+
return dx.reshape(ctx.first_axis_dim, *other_shape), None
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
index_first_axis = IndexFirstAxis.apply
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class IndexPutFirstAxis(torch.autograd.Function):
|
| 50 |
+
|
| 51 |
+
@staticmethod
|
| 52 |
+
def forward(ctx, x, indices, first_axis_dim):
|
| 53 |
+
ctx.save_for_backward(indices)
|
| 54 |
+
assert indices.ndim == 1
|
| 55 |
+
assert x.ndim >= 2
|
| 56 |
+
y = torch.zeros(first_axis_dim, *x.shape[1:], device=x.device, dtype=x.dtype)
|
| 57 |
+
# TODO [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
|
| 58 |
+
y[indices] = x
|
| 59 |
+
# y.scatter_(0, repeat(indices, 'z -> z d', d=x.shape[1]), x)
|
| 60 |
+
return y
|
| 61 |
+
|
| 62 |
+
@staticmethod
|
| 63 |
+
def backward(ctx, do):
|
| 64 |
+
(indices,) = ctx.saved_tensors
|
| 65 |
+
# TODO [2022-03-04] For some reason torch.gather is a bit faster than indexing.
|
| 66 |
+
dx = do[indices]
|
| 67 |
+
# dx = torch.gather(do, 0, repeat(indices, 'z -> z d', d=do.shape[1]))
|
| 68 |
+
return dx, None, None
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
index_put_first_axis = IndexPutFirstAxis.apply
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
@tensor_cache
|
| 75 |
+
def get_unpad_data(
|
| 76 |
+
attention_mask: torch.Tensor,
|
| 77 |
+
) -> tuple[torch.Tensor, torch.Tensor, int]:
|
| 78 |
+
"""
|
| 79 |
+
Retrieves indexing data required to repad unpadded (ragged) tensors.
|
| 80 |
+
|
| 81 |
+
Args:
|
| 82 |
+
attention_mask (`torch.Tensor`):
|
| 83 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 84 |
+
|
| 85 |
+
Return:
|
| 86 |
+
indices (`torch.Tensor`):
|
| 87 |
+
The indices of non-masked tokens from the flattened input sequence.
|
| 88 |
+
cu_seqlens (`torch.Tensor`):
|
| 89 |
+
The cumulative sequence lengths, used to index into ragged (unpadded) tensors.
|
| 90 |
+
`cu_seqlens` shape is [batch_size + 1].
|
| 91 |
+
max_seqlen_in_batch (`int`):
|
| 92 |
+
Maximum sequence length in batch.
|
| 93 |
+
"""
|
| 94 |
+
lens = prepare_lens_from_mask(attention_mask)
|
| 95 |
+
indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
|
| 96 |
+
max_seqlen_in_batch = lens.max().item()
|
| 97 |
+
cu_seqlens = prepare_cu_seqlens_from_mask(attention_mask)
|
| 98 |
+
return indices, cu_seqlens, max_seqlen_in_batch
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def unpad_input(
|
| 102 |
+
q: torch.Tensor,
|
| 103 |
+
states: tuple[torch.Tensor],
|
| 104 |
+
attention_mask: torch.Tensor,
|
| 105 |
+
q_len: int,
|
| 106 |
+
keepdim: bool = False,
|
| 107 |
+
):
|
| 108 |
+
"""
|
| 109 |
+
Unpads query, key, and values tensors, using a single dimension for all tokens
|
| 110 |
+
even though they belong to different batches.
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
Arguments:
|
| 114 |
+
q (`torch.Tensor`):
|
| 115 |
+
Query state with padding. Shape: [batch_size, q_len, ...].
|
| 116 |
+
states (`Tuple[torch.Tensor]`):
|
| 117 |
+
Attention state with padding. Shape: [batch_size, seq_len, ...].
|
| 118 |
+
attention_mask (`torch.Tensor`):
|
| 119 |
+
Boolean or int tensor of shape [batch_size, sequence_length], 1 means valid and 0 means not valid.
|
| 120 |
+
q_len (`int`):
|
| 121 |
+
Target length.
|
| 122 |
+
keepdim (`bool`):
|
| 123 |
+
Whether to keep the batch dimension. Default: `False`.
|
| 124 |
+
|
| 125 |
+
Return:
|
| 126 |
+
q (`torch.Tensor`):
|
| 127 |
+
Query state without padding.
|
| 128 |
+
Shape: [1, total_target_length, ...] if `keepdim=True` else [total_target_length, ...].
|
| 129 |
+
states (`Tuple[torch.Tensor]`):
|
| 130 |
+
Attention state without padding.
|
| 131 |
+
Shape: [1, total_source_length, ...] if `keepdim=True` else [total_source_length, ...].
|
| 132 |
+
indices_q (`torch.Tensor`):
|
| 133 |
+
The indices of non-masked tokens from the flattened input target sequence.
|
| 134 |
+
(cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
|
| 135 |
+
The cumulative sequence lengths for the target (query) and source (key, value),
|
| 136 |
+
used to index into ragged (unpadded) tensors.
|
| 137 |
+
`cu_seqlens` shape is [batch_size + 1].
|
| 138 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
|
| 139 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence
|
| 140 |
+
i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 141 |
+
"""
|
| 142 |
+
indices_k, cu_seqlens_k, max_seqlen_in_batch_k = get_unpad_data(attention_mask)
|
| 143 |
+
batch_size, seq_len, *_ = states[0].shape
|
| 144 |
+
|
| 145 |
+
state = tuple(
|
| 146 |
+
index_first_axis(rearrange(s, "b s ... -> (b s) ..."), indices_k)
|
| 147 |
+
for s in states
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
if q_len == seq_len:
|
| 151 |
+
q = index_first_axis(rearrange(q, "b s ... -> (b s) ..."), indices_k)
|
| 152 |
+
cu_seqlens_q = cu_seqlens_k
|
| 153 |
+
max_seqlen_in_batch_q = max_seqlen_in_batch_k
|
| 154 |
+
indices_q = indices_k
|
| 155 |
+
elif q_len == 1:
|
| 156 |
+
max_seqlen_in_batch_q = 1
|
| 157 |
+
cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
|
| 158 |
+
indices_q = cu_seqlens_q[:-1]
|
| 159 |
+
q = q.squeeze(1)
|
| 160 |
+
else:
|
| 161 |
+
raise NotImplementedError("We only support either q_len == k_len (prefilling) or q_len == 1 (decoding)")
|
| 162 |
+
|
| 163 |
+
if keepdim:
|
| 164 |
+
q = q.unsqueeze(0)
|
| 165 |
+
state = tuple(s.unsqueeze(0) for s in state)
|
| 166 |
+
|
| 167 |
+
return (
|
| 168 |
+
q,
|
| 169 |
+
state,
|
| 170 |
+
indices_q,
|
| 171 |
+
(cu_seqlens_q, cu_seqlens_k),
|
| 172 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k),
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def pad_input(
|
| 177 |
+
hidden_states: torch.Tensor,
|
| 178 |
+
indices: torch.LongTensor,
|
| 179 |
+
batch_size: int,
|
| 180 |
+
seq_len: int,
|
| 181 |
+
) -> torch.Tensor:
|
| 182 |
+
"""
|
| 183 |
+
Args:
|
| 184 |
+
hidden_states ([total_tokens, ...]):
|
| 185 |
+
where total_tokens denotes the number of tokens in selected in attention_mask.
|
| 186 |
+
indices ([total_tokens]):
|
| 187 |
+
the indices that represent the non-masked tokens of the original padded input sequence.
|
| 188 |
+
batch_size (int):
|
| 189 |
+
batch_size size for the padded sequence.
|
| 190 |
+
seq_len (int):
|
| 191 |
+
maximum sequence length for the padded sequence.
|
| 192 |
+
|
| 193 |
+
Return:
|
| 194 |
+
hidden_states of shape [batch_size, seq_len, ...]
|
| 195 |
+
"""
|
| 196 |
+
output = index_put_first_axis(hidden_states, indices, batch_size * seq_len)
|
| 197 |
+
return rearrange(output, "(b s) ... -> b s ...", b=batch_size)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def require_cache_layer_idx(module, past_key_values):
|
| 201 |
+
layer_idx = getattr(module, "layer_idx", None)
|
| 202 |
+
if past_key_values is not None and layer_idx is None:
|
| 203 |
+
raise ValueError(_LAYER_IDX_REQUIRED_MSG.format(cls=module.__class__.__name__))
|
| 204 |
+
return layer_idx
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def get_layer_cache(module, past_key_values):
|
| 208 |
+
layer_idx = require_cache_layer_idx(module, past_key_values)
|
| 209 |
+
if past_key_values is not None and len(past_key_values) > layer_idx:
|
| 210 |
+
return past_key_values[layer_idx]
|
| 211 |
+
return None
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def update_layer_cache(module, past_key_values, **kwargs):
|
| 215 |
+
layer_idx = require_cache_layer_idx(module, past_key_values)
|
| 216 |
+
if past_key_values is not None:
|
| 217 |
+
return past_key_values.update(layer_idx=layer_idx, **kwargs)
|
| 218 |
+
return None
|
fla/models/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep model package imports lazy.
|
| 2 |
+
__all__ = []
|
| 3 |
+
|
fla/models/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (179 Bytes). View file
|
|
|
fla/models/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (23.1 kB). View file
|
|
|
fla/models/abc/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 3 |
+
|
| 4 |
+
from fla.models.abc.configuration_abc import ABCConfig
|
| 5 |
+
from fla.models.abc.modeling_abc import ABCForCausalLM, ABCModel
|
| 6 |
+
|
| 7 |
+
AutoConfig.register(ABCConfig.model_type, ABCConfig, exist_ok=True)
|
| 8 |
+
AutoModel.register(ABCConfig, ABCModel, exist_ok=True)
|
| 9 |
+
AutoModelForCausalLM.register(ABCConfig, ABCForCausalLM, exist_ok=True)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
__all__ = ['ABCConfig', 'ABCForCausalLM', 'ABCModel']
|
fla/models/abc/configuration_abc.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import warnings
|
| 3 |
+
|
| 4 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class ABCConfig(PretrainedConfig):
|
| 8 |
+
|
| 9 |
+
model_type = 'abc'
|
| 10 |
+
keys_to_ignore_at_inference = ['past_key_values']
|
| 11 |
+
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
hidden_size: int = 2048,
|
| 15 |
+
gate_low_rank_dim: int = 16,
|
| 16 |
+
clamp_min: float = -32,
|
| 17 |
+
clamp_max: float = 32,
|
| 18 |
+
hidden_ratio: int | None = 4,
|
| 19 |
+
intermediate_size: int | None = None,
|
| 20 |
+
num_hidden_layers: int = 24,
|
| 21 |
+
num_heads: int = 4,
|
| 22 |
+
num_slots: int | None = 64,
|
| 23 |
+
use_short_conv: bool = False,
|
| 24 |
+
conv_size: int = 4,
|
| 25 |
+
exapnd_k: float = 0.5,
|
| 26 |
+
exapnd_v: float = 1,
|
| 27 |
+
hidden_act: str = "swish",
|
| 28 |
+
max_position_embeddings: int = 2048,
|
| 29 |
+
elementwise_affine: bool | None = True,
|
| 30 |
+
norm_eps: float = 1e-6,
|
| 31 |
+
use_rope: bool = True,
|
| 32 |
+
attn: dict | None = None,
|
| 33 |
+
use_cache: bool = True,
|
| 34 |
+
pad_token_id: int | None = None,
|
| 35 |
+
bos_token_id: int = 1,
|
| 36 |
+
eos_token_id: int = 2,
|
| 37 |
+
tie_word_embeddings: bool = False,
|
| 38 |
+
initializer_range: float = 0.02,
|
| 39 |
+
fuse_norm: bool = True,
|
| 40 |
+
fuse_swiglu: bool = True,
|
| 41 |
+
fuse_cross_entropy: bool = True,
|
| 42 |
+
fuse_linear_cross_entropy: bool = False,
|
| 43 |
+
use_l2warp: bool = False,
|
| 44 |
+
vocab_size: int = 32000,
|
| 45 |
+
**kwargs,
|
| 46 |
+
):
|
| 47 |
+
self.hidden_size = hidden_size
|
| 48 |
+
self.gate_low_rank_dim = gate_low_rank_dim
|
| 49 |
+
self.clamp_min = clamp_min
|
| 50 |
+
self.clamp_max = clamp_max
|
| 51 |
+
self.hidden_ratio = hidden_ratio
|
| 52 |
+
self.intermediate_size = intermediate_size
|
| 53 |
+
self.num_hidden_layers = num_hidden_layers
|
| 54 |
+
self.num_heads = num_heads
|
| 55 |
+
self.num_slots = num_slots
|
| 56 |
+
self.use_short_conv = use_short_conv
|
| 57 |
+
self.conv_size = conv_size
|
| 58 |
+
self.expand_k = exapnd_k
|
| 59 |
+
self.expand_v = exapnd_v
|
| 60 |
+
self.hidden_act = hidden_act
|
| 61 |
+
self.max_position_embeddings = max_position_embeddings
|
| 62 |
+
self.elementwise_affine = elementwise_affine
|
| 63 |
+
self.norm_eps = norm_eps
|
| 64 |
+
self.use_rope = use_rope
|
| 65 |
+
self.attn = attn
|
| 66 |
+
self.use_cache = use_cache
|
| 67 |
+
self.initializer_range = initializer_range
|
| 68 |
+
|
| 69 |
+
self.fuse_norm = fuse_norm
|
| 70 |
+
self.fuse_swiglu = fuse_swiglu
|
| 71 |
+
self.fuse_cross_entropy = fuse_cross_entropy
|
| 72 |
+
self.fuse_linear_cross_entropy = fuse_linear_cross_entropy
|
| 73 |
+
self.use_l2warp = use_l2warp
|
| 74 |
+
self.vocab_size = vocab_size
|
| 75 |
+
|
| 76 |
+
if fuse_cross_entropy and fuse_linear_cross_entropy:
|
| 77 |
+
raise ValueError(
|
| 78 |
+
"`fuse_cross_entropy` and `fuse_linear_cross_entropy` cannot be True at the same time.",
|
| 79 |
+
)
|
| 80 |
+
if fuse_linear_cross_entropy:
|
| 81 |
+
warnings.warn(
|
| 82 |
+
"`fuse_linear_cross_entropy` is enabled, which can improves memory efficiency "
|
| 83 |
+
"at the potential cost of reduced precision. "
|
| 84 |
+
"If you observe issues like loss divergence, consider disabling this setting.",
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
if attn is not None:
|
| 88 |
+
if not isinstance(attn, dict):
|
| 89 |
+
raise ValueError("attn must be a dictionary")
|
| 90 |
+
if 'layers' not in attn:
|
| 91 |
+
raise ValueError("Layer indices must be provided to initialize hybrid attention layers")
|
| 92 |
+
if 'num_heads' not in attn:
|
| 93 |
+
raise ValueError("Number of heads must be provided to initialize hybrid attention layers")
|
| 94 |
+
attn['num_kv_heads'] = attn.get('num_kv_heads', attn['num_heads'])
|
| 95 |
+
attn['qkv_bias'] = attn.get('qkv_bias', False)
|
| 96 |
+
attn['window_size'] = attn.get('window_size', None)
|
| 97 |
+
attn['rope_theta'] = attn.get('rope_theta', 10000.)
|
| 98 |
+
|
| 99 |
+
super().__init__(
|
| 100 |
+
pad_token_id=pad_token_id,
|
| 101 |
+
bos_token_id=bos_token_id,
|
| 102 |
+
eos_token_id=eos_token_id,
|
| 103 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 104 |
+
**kwargs,
|
| 105 |
+
)
|
fla/models/abc/modeling_abc.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import warnings
|
| 5 |
+
from typing import TYPE_CHECKING, Optional
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
|
| 10 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 11 |
+
from transformers.utils import logging
|
| 12 |
+
from transformers.utils.deprecation import deprecate_kwarg
|
| 13 |
+
|
| 14 |
+
from fla.layers.abc import ABCAttention
|
| 15 |
+
from fla.layers.attn import Attention
|
| 16 |
+
from fla.models.abc.configuration_abc import ABCConfig
|
| 17 |
+
from fla.models.utils import Cache, FLAGenerationMixin
|
| 18 |
+
from fla.modules import FusedCrossEntropyLoss, FusedLinearCrossEntropyLoss, RMSNorm
|
| 19 |
+
from fla.modules import GatedMLP as ABCMLP
|
| 20 |
+
from fla.modules.l2warp import l2_warp
|
| 21 |
+
|
| 22 |
+
try:
|
| 23 |
+
from transformers.modeling_layers import GradientCheckpointingLayer
|
| 24 |
+
except ImportError:
|
| 25 |
+
from fla.models.modeling_layers import GradientCheckpointingLayer
|
| 26 |
+
|
| 27 |
+
logger = logging.get_logger(__name__)
|
| 28 |
+
|
| 29 |
+
if TYPE_CHECKING:
|
| 30 |
+
from transformers.processing_utils import Unpack
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class ABCBlock(GradientCheckpointingLayer):
|
| 34 |
+
|
| 35 |
+
def __init__(self, config: ABCConfig, layer_idx: int):
|
| 36 |
+
super().__init__()
|
| 37 |
+
|
| 38 |
+
self.config = config
|
| 39 |
+
self.layer_idx = layer_idx
|
| 40 |
+
|
| 41 |
+
self.attn_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 42 |
+
if config.attn is not None and layer_idx in config.attn['layers']:
|
| 43 |
+
self.attn = Attention(
|
| 44 |
+
hidden_size=config.hidden_size,
|
| 45 |
+
num_heads=config.attn['num_heads'],
|
| 46 |
+
num_kv_heads=config.attn['num_kv_heads'],
|
| 47 |
+
qkv_bias=config.attn['qkv_bias'],
|
| 48 |
+
window_size=config.attn['window_size'],
|
| 49 |
+
rope_theta=config.attn['rope_theta'],
|
| 50 |
+
max_position_embeddings=config.max_position_embeddings,
|
| 51 |
+
layer_idx=layer_idx,
|
| 52 |
+
)
|
| 53 |
+
else:
|
| 54 |
+
self.attn = ABCAttention(
|
| 55 |
+
hidden_size=config.hidden_size,
|
| 56 |
+
expand_k=config.expand_k,
|
| 57 |
+
expand_v=config.expand_v,
|
| 58 |
+
num_heads=config.num_heads,
|
| 59 |
+
num_slots=config.num_slots,
|
| 60 |
+
use_short_conv=config.use_short_conv,
|
| 61 |
+
conv_size=config.conv_size,
|
| 62 |
+
gate_fn=config.hidden_act,
|
| 63 |
+
elementwise_affine=config.elementwise_affine,
|
| 64 |
+
norm_eps=config.norm_eps,
|
| 65 |
+
use_rope=config.use_rope,
|
| 66 |
+
clamp_min=config.clamp_min,
|
| 67 |
+
clamp_max=config.clamp_max,
|
| 68 |
+
fuse_norm=config.fuse_norm,
|
| 69 |
+
layer_idx=layer_idx,
|
| 70 |
+
)
|
| 71 |
+
self.mlp_norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 72 |
+
self.mlp = ABCMLP(
|
| 73 |
+
hidden_size=config.hidden_size,
|
| 74 |
+
hidden_ratio=config.hidden_ratio,
|
| 75 |
+
intermediate_size=config.intermediate_size,
|
| 76 |
+
hidden_act=config.hidden_act,
|
| 77 |
+
fuse_swiglu=config.fuse_swiglu,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
def forward(
|
| 81 |
+
self,
|
| 82 |
+
hidden_states: torch.Tensor,
|
| 83 |
+
attention_mask: torch.Tensor | None = None,
|
| 84 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 85 |
+
use_cache: bool | None = False,
|
| 86 |
+
output_attentions: bool | None = False,
|
| 87 |
+
**kwargs: Unpack[dict],
|
| 88 |
+
) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
|
| 89 |
+
|
| 90 |
+
residual = hidden_states
|
| 91 |
+
|
| 92 |
+
hidden_states = self.attn_norm(hidden_states)
|
| 93 |
+
hidden_states, attentions, past_key_values = self.attn(
|
| 94 |
+
hidden_states=hidden_states,
|
| 95 |
+
attention_mask=attention_mask,
|
| 96 |
+
past_key_values=past_key_values,
|
| 97 |
+
use_cache=use_cache,
|
| 98 |
+
output_attentions=output_attentions,
|
| 99 |
+
**kwargs,
|
| 100 |
+
)
|
| 101 |
+
if self.config.fuse_norm:
|
| 102 |
+
hidden_states, residual = self.mlp_norm(hidden_states, residual, True)
|
| 103 |
+
else:
|
| 104 |
+
hidden_states = residual + hidden_states
|
| 105 |
+
residual = hidden_states
|
| 106 |
+
hidden_states = self.mlp_norm(hidden_states)
|
| 107 |
+
hidden_states = self.mlp(hidden_states)
|
| 108 |
+
hidden_states = residual + hidden_states
|
| 109 |
+
|
| 110 |
+
outputs = (hidden_states, attentions, past_key_values)
|
| 111 |
+
|
| 112 |
+
return outputs
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class ABCPreTrainedModel(PreTrainedModel):
|
| 116 |
+
|
| 117 |
+
config_class = ABCConfig
|
| 118 |
+
base_model_prefix = 'model'
|
| 119 |
+
supports_gradient_checkpointing = True
|
| 120 |
+
_no_split_modules = ['ABCBlock']
|
| 121 |
+
_supports_cache_class = True
|
| 122 |
+
|
| 123 |
+
def __init__(self, *inputs, **kwargs):
|
| 124 |
+
super().__init__(*inputs, **kwargs)
|
| 125 |
+
|
| 126 |
+
def _init_weights(
|
| 127 |
+
self,
|
| 128 |
+
module: nn.Module,
|
| 129 |
+
prenorm_residual_strategy: str | None = None,
|
| 130 |
+
num_residuals_per_layer: int = 2,
|
| 131 |
+
):
|
| 132 |
+
if isinstance(module, (nn.Linear, nn.Conv1d)):
|
| 133 |
+
# Slightly different from the TF version which uses truncated_normal for initialization
|
| 134 |
+
# cf https://github.com/pytorch/pytorch/pull/5617
|
| 135 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 136 |
+
if module.bias is not None:
|
| 137 |
+
nn.init.zeros_(module.bias)
|
| 138 |
+
elif isinstance(module, nn.Embedding):
|
| 139 |
+
nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
|
| 140 |
+
elif hasattr(module, 'reset_parameters'):
|
| 141 |
+
module.reset_parameters()
|
| 142 |
+
|
| 143 |
+
if prenorm_residual_strategy is not None:
|
| 144 |
+
# Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
|
| 145 |
+
# > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
|
| 146 |
+
# > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
|
| 147 |
+
# > -- GPT-2 :: https://openai.com/blog/better-language-models/
|
| 148 |
+
#
|
| 149 |
+
# Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
|
| 150 |
+
p = None
|
| 151 |
+
if hasattr(module, 'o_proj'):
|
| 152 |
+
p = module.o_proj.weight
|
| 153 |
+
elif hasattr(module, 'down_proj'):
|
| 154 |
+
p = module.down_proj.weight
|
| 155 |
+
if p is not None:
|
| 156 |
+
# Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
|
| 157 |
+
# Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
|
| 158 |
+
# We need to reinit p since this code could be called multiple times
|
| 159 |
+
# Having just p *= scale would repeatedly scale it down
|
| 160 |
+
if prenorm_residual_strategy == 'rescale':
|
| 161 |
+
nn.init.kaiming_uniform_(p, a=math.sqrt(5))
|
| 162 |
+
with torch.no_grad():
|
| 163 |
+
p /= math.sqrt(num_residuals_per_layer * self.config.num_hidden_layers)
|
| 164 |
+
elif prenorm_residual_strategy == 'zero':
|
| 165 |
+
nn.init.zeros_(p)
|
| 166 |
+
else:
|
| 167 |
+
raise ValueError(f"Invalid prenorm_residual_strategy: {prenorm_residual_strategy}")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
class ABCModel(ABCPreTrainedModel):
|
| 171 |
+
|
| 172 |
+
def __init__(self, config: ABCConfig):
|
| 173 |
+
super().__init__(config)
|
| 174 |
+
self.padding_idx = config.pad_token_id
|
| 175 |
+
self.vocab_size = config.vocab_size
|
| 176 |
+
|
| 177 |
+
self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
|
| 178 |
+
self.layers = nn.ModuleList([ABCBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
|
| 179 |
+
self.norm = (RMSNorm if config.fuse_norm else nn.RMSNorm)(config.hidden_size, eps=config.norm_eps)
|
| 180 |
+
|
| 181 |
+
self.gradient_checkpointing = False
|
| 182 |
+
|
| 183 |
+
self.post_init()
|
| 184 |
+
|
| 185 |
+
def get_input_embeddings(self):
|
| 186 |
+
return self.embeddings
|
| 187 |
+
|
| 188 |
+
def set_input_embeddings(self, value):
|
| 189 |
+
self.embeddings = value
|
| 190 |
+
|
| 191 |
+
def forward(
|
| 192 |
+
self,
|
| 193 |
+
input_ids: torch.LongTensor | None = None,
|
| 194 |
+
attention_mask: Optional[torch.Tensor] = None, # noqa
|
| 195 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 196 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 197 |
+
use_cache: bool | None = None,
|
| 198 |
+
output_attentions: bool | None = None,
|
| 199 |
+
output_hidden_states: bool | None = None,
|
| 200 |
+
return_dict: bool | None = None,
|
| 201 |
+
**kwargs: Unpack[dict],
|
| 202 |
+
) -> tuple | BaseModelOutputWithPast:
|
| 203 |
+
if output_attentions:
|
| 204 |
+
warnings.warn("`ABCModel` does not `output_attentions` now, setting it to `False`.")
|
| 205 |
+
output_attentions = False
|
| 206 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 207 |
+
output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 208 |
+
use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
|
| 209 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 210 |
+
|
| 211 |
+
# retrieve input_ids and inputs_embeds
|
| 212 |
+
if input_ids is not None and inputs_embeds is not None:
|
| 213 |
+
raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
|
| 214 |
+
if input_ids is None and inputs_embeds is None:
|
| 215 |
+
raise ValueError("You have to specify either input_ids or inputs_embeds")
|
| 216 |
+
|
| 217 |
+
if inputs_embeds is None:
|
| 218 |
+
inputs_embeds = self.embeddings(input_ids)
|
| 219 |
+
hidden_states = inputs_embeds
|
| 220 |
+
|
| 221 |
+
if use_cache and not isinstance(past_key_values, Cache):
|
| 222 |
+
past_key_values = Cache.from_legacy_cache(past_key_values)
|
| 223 |
+
|
| 224 |
+
all_hidden_states = () if output_hidden_states else None
|
| 225 |
+
all_attns = () if output_attentions else None
|
| 226 |
+
for layer in self.layers:
|
| 227 |
+
if output_hidden_states:
|
| 228 |
+
all_hidden_states += (hidden_states,)
|
| 229 |
+
|
| 230 |
+
hidden_states, attentions, past_key_values = layer(
|
| 231 |
+
hidden_states,
|
| 232 |
+
attention_mask,
|
| 233 |
+
past_key_values=past_key_values,
|
| 234 |
+
use_cache=use_cache,
|
| 235 |
+
output_attentions=output_attentions,
|
| 236 |
+
**kwargs,
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
if output_attentions:
|
| 240 |
+
all_attns += (attentions,)
|
| 241 |
+
|
| 242 |
+
hidden_states = self.norm(hidden_states)
|
| 243 |
+
|
| 244 |
+
# add hidden states from the last decoder layer
|
| 245 |
+
if output_hidden_states:
|
| 246 |
+
all_hidden_states += (hidden_states,)
|
| 247 |
+
|
| 248 |
+
if not return_dict:
|
| 249 |
+
return tuple(i for i in [hidden_states, past_key_values, all_hidden_states, all_attns] if i is not None)
|
| 250 |
+
return BaseModelOutputWithPast(
|
| 251 |
+
last_hidden_state=hidden_states,
|
| 252 |
+
past_key_values=past_key_values,
|
| 253 |
+
hidden_states=all_hidden_states,
|
| 254 |
+
attentions=all_attns,
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
class ABCForCausalLM(ABCPreTrainedModel, FLAGenerationMixin):
|
| 259 |
+
|
| 260 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 261 |
+
|
| 262 |
+
def __init__(self, config):
|
| 263 |
+
super().__init__(config)
|
| 264 |
+
self.model = ABCModel(config)
|
| 265 |
+
self.vocab_size = config.vocab_size
|
| 266 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 267 |
+
self.criterion = None
|
| 268 |
+
|
| 269 |
+
# Initialize weights and apply final processing
|
| 270 |
+
self.post_init()
|
| 271 |
+
|
| 272 |
+
def get_input_embeddings(self):
|
| 273 |
+
return self.model.embeddings
|
| 274 |
+
|
| 275 |
+
def set_input_embeddings(self, value):
|
| 276 |
+
self.model.embeddings = value
|
| 277 |
+
|
| 278 |
+
def get_output_embeddings(self):
|
| 279 |
+
return self.lm_head
|
| 280 |
+
|
| 281 |
+
def set_output_embeddings(self, new_embeddings):
|
| 282 |
+
self.lm_head = new_embeddings
|
| 283 |
+
|
| 284 |
+
def set_decoder(self, decoder):
|
| 285 |
+
self.model = decoder
|
| 286 |
+
|
| 287 |
+
def get_decoder(self):
|
| 288 |
+
return self.model
|
| 289 |
+
|
| 290 |
+
def generate(self, *args, **kwargs):
|
| 291 |
+
try:
|
| 292 |
+
return super().generate(*args, **kwargs)
|
| 293 |
+
except AttributeError as exception:
|
| 294 |
+
if 'past_key_values' in str(exception):
|
| 295 |
+
raise AttributeError(
|
| 296 |
+
f"You tried to call `generate` with a decoding strategy that manipulates `past_key_values`, "
|
| 297 |
+
f"which is not supported for {self.__class__.__name__}. "
|
| 298 |
+
f"Try another generation strategy instead. "
|
| 299 |
+
f"For the available generation strategies, check this doc: "
|
| 300 |
+
f"https://huggingface.co/docs/transformers/en/generation_strategies#decoding-strategies",
|
| 301 |
+
)
|
| 302 |
+
else:
|
| 303 |
+
raise exception
|
| 304 |
+
|
| 305 |
+
@deprecate_kwarg("num_logits_to_keep", version="4.50", new_name="logits_to_keep")
|
| 306 |
+
def forward(
|
| 307 |
+
self,
|
| 308 |
+
input_ids: torch.LongTensor = None,
|
| 309 |
+
attention_mask: torch.Tensor | None = None,
|
| 310 |
+
inputs_embeds: torch.Tensor | None = None,
|
| 311 |
+
past_key_values: Cache | list[torch.FloatTensor] | None = None,
|
| 312 |
+
labels: torch.LongTensor | None = None,
|
| 313 |
+
use_cache: bool | None = None,
|
| 314 |
+
output_attentions: bool | None = None,
|
| 315 |
+
output_hidden_states: bool | None = None,
|
| 316 |
+
return_dict: bool | None = None,
|
| 317 |
+
logits_to_keep: int | None = 0,
|
| 318 |
+
**kwargs: Unpack[dict],
|
| 319 |
+
) -> tuple | CausalLMOutputWithPast:
|
| 320 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 321 |
+
output_hidden_states = (
|
| 322 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 323 |
+
)
|
| 324 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 325 |
+
|
| 326 |
+
outputs = self.model(
|
| 327 |
+
input_ids=input_ids,
|
| 328 |
+
attention_mask=attention_mask,
|
| 329 |
+
inputs_embeds=inputs_embeds,
|
| 330 |
+
past_key_values=past_key_values,
|
| 331 |
+
use_cache=use_cache,
|
| 332 |
+
output_attentions=output_attentions,
|
| 333 |
+
output_hidden_states=output_hidden_states,
|
| 334 |
+
return_dict=return_dict,
|
| 335 |
+
**kwargs,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
hidden_states = outputs[0]
|
| 339 |
+
|
| 340 |
+
loss, logits = None, None
|
| 341 |
+
if not self.config.fuse_linear_cross_entropy or labels is None:
|
| 342 |
+
logits = self.lm_head(hidden_states if logits_to_keep is None else hidden_states[:, -logits_to_keep:])
|
| 343 |
+
if labels is not None:
|
| 344 |
+
if getattr(self, 'criterion', None) is None:
|
| 345 |
+
if self.config.fuse_linear_cross_entropy:
|
| 346 |
+
criterion = FusedLinearCrossEntropyLoss(use_l2warp=self.config.use_l2warp)
|
| 347 |
+
elif self.config.fuse_cross_entropy:
|
| 348 |
+
criterion = FusedCrossEntropyLoss(inplace_backward=True)
|
| 349 |
+
else:
|
| 350 |
+
criterion = nn.CrossEntropyLoss()
|
| 351 |
+
else:
|
| 352 |
+
criterion = self.criterion
|
| 353 |
+
labels = labels.to(hidden_states.device)
|
| 354 |
+
labels = torch.cat((labels[..., 1:], torch.full_like(labels[:, :1], criterion.ignore_index)), 1)
|
| 355 |
+
if self.config.fuse_linear_cross_entropy:
|
| 356 |
+
loss = criterion(hidden_states, labels, self.lm_head.weight, self.lm_head.bias)
|
| 357 |
+
else:
|
| 358 |
+
loss = criterion(logits.view(labels.numel(), -1), labels.view(-1))
|
| 359 |
+
loss = l2_warp(loss, logits) if self.config.use_l2warp else loss
|
| 360 |
+
|
| 361 |
+
if not return_dict:
|
| 362 |
+
output = (logits,) + outputs[1:]
|
| 363 |
+
return (loss,) + output if loss is not None else output
|
| 364 |
+
|
| 365 |
+
return CausalLMOutputWithPast(
|
| 366 |
+
loss=loss,
|
| 367 |
+
logits=logits,
|
| 368 |
+
past_key_values=outputs.past_key_values,
|
| 369 |
+
hidden_states=outputs.hidden_states,
|
| 370 |
+
attentions=outputs.attentions,
|
| 371 |
+
)
|
fla/models/bitnet/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 3 |
+
|
| 4 |
+
from fla.models.bitnet.configuration_bitnet import BitNetConfig
|
| 5 |
+
from fla.models.bitnet.modeling_bitnet import BitNetForCausalLM, BitNetModel
|
| 6 |
+
|
| 7 |
+
AutoConfig.register(BitNetConfig.model_type, BitNetConfig, exist_ok=True)
|
| 8 |
+
AutoModel.register(BitNetConfig, BitNetModel, exist_ok=True)
|
| 9 |
+
AutoModelForCausalLM.register(BitNetConfig, BitNetForCausalLM, exist_ok=True)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
__all__ = ['BitNetConfig', 'BitNetForCausalLM', 'BitNetModel']
|