veneres-domyn commited on
Commit
9a379e4
·
verified ·
1 Parent(s): f78af14

Sync modeling_domynedge.py, configuration_domynedge.py from domyn-forge@9b691e2

Browse files
Files changed (2) hide show
  1. configuration_domynedge.py +49 -18
  2. modeling_domynedge.py +158 -67
configuration_domynedge.py CHANGED
@@ -1,6 +1,10 @@
 
 
 
1
  import transformers
2
 
3
- class RopeParameters():
 
4
  def __init__(
5
  self,
6
  rope_type: str = "default",
@@ -9,10 +13,18 @@ class RopeParameters():
9
  self.rope_type = rope_type
10
  self.rope_theta = rope_theta
11
 
 
12
  # to support both transformers > 5 and older
13
- PretrainedConfig = transformers.modeling_utils.PreTrainedConfig if hasattr(transformers.modeling_utils, "PreTrainedConfig") else transformers.configuration_utils.PretrainedConfig
 
 
 
 
 
14
 
15
  class DomynEdgeConfig(PretrainedConfig):
 
 
16
  model_type: str = "domynedge"
17
 
18
  def __init__(
@@ -26,6 +38,7 @@ class DomynEdgeConfig(PretrainedConfig):
26
  max_position_embeddings: int = 8192,
27
  initializer_range: float = 0.02,
28
  rms_norm_eps: float = 1e-6,
 
29
  use_cache: bool = True,
30
  tie_word_embeddings: bool = True,
31
  rope_parameters: RopeParameters | None = None,
@@ -33,13 +46,16 @@ class DomynEdgeConfig(PretrainedConfig):
33
  attention_bias: bool = False,
34
  attention_dropout: float = 0.0,
35
  position_layers: list[str] = ["nope", "nope"] + ["rope", "rope", "rope", "nope"] * 8 + ["nope", "nope"],
36
- window_sizes: list[tuple[int,int]] = [(-1,0), (-1,0)] + [(1024,0), (1024,0), (1024,0), (-1,0)] * 8 + [(-1,0), (-1,0)],
37
- head_sizes: list[int] = [256,256] + [64, 64, 64, 256] * 8 + [256, 256],
 
 
38
  pad_token_id: int | None = None,
39
  bos_token_id: int | None = None,
40
  eos_token_id: int | None = None,
41
  **kwargs,
42
  ):
 
43
  self.vocab_size = vocab_size
44
  self.max_position_embeddings = max_position_embeddings
45
  self.hidden_size = hidden_size
@@ -58,35 +74,50 @@ class DomynEdgeConfig(PretrainedConfig):
58
  self.use_cache = use_cache
59
  self.attention_bias = attention_bias
60
  self.attention_dropout = attention_dropout
61
-
62
  self.position_layers = position_layers
63
  self.window_sizes = window_sizes
64
  self.head_sizes = head_sizes
65
 
66
- # validate position_layers
67
  if len(self.position_layers) != self.num_hidden_layers:
68
- raise ValueError(f"Length of position_layers ({len(self.position_layers)}) must be equal to num_hidden_layers ({self.num_hidden_layers})")
 
 
 
 
69
  if any(pl not in ["rope", "nope"] for pl in self.position_layers):
70
- raise ValueError(f"position_layers must be one of 'rope', 'nope'. Got {self.position_layers}")
71
- # validate window_sizes
 
72
  if len(self.window_sizes) != self.num_hidden_layers:
73
- raise ValueError(f"Length of window_sizes ({len(self.window_sizes)}) must be equal to num_hidden_layers ({self.num_hidden_layers})")
74
- # validate head_sizes
 
 
 
 
75
  if len(head_sizes) != self.num_hidden_layers:
76
- raise ValueError(f"Length of head_sizes ({len(head_sizes)}) must be equal to num_hidden_layers ({self.num_hidden_layers})")
 
 
 
 
77
  if any(not isinstance(hs, int) for hs in head_sizes):
78
- raise ValueError(f"head_sizes must be a list of integers. Got {head_sizes}")
79
- # only one rope dim
80
- rope_dims = [head_size for rope,head_size in zip(position_layers, head_sizes) if rope == "rope"]
 
81
  if len(set(rope_dims)) > 1:
82
- raise ValueError(f"All rope layers must have the same head size. Got {rope_dims}")
 
83
  self.rope_dim = rope_dims[0]
84
 
85
  # Set default rope_parameters if not provided
86
  if rope_parameters is None:
87
  self.rope_parameters = RopeParameters(
88
- rope_type = "default",
89
- rope_theta = 10_000,
90
  )
91
 
92
  self.pad_token_id = pad_token_id
 
1
+ # SPDX-FileCopyrightText: 2026 Domyn
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
  import transformers
5
 
6
+
7
+ class RopeParameters:
8
  def __init__(
9
  self,
10
  rope_type: str = "default",
 
13
  self.rope_type = rope_type
14
  self.rope_theta = rope_theta
15
 
16
+
17
  # to support both transformers > 5 and older
18
+ PretrainedConfig = (
19
+ transformers.modeling_utils.PreTrainedConfig
20
+ if hasattr(transformers.modeling_utils, "PreTrainedConfig")
21
+ else transformers.configuration_utils.PretrainedConfig
22
+ )
23
+
24
 
25
  class DomynEdgeConfig(PretrainedConfig):
26
+ """Configuration of a DomynEdge model, with per-layer position encoding, sliding window and head size."""
27
+
28
  model_type: str = "domynedge"
29
 
30
  def __init__(
 
38
  max_position_embeddings: int = 8192,
39
  initializer_range: float = 0.02,
40
  rms_norm_eps: float = 1e-6,
41
+ *,
42
  use_cache: bool = True,
43
  tie_word_embeddings: bool = True,
44
  rope_parameters: RopeParameters | None = None,
 
46
  attention_bias: bool = False,
47
  attention_dropout: float = 0.0,
48
  position_layers: list[str] = ["nope", "nope"] + ["rope", "rope", "rope", "nope"] * 8 + ["nope", "nope"],
49
+ window_sizes: list[tuple[int, int]] = [(-1, 0), (-1, 0)]
50
+ + [(1024, 0), (1024, 0), (1024, 0), (-1, 0)] * 8
51
+ + [(-1, 0), (-1, 0)],
52
+ head_sizes: list[int] = [256, 256] + [64, 64, 64, 256] * 8 + [256, 256],
53
  pad_token_id: int | None = None,
54
  bos_token_id: int | None = None,
55
  eos_token_id: int | None = None,
56
  **kwargs,
57
  ):
58
+ """Initialize and validate the per-layer settings, which must all have `num_hidden_layers` entries."""
59
  self.vocab_size = vocab_size
60
  self.max_position_embeddings = max_position_embeddings
61
  self.hidden_size = hidden_size
 
74
  self.use_cache = use_cache
75
  self.attention_bias = attention_bias
76
  self.attention_dropout = attention_dropout
77
+
78
  self.position_layers = position_layers
79
  self.window_sizes = window_sizes
80
  self.head_sizes = head_sizes
81
 
82
+ # Validate position_layers
83
  if len(self.position_layers) != self.num_hidden_layers:
84
+ msg = (
85
+ f"Length of position_layers ({len(self.position_layers)}) "
86
+ f"must be equal to num_hidden_layers ({self.num_hidden_layers})"
87
+ )
88
+ raise ValueError(msg)
89
  if any(pl not in ["rope", "nope"] for pl in self.position_layers):
90
+ msg = f"position_layers must be one of 'rope', 'nope'. Got {self.position_layers}"
91
+ raise ValueError(msg)
92
+ # Validate window_sizes
93
  if len(self.window_sizes) != self.num_hidden_layers:
94
+ msg = (
95
+ f"Length of window_sizes ({len(self.window_sizes)}) "
96
+ f"must be equal to num_hidden_layers ({self.num_hidden_layers})"
97
+ )
98
+ raise ValueError(msg)
99
+ # Validate head_sizes
100
  if len(head_sizes) != self.num_hidden_layers:
101
+ msg = (
102
+ f"Length of head_sizes ({len(head_sizes)}) "
103
+ f"must be equal to num_hidden_layers ({self.num_hidden_layers})"
104
+ )
105
+ raise ValueError(msg)
106
  if any(not isinstance(hs, int) for hs in head_sizes):
107
+ msg = f"head_sizes must be a list of integers. Got {head_sizes}"
108
+ raise ValueError(msg)
109
+ # Only one rope dim
110
+ rope_dims = [head_size for rope, head_size in zip(position_layers, head_sizes, strict=True) if rope == "rope"]
111
  if len(set(rope_dims)) > 1:
112
+ msg = f"All rope layers must have the same head size. Got {rope_dims}"
113
+ raise ValueError(msg)
114
  self.rope_dim = rope_dims[0]
115
 
116
  # Set default rope_parameters if not provided
117
  if rope_parameters is None:
118
  self.rope_parameters = RopeParameters(
119
+ rope_type="default",
120
+ rope_theta=10_000,
121
  )
122
 
123
  self.pad_token_id = pad_token_id
modeling_domynedge.py CHANGED
@@ -1,13 +1,59 @@
1
- from .configuration_domynedge import DomynEdgeConfig
2
- import transformers.modeling_outputs
3
- import transformers.modeling_utils
4
- import torch.nn.attention.varlen
5
- import transformers.utils
6
- import transformers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import dataclasses
8
- import einops
9
  import typing
 
 
10
  import torch
 
 
 
 
 
 
11
 
12
  @dataclasses.dataclass
13
  class VarlenMetadataMask:
@@ -16,17 +62,18 @@ class VarlenMetadataMask:
16
  max_q: int
17
  max_k: int
18
 
 
19
  class IndexFirstAxis(torch.autograd.Function):
20
- """ https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/bert_padding.py """
21
 
22
  @staticmethod
23
- def forward(ctx, input, indices):
24
  ctx.save_for_backward(indices)
25
- assert input.ndim >= 2
26
- ctx.first_axis_dim, other_shape = input.shape[0], input.shape[1:]
27
  second_dim = other_shape.numel()
28
  return torch.gather(
29
- einops.rearrange(input, "b ... -> b (...)"), 0, einops.repeat(indices, "z -> z d", d=second_dim)
30
  ).reshape(-1, *other_shape)
31
 
32
  @staticmethod
@@ -43,17 +90,16 @@ class IndexFirstAxis(torch.autograd.Function):
43
  grad_input.scatter_(0, einops.repeat(indices, "z -> z d", d=grad_output.shape[1]), grad_output)
44
  return grad_input.reshape(ctx.first_axis_dim, *other_shape), None
45
 
 
46
  class IndexPutFirstAxis(torch.autograd.Function):
47
- """ https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/bert_padding.py """
48
 
49
  @staticmethod
50
  def forward(ctx, values, indices, first_axis_dim):
51
  ctx.save_for_backward(indices)
52
  assert indices.ndim == 1
53
  assert values.ndim >= 2
54
- output = torch.zeros(
55
- first_axis_dim, *values.shape[1:], device=values.device, dtype=values.dtype
56
- )
57
  output[indices] = values
58
  return output
59
 
@@ -64,12 +110,15 @@ class IndexPutFirstAxis(torch.autograd.Function):
64
  return grad_values, None, None
65
 
66
 
67
- def unpad_input(hidden_states: torch.Tensor, attention_mask: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, int]:
68
- """ https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/bert_padding.py
 
 
69
 
70
  Arguments:
71
  hidden_states: (batch, seqlen, ...)
72
  attention_mask: (batch, seqlen), bool / int, 1 means valid and 0 means not valid.
 
73
  Return:
74
  hidden_states: (total_nnz, ...), where total_nnz = number of tokens selected in attention_mask + unused_mask.
75
  indices: (total_nnz), the indices of masked tokens from the flattened input sequence.
@@ -88,14 +137,16 @@ def unpad_input(hidden_states: torch.Tensor, attention_mask: torch.Tensor) -> tu
88
  max_seqlen,
89
  )
90
 
 
91
  def pad_input(hidden_states: torch.Tensor, indices: torch.Tensor, batch_size: int, seqlen: int) -> torch.Tensor:
92
- """ https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/bert_padding.py
93
 
94
  Arguments:
95
  hidden_states: (total_nnz, ...), where total_nnz = number of tokens in selected in attention_mask.
96
  indices: (total_nnz), the indices that represent the non-masked tokens of the original padded input sequence.
97
- batch: int, batch size for the padded sequence.
98
  seqlen: int, maximum sequence length for the padded sequence.
 
99
  Return:
100
  hidden_states: (batch, seqlen, ...)
101
 
@@ -103,6 +154,7 @@ def pad_input(hidden_states: torch.Tensor, indices: torch.Tensor, batch_size: in
103
  output = IndexPutFirstAxis.apply(hidden_states, indices, batch_size * seqlen)
104
  return einops.rearrange(output, "(b s) ... -> b s ...", b=batch_size)
105
 
 
106
  class DomynEdgeFeedForward(torch.nn.Module):
107
  def __init__(self, config):
108
  super().__init__()
@@ -116,6 +168,7 @@ class DomynEdgeFeedForward(torch.nn.Module):
116
  def forward(self, x):
117
  return self.down_proj(torch.nn.functional.silu(self.gate_proj(x)) * self.up_proj(x))
118
 
 
119
  class DomynEdgeRotaryEmbedding(torch.nn.Module):
120
  inv_freq: torch.Tensor # fix linting for `register_buffer`
121
 
@@ -137,17 +190,15 @@ class DomynEdgeRotaryEmbedding(torch.nn.Module):
137
  def compute_default_rope_parameters(
138
  config: DomynEdgeConfig | None = None,
139
  device: typing.Optional["torch.device"] = None,
140
- seq_len: int | None = None,
141
  ) -> tuple["torch.Tensor", float]:
142
- """
143
- Computes the inverse frequencies according to the original RoPE implementation
144
  Args:
145
  config ([`~transformers.PreTrainedConfig`]):
146
  The model configuration.
147
  device (`torch.device`):
148
  The device to use for initialization of the inverse frequencies.
149
- seq_len (`int`, *optional*):
150
- The current sequence length. Unused for this type of RoPE.
151
  Returns:
152
  Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
153
  post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
@@ -168,6 +219,11 @@ class DomynEdgeRotaryEmbedding(torch.nn.Module):
168
  inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
169
  position_ids_expanded = position_ids[:, None, :].float()
170
 
 
 
 
 
 
171
  freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
172
  emb = torch.cat((freqs, freqs), dim=-1)
173
  cos = emb.cos() * self.attention_scaling
@@ -198,6 +254,7 @@ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
198
  k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
199
  cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
200
  the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
 
201
  Returns:
202
  `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
203
  """
@@ -208,23 +265,36 @@ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
208
  k_embed = (kf * cos) + (rotate_half(kf) * sin)
209
  return q_embed.to(q.dtype), k_embed.to(k.dtype)
210
 
 
211
  class DomynEdgeAttention(torch.nn.Module):
212
- """ Grouped Query Attention with support for sliding window """
213
 
214
  def __init__(self, config: DomynEdgeConfig, layer_idx: int):
215
  super().__init__()
216
  self.config = config
217
  self.head_dim = config.head_sizes[layer_idx]
218
- self.scaling = self.head_dim ** -0.5
219
  self.window_size = config.window_sizes[layer_idx]
220
- self.apply_rope = apply_rotary_pos_emb if config.position_layers[layer_idx] == "rope" else lambda q, k, cos, sin, unsqueeze_dim: (q, k)
 
 
 
 
221
  self.layer_idx = layer_idx
222
  self.enable_gqa = config.num_attention_heads > config.num_key_value_heads
223
 
224
- self.q_proj = torch.nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias)
225
- self.k_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias)
226
- self.v_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias)
227
- self.o_proj = torch.nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias)
 
 
 
 
 
 
 
 
228
  self.q_norm = torch.nn.RMSNorm(self.head_dim, eps=config.rms_norm_eps, elementwise_affine=False)
