KitsuVp commited on
Commit
348f225
·
verified ·
1 Parent(s): c51425d

Update modeling_neollm.py

Browse files
Files changed (1) hide show
  1. modeling_neollm.py +120 -31
modeling_neollm.py CHANGED
@@ -2245,23 +2245,54 @@ class RepoGrapePositioning(nn.Module):
2245
  r"""
2246
  Minimal REPO-GRAPE-M positional operator.
2247
 
2248
- This module intentionally keeps only the specific operator from the
2249
- proposal:
2250
 
2251
  1. REPO predicts contextual coordinates z_i^(h)=f_phi^(h)(h_i).
2252
  2. REPO-GRAPE uses u_i^(h)=z_i^(h), with no extra position mode.
2253
  3. GRAPE-M applies canonical commuting SO(d) rotary planes to Q/K.
2254
- 4. Each query head learns its own angular spectrum:
2255
 
2256
- theta_{h,r}=inv_freq_r*exp(s_{h,r}).
2257
 
2258
- Therefore
 
 
 
 
 
2259
 
2260
  (G_h(u_i)q_i)^T(G_h(u_j)k_j)
2261
- = q_i^T G_h(u_j-u_i) k_j,
 
 
 
 
 
 
 
 
 
 
 
 
 
2262
 
2263
- preserving the relative GRAPE law while staying compatible with the
2264
- existing attention backend.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2265
  """
2266
 
2267
  def __init__(
@@ -2335,15 +2366,38 @@ class RepoGrapePositioning(nn.Module):
2335
  inv_freq: torch.Tensor,
2336
  attention_scaling: float,
2337
  ) -> Tuple[torch.Tensor, torch.Tensor]:
2338
- """
2339
  Apply REPO-GRAPE-M rotation to Q/K using contextual REPO coordinates.
2340
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2341
  Args:
2342
  q: [B, H_q_eff, S, head_dim]
2343
  k: [B, H_k_eff, S, head_dim]
2344
  z: [B, H_q_base, S]
2345
  inv_freq: [rotary_dim/2]
2346
  attention_scaling: scaling from NeoLLMRotaryEmbedding
 
 
 
 
2347
  """
2348
  B, H_repo, S = z.shape
2349
  H_q_eff = q.shape[1]
@@ -2372,11 +2426,6 @@ class RepoGrapePositioning(nn.Module):
2372
 
2373
  q_per_k_base = H_repo // H_k_base
2374
  z_q = z_q_struct.reshape(B, H_q_eff, S)
2375
- z_k = (
2376
- z_q_struct.view(B, H_k_base, q_per_k_base, P, S)
2377
- .mean(dim=2)
2378
- .reshape(B, H_k_eff, S)
2379
- )
2380
 
2381
  rot_half = int(inv_freq.shape[0])
2382
  rotary_dim = rot_half * 2
@@ -2384,26 +2433,66 @@ class RepoGrapePositioning(nn.Module):
2384
  freq_repo = self._query_freq(inv_freq, H_repo)
2385
  freq_q_struct = freq_repo.unsqueeze(1).expand(H_repo, P, rot_half)
2386
  freq_q = freq_q_struct.reshape(H_q_eff, rot_half)
2387
- freq_k = (
2388
- freq_q_struct.view(H_k_base, q_per_k_base, P, rot_half)
2389
- .mean(dim=1)
2390
- .reshape(H_k_eff, rot_half)
2391
- )
2392
 
2393
- freqs_q = z_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
 
 
 
2394
  1, H_q_eff, 1, rot_half
2395
  )
2396
- freqs_k = z_k.float().unsqueeze(-1) * freq_k.to(device=k.device).view(
2397
- 1, H_k_eff, 1, rot_half
2398
- )
2399
-
2400
- emb_q = torch.cat([freqs_q, freqs_q], dim=-1)
2401
- emb_k = torch.cat([freqs_k, freqs_k], dim=-1)
2402
-
2403
- cos_q = (emb_q.cos() * attention_scaling).to(q.dtype)
2404
- sin_q = (emb_q.sin() * attention_scaling).to(q.dtype)
2405
- cos_k = (emb_k.cos() * attention_scaling).to(k.dtype)
2406
- sin_k = (emb_k.sin() * attention_scaling).to(k.dtype)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2407
 
