KitsuVp commited on
Commit
5a23396
·
verified ·
1 Parent(s): c945d89

Update modeling_neollm.py

Browse files
Files changed (1) hide show
  1. modeling_neollm.py +22 -46
modeling_neollm.py CHANGED
@@ -1911,8 +1911,8 @@ def _apply_repo_rope(
1911
  n_groups = H // H_kv
1912
  rotary_dim = inv_freq.shape[0] * 2 # inv_freq covers half the rotary dim
1913
 
1914
- # inv_freq is already float32 on the correct device (registered as buffer
1915
- # via set_repo_inv_freq) no .to() needed, no DeviceCopy op.
1916
  # No autocast barrier: explicit .float() casts on z_q/z_k are sufficient
1917
  # to maintain float32 precision for the trig ops. Removing the context
1918
  # manager lets Inductor plan all intermediate tensors as part of a single
@@ -2182,37 +2182,9 @@ class NeoLLMAttention(nn.Module):
2182
  d_p=_d_p,
2183
  num_heads=config.num_attention_heads,
2184
  )
2185
- # _repo_inv_freq is registered as a non-persistent buffer by
2186
- # set_repo_inv_freq(), called from NeoLLMModel.__init__ after
2187
- # rotary_emb is built. Declaring it here would conflict.
2188
- self._repo_attn_scaling: float = 1.0
2189
  else:
2190
  self.repo_module = None
2191
 
2192
- def set_repo_inv_freq(
2193
- self,
2194
- inv_freq: torch.Tensor,
2195
- attention_scaling: float,
2196
- ) -> None:
2197
- """
2198
- Inject the rotary frequency vector from NeoLLMRotaryEmbedding so that
2199
- REPO can build cos/sin inline from continuous positions.
2200
-
2201
- Called once by NeoLLMModel.__init__ after rotary_emb is constructed.
2202
- Only has effect when use_repo=True for this layer.
2203
-
2204
- Args:
2205
- inv_freq: [rotary_dim/2] — frozen inv_freq buffer from
2206
- NeoLLMRotaryEmbedding.
2207
- attention_scaling: float — attention_scaling from the same module.
2208
- """
2209
- if self.use_repo:
2210
- # Register as non-persistent buffer so .to(device) / .cuda() moves
2211
- # it automatically — eliminates the DeviceCopy op that splits the
2212
- # CUDAGraph into 2 partitions when _apply_repo_rope runs.
2213
- self.register_buffer("_repo_inv_freq", inv_freq.float(), persistent=False)
2214
- self._repo_attn_scaling = attention_scaling
2215
-
2216
  def _apply_momentum_attention(
2217
  self,
2218
  q: torch.Tensor,
@@ -2348,6 +2320,7 @@ class NeoLLMAttention(nn.Module):
2348
  attention_mask: Optional[torch.Tensor] = None,
2349
  first_layer_fan: Optional[torch.Tensor] = None,
2350
  attn_analysis: Optional[AttentionAnalysis] = None,
 
2351
  **kwargs: Unpack[FlashAttentionKwargs],
2352
  ) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
2353
  input_shape = hidden_states.shape[:-1]
@@ -2385,14 +2358,14 @@ class NeoLLMAttention(nn.Module):
2385
  # REPO path: f_ϕ predicts continuous per-head positions from the
2386
  # residual stream, then cos/sin are built inline from those positions
2387
  # so the rotation is differentiable w.r.t. REPOModule parameters.
 
 
 
2388
  # (Li et al., 2026, §3.2 — Eq. 6–7)
2389
  repo_a = attn_analysis.repo if attn_analysis is not None else None
2390
  z = self.repo_module(hidden_states, repo_analysis=repo_a) # [B, H, S]
2391
- q, k = _apply_repo_rope(
2392
- q, k, z,
2393
- self._repo_inv_freq,
2394
- self._repo_attn_scaling,
2395
- )
2396
  else:
2397
  # Standard path: integer positions pre-computed by NeoLLMModel.
2398
  q, k = apply_rotary_pos_emb(q, k, cos, sin)
@@ -2773,6 +2746,7 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
2773
  attn_res_partial: Optional[torch.Tensor] = None,
2774
  layer_analysis: Optional[LayerAnalysis] = None,
2775
  output_attentions: Optional[bool] = False,
 
2776
  **kwargs: Unpack[FlashAttentionKwargs],
2777
  ) -> Tuple:
2778
  # ── Snapshot input ────────────────────────────────────────────────
@@ -2810,6 +2784,7 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
2810
  position_embeddings=position_embeddings,
2811
  first_layer_fan=first_layer_fan,
2812
  attn_analysis=layer_analysis.attention if layer_analysis is not None else None,
 
2813
  **kwargs,
2814
  )
2815
 
@@ -3329,17 +3304,6 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
3329
 
3330
  self.post_init()
3331
 
3332
- # ── REPO: inject inv_freq into every attention layer that uses it ─────
3333
- # Done after post_init so rotary_emb.inv_freq is already initialized.
3334
- # Layers below repo_start_layer never call set_repo_inv_freq (their
3335
- # use_repo flag is False) so the call is harmless for those layers.
3336
- if getattr(config, "use_repo", False):
3337
- for layer in self.layers:
3338
- layer.self_attn.set_repo_inv_freq(
3339
- self.rotary_emb.inv_freq,
3340
- self.rotary_emb.attention_scaling,
3341
- )
3342
-
3343
  def get_input_embeddings(self):