229
  self.k_norm = torch.nn.RMSNorm(self.head_dim, eps=config.rms_norm_eps, elementwise_affine=True)
230
 
@@ -234,13 +304,17 @@ class DomynEdgeAttention(torch.nn.Module):
234
  position_embeddings: tuple[torch.Tensor, torch.Tensor],
235
  attention_mask: torch.Tensor,
236
  past_key_values: transformers.Cache | None = None,
237
- **kwargs,
238
  ) -> tuple[torch.Tensor, torch.Tensor | None]:
239
 
240
- batch_size, sequence_length, hidden_size = hidden_states.shape
241
 
242
- q = self.q_norm(self.q_proj(hidden_states).view(batch_size, sequence_length, self.config.num_attention_heads, self.head_dim))
243
- k = self.k_norm(self.k_proj(hidden_states).view(batch_size, sequence_length, self.config.num_key_value_heads, self.head_dim))
 
 
 
 
244
  v = self.v_proj(hidden_states).view(batch_size, sequence_length, self.config.num_key_value_heads, self.head_dim)
245
 
246
  cos, sin = position_embeddings
@@ -252,33 +326,33 @@ class DomynEdgeAttention(torch.nn.Module):
252
  k, v = k.transpose(1, 2), v.transpose(1, 2)
253
 
254
  if q.size(1) > 1:
255
- q, q_indices, q_cu_seqlen, q_maxseq = unpad_input(q, attention_mask)
256
  else:
257
  q_maxseq = 1
258
  q_cu_seqlen = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
259
  q_indices = q_cu_seqlen[:-1]
260
- q = q.squeeze(1)
261
 
262
- k, k_indices, k_cu_seqlen, k_maxseq = unpad_input(k, attention_mask)
263
- v, v_indices, v_cu_seqlen, v_maxseq = unpad_input(v, attention_mask)
264
 
265
  attn_output = torch.nn.attention.varlen.varlen_attn(
266
  q if q.dtype in [torch.float16, torch.bfloat16] else q.to(torch.bfloat16),
267
  k if k.dtype in [torch.float16, torch.bfloat16] else k.to(torch.bfloat16),
268
  v if v.dtype in [torch.float16, torch.bfloat16] else v.to(torch.bfloat16),
269
- cu_seq_q = q_cu_seqlen,
270
- cu_seq_k = k_cu_seqlen,
271
- max_q = q_maxseq,
272
- max_k = k_maxseq,
273
- scale = self.scaling,
274
- window_size = self.window_size,
275
- enable_gqa = self.enable_gqa,
276
  ).to(q.dtype)
277
 