2408
  q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
2409
  k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
 
2245
  r"""
2246
  Minimal REPO-GRAPE-M positional operator.
2247
 
2248
+ This module intentionally keeps only the specific multiplicative path:
 
2249
 
2250
  1. REPO predicts contextual coordinates z_i^(h)=f_phi^(h)(h_i).
2251
  2. REPO-GRAPE uses u_i^(h)=z_i^(h), with no extra position mode.
2252
  3. GRAPE-M applies canonical commuting SO(d) rotary planes to Q/K.
2253
+ 4. Each query head learns its own angular spectrum
2254
 
2255
+ theta_{h,r}=inv_freq_r*exp(s_{h,r}),
2256
 
2257
+ and therefore its own phase
2258
+
2259
+ phi_{b,h,i,r}=u_{b,h,i}*theta_{h,r}.
2260
+
2261
+ With one key per query head, the multiplicative action obeys the exact
2262
+ relative law
2263
 
2264
  (G_h(u_i)q_i)^T(G_h(u_j)k_j)
2265
+ = q_i^T G_h(u_j-u_i) k_j.
2266
+
2267
+ GQA introduces a separate geometric restriction: several query heads
2268
+ share one key, although their phases phi_{b,h,i,r} can differ. A shared
2269
+ key cannot realize every head-specific rotation simultaneously. For a
2270
+ KV group H_m and rotary plane r, this implementation therefore projects
2271
+ the requested rotations onto the single optimal shared SO(2) rotation:
2272
+
2273
+ Z_{b,m,i,r} = (1/g) * sum_{h in H_m} exp(i*phi_{b,h,i,r})
2274
+ = C_{b,m,i,r} + i*S_{b,m,i,r},
2275
+
2276
+ psi*_{b,m,i,r} = arg Z_{b,m,i,r},
2277
+
2278
+ or, equivalently when |Z| > 0,
2279
 
2280
+ cos(psi*) = C/|Z|,
2281
+ sin(psi*) = S/|Z|.
2282
+
2283
+ This is the global minimizer of
2284
+
2285
+ sum_{h in H_m} ||R(phi_{b,h,i,r})k - R(psi)k||_2^2,
2286
+
2287
+ so it replaces the previous arithmetic approximation
2288
+
2289
+ mean(u_h) * mean(theta_h),
2290
+
2291
+ which neither averages the effective phase u_h*theta_h nor respects its
2292
+ 2*pi periodicity. If the circular resultant is numerically zero, the
2293
+ objective does not identify a preferred shared direction; the code uses
2294
+ the identity rotation as a deterministic finite convention. Under IHA,
2295
+ the projection is computed independently inside every pseudo-slot.
2296
  """
2297
 
2298
  def __init__(
 
2366
  inv_freq: torch.Tensor,
2367
  attention_scaling: float,
2368
  ) -> Tuple[torch.Tensor, torch.Tensor]:
2369
+ r"""
2370
  Apply REPO-GRAPE-M rotation to Q/K using contextual REPO coordinates.
2371
 
2372
+ Query heads keep their individual phases
2373
+
2374
+ phi_{b,h,i,r} = z_{b,h,i} * theta_{h,r}.
2375
+
2376
+ For GQA, each KV head m serves a group H_m of query heads. Its shared
2377
+ rotation is the circular projection
2378
+
2379
+ C = mean_{h in H_m} cos(phi_h),
2380
+ S = mean_{h in H_m} sin(phi_h),
2381
+ (cos psi*, sin psi*) = (C, S) / sqrt(C^2 + S^2).
2382
+
2383
+ This is equivalent to psi*=arg(mean_h exp(i*phi_h)) and minimizes the
2384
+ total squared discrepancy between the head-specific rotated keys and
2385
+ one shared rotated key. The reduction is performed independently for
2386
+ every batch element, current sequence index, rotary plane and KV group.
2387
+ Because seq-expand IHA stores each pseudo-slot at a different sequence
2388
+ index, its pseudo-slots remain independent automatically. No extra
2389
+ mode or configuration flag is introduced.
2390
+
2391
  Args:
2392
  q: [B, H_q_eff, S, head_dim]
2393
  k: [B, H_k_eff, S, head_dim]
2394
  z: [B, H_q_base, S]
2395
  inv_freq: [rotary_dim/2]
2396
  attention_scaling: scaling from NeoLLMRotaryEmbedding
2397
+
2398
+ Returns:
2399
+ Rotated ``(q, k)`` tensors with the same shapes and dtypes as the
2400
+ inputs. Channels beyond ``rotary_dim`` pass through unchanged.
2401
  """