3344
  if self.config.use_token_generator:
3345
  return self.token_generator
@@ -3471,6 +3435,17 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
3471
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
3472
  self.first_layer_fan = None
3473
 
 
 
 
 
 
 
 
 
 
 
 
3474
  # ── Attention Residuals state ──────────────────────────────────────
3475
  # Full AttnRes (attn_res_num_blocks=0): sources grows by one entry per
3476
  # decoder layer — all previous outputs are kept, max N=num_layers+1.
@@ -3531,6 +3506,7 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
3531
  attn_res_partial=attn_res_partial if use_attn_res else None,
3532
  layer_analysis=layer_analysis,
3533
  output_attentions=output_attentions,
 
3534
  **kwargs,
3535
  )
3536
  hidden_states = layer_outputs[0]
 
1911
  n_groups = H // H_kv
1912
  rotary_dim = inv_freq.shape[0] * 2 # inv_freq covers half the rotary dim
1913
 
1914
+ # inv_freq arrives from rotary_emb at forward time via repo_rope_args
1915
+ # already float32 on the correct device, no .to() needed, no DeviceCopy op.
1916
  # No autocast barrier: explicit .float() casts on z_q/z_k are sufficient
1917
  # to maintain float32 precision for the trig ops. Removing the context
1918
  # manager lets Inductor plan all intermediate tensors as part of a single
 
2182
  d_p=_d_p,
2183
  num_heads=config.num_attention_heads,
2184
  )
 
 
 
 
2185
  else:
2186
  self.repo_module = None
2187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2188
  def _apply_momentum_attention(
2189
  self,
2190
  q: torch.Tensor,
 
2320
  attention_mask: Optional[torch.Tensor] = None,
2321
  first_layer_fan: Optional[torch.Tensor] = None,
2322
  attn_analysis: Optional[AttentionAnalysis] = None,
2323
+ repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
2324
  **kwargs: Unpack[FlashAttentionKwargs],
2325
  ) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
2326
  input_shape = hidden_states.shape[:-1]
 
2358
  # REPO path: f_ϕ predicts continuous per-head positions from the
2359
  # residual stream, then cos/sin are built inline from those positions
2360
  # so the rotation is differentiable w.r.t. REPOModule parameters.
2361
+ # inv_freq and attention_scaling arrive via repo_rope_args, sourced
2362
+ # directly from rotary_emb at forward time — no buffer on this module,
2363
+ # no meta-tensor issue on lm_eval / to(device) paths.
2364
  # (Li et al., 2026, §3.2 — Eq. 6–7)
2365
  repo_a = attn_analysis.repo if attn_analysis is not None else None
2366
  z = self.repo_module(hidden_states, repo_analysis=repo_a) # [B, H, S]
2367
+ inv_freq, attn_scaling = repo_rope_args
2368
+ q, k = _apply_repo_rope(q, k, z, inv_freq, attn_scaling)
 
 
 
2369
  else:
2370
  # Standard path: integer positions pre-computed by NeoLLMModel.
2371
  q, k = apply_rotary_pos_emb(q, k, cos, sin)
 
2746
  attn_res_partial: Optional[torch.Tensor] = None,
2747
  layer_analysis: Optional[LayerAnalysis] = None,
2748
  output_attentions: Optional[bool] = False,
2749
+ repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
2750
  **kwargs: Unpack[FlashAttentionKwargs],
2751
  ) -> Tuple:
2752
  # ── Snapshot input ────────────────────────────────────────────────
 
2784
  position_embeddings=position_embeddings,
2785
  first_layer_fan=first_layer_fan,
2786
  attn_analysis=layer_analysis.attention if layer_analysis is not None else None,
2787
+ repo_rope_args=repo_rope_args,
2788
  **kwargs,
2789
  )
2790
 
 
3304
 
3305
  self.post_init()
3306
 
 
 
 
 
 
 
 
 
 
 
 
3307
  def get_input_embeddings(self):
3308
  if self.config.use_token_generator:
3309
  return self.token_generator
 
3435
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
3436
  self.first_layer_fan = None
3437
 
3438
+ # ── REPO: pass inv_freq by reference at forward time ──────────────────
3439
+ # rotary_emb.inv_freq is already on the correct device (managed by
3440
+ # NeoLLMRotaryEmbedding as a buffer) — no .to(), no DeviceCopy op.
3441
+ # Computed once here and passed through the decoder layer chain so
3442
+ # NeoLLMAttention never needs to store it as a buffer itself, avoiding
3443
+ # the meta-tensor issue that occurs when lm_eval calls .to(device).
3444
+ repo_rope_args = (
3445
+ (self.rotary_emb.inv_freq, self.rotary_emb.attention_scaling)
3446
+ if getattr(self.config, "use_repo", False) else None
3447
+ )
3448
+
3449
  # ── Attention Residuals state ──────────────────────────────────────
3450
  # Full AttnRes (attn_res_num_blocks=0): sources grows by one entry per
3451
  # decoder layer — all previous outputs are kept, max N=num_layers+1.
 
3506
  attn_res_partial=attn_res_partial if use_attn_res else None,
3507
  layer_analysis=layer_analysis,
3508
  output_attentions=output_attentions,
3509
+ repo_rope_args=repo_rope_args,
3510
  **kwargs,
3511
  )
3512
  hidden_states = layer_outputs[0]