278
  attn_output = pad_input(attn_output, q_indices, batch_size, sequence_length)
279
  attn_output = attn_output.view(batch_size, sequence_length, self.config.num_attention_heads * self.head_dim)
280
- attn_output = self.o_proj(attn_output)
281
- return attn_output
282
 
283
  class DomynEdgeDecoderLayer(torch.nn.Module):
284
  def __init__(self, config: DomynEdgeConfig, layer_idx: int):
@@ -305,24 +379,21 @@ class DomynEdgeDecoderLayer(torch.nn.Module):
305
  # Self Attention
306
  hidden_states = hidden_states + self.post_attention_norm(
307
  self.attention(
308
- hidden_states = self.pre_attention_norm(hidden_states),
309
- attention_mask = attention_mask,
310
- past_key_values = past_key_values,
311
- position_embeddings = position_embeddings,
312
  **kwargs,
313
  )
314
  )
315
 
316
  ## Fully Connected
317
- hidden_states = hidden_states + self.post_feedforward_norm(
318
- self.feedforward(
319
- self.pre_feedforward_norm(hidden_states)
320
- )
321
- )
322
 
323
- return hidden_states
324
 
325
  class DomynEdgePreTrainedModel(transformers.PreTrainedModel):
 
 
326
  config: DomynEdgeConfig
