ramnet 0.1.0rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ramnet/__init__.py +11 -0
- ramnet/layers/__init__.py +4 -0
- ramnet/layers/cache.py +140 -0
- ramnet/layers/ram_attn_block.py +351 -0
- ramnet/models/__init__.py +10 -0
- ramnet/models/config_ramnet.py +188 -0
- ramnet/models/modeling_ramnet.py +368 -0
- ramnet-0.1.0rc0.dist-info/METADATA +261 -0
- ramnet-0.1.0rc0.dist-info/RECORD +12 -0
- ramnet-0.1.0rc0.dist-info/WHEEL +5 -0
- ramnet-0.1.0rc0.dist-info/licenses/LICENSE +21 -0
- ramnet-0.1.0rc0.dist-info/top_level.txt +1 -0
ramnet/__init__.py
ADDED
ramnet/layers/cache.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from typing import Any, Callable, NamedTuple
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from transformers.cache_utils import Cache, CacheLayerMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RAMState(NamedTuple):
|
|
8
|
+
"""Recurrent memory contents and mass."""
|
|
9
|
+
|
|
10
|
+
slot: torch.Tensor # (..., H, A, C)
|
|
11
|
+
mass: torch.Tensor # (..., H, A)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RAMCacheLayer(CacheLayerMixin):
|
|
15
|
+
"""One recurrent state, its physical length, and per-sample CAPE positions."""
|
|
16
|
+
|
|
17
|
+
is_compileable = False
|
|
18
|
+
is_sliding = False
|
|
19
|
+
is_croppable = False
|
|
20
|
+
supports_early_init = False
|
|
21
|
+
max_cache_len = -1
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.state: RAMState | None = None
|
|
26
|
+
self.next_position: torch.Tensor | None = None
|
|
27
|
+
self.cache_length = 0
|
|
28
|
+
self.is_initialized = False
|
|
29
|
+
self.device = None
|
|
30
|
+
|
|
31
|
+
def lazy_initialization(self, recurrent_state: RAMState) -> None:
|
|
32
|
+
self.device = recurrent_state.slot.device
|
|
33
|
+
self.is_initialized = True
|
|
34
|
+
|
|
35
|
+
def update(
|
|
36
|
+
self,
|
|
37
|
+
recurrent_state: RAMState | tuple[torch.Tensor, torch.Tensor],
|
|
38
|
+
offset: int = 1,
|
|
39
|
+
cache_kwargs: dict[str, Any] | None = None,
|
|
40
|
+
) -> RAMState:
|
|
41
|
+
state = RAMState(*recurrent_state)
|
|
42
|
+
if not self.is_initialized:
|
|
43
|
+
self.lazy_initialization(state)
|
|
44
|
+
|
|
45
|
+
# Assign functional outputs directly to preserve autograd across chunks.
|
|
46
|
+
self.state = state
|
|
47
|
+
self.cache_length += offset
|
|
48
|
+
if cache_kwargs is not None:
|
|
49
|
+
self.next_position = cache_kwargs.get('next_position', self.next_position)
|
|
50
|
+
return state
|
|
51
|
+
|
|
52
|
+
def get_seq_length(self) -> int:
|
|
53
|
+
return self.cache_length
|
|
54
|
+
|
|
55
|
+
def get_mask_sizes(self, query_length: int | torch.Tensor) -> tuple[int, int]:
|
|
56
|
+
if isinstance(query_length, torch.Tensor):
|
|
57
|
+
query_length = query_length.numel()
|
|
58
|
+
return self.cache_length + query_length, 0
|
|
59
|
+
|
|
60
|
+
def get_max_cache_shape(self) -> int:
|
|
61
|
+
return -1
|
|
62
|
+
|
|
63
|
+
def get_max_length(self) -> int:
|
|
64
|
+
return -1
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def batch_size(self) -> int:
|
|
68
|
+
return self.state.slot.size(0) if self.state is not None else -1
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def max_batch_size(self) -> int:
|
|
72
|
+
return self.batch_size
|
|
73
|
+
|
|
74
|
+
def _map_tensors(self, fn: Callable[[torch.Tensor], torch.Tensor]) -> None:
|
|
75
|
+
if self.state is not None:
|
|
76
|
+
self.state = RAMState(*(fn(value) for value in self.state))
|
|
77
|
+
if self.next_position is not None:
|
|
78
|
+
self.next_position = fn(self.next_position)
|
|
79
|
+
|
|
80
|
+
def offload(self) -> None:
|
|
81
|
+
self._map_tensors(lambda value: value.to('cpu', non_blocking=True))
|
|
82
|
+
|
|
83
|
+
def prefetch(self) -> None:
|
|
84
|
+
if self.is_initialized:
|
|
85
|
+
self._map_tensors(lambda value: value.to(self.device, non_blocking=True))
|
|
86
|
+
|
|
87
|
+
def reorder_cache(self, beam_idx: torch.LongTensor) -> None:
|
|
88
|
+
self.batch_select_indices(beam_idx)
|
|
89
|
+
|
|
90
|
+
def batch_select_indices(self, indices: torch.LongTensor) -> None:
|
|
91
|
+
self._map_tensors(lambda value: value.index_select(0, indices.to(value.device)))
|
|
92
|
+
|
|
93
|
+
def batch_repeat_interleave(self, repeats: int) -> None:
|
|
94
|
+
self._map_tensors(lambda value: value.repeat_interleave(repeats, dim=0))
|
|
95
|
+
|
|
96
|
+
def crop(self, max_length: int) -> None:
|
|
97
|
+
raise ValueError('RAMCache cannot roll back recurrent states; replay the retained prefix instead.')
|
|
98
|
+
|
|
99
|
+
def reset(self) -> None:
|
|
100
|
+
self.state = None
|
|
101
|
+
self.next_position = None
|
|
102
|
+
self.cache_length = 0
|
|
103
|
+
self.is_initialized = False
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class RAMCache(Cache):
|
|
107
|
+
"""A Hugging Face cache with FLA-style ``recurrent_state`` layer entries.
|
|
108
|
+
|
|
109
|
+
Storage is allocated by attention on first use, in the operator's state dtype.
|
|
110
|
+
Cache length counts all processed tokens; CAPE positions count valid tokens.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self) -> None:
|
|
114
|
+
super().__init__(layer_class_to_replicate=RAMCacheLayer)
|
|
115
|
+
|
|
116
|
+
def get_layer(self, layer_idx: int) -> RAMCacheLayer:
|
|
117
|
+
if layer_idx < 0:
|
|
118
|
+
raise ValueError('layer_idx must be non-negative.')
|
|
119
|
+
while len(self.layers) <= layer_idx:
|
|
120
|
+
self.layers.append(RAMCacheLayer())
|
|
121
|
+
return self.layers[layer_idx]
|
|
122
|
+
|
|
123
|
+
def update(
|
|
124
|
+
self,
|
|
125
|
+
recurrent_state: RAMState | tuple[torch.Tensor, torch.Tensor],
|
|
126
|
+
layer_idx: int,
|
|
127
|
+
offset: int = 1,
|
|
128
|
+
cache_kwargs: dict[str, Any] | None = None,
|
|
129
|
+
) -> RAMState:
|
|
130
|
+
return self.get_layer(layer_idx).update(recurrent_state, offset, cache_kwargs)
|
|
131
|
+
|
|
132
|
+
def get_max_length(self, layer_idx: int | None = None) -> int:
|
|
133
|
+
return -1
|
|
134
|
+
|
|
135
|
+
def __getitem__(self, layer_idx: int) -> dict[str, RAMState | None]:
|
|
136
|
+
return {'recurrent_state': self.layers[layer_idx].state}
|
|
137
|
+
|
|
138
|
+
def __iter__(self):
|
|
139
|
+
for layer_idx in range(len(self.layers)):
|
|
140
|
+
yield self[layer_idx]
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import einops
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from torch.distributed.tensor import DTensor, distribute_tensor
|
|
5
|
+
|
|
6
|
+
from .cache import RAMCache, RAMState
|
|
7
|
+
from ramnet_ops import random_access_memory_attention
|
|
8
|
+
|
|
9
|
+
Activation_mapping = {
|
|
10
|
+
'relu': torch.nn.ReLU,
|
|
11
|
+
'sigmoid': torch.nn.Sigmoid,
|
|
12
|
+
'silu': torch.nn.SiLU,
|
|
13
|
+
'swish': torch.nn.SiLU,
|
|
14
|
+
'gelu': torch.nn.GELU,
|
|
15
|
+
}
|
|
16
|
+
OutputGateType = Literal['relu', 'sigmoid', 'silu', 'swish', 'gelu']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RAMNetAttention(torch.nn.Module):
|
|
20
|
+
"""
|
|
21
|
+
the layer of RAM Net Attention for [RAM-Net: Expressive Linear Attention with Selectively Addressable Memory] (https://arxiv.org/abs/2602.11958)
|
|
22
|
+
|
|
23
|
+
:param hidden_size: Dimensionality of the input and output layers.
|
|
24
|
+
:param n_heads: Number of attention heads.
|
|
25
|
+
|
|
26
|
+
:param addr_order: Order U of the address space (U-order product softmax).
|
|
27
|
+
:param addr_radix: Address radix P (paper d_p): choices per digit, giving P^U memory slots.
|
|
28
|
+
:param value_chs: Dimensionality of value channels (d_v), total state size = d_p^U * d_v. Default: hidden_size // num_attention_heads.
|
|
29
|
+
|
|
30
|
+
:param gamma: prior forget-rate control. Negative values favor retention, zero balances writing and forgetting, and positive values favor forgetting.
|
|
31
|
+
:param gamma_mode: Mode for gamma: 'fixed' (non-trainable constant), 'learnable' (per-head learnable parameter), 'dynamic' (input-conditioned via linear map).
|
|
32
|
+
|
|
33
|
+
:param init_mass: Persistence strength of the initial zero state. None defaults to 1 / (addr_radix ** addr_order).
|
|
34
|
+
|
|
35
|
+
:param embed_num: Number of heads applying Cyclic Address Positional Embedding (CAPE). 0 disables CAPE; explicit values must be in ``[0, n_heads]``.
|
|
36
|
+
|
|
37
|
+
:param addr_temperature: Temperature for address localization; smaller values precise memory access.
|
|
38
|
+
:param top_k: Limits sparsity of the address vector. A two-item tuple/list applies separately to (write_k, read_k); int applies to both. Active state per token is proportional to k * d_v.
|
|
39
|
+
:param top_k_re_norm: Whether to re-normalize the address vector after top-k filtering, forcing the top-k entries to sum to 1.0.
|
|
40
|
+
A bool applies to both write and read; a two-item tuple/list specifies separate (write, read) bools.
|
|
41
|
+
|
|
42
|
+
:param mass_aware: Weight reads by updated memory mass with epsilon-stabilized normalization.
|
|
43
|
+
:param output_gate: Element-wise output gate function: 'relu', 'sigmoid', 'silu', 'swish', 'gelu', or None.
|
|
44
|
+
:param output_norm: Whether to enable head-wise output RMS-norm.
|
|
45
|
+
|
|
46
|
+
:param addr_proj_bias: Whether to enable bias in address projection layers (bias = address prior).
|
|
47
|
+
:param value_proj_bias: Whether to enable bias in the value projection layer.
|
|
48
|
+
:param address_scaling: Whether to re-parameterize address projection with learnable scaling (w = exp(alpha) * w' for training).
|
|
49
|
+
|
|
50
|
+
:param ram_attn_eps: Epsilon value for RAM read out and proxy backward limit in the forget gate.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
# noinspection PyTypeChecker
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
# === Model Architecture ===
|
|
57
|
+
hidden_size: int,
|
|
58
|
+
n_heads: int,
|
|
59
|
+
# === RAM Structure ===
|
|
60
|
+
addr_order: int = 5,
|
|
61
|
+
addr_radix: int = 4,
|
|
62
|
+
value_chs: int | None = None,
|
|
63
|
+
# === Forgetting Modulation ===
|
|
64
|
+
gamma: float = 2.197,
|
|
65
|
+
gamma_mode: Literal['fixed', 'learnable', 'dynamic'] = 'dynamic',
|
|
66
|
+
# === RAM initial mass ===
|
|
67
|
+
init_mass: float | None = None,
|
|
68
|
+
# === Positional Encoding (CAPE) ===
|
|
69
|
+
embed_num: int = 0,
|
|
70
|
+
# === Address Decoder ===
|
|
71
|
+
addr_temperature: float = 2.2,
|
|
72
|
+
top_k: tuple[int, int] | list[int] | int | None = 8,
|
|
73
|
+
top_k_re_norm: bool | tuple[bool, bool] | list[bool] = False,
|
|
74
|
+
# === Readout ===
|
|
75
|
+
mass_aware: bool = False,
|
|
76
|
+
output_gate: OutputGateType | None = 'sigmoid',
|
|
77
|
+
output_norm: bool = False,
|
|
78
|
+
# === Projection ===
|
|
79
|
+
addr_proj_bias: bool = True,
|
|
80
|
+
value_proj_bias: bool = False,
|
|
81
|
+
address_scaling: bool = True,
|
|
82
|
+
# === Numerical Stability ===
|
|
83
|
+
ram_attn_eps: float = 1e-12,
|
|
84
|
+
layer_idx: int | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
super().__init__()
|
|
87
|
+
|
|
88
|
+
if gamma_mode not in ('fixed', 'learnable', 'dynamic'):
|
|
89
|
+
raise ValueError(f"Unsupported gamma_mode: {gamma_mode!r}.")
|
|
90
|
+
if output_gate is not None and output_gate not in Activation_mapping:
|
|
91
|
+
raise ValueError(f"Unsupported output_gate: {output_gate!r}.")
|
|
92
|
+
assert hidden_size > 0 and n_heads > 0 and addr_order > 0 and addr_radix > 0, f"Expected positive dimensions, got {hidden_size=}, {n_heads=}, {addr_order=}, {addr_radix=}."
|
|
93
|
+
assert addr_temperature > 0, f"Expected addr_temperature > 0, got {addr_temperature}."
|
|
94
|
+
assert isinstance(embed_num, int) and 0 <= embed_num <= n_heads, f"Expected embed_num in [0, {n_heads}], got {embed_num!r}."
|
|
95
|
+
assert ram_attn_eps > 0, f"Expected ram_attn_eps > 0, got {ram_attn_eps}."
|
|
96
|
+
if layer_idx is not None and layer_idx < 0:
|
|
97
|
+
raise ValueError('layer_idx must be non-negative.')
|
|
98
|
+
|
|
99
|
+
if value_chs is None:
|
|
100
|
+
value_chs = int(hidden_size // n_heads)
|
|
101
|
+
assert value_chs > 0, f"Expected value_chs > 0, got {value_chs}."
|
|
102
|
+
|
|
103
|
+
self.layer_idx = layer_idx
|
|
104
|
+
self.n_heads = n_heads
|
|
105
|
+
self.addr_order = addr_order
|
|
106
|
+
self.addr_radix = addr_radix
|
|
107
|
+
self.value_chs = value_chs
|
|
108
|
+
self.gamma = gamma
|
|
109
|
+
self.gamma_mode = gamma_mode
|
|
110
|
+
self.addr_temperature = addr_temperature
|
|
111
|
+
self.embed_num = embed_num
|
|
112
|
+
self.init_mass = addr_radix ** (- addr_order) if init_mass is None else init_mass
|
|
113
|
+
self.top_k = top_k
|
|
114
|
+
self.top_k_re_norm = top_k_re_norm
|
|
115
|
+
self.mass_aware = mass_aware
|
|
116
|
+
self.addr_proj_bias = addr_proj_bias
|
|
117
|
+
self.value_proj_bias = value_proj_bias
|
|
118
|
+
self.ram_attn_eps = ram_attn_eps
|
|
119
|
+
|
|
120
|
+
proj_v_chs: int = self.n_heads * self.value_chs
|
|
121
|
+
proj_addr_chs: int = self.n_heads * self.addr_radix * self.addr_order
|
|
122
|
+
|
|
123
|
+
self.r_proj = torch.nn.Linear(hidden_size, proj_addr_chs, bias=self.addr_proj_bias)
|
|
124
|
+
self.w_proj = torch.nn.Linear(hidden_size, proj_addr_chs, bias=self.addr_proj_bias)
|
|
125
|
+
self.r_proj._ramnet_address = (self.n_heads, self.addr_order, self.addr_radix)
|
|
126
|
+
self.w_proj._ramnet_address = (self.n_heads, self.addr_order, self.addr_radix)
|
|
127
|
+
self.v_proj = torch.nn.Linear(hidden_size, proj_v_chs, bias=self.value_proj_bias)
|
|
128
|
+
self.o_proj = torch.nn.Linear(proj_v_chs, hidden_size, bias=self.value_proj_bias)
|
|
129
|
+
|
|
130
|
+
if gamma_mode == 'learnable':
|
|
131
|
+
self.gamma_modifier = torch.nn.Parameter(torch.zeros(self.n_heads))
|
|
132
|
+
if gamma_mode == 'dynamic':
|
|
133
|
+
self.gamma_modifier = torch.nn.Linear(hidden_size, self.n_heads)
|
|
134
|
+
|
|
135
|
+
self.o_norm = torch.nn.RMSNorm(self.value_chs) if output_norm else None
|
|
136
|
+
self.o_gate = torch.nn.Sequential(torch.nn.Linear(hidden_size, proj_v_chs), Activation_mapping[output_gate]()) if output_gate else None
|
|
137
|
+
if self.o_gate is not None:
|
|
138
|
+
self.o_gate[0]._ramnet_output_gate = True
|
|
139
|
+
|
|
140
|
+
if address_scaling:
|
|
141
|
+
self.r_scale = torch.nn.Parameter(torch.zeros(self.n_heads, self.addr_order))
|
|
142
|
+
self.w_scale = torch.nn.Parameter(torch.zeros(self.n_heads, self.addr_order))
|
|
143
|
+
else:
|
|
144
|
+
self.r_scale = None
|
|
145
|
+
self.w_scale = None
|
|
146
|
+
|
|
147
|
+
@torch.no_grad()
|
|
148
|
+
def _init_weights(self, *, initializer_range: float = 0.02, addr_prior_range: float = 1.5, gate_initializer_bias: float = 3.0) -> None:
|
|
149
|
+
"""Initialize all parameters and submodules owned by this attention layer."""
|
|
150
|
+
for module in self.modules():
|
|
151
|
+
self._init_module_weights(
|
|
152
|
+
module,
|
|
153
|
+
initializer_range=initializer_range,
|
|
154
|
+
addr_prior_range=addr_prior_range,
|
|
155
|
+
gate_initializer_bias=gate_initializer_bias,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
@torch.no_grad()
|
|
160
|
+
def _init_module_weights(
|
|
161
|
+
module: torch.nn.Module,
|
|
162
|
+
*,
|
|
163
|
+
initializer_range: float,
|
|
164
|
+
addr_prior_range: float,
|
|
165
|
+
gate_initializer_bias: float,
|
|
166
|
+
) -> None:
|
|
167
|
+
"""Initialize one module; leave traversal and initialization tracking to the caller."""
|
|
168
|
+
if isinstance(module, torch.nn.Linear):
|
|
169
|
+
torch.nn.init.normal_(module.weight, mean=0.0, std=initializer_range)
|
|
170
|
+
if module.bias is not None:
|
|
171
|
+
address_shape = getattr(module, '_ramnet_address', None)
|
|
172
|
+
if address_shape is not None:
|
|
173
|
+
H, U, P = address_shape
|
|
174
|
+
assert module.out_features == H * U * P, "Address projection shape mismatch."
|
|
175
|
+
bias = torch.linspace(0, addr_prior_range, U)[:, None] * torch.linspace(-1, 1, P)[None, :]
|
|
176
|
+
bias = bias.expand(H, U, P).flatten()
|
|
177
|
+
if isinstance(module.bias, DTensor):
|
|
178
|
+
bias = distribute_tensor(bias, module.bias.device_mesh, module.bias.placements)
|
|
179
|
+
module.bias.to_local().copy_(bias.to_local())
|
|
180
|
+
else:
|
|
181
|
+
module.bias.copy_(bias.to(module.bias))
|
|
182
|
+
elif getattr(module, '_ramnet_output_gate', False):
|
|
183
|
+
torch.nn.init.constant_(module.bias, gate_initializer_bias)
|
|
184
|
+
else:
|
|
185
|
+
torch.nn.init.zeros_(module.bias)
|
|
186
|
+
elif isinstance(module, torch.nn.RMSNorm):
|
|
187
|
+
module.reset_parameters()
|
|
188
|
+
elif isinstance(module, RAMNetAttention):
|
|
189
|
+
for parameter in (module.r_scale, module.w_scale, getattr(module, 'gamma_modifier', None)):
|
|
190
|
+
if isinstance(parameter, torch.nn.Parameter):
|
|
191
|
+
torch.nn.init.zeros_(parameter)
|
|
192
|
+
|
|
193
|
+
def forward(
|
|
194
|
+
self,
|
|
195
|
+
hidden_states: torch.Tensor,
|
|
196
|
+
attention_mask: torch.Tensor | None = None,
|
|
197
|
+
past_key_values: RAMCache | None = None,
|
|
198
|
+
use_cache: bool = False,
|
|
199
|
+
output_attentions: bool = False,
|
|
200
|
+
position_ids: torch.Tensor | None = None,
|
|
201
|
+
) -> tuple[torch.Tensor, None, RAMCache | None]:
|
|
202
|
+
"""Run attention on (B, T, D) inputs using the model cache protocol."""
|
|
203
|
+
if hidden_states.ndim != 3:
|
|
204
|
+
raise ValueError('forward expects (B, T, D); use forward_with_state for other layouts.')
|
|
205
|
+
if output_attentions:
|
|
206
|
+
raise ValueError('RAMNetAttention does not materialize attention matrices.')
|
|
207
|
+
if use_cache and self.layer_idx is None:
|
|
208
|
+
raise ValueError('layer_idx is required when use_cache=True.')
|
|
209
|
+
|
|
210
|
+
if use_cache and past_key_values is None:
|
|
211
|
+
past_key_values = RAMCache()
|
|
212
|
+
cache_layer = None
|
|
213
|
+
if past_key_values is not None:
|
|
214
|
+
if self.layer_idx is None:
|
|
215
|
+
raise ValueError('layer_idx is required when reading a cache.')
|
|
216
|
+
if len(past_key_values) > self.layer_idx:
|
|
217
|
+
cache_layer = past_key_values.get_layer(self.layer_idx)
|
|
218
|
+
|
|
219
|
+
B, T, _ = hidden_states.shape
|
|
220
|
+
if T == 0:
|
|
221
|
+
raise ValueError('The sequence must contain at least one token.')
|
|
222
|
+
device = hidden_states.device
|
|
223
|
+
if cache_layer is not None and cache_layer.next_position is not None:
|
|
224
|
+
offset = cache_layer.next_position
|
|
225
|
+
else:
|
|
226
|
+
cache_length = cache_layer.get_seq_length() if cache_layer is not None else 0
|
|
227
|
+
offset = torch.full((B, 1), cache_length, device=device, dtype=torch.long)
|
|
228
|
+
|
|
229
|
+
mask = None
|
|
230
|
+
if attention_mask is not None:
|
|
231
|
+
if attention_mask.ndim != 2 or attention_mask.size(-1) < T:
|
|
232
|
+
raise ValueError('attention_mask must be a (B, T) token mask or include the cached prefix.')
|
|
233
|
+
attention_mask = attention_mask.to(device=device, dtype=torch.bool).expand(B, -1)
|
|
234
|
+
mask = attention_mask[:, -T:]
|
|
235
|
+
|
|
236
|
+
if position_ids is not None:
|
|
237
|
+
if position_ids.ndim not in (1, 2) or position_ids.size(-1) < T:
|
|
238
|
+
raise ValueError('position_ids must cover the current query.')
|
|
239
|
+
position_ids = position_ids.to(device=device, dtype=torch.long)[..., -T:].expand(B, T)
|
|
240
|
+
elif attention_mask is not None:
|
|
241
|
+
position_ids = attention_mask.long().cumsum(-1)[:, -T:] - 1
|
|
242
|
+
if attention_mask.size(-1) == T: # in case mask don't include prefix
|
|
243
|
+
position_ids = position_ids + offset
|
|
244
|
+
else:
|
|
245
|
+
position_ids = offset + torch.arange(T, device=device)
|
|
246
|
+
|
|
247
|
+
if mask is not None:
|
|
248
|
+
position_ids = position_ids.masked_fill(~mask, 0)
|
|
249
|
+
|
|
250
|
+
output, state = self.forward_with_state(
|
|
251
|
+
hidden_states,
|
|
252
|
+
init_ram=None if cache_layer is None else cache_layer.state,
|
|
253
|
+
mask=mask,
|
|
254
|
+
position_ids=position_ids,
|
|
255
|
+
inplace=None if use_cache else False,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
if use_cache:
|
|
259
|
+
next_position = position_ids[:, -1:] + 1
|
|
260
|
+
if mask is not None:
|
|
261
|
+
# Keep the last valid position, including for fully masked chunks.
|
|
262
|
+
indices = torch.arange(T, device=device).expand(B, T).masked_fill(~mask, -1)
|
|
263
|
+
last = indices.amax(-1, keepdim=True)
|
|
264
|
+
next_position = torch.where(last >= 0, position_ids.gather(-1, last.clamp_min(0)) + 1, offset)
|
|
265
|
+
past_key_values.update(
|
|
266
|
+
recurrent_state=state,
|
|
267
|
+
layer_idx=self.layer_idx,
|
|
268
|
+
offset=T,
|
|
269
|
+
cache_kwargs={'next_position': next_position},
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return output, None, past_key_values if use_cache else None
|
|
273
|
+
|
|
274
|
+
def forward_with_state(
|
|
275
|
+
self,
|
|
276
|
+
hidden_states: torch.Tensor,
|
|
277
|
+
init_ram: RAMState | tuple[torch.Tensor, torch.Tensor] | None = None,
|
|
278
|
+
|
|
279
|
+
mask: torch.Tensor | None = None,
|
|
280
|
+
position_ids: torch.Tensor | None = None,
|
|
281
|
+
|
|
282
|
+
t_dim: int = 1,
|
|
283
|
+
inplace: bool | None = False,
|
|
284
|
+
) -> tuple[torch.Tensor, RAMState]:
|
|
285
|
+
"""Return output and explicit state, without creating or updating a cache.
|
|
286
|
+
|
|
287
|
+
A (slot, mass) pair initializes RAM directly.
|
|
288
|
+
t_dim indexes a non-channel axis of hidden_states and its metadata.
|
|
289
|
+
"""
|
|
290
|
+
if not -(hidden_states.ndim - 1) <= t_dim < hidden_states.ndim - 1:
|
|
291
|
+
raise ValueError('t_dim must index a non-channel axis of hidden_states.')
|
|
292
|
+
if hidden_states.size(t_dim) == 0:
|
|
293
|
+
raise ValueError('The sequence must contain at least one token.')
|
|
294
|
+
match init_ram:
|
|
295
|
+
case None:
|
|
296
|
+
init_slot, init_mass = None, self.init_mass
|
|
297
|
+
case (init_slot, init_mass):
|
|
298
|
+
pass
|
|
299
|
+
case _:
|
|
300
|
+
raise TypeError('init_ram must be None or a (slot, mass) pair.')
|
|
301
|
+
|
|
302
|
+
# project address and value
|
|
303
|
+
r_addr = einops.rearrange(self.r_proj(hidden_states), '... (h u p) -> ... h u p', h=self.n_heads, u=self.addr_order, p=self.addr_radix)
|
|
304
|
+
w_addr = einops.rearrange(self.w_proj(hidden_states), '... (h u p) -> ... h u p', h=self.n_heads, u=self.addr_order, p=self.addr_radix)
|
|
305
|
+
values = einops.rearrange(self.v_proj(hidden_states), '... (h c) -> ... h c', h=self.n_heads)
|
|
306
|
+
|
|
307
|
+
# apply address scale and temperature
|
|
308
|
+
r_addr = (torch.exp(self.r_scale) / self.addr_temperature).to(dtype=r_addr.dtype).unsqueeze(-1) * r_addr if self.r_scale is not None else r_addr / self.addr_temperature
|
|
309
|
+
w_addr = (torch.exp(self.w_scale) / self.addr_temperature).to(dtype=w_addr.dtype).unsqueeze(-1) * w_addr if self.r_scale is not None else w_addr / self.addr_temperature
|
|
310
|
+
|
|
311
|
+
# project forgetting modulator
|
|
312
|
+
gamma = self.gamma
|
|
313
|
+
if self.gamma_mode == 'learnable':
|
|
314
|
+
gamma = (self.gamma_modifier + gamma).to(dtype=values.dtype).expand(*values.shape[:-1])
|
|
315
|
+
if self.gamma_mode == 'dynamic':
|
|
316
|
+
gamma = self.gamma_modifier(hidden_states) + gamma
|
|
317
|
+
|
|
318
|
+
if inplace is None:
|
|
319
|
+
inplace = (
|
|
320
|
+
not self.training
|
|
321
|
+
and init_slot is not None
|
|
322
|
+
and all(not value.requires_grad for value in (r_addr, w_addr, values, gamma, init_slot, init_mass) if isinstance(value, torch.Tensor))
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# run RAM-Attn
|
|
326
|
+
outputs, (slot, mass) = random_access_memory_attention(
|
|
327
|
+
w_addr=w_addr, r_addr=r_addr,
|
|
328
|
+
values=values, gamma=gamma,
|
|
329
|
+
init_ram=(init_slot, init_mass),
|
|
330
|
+
mask=mask, embed_num=self.embed_num, position_ids=position_ids,
|
|
331
|
+
top_k=self.top_k, top_k_re_norm=self.top_k_re_norm,
|
|
332
|
+
mass_aware=self.mass_aware, eps=self.ram_attn_eps,
|
|
333
|
+
t_dim=t_dim, inplace=inplace, impl_backend='cuda',
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
if self.o_norm is not None:
|
|
337
|
+
outputs = self.o_norm(outputs)
|
|
338
|
+
|
|
339
|
+
outputs = einops.rearrange(outputs, '... h c -> ... (h c)')
|
|
340
|
+
|
|
341
|
+
if self.o_gate is not None:
|
|
342
|
+
outputs = self.o_gate(hidden_states) * outputs
|
|
343
|
+
|
|
344
|
+
return self.o_proj(outputs), RAMState(slot, mass)
|
|
345
|
+
|
|
346
|
+
def extra_repr(self) -> str:
|
|
347
|
+
return (
|
|
348
|
+
f"{self.n_heads=}, U={self.addr_order}, P={self.addr_radix}, slots={self.addr_radix ** self.addr_order}, C={self.value_chs}, "
|
|
349
|
+
f"{self.addr_temperature=:.1f}, {self.gamma=:.1f}, {self.gamma_mode=}, "
|
|
350
|
+
f"{self.embed_num=}, {self.top_k=}, {self.top_k_re_norm=}"
|
|
351
|
+
).replace("self.", '')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
|
2
|
+
|
|
3
|
+
from .config_ramnet import RAMNetConfig
|
|
4
|
+
from .modeling_ramnet import RAMNetModel, RAMNetForCausalLM
|
|
5
|
+
|
|
6
|
+
AutoConfig.register(RAMNetConfig.model_type, RAMNetConfig, exist_ok=True)
|
|
7
|
+
AutoModel.register(RAMNetConfig, RAMNetModel, exist_ok=True)
|
|
8
|
+
AutoModelForCausalLM.register(RAMNetConfig, RAMNetForCausalLM, exist_ok=True)
|
|
9
|
+
|
|
10
|
+
__all__ = ['RAMNetConfig', 'RAMNetModel', 'RAMNetForCausalLM', ]
|