KitsuVp commited on
Commit
6b76be6
·
verified ·
1 Parent(s): 808dfae

Update modeling_neollm.py

Browse files
Files changed (1) hide show
  1. modeling_neollm.py +206 -14
modeling_neollm.py CHANGED
@@ -12,6 +12,10 @@ factorised attention log-priors (Litman & Guo, 2026), optional StackMemory
12
  (Zhang et al., NeurIPS 2025), and optional HOLA-derived episodic memory for
13
  the FlashAttention-2 IHA seq-expand path (Cui, 2026).
14
 
 
 
 
 
15
  Attention stack (orthogonal, all active simultaneously when enabled):
16
  1. Gated Attention (use_gated_attention implicit via q_proj gate chunk):
17
  applies a head-specific elementwise sigmoid gate to the concatenated
@@ -105,7 +109,11 @@ except ImportError:
105
 
106
  from transformers.generation import GenerationMixin
107
  from transformers.masking_utils import create_causal_mask
108
- from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
 
 
 
 
109
  from transformers.modeling_layers import GradientCheckpointingLayer
110
  from transformers.modeling_outputs import (
111
  BaseModelOutputWithPast,
@@ -121,6 +129,7 @@ from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
121
 
122
  torch._dynamo.config.capture_scalar_outputs = True
123
  logger = logging.get_logger(__name__)
 
124
 
125
  # ── Optional flash_attn direct import (required only for IHA seq-expand, P>1) ──
126
  # Attempted once at module load time so the symbols are available when
@@ -1351,6 +1360,93 @@ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
1351
  return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
1352
 
1353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1354
  def causal_first_difference(x: torch.Tensor) -> torch.Tensor:
1355
  return x - F.pad(x[..., :-1, :], (0, 0, 1, 0))
1356
 
@@ -1795,7 +1891,12 @@ def affine_scaled_flash_attention_forward(
1795
  """
1796
  # ── Term 1: standard flash / sdpa output ─────────────────────────────
1797
  # dropout=0.0: we apply dropout to the combined output below instead.
1798
- attn_fn = ALL_ATTENTION_FUNCTIONS[module.config._attn_implementation]
 
 
 
 
 
1799
  flash_out, _ = attn_fn(
1800
  module,
1801
  query,
@@ -4010,6 +4111,18 @@ class NeoLLMAttention(nn.Module):
4010
  # was designed for the head-expand layout and would mismatch here.
4011
  # For P=1 or no IHA: the existing branch logic in _apply_mea_head_mixing
4012
  # is unchanged and handles both the pseudo-slot-aware and normal cases.
 
 
 
 
 
 
 
 
 
 
 
 
4013
  if _iha_seq_expand:
4014
  k, v = self._apply_mea_head_mixing(k, v, _force_standard=True)
4015
  else:
@@ -4033,6 +4146,23 @@ class NeoLLMAttention(nn.Module):
4033
  position_ids=position_ids,
4034
  )
4035
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4036
  # ── IHA: local sliding-window mask for non-seq-expand path (P=1) ─────
4037
  # For P>1 (seq-expand): the sliding window is expressed as window_size
4038
  # in flash_attn_func and handled inside _iha_seq_expand_flash_forward.
@@ -4164,7 +4294,7 @@ class NeoLLMAttention(nn.Module):
4164
  q,
4165
  k,
4166
  v,
4167
- attention_mask,
4168
  scaling=self.scaling,
4169
  alpha=alpha,
4170
  beta=beta,
@@ -4174,6 +4304,11 @@ class NeoLLMAttention(nn.Module):
4174
  else:
4175
  if self.config._attn_implementation == "eager":
4176
  attn_fn = eager_attention_forward
 
 
 
 
 
4177
  else:
4178
  attn_fn = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
4179
  backend_kwargs = kwargs
@@ -4187,7 +4322,7 @@ class NeoLLMAttention(nn.Module):
4187
  q,
4188
  k,
4189
  v,
4190
- attention_mask,
4191
  dropout=0.0 if not self.training else self.attention_dropout,
4192
  scaling=self.scaling,
4193
  **backend_kwargs,
@@ -4414,6 +4549,21 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
4414
  self.siamese_attn_x_scale_init = float(
4415
  getattr(config, "siamese_attn_x_scale_init", 1.0)
4416
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4417
  # Controls only the first pre-attention normalisation applied directly
4418
  # to the embedding stream. Defaults to True for checkpoint/config
4419
  # backward compatibility. When False, layer 0 does not instantiate
@@ -4710,9 +4860,10 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
4710
  return delta + lr_delta + residual
4711
 
4712
  def _siamese_stream_scale(self, ref: torch.Tensor) -> torch.Tensor:
4713
- if not self.siamese_depth_scaling:
4714
- return ref.new_tensor(1.0)
4715
- return ref.new_tensor(1.0 / math.sqrt(2.0 * float(self.layer_idx + 1)))
 
4716
 
4717
  def forward_siamesenorm(
4718
  self,
@@ -5647,13 +5798,54 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
5647
  0, inputs_embeds.shape[1], device=inputs_embeds.device
5648
  ).unsqueeze(0)
5649
 
5650
- causal_mask = create_causal_mask(
5651
- config=self.config,
5652
- inputs_embeds=inputs_embeds,
5653
- attention_mask=attention_mask,
5654
- past_key_values=None,
5655
- position_ids=position_ids,
5656
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5657
 
5658
  hidden_states = inputs_embeds
5659
  use_siamesenorm = bool(getattr(self.config, "use_siamesenorm", False))
 
12
  (Zhang et al., NeurIPS 2025), and optional HOLA-derived episodic memory for
13
  the FlashAttention-2 IHA seq-expand path (Cui, 2026).
14
 
15
+ The FlashAttention training paths deliberately use dense right-padded
16
+ attention with a shared dtype and compilation contract across IHA and non-IHA.
17
+ See ``NeoLLMModel.forward`` for its padding/packing constraints.
18
+
19
  Attention stack (orthogonal, all active simultaneously when enabled):
20
  1. Gated Attention (use_gated_attention implicit via q_proj gate chunk):
21
  applies a head-specific elementwise sigmoid gate to the concatenated
 
109
 
110
  from transformers.generation import GenerationMixin
111
  from transformers.masking_utils import create_causal_mask
112
+ from transformers.modeling_flash_attention_utils import (
113
+ FlashAttentionKwargs,
114
+ _flash_attention_forward,
115
+ flash_attn_supports_top_left_mask,
116
+ )
117
  from transformers.modeling_layers import GradientCheckpointingLayer
118
  from transformers.modeling_outputs import (
119
  BaseModelOutputWithPast,
 
129
 
130
  torch._dynamo.config.capture_scalar_outputs = True
131
  logger = logging.get_logger(__name__)
132
+ _NEOLLM_FA_USE_TOP_LEFT_MASK = flash_attn_supports_top_left_mask()
133
 
134
  # ── Optional flash_attn direct import (required only for IHA seq-expand, P>1) ──
135
  # Attempted once at module load time so the symbols are available when
 
1360
  return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
1361
 
1362
 
1363
+ def _flash_target_dtype(
1364
+ query: torch.Tensor,
1365
+ module: nn.Module,
1366
+ ) -> Optional[torch.dtype]:
1367
+ """Return the half dtype required by FlashAttention for FP32 Q/K states.
1368
+
1369
+ PyTorch RMSNorm intentionally returns FP32 under BF16 autocast. IHA's
1370
+ pseudo-head einsums cast those states back through autocast, while the
1371
+ standard path has no equivalent operation. Resolve the target here so
1372
+ both routes enter every FlashAttention backend with the same dtype instead
1373
+ of relying on a backend-specific compatibility cast.
1374
+ """
1375
+ if query.dtype != torch.float32:
1376
+ return None
1377
+ device_type = query.device.type
1378
+ if torch.is_autocast_enabled(device_type):
1379
+ return torch.get_autocast_dtype(device_type)
1380
+ if hasattr(module.config, "_is_quantized"):
1381
+ return module.config.dtype
1382
+ return next(
1383
+ layer.weight.dtype
1384
+ for layer in module.modules()
1385
+ if isinstance(layer, nn.Linear)
1386
+ )
1387
+
1388
+
1389
+ def neollm_flash_attention_forward(
1390
+ module: nn.Module,
1391
+ query: torch.Tensor,
1392
+ key: torch.Tensor,
1393
+ value: torch.Tensor,
1394
+ attention_mask: Optional[torch.Tensor],
1395
+ dropout: float = 0.0,
1396
+ scaling: Optional[float] = None,
1397
+ sliding_window: Optional[int] = None,
1398
+ softcap: Optional[float] = None,
1399
+ is_causal: Optional[bool] = None,
1400
+ s_aux: Optional[torch.Tensor] = None,
1401
+ **kwargs: Unpack[TransformersKwargs],
1402
+ ):
1403
+ """Compile-stable HF FlashAttention bridge for NeoLLM.
1404
+
1405
+ This mirrors the Transformers FA2/FA3 integration but deliberately does
1406
+ not forward ``module.layer_idx``. That integer is not consumed by the
1407
+ current FA2/FA3 implementation (it is discarded through ``**kwargs``), yet
1408
+ Dynamo guards its value and otherwise compiles one copy per decoder layer.
1409
+ Keeping the bridge local preserves the public integer attribute for model
1410
+ tooling, checkpoints, and diagnostics without making it part of the
1411
+ attention graph signature.
1412
+ """
1413
+ if kwargs.get("output_attentions", False):
1414
+ logger.warning_once(
1415
+ "Flash Attention does not support `output_attentions=True`. "
1416
+ "Please set attention to `eager` to request attention weights."
1417
+ )
1418
+
1419
+ seq_len = query.shape[2]
1420
+ if any(dim == 0 for dim in query.shape):
1421
+ raise ValueError(
1422
+ "FlashAttention does not support a query tensor with a zero dimension."
1423
+ )
1424
+
1425
+ query = query.transpose(1, 2)
1426
+ key = key.transpose(1, 2)
1427
+ value = value.transpose(1, 2)
1428
+ is_causal = is_causal if is_causal is not None else module.is_causal
1429
+
1430
+ attn_output = _flash_attention_forward(
1431
+ query,
1432
+ key,
1433
+ value,
1434
+ attention_mask,
1435
+ query_length=seq_len,
1436
+ is_causal=is_causal,
1437
+ dropout=dropout,
1438
+ softmax_scale=scaling,
1439
+ sliding_window=sliding_window,
1440
+ softcap=softcap,
1441
+ use_top_left_mask=_NEOLLM_FA_USE_TOP_LEFT_MASK,
1442
+ target_dtype=None, # NeoLLMAttention normalizes Q/K/V before dispatch.
1443
+ attn_implementation=module.config._attn_implementation,
1444
+ s_aux=s_aux.to(query.dtype) if s_aux is not None else None,
1445
+ **kwargs,
1446
+ )
1447
+ return attn_output, None
1448
+
1449
+
1450
  def causal_first_difference(x: torch.Tensor) -> torch.Tensor:
1451
  return x - F.pad(x[..., :-1, :], (0, 0, 1, 0))
1452
 
 
1891
  """
1892
  # ── Term 1: standard flash / sdpa output ─────────────────────────────
1893
  # dropout=0.0: we apply dropout to the combined output below instead.
1894
+ attn_impl = module.config._attn_implementation
1895
+ attn_fn = (
1896
+ neollm_flash_attention_forward
1897
+ if attn_impl in {"flash_attention_2", "flash_attention_3"}
1898
+ else ALL_ATTENTION_FUNCTIONS[attn_impl]
1899
+ )
1900
  flash_out, _ = attn_fn(
1901
  module,
1902
  query,
 
4111
  # was designed for the head-expand layout and would mismatch here.
4112
  # For P=1 or no IHA: the existing branch logic in _apply_mea_head_mixing
4113
  # is unchanged and handles both the pseudo-slot-aware and normal cases.
4114
+ # Standard FlashAttention layers use the dense right-padded training
4115
+ # contract even inside a mixed IHA schedule. Keep ``attention_mask``
4116
+ # itself untouched for IHA packing detection and auxiliary operators;
4117
+ # only the backend argument is normalized to ``None``.
4118
+ backend_attention_mask = attention_mask
4119
+ if (
4120
+ self.training
4121
+ and self.config._attn_implementation
4122
+ in {"flash_attention_2", "flash_attention_3"}
4123
+ ):
4124
+ backend_attention_mask = None
4125
+
4126
  if _iha_seq_expand:
4127
  k, v = self._apply_mea_head_mixing(k, v, _force_standard=True)
4128
  else:
 
4146
  position_ids=position_ids,
4147
  )
4148
 
4149
+ # Normalize FlashAttention inputs for every topology, not only the
4150
+ # non-IHA fallback. RMSNorm produces FP32 under BF16 autocast; IHA's
4151
+ # pseudo-head einsums happened to cast it back implicitly, whereas the
4152
+ # standard route reached the Transformers wrapper in FP32. An explicit
4153
+ # dispatch-boundary cast gives IHA and non-IHA identical dtype behavior,
4154
+ # avoids hidden backend casts/warnings, and keeps FP32 work where it is
4155
+ # intentional (normalization, RoPE/REPO and momentum calculations).
4156
+ if self.config._attn_implementation in {
4157
+ "flash_attention_2",
4158
+ "flash_attention_3",
4159
+ }:
4160
+ flash_dtype = _flash_target_dtype(q, self)
4161
+ if flash_dtype is not None:
4162
+ q = q.to(flash_dtype)
4163
+ k = k.to(flash_dtype)
4164
+ v = v.to(flash_dtype)
4165
+
4166
  # ── IHA: local sliding-window mask for non-seq-expand path (P=1) ─────
4167
  # For P>1 (seq-expand): the sliding window is expressed as window_size
4168
  # in flash_attn_func and handled inside _iha_seq_expand_flash_forward.
 
4294
  q,
4295
  k,
4296
  v,
4297
+ backend_attention_mask,
4298
  scaling=self.scaling,
4299
  alpha=alpha,
4300
  beta=beta,
 
4304
  else:
4305
  if self.config._attn_implementation == "eager":
4306
  attn_fn = eager_attention_forward
4307
+ elif self.config._attn_implementation in {
4308
+ "flash_attention_2",
4309
+ "flash_attention_3",
4310
+ }:
4311
+ attn_fn = neollm_flash_attention_forward
4312
  else:
4313
  attn_fn = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
4314
  backend_kwargs = kwargs
 
4322
  q,
4323
  k,
4324
  v,
4325
+ backend_attention_mask,
4326
  dropout=0.0 if not self.training else self.attention_dropout,
4327
  scaling=self.scaling,
4328
  **backend_kwargs,
 
4549
  self.siamese_attn_x_scale_init = float(
4550
  getattr(config, "siamese_attn_x_scale_init", 1.0)
4551
  )
4552
+ siamese_stream_scale = (
4553
+ 1.0
4554
+ if not self.siamese_depth_scaling
4555
+ else 1.0 / math.sqrt(2.0 * float(layer_idx + 1))
4556
+ )
4557
+ # A tensor buffer keeps the depth-dependent value out of Python control
4558
+ # flow during forward. Computing it from ``self.layer_idx`` inside a
4559
+ # Dynamo resume frame specialized that frame once per decoder layer.
4560
+ # Non-persistent preserves checkpoint compatibility: the value is fully
4561
+ # determined by layer_idx and the config on every construction.
4562
+ self.register_buffer(
4563
+ "_siamese_stream_scale_value",
4564
+ torch.tensor(siamese_stream_scale, dtype=torch.float32),
4565
+ persistent=False,
4566
+ )
4567
  # Controls only the first pre-attention normalisation applied directly
4568
  # to the embedding stream. Defaults to True for checkpoint/config
4569
  # backward compatibility. When False, layer 0 does not instantiate
 
4860
  return delta + lr_delta + residual
4861
 
4862
  def _siamese_stream_scale(self, ref: torch.Tensor) -> torch.Tensor:
4863
+ return self._siamese_stream_scale_value.to(
4864
+ device=ref.device,
4865
+ dtype=ref.dtype,
4866
+ )
4867
 
4868
  def forward_siamesenorm(
4869
  self,
 
5798
  0, inputs_embeds.shape[1], device=inputs_embeds.device
5799
  ).unsqueeze(0)
5800
 
5801
+ # ── Compile-stable FlashAttention training mask contract ─────────────
5802
+ # The Transformers FA2/FA3 wrapper treats a 2-D padding mask as a
5803
+ # variable-length problem: it branches between ``None`` and a tensor,
5804
+ # calls ``nonzero`` to unpad it, and specializes on both the resulting
5805
+ # token count and ``layer_idx``. With batches containing different
5806
+ # amounts of padding, Dynamo therefore recompiles layer by layer and
5807
+ # retains several compiled graphs, which can *increase* VRAM precisely
5808
+ # when IHA is disabled. IHA's seq-expand kernel does not use that
5809
+ # wrapper, so it did not expose the same behavior.
5810
+ #
5811
+ # Training in this project has a strict dense, causal, right-padding
5812
+ # contract (no packed sequences and no interior holes). Under that
5813
+ # contract, valid queries occur before every padded key and causal
5814
+ # attention cannot see those future padded positions. Outputs for
5815
+ # padded queries are discarded by the masked objectives. Passing
5816
+ # ``None`` to standard decoder attention backends is thus equivalent on
5817
+ # trainable tokens while keeping one fixed dense kernel topology.
5818
+ #
5819
+ # IMPORTANT: the original ``attention_mask`` remains intact above and
5820
+ # outside the decoder: it still builds ``position_ids`` and is still
5821
+ # consumed by CCE/TWEO/NITP/temporal/NextLat and label masking. Do not
5822
+ # broaden this branch to packed/left-padded data. Validate that kind
5823
+ # of input at the data-pipeline boundary rather than with a tensor-value
5824
+ # check here, because a runtime ``all()``/``nonzero()`` would recreate
5825
+ # the graph breaks this branch is intended to remove. Evaluation,
5826
+ # generation and non-FlashAttention backends retain standard mask
5827
+ # construction below. IHA receives the original 2-D mask only as a
5828
+ # static dense/padded sentinel for its packing detector; its direct
5829
+ # kernel intentionally does not unpad fixed-length right-padded batches.
5830
+ use_dense_right_padded_flash_train = (
5831
+ self.training
5832
+ and self.config._attn_implementation
5833
+ in {"flash_attention_2", "flash_attention_3"}
5834
+ )
5835
+ if use_dense_right_padded_flash_train:
5836
+ causal_mask = (
5837
+ attention_mask
5838
+ if bool(getattr(self.config, "use_iha", False))
5839
+ else None
5840
+ )
5841
+ else:
5842
+ causal_mask = create_causal_mask(
5843
+ config=self.config,
5844
+ inputs_embeds=inputs_embeds,
5845
+ attention_mask=attention_mask,
5846
+ past_key_values=None,
5847
+ position_ids=position_ids,
5848
+ )
5849
 
5850
  hidden_states = inputs_embeds
5851
  use_siamesenorm = bool(getattr(self.config, "use_siamesenorm", False))