327
  base_model_prefix = "model"
328
  supports_gradient_checkpointing = False
@@ -338,13 +409,18 @@ class DomynEdgePreTrainedModel(transformers.PreTrainedModel):
338
 
339
 
340
  class DomynEdgeModel(DomynEdgePreTrainedModel):
 
 
341
  def __init__(self, config: DomynEdgeConfig):
 
342
  super().__init__(config)
343
  self.padding_idx = config.pad_token_id
344
  self.vocab_size = config.vocab_size
345
 
346
  self.embed_tokens = torch.nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
347
- self.layers = torch.nn.ModuleList([DomynEdgeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
 
 
348
  self.norm = torch.nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
349
  self.rotary_emb = DomynEdgeRotaryEmbedding(config=config)
350
 
@@ -361,9 +437,10 @@ class DomynEdgeModel(DomynEdgePreTrainedModel):
361
  use_cache: bool | None = None,
362
  **kwargs,
363
  ) -> transformers.modeling_outputs.BaseModelOutputWithPast:
364
-
365
  if input_ids is not None and inputs_embeds is not None:
366
- raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
 
367
 
368
  if inputs_embeds is None:
369
  inputs_embeds = self.embed_tokens(input_ids)
@@ -371,8 +448,9 @@ class DomynEdgeModel(DomynEdgePreTrainedModel):
371
  if use_cache and past_key_values is None:
372
  past_key_values = transformers.DynamicCache(config=self.config)
373
 
 
 
374
  if position_ids is None:
375
- past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
376
  position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
377
  position_ids = position_ids.unsqueeze(0)
378
 
@@ -380,7 +458,11 @@ class DomynEdgeModel(DomynEdgePreTrainedModel):
380
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
381
 
382
  if attention_mask is None:
383
- attention_mask = torch.ones(hidden_states.shape[:2], device=hidden_states.device, dtype=torch.bool)
 
 
 
 
384
 
385
  for decoder_layer in self.layers:
386
  hidden_states = decoder_layer(
@@ -397,10 +479,14 @@ class DomynEdgeModel(DomynEdgePreTrainedModel):
397
  past_key_values=past_key_values if use_cache else None,
398
  )
399
 
 
400
  class DomynEdgeForCausalLM(DomynEdgePreTrainedModel, transformers.GenerationMixin):
 
 
401
  _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
402
 
403
  def __init__(self, config):
 
404
  super().__init__(config)
405
  self.model = DomynEdgeModel(config)
406
  self.vocab_size = config.vocab_size
@@ -419,9 +505,9 @@ class DomynEdgeForCausalLM(DomynEdgePreTrainedModel, transformers.GenerationMixi
419
  labels: torch.LongTensor | None = None,
420
  use_cache: bool | None = None,
421
  logits_to_keep: int | torch.Tensor = 0,
422
- **kwargs
423
  ) -> transformers.modeling_outputs.CausalLMOutputWithPast:
424
-
425
  outputs = self.model(
426
  input_ids=input_ids,
427
  attention_mask=attention_mask,
@@ -436,7 +522,11 @@ class DomynEdgeForCausalLM(DomynEdgePreTrainedModel, transformers.GenerationMixi
436
  # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
437
  slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
438
  logits = self.lm_head(hidden_states[:, slice_indices, :])
439
- loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs) if labels is not None else None
 
 
 
 
440
 
441
  return transformers.modeling_outputs.CausalLMOutputWithPast(
442
  loss=loss,
@@ -446,6 +536,7 @@ class DomynEdgeForCausalLM(DomynEdgePreTrainedModel, transformers.GenerationMixi
446
  attentions=outputs.attentions,
447
  )
448
 
 
449
  __all__ = [
450
  "DomynEdgeForCausalLM",
451
  "DomynEdgeModel",
 
1
+ # SPDX-FileCopyrightText: 2026 Domyn
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # This file includes third-party code:
5
+ #
6
+ # - `IndexFirstAxis`, `IndexPutFirstAxis`, `unpad_input` and `pad_input` are adapted from
7
+ # https://github.com/Dao-AILab/flash-attention/blob/main/flash_attn/bert_padding.py
8
+ # and are distributed under the following license:
9
+ #
10
+ # BSD 3-Clause License
11
+ #
12
+ # Copyright (c) 2022, the respective contributors, as shown by the AUTHORS file.
13
+ # All rights reserved.
14
+ #
15
+ # Redistribution and use in source and binary forms, with or without
16
+ # modification, are permitted provided that the following conditions are met:
17
+ #
18
+ # * Redistributions of source code must retain the above copyright notice, this
19
+ # list of conditions and the following disclaimer.
20
+ #
21
+ # * Redistributions in binary form must reproduce the above copyright notice,
22
+ # this list of conditions and the following disclaimer in the documentation
23
+ # and/or other materials provided with the distribution.
24
+ #
25
+ # * Neither the name of the copyright holder nor the names of its
26
+ # contributors may be used to endorse or promote products derived from
27
+ # this software without specific prior written permission.
28
+ #
29
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
33
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
36
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ #
40
+ # - `DomynEdgeRotaryEmbedding.compute_default_rope_parameters`, `rotate_half` and
41
+ # `apply_rotary_pos_emb` are adapted from HuggingFace transformers
42
+ # (https://github.com/huggingface/transformers/blob/main/src/transformers/models/llama/modeling_llama.py),
43
+ # Copyright The HuggingFace Team, licensed under the Apache License, Version 2.0.
44
+ # Modified for DomynEdge.
45
+
46
  import dataclasses
 
47
  import typing
48
+
49
+ import einops
50
  import torch
51
+ import torch.nn.attention.varlen
52
+ import transformers
53
+ import transformers.modeling_outputs
54
+
55
+ from .configuration_domynedge import DomynEdgeConfig
56
+
57
 
58
  @dataclasses.dataclass
59
  class VarlenMetadataMask:
 
62
  max_q: int
63
  max_k: int
64
 
65
+
66
  class IndexFirstAxis(torch.autograd.Function):
67
+ """Gather the rows of the first axis at `indices`, scattering the gradient back in backward."""
68
 
69
  @staticmethod
70
+ def forward(ctx, input_, indices):
71
  ctx.save_for_backward(indices)
72
+ assert input_.ndim >= 2
73
+ ctx.first_axis_dim, other_shape = input_.shape[0], input_.shape[1:]
74
  second_dim = other_shape.numel()
75
  return torch.gather(
76
+ einops.rearrange(input_, "b ... -> b (...)"), 0, einops.repeat(indices, "z -> z d", d=second_dim)
77
  ).reshape(-1, *other_shape)
78
 
79
  @staticmethod
 
90
  grad_input.scatter_(0, einops.repeat(indices, "z -> z d", d=grad_output.shape[1]), grad_output)
91
  return grad_input.reshape(ctx.first_axis_dim, *other_shape), None
92
 
93
+
94
  class IndexPutFirstAxis(torch.autograd.Function):
95
+ """Scatter `values` into a zero tensor at the first-axis `indices`, gathering the gradient back in backward."""
96
 
97
  @staticmethod
98
  def forward(ctx, values, indices, first_axis_dim):
99
  ctx.save_for_backward(indices)
100
  assert indices.ndim == 1
101
  assert values.ndim >= 2
102
+ output = torch.zeros(first_axis_dim, *values.shape[1:], device=values.device, dtype=values.dtype)
 
 
103
  output[indices] = values
104
  return output
105
 
 
110
  return grad_values, None, None
111
 
112
 
113
+ def unpad_input(
114
+ hidden_states: torch.Tensor, attention_mask: torch.Tensor
115
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, int]:
116
+ """Remove the padding tokens, packing the valid tokens of all sequences along one axis.
117
 
118
  Arguments:
119
  hidden_states: (batch, seqlen, ...)
120
  attention_mask: (batch, seqlen), bool / int, 1 means valid and 0 means not valid.
121
+
122
  Return:
123
  hidden_states: (total_nnz, ...), where total_nnz = number of tokens selected in attention_mask + unused_mask.
124
  indices: (total_nnz), the indices of masked tokens from the flattened input sequence.
 
137
  max_seqlen,
138
  )
139
 
140
+
141
  def pad_input(hidden_states: torch.Tensor, indices: torch.Tensor, batch_size: int, seqlen: int) -> torch.Tensor:
142
+ """Restore the padded (batch, seqlen, ...) layout from packed tokens, the inverse of `unpad_input`.
143
 
144
  Arguments:
145
  hidden_states: (total_nnz, ...), where total_nnz = number of tokens in selected in attention_mask.
146
  indices: (total_nnz), the indices that represent the non-masked tokens of the original padded input sequence.
147
+ batch_size: int, batch size for the padded sequence.
148
  seqlen: int, maximum sequence length for the padded sequence.
149
+
150
  Return:
151
  hidden_states: (batch, seqlen, ...)
152
 
 
154
  output = IndexPutFirstAxis.apply(hidden_states, indices, batch_size * seqlen)
155
  return einops.rearrange(output, "(b s) ... -> b s ...", b=batch_size)
156
 
157
+
158
  class DomynEdgeFeedForward(torch.nn.Module):
159
  def __init__(self, config):
160
  super().__init__()
 
168
  def forward(self, x):
169
  return self.down_proj(torch.nn.functional.silu(self.gate_proj(x)) * self.up_proj(x))
170
 
171
+
172
  class DomynEdgeRotaryEmbedding(torch.nn.Module):
173
  inv_freq: torch.Tensor # fix linting for `register_buffer`
174
 
 
190
  def compute_default_rope_parameters(
191
  config: DomynEdgeConfig | None = None,
192
  device: typing.Optional["torch.device"] = None,
 
193
  ) -> tuple["torch.Tensor", float]:
194
+ """Computes the inverse frequencies according to the original RoPE implementation.
195
+
196
  Args:
197
  config ([`~transformers.PreTrainedConfig`]):
198
  The model configuration.
199
  device (`torch.device`):
200
  The device to use for initialization of the inverse frequencies.
201
+
 
202
  Returns:
203
  Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
204
  post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
 
219
  inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
220
  position_ids_expanded = position_ids[:, None, :].float()
221
 
222
+ # TODO: the angles lose precision in two cases, both wrong at large positions:
223
+ # - under torch.autocast(bfloat16) the matmul below runs in bf16 despite the `.float()` casts;
224
+ # - after model.to(torch.bfloat16) / model.bfloat16() the `inv_freq` buffer itself is cast to bf16.
225
+ # Loading with `from_pretrained(dtype=torch.bfloat16)` and no autocast is not affected. Candidate fix:
226
+ # recompute `inv_freq` in fp32 here, with autocast disabled, instead of reading the buffer.
227
  freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
228
  emb = torch.cat((freqs, freqs), dim=-1)
229
  cos = emb.cos() * self.attention_scaling
 
254
  k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
255
  cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
256
  the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
257
+
258
  Returns:
259
  `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
260
  """
 
265
  k_embed = (kf * cos) + (rotate_half(kf) * sin)
266
  return q_embed.to(q.dtype), k_embed.to(k.dtype)
267
 
268
+
269
  class DomynEdgeAttention(torch.nn.Module):
270
+ """Grouped Query Attention with support for sliding window."""
271
 
272
  def __init__(self, config: DomynEdgeConfig, layer_idx: int):
273
  super().__init__()
274
  self.config = config
275
  self.head_dim = config.head_sizes[layer_idx]
276
+ self.scaling = self.head_dim**-0.5
277
  self.window_size = config.window_sizes[layer_idx]
278
+ self.apply_rope = (
279
+ apply_rotary_pos_emb
280
+ if config.position_layers[layer_idx] == "rope"
281
+ else lambda q, k, cos, sin, unsqueeze_dim: (q, k) # noqa: ARG005
282
+ )
283
  self.layer_idx = layer_idx
284
  self.enable_gqa = config.num_attention_heads > config.num_key_value_heads
285
 
286
+ self.q_proj = torch.nn.Linear(
287
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
288
+ )
289
+ self.k_proj = torch.nn.Linear(
290
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
291
+ )
292
+ self.v_proj = torch.nn.Linear(
293
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
294
+ )
295
+ self.o_proj = torch.nn.Linear(
296
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
297
+ )
298
  self.q_norm = torch.nn.RMSNorm(self.head_dim, eps=config.rms_norm_eps, elementwise_affine=False)
299
  self.k_norm = torch.nn.RMSNorm(self.head_dim, eps=config.rms_norm_eps, elementwise_affine=True)
300
 
 
304
  position_embeddings: tuple[torch.Tensor, torch.Tensor],
305
  attention_mask: torch.Tensor,
306
  past_key_values: transformers.Cache | None = None,
307
+ **_kwargs,
308
  ) -> tuple[torch.Tensor, torch.Tensor | None]:
309
 
310
+ batch_size, sequence_length, _ = hidden_states.shape
311
 
312
+ q = self.q_norm(
313
+ self.q_proj(hidden_states).view(batch_size, sequence_length, self.config.num_attention_heads, self.head_dim)
314
+ )
315
+ k = self.k_norm(
316
+ self.k_proj(hidden_states).view(batch_size, sequence_length, self.config.num_key_value_heads, self.head_dim)
317
+ )
318
  v = self.v_proj(hidden_states).view(batch_size, sequence_length, self.config.num_key_value_heads, self.head_dim)
319
 
320
  cos, sin = position_embeddings
 
326
  k, v = k.transpose(1, 2), v.transpose(1, 2)
327
 
328
  if q.size(1) > 1:
329
+ q, q_indices, q_cu_seqlen, q_maxseq = unpad_input(q, attention_mask[:, -sequence_length:])
330
  else:
331
  q_maxseq = 1
332
  q_cu_seqlen = torch.arange(batch_size + 1, dtype=torch.int32, device=q.device)
333
  q_indices = q_cu_seqlen[:-1]
334
+ q = q.squeeze(1)
335
 
336
+ k, _, k_cu_seqlen, k_maxseq = unpad_input(k, attention_mask)
337
+ v, *_ = unpad_input(v, attention_mask)
338
 
339
  attn_output = torch.nn.attention.varlen.varlen_attn(
340
  q if q.dtype in [torch.float16, torch.bfloat16] else q.to(torch.bfloat16),
341
  k if k.dtype in [torch.float16, torch.bfloat16] else k.to(torch.bfloat16),
342
  v if v.dtype in [torch.float16, torch.bfloat16] else v.to(torch.bfloat16),
343
+ cu_seq_q=q_cu_seqlen,
344
+ cu_seq_k=k_cu_seqlen,
345
+ max_q=q_maxseq,
346
+ max_k=k_maxseq,
347
+ scale=self.scaling,
348
+ window_size=self.window_size,
349
+ enable_gqa=self.enable_gqa,
350
  ).to(q.dtype)
351
 
352
  attn_output = pad_input(attn_output, q_indices, batch_size, sequence_length)
353
  attn_output = attn_output.view(batch_size, sequence_length, self.config.num_attention_heads * self.head_dim)
354
+ return self.o_proj(attn_output)
355
+
356
 
357
  class DomynEdgeDecoderLayer(torch.nn.Module):
358
  def __init__(self, config: DomynEdgeConfig, layer_idx: int):
 
379
  # Self Attention
380
  hidden_states = hidden_states + self.post_attention_norm(
381
  self.attention(
382
+ hidden_states=self.pre_attention_norm(hidden_states),
383
+ attention_mask=attention_mask,
384
+ past_key_values=past_key_values,
385
+ position_embeddings=position_embeddings,
386
  **kwargs,
387
  )
388
  )
389
 
390
  ## Fully Connected
391
+ return hidden_states + self.post_feedforward_norm(self.feedforward(self.pre_feedforward_norm(hidden_states)))
 
 
 
 
392
 
 
393
 
394
  class DomynEdgePreTrainedModel(transformers.PreTrainedModel):
395
+ """Base class wiring DomynEdge into the HuggingFace `PreTrainedModel` machinery."""
396
+
397
  config: DomynEdgeConfig
398
  base_model_prefix = "model"
399
  supports_gradient_checkpointing = False
 
409
 
410
 
411
  class DomynEdgeModel(DomynEdgePreTrainedModel):
412
+ """DomynEdge decoder stack without the language modeling head."""
413
+
414
  def __init__(self, config: DomynEdgeConfig):
415
+ """Build the embeddings, decoder layers, final norm and rotary embedding from `config`."""
416
  super().__init__(config)
417
  self.padding_idx = config.pad_token_id
418
  self.vocab_size = config.vocab_size
419
 
420
  self.embed_tokens = torch.nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
421
+ self.layers = torch.nn.ModuleList(
422
+ [DomynEdgeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
423
+ )
424
  self.norm = torch.nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
425
  self.rotary_emb = DomynEdgeRotaryEmbedding(config=config)
426
 
 
437
  use_cache: bool | None = None,
438
  **kwargs,
439
  ) -> transformers.modeling_outputs.BaseModelOutputWithPast:
440
+ """Run the decoder stack and return the final hidden states (and the cache when `use_cache` is set)."""
441
  if input_ids is not None and inputs_embeds is not None:
442
+ msg = "You must specify exactly one of input_ids or inputs_embeds"
443
+ raise ValueError(msg)
444
 
445
  if inputs_embeds is None:
446
  inputs_embeds = self.embed_tokens(input_ids)
 
448
  if use_cache and past_key_values is None:
449
  past_key_values = transformers.DynamicCache(config=self.config)
450
 
451
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
452
+
453
  if position_ids is None:
 
454
  position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
455
  position_ids = position_ids.unsqueeze(0)
456
 
 
458
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
459
 
460
  if attention_mask is None:
461
+ attention_mask = torch.ones(
462
+ (hidden_states.shape[0], past_seen_tokens + hidden_states.shape[1]),
463
+ device=hidden_states.device,
464
+ dtype=torch.bool,
465
+ )
466
 
467
  for decoder_layer in self.layers:
468
  hidden_states = decoder_layer(
 
479
  past_key_values=past_key_values if use_cache else None,
480
  )
481
 
482
+
483
  class DomynEdgeForCausalLM(DomynEdgePreTrainedModel, transformers.GenerationMixin):
484
+ """DomynEdge model with a language modeling head tied to the input embeddings."""
485
+
486
  _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
487
 
488
  def __init__(self, config):
489
+ """Build the decoder stack and the language modeling head from `config`."""
490
  super().__init__(config)
491
  self.model = DomynEdgeModel(config)
492
  self.vocab_size = config.vocab_size
 
505
  labels: torch.LongTensor | None = None,
506
  use_cache: bool | None = None,
507
  logits_to_keep: int | torch.Tensor = 0,
508
+ **kwargs,
509
  ) -> transformers.modeling_outputs.CausalLMOutputWithPast:
510
+ """Compute logits for the last `logits_to_keep` positions, plus the loss when `labels` are given."""
511
  outputs = self.model(
512
  input_ids=input_ids,
513
  attention_mask=attention_mask,
 
522
  # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
523
  slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
524
  logits = self.lm_head(hidden_states[:, slice_indices, :])
525
+ loss = (
526
+ self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
527
+ if labels is not None
528
+ else None
529
+ )
530
 
531
  return transformers.modeling_outputs.CausalLMOutputWithPast(
532
  loss=loss,
 
536
  attentions=outputs.attentions,
537
  )
538
 
539
+
540
  __all__ = [
541
  "DomynEdgeForCausalLM",
542
  "DomynEdgeModel",