Update modeling_neollm.py
Browse files- 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 2264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 2394 |
1, H_q_eff, 1, rot_half
|
| 2395 |
)
|
| 2396 |
-
|
| 2397 |
-
|
| 2398 |
-
|
| 2399 |
-
|
| 2400 |
-
|
| 2401 |
-
|
| 2402 |
-
|
| 2403 |
-
|
| 2404 |
-
|
| 2405 |
-
|
| 2406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:]
|