2402
  B, H_repo, S = z.shape
2403
  H_q_eff = q.shape[1]
 
2426
 
2427
  q_per_k_base = H_repo // H_k_base
2428
  z_q = z_q_struct.reshape(B, H_q_eff, S)
 
 
 
 
 
2429
 
2430
  rot_half = int(inv_freq.shape[0])
2431
  rotary_dim = rot_half * 2
 
2433
  freq_repo = self._query_freq(inv_freq, H_repo)
2434
  freq_q_struct = freq_repo.unsqueeze(1).expand(H_repo, P, rot_half)
2435
  freq_q = freq_q_struct.reshape(H_q_eff, rot_half)
 
 
 
 
 
2436
 
2437
+ # Individual query phases are the fundamental quantity. They are
2438
+ # calculated in fp32 before trigonometric evaluation, as in the
2439
+ # original REPO-GRAPE path.
2440
+ phase_q = z_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
2441
  1, H_q_eff, 1, rot_half
2442
  )
2443
+ cos_q_half = phase_q.cos()
2444
+ sin_q_half = phase_q.sin()
2445
+
2446
+ # GQA circular projection. Restore the logical head factorization
2447
+ # (KV group, query-within-group, retained head-expansion slot) and
2448
+ # average only over query-within-group. In the current seq-expand IHA
2449
+ # path pseudo-slots live on the sequence axis, which is never reduced.
2450
+ # Averaging cos/sin and normalizing the resultant is exactly equivalent
2451
+ # to arg(mean(exp(i*phase))).
2452
+ cos_q_grouped = cos_q_half.reshape(
2453
+ B, H_k_base, q_per_k_base, P, S, rot_half
2454
+ )
2455
+ sin_q_grouped = sin_q_half.reshape(
2456
+ B, H_k_base, q_per_k_base, P, S, rot_half
2457
+ )
2458
+ cos_k_half = cos_q_grouped.mean(dim=2)
2459
+ sin_k_half = sin_q_grouped.mean(dim=2)
2460
+
2461
+ resultant_sq = cos_k_half.square() + sin_k_half.square()
2462
+ well_defined = resultant_sq > torch.finfo(resultant_sq.dtype).eps
2463
+ safe_resultant_sq = torch.where(
2464
+ well_defined, resultant_sq, torch.ones_like(resultant_sq)
2465
+ )
2466
+ inv_resultant = torch.rsqrt(safe_resultant_sq)
2467
+
2468
+ # If the resultant vanishes, every shared angle has the same objective
2469
+ # value. Select identity (cos=1, sin=0) to keep forward/backward finite
2470
+ # and deterministic; this is a numerical convention, not a new mode.
2471
+ cos_k_half = torch.where(
2472
+ well_defined,
2473
+ cos_k_half * inv_resultant,
2474
+ torch.ones_like(cos_k_half),
2475
+ )
2476
+ sin_k_half = torch.where(
2477
+ well_defined,
2478
+ sin_k_half * inv_resultant,
2479
+ torch.zeros_like(sin_k_half),
2480
+ )
2481
+ cos_k_half = cos_k_half.reshape(B, H_k_eff, S, rot_half)
2482
+ sin_k_half = sin_k_half.reshape(B, H_k_eff, S, rot_half)
2483
+
2484
+ cos_q = (
2485
+ torch.cat([cos_q_half, cos_q_half], dim=-1) * attention_scaling
2486
+ ).to(q.dtype)
2487
+ sin_q = (
2488
+ torch.cat([sin_q_half, sin_q_half], dim=-1) * attention_scaling
2489
+ ).to(q.dtype)
2490
+ cos_k = (
2491
+ torch.cat([cos_k_half, cos_k_half], dim=-1) * attention_scaling
2492
+ ).to(k.dtype)
2493
+ sin_k = (
2494
+ torch.cat([sin_k_half, sin_k_half], dim=-1) * attention_scaling
2495
+ ).to(k.dtype)
2496
 
2497
  q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
2498
  k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]