Update modeling_neollm.py
Browse files- modeling_neollm.py +589 -95
modeling_neollm.py
CHANGED
|
@@ -81,6 +81,7 @@ from typing import Optional, Union, Tuple
|
|
| 81 |
import torch
|
| 82 |
import torch.nn.functional as F
|
| 83 |
from torch import nn
|
|
|
|
| 84 |
|
| 85 |
try:
|
| 86 |
from cut_cross_entropy import linear_cross_entropy # type: ignore[import-not-found]
|
|
@@ -95,6 +96,35 @@ try:
|
|
| 95 |
except ImportError:
|
| 96 |
meap_mask_inputs = None
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
_LEV_KERNEL_AVAILABLE = False
|
| 99 |
_leviathan_embedding_compiler_safe = None
|
| 100 |
_leviathan_supports = None
|
|
@@ -497,7 +527,13 @@ class LeviathanGenerator(nn.Module):
|
|
| 497 |
# in the compiled graph while Inductor excludes only the unsafe Triton
|
| 498 |
# launch from CUDA-graph capture. A CUDA launch error is allowed to
|
| 499 |
# propagate instead of falling back to reference work on the same stream.
|
| 500 |
-
def forward(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
"""
|
| 502 |
Generate embeddings from discrete token indices.
|
| 503 |
|
|
@@ -508,6 +544,24 @@ class LeviathanGenerator(nn.Module):
|
|
| 508 |
Returns:
|
| 509 |
embeddings [*token_ids.shape, hidden_size].
|
| 510 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
if (
|
| 512 |
self.use_leviathan_triton
|
| 513 |
and _LEV_KERNEL_AVAILABLE
|
|
@@ -541,6 +595,8 @@ class LeviathanGenerator(nn.Module):
|
|
| 541 |
params,
|
| 542 |
self.config,
|
| 543 |
self.knot_grid,
|
|
|
|
|
|
|
| 544 |
)
|
| 545 |
except (TypeError, ValueError, AttributeError):
|
| 546 |
# Only validation/configuration failures use the reference
|
|
@@ -592,7 +648,14 @@ class LeviathanGenerator(nn.Module):
|
|
| 592 |
|
| 593 |
e = e + modes.to(self.head_out_weight.dtype) @ self.head_out_weight[m]
|
| 594 |
|
| 595 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 596 |
|
| 597 |
|
| 598 |
|
|
@@ -870,6 +933,18 @@ class NeoLLMRotaryEmbedding(nn.Module):
|
|
| 870 |
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 871 |
self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
|
| 872 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
@staticmethod
|
| 874 |
def compute_default_rope_parameters(
|
| 875 |
config: NeoLLMConfig = None,
|
|
@@ -1697,10 +1772,16 @@ class REPOModule(nn.Module):
|
|
| 1697 |
where ``h_i ∈ R^d`` is the hidden state of token ``i`` entering the
|
| 1698 |
decoder layer (pre-FANLayer), and ``d_p = hidden_size // 8`` by default.
|
| 1699 |
|
| 1700 |
-
The resulting
|
| 1701 |
-
unconstrained.
|
| 1702 |
-
|
| 1703 |
-
``
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1704 |
|
| 1705 |
Design notes:
|
| 1706 |
- ``W_g`` and ``W_c`` are shared across heads (parameter efficiency).
|
|
@@ -1870,20 +1951,64 @@ def _apply_repo_rope(
|
|
| 1870 |
|
| 1871 |
class RepoGrapePositioning(nn.Module):
|
| 1872 |
r"""
|
| 1873 |
-
Minimal REPO-GRAPE-M positional operator.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1874 |
|
| 1875 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1876 |
|
| 1877 |
-
|
| 1878 |
-
|
| 1879 |
-
|
| 1880 |
-
|
|
|
|
|
|
|
| 1881 |
|
| 1882 |
-
|
|
|
|
| 1883 |
|
| 1884 |
-
|
|
|
|
| 1885 |
|
| 1886 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1887 |
|
| 1888 |
With one key per query head, the multiplicative action obeys the exact
|
| 1889 |
relative law
|
|
@@ -1894,32 +2019,23 @@ class RepoGrapePositioning(nn.Module):
|
|
| 1894 |
GQA introduces a separate geometric restriction: several query heads
|
| 1895 |
share one key, although their phases phi_{b,h,i,r} can differ. A shared
|
| 1896 |
key cannot realize every head-specific rotation simultaneously. For a
|
| 1897 |
-
KV group H_m and rotary plane r, this implementation
|
| 1898 |
-
|
| 1899 |
|
| 1900 |
Z_{b,m,i,r} = (1/g) * sum_{h in H_m} exp(i*phi_{b,h,i,r})
|
| 1901 |
= C_{b,m,i,r} + i*S_{b,m,i,r},
|
| 1902 |
|
| 1903 |
-
psi*_{b,m,i,r} = arg Z_{b,m,i,r}
|
| 1904 |
|
| 1905 |
-
|
| 1906 |
|
| 1907 |
cos(psi*) = C/|Z|,
|
| 1908 |
sin(psi*) = S/|Z|.
|
| 1909 |
|
| 1910 |
-
|
| 1911 |
-
|
| 1912 |
-
|
| 1913 |
-
|
| 1914 |
-
so it replaces the previous arithmetic approximation
|
| 1915 |
-
|
| 1916 |
-
mean(u_h) * mean(theta_h),
|
| 1917 |
-
|
| 1918 |
-
which neither averages the effective phase u_h*theta_h nor respects its
|
| 1919 |
-
2*pi periodicity. If the circular resultant is numerically zero, the
|
| 1920 |
-
objective does not identify a preferred shared direction; the code uses
|
| 1921 |
-
the identity rotation as a deterministic finite convention. Under IHA,
|
| 1922 |
-
the projection is computed independently inside every pseudo-slot.
|
| 1923 |
"""
|
| 1924 |
|
| 1925 |
def __init__(
|
|
@@ -1938,8 +2054,21 @@ class RepoGrapePositioning(nn.Module):
|
|
| 1938 |
self.head_dim = int(head_dim)
|
| 1939 |
self.max_rot_half = self.head_dim // 2
|
| 1940 |
|
| 1941 |
-
#
|
| 1942 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1943 |
self.freq_log_scale = nn.Parameter(
|
| 1944 |
torch.zeros(
|
| 1945 |
self.num_attention_heads, self.max_rot_half, dtype=torch.float32
|
|
@@ -1947,21 +2076,108 @@ class RepoGrapePositioning(nn.Module):
|
|
| 1947 |
requires_grad=True,
|
| 1948 |
)
|
| 1949 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1950 |
def _apply(self, fn):
|
| 1951 |
-
# Keep the
|
|
|
|
|
|
|
| 1952 |
super()._apply(fn)
|
| 1953 |
-
p
|
| 1954 |
-
|
| 1955 |
-
|
| 1956 |
-
|
| 1957 |
return self
|
| 1958 |
|
| 1959 |
def transform_positions(
|
| 1960 |
self,
|
| 1961 |
z: torch.Tensor,
|
|
|
|
| 1962 |
) -> torch.Tensor:
|
| 1963 |
-
"""
|
| 1964 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1965 |
|
| 1966 |
def _query_freq(
|
| 1967 |
self,
|
|
@@ -1989,16 +2205,18 @@ class RepoGrapePositioning(nn.Module):
|
|
| 1989 |
self,
|
| 1990 |
q: torch.Tensor,
|
| 1991 |
k: torch.Tensor,
|
| 1992 |
-
|
| 1993 |
inv_freq: torch.Tensor,
|
| 1994 |
attention_scaling: float,
|
| 1995 |
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 1996 |
r"""
|
| 1997 |
-
Apply REPO-GRAPE-M rotation to Q/K using
|
| 1998 |
|
|
|
|
|
|
|
| 1999 |
Query heads keep their individual phases
|
| 2000 |
|
| 2001 |
-
phi_{b,h,i,r} =
|
| 2002 |
|
| 2003 |
For GQA, each KV head m serves a group H_m of query heads. Its shared
|
| 2004 |
rotation is the circular projection
|
|
@@ -2018,7 +2236,7 @@ class RepoGrapePositioning(nn.Module):
|
|
| 2018 |
Args:
|
| 2019 |
q: [B, H_q_eff, S, head_dim]
|
| 2020 |
k: [B, H_k_eff, S, head_dim]
|
| 2021 |
-
|
| 2022 |
inv_freq: [rotary_dim/2]
|
| 2023 |
attention_scaling: scaling from NeoLLMRotaryEmbedding
|
| 2024 |
|
|
@@ -2026,13 +2244,13 @@ class RepoGrapePositioning(nn.Module):
|
|
| 2026 |
Rotated ``(q, k)`` tensors with the same shapes and dtypes as the
|
| 2027 |
inputs. Channels beyond ``rotary_dim`` pass through unchanged.
|
| 2028 |
"""
|
| 2029 |
-
B, H_repo, S =
|
| 2030 |
H_q_eff = q.shape[1]
|
| 2031 |
H_k_eff = k.shape[1]
|
| 2032 |
|
| 2033 |
if H_q_eff % H_repo != 0:
|
| 2034 |
raise ValueError(
|
| 2035 |
-
f"REPO-GRAPE expected q heads divisible by
|
| 2036 |
f"got H_q={H_q_eff} vs H_repo={H_repo}."
|
| 2037 |
)
|
| 2038 |
P = H_q_eff // H_repo
|
|
@@ -2043,7 +2261,7 @@ class RepoGrapePositioning(nn.Module):
|
|
| 2043 |
f"got H_k={H_k_eff} vs P={P}."
|
| 2044 |
)
|
| 2045 |
|
| 2046 |
-
|
| 2047 |
H_k_base = H_k_eff // max(P, 1)
|
| 2048 |
if H_repo % H_k_base != 0:
|
| 2049 |
raise ValueError(
|
|
@@ -2052,7 +2270,7 @@ class RepoGrapePositioning(nn.Module):
|
|
| 2052 |
)
|
| 2053 |
|
| 2054 |
q_per_k_base = H_repo // H_k_base
|
| 2055 |
-
|
| 2056 |
|
| 2057 |
rot_half = int(inv_freq.shape[0])
|
| 2058 |
rotary_dim = rot_half * 2
|
|
@@ -2064,7 +2282,7 @@ class RepoGrapePositioning(nn.Module):
|
|
| 2064 |
# Individual query phases are the fundamental quantity. They are
|
| 2065 |
# calculated in fp32 before trigonometric evaluation, as in the
|
| 2066 |
# original REPO-GRAPE path.
|
| 2067 |
-
phase_q =
|
| 2068 |
1, H_q_eff, 1, rot_half
|
| 2069 |
)
|
| 2070 |
cos_q_half = phase_q.cos()
|
|
@@ -2681,6 +2899,9 @@ class NeoLLMAttention(nn.Module):
|
|
| 2681 |
# REPO-GRAPE reuses the REPO coordinate module f_phi and replaces only
|
| 2682 |
# the positional action applied to Q/K. Enabling use_repo_grape=True
|
| 2683 |
# therefore activates the REPO coordinate path even when use_repo=False.
|
|
|
|
|
|
|
|
|
|
| 2684 |
_repo_requested = bool(getattr(config, "use_repo", False))
|
| 2685 |
_repo_grape_requested = bool(getattr(config, "use_repo_grape", False))
|
| 2686 |
_repo_start_layer = getattr(
|
|
@@ -2709,8 +2930,14 @@ class NeoLLMAttention(nn.Module):
|
|
| 2709 |
num_key_value_heads=config.num_key_value_heads,
|
| 2710 |
head_dim=self.head_dim,
|
| 2711 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2712 |
else:
|
| 2713 |
self.repo_grape = None
|
|
|
|
| 2714 |
|
| 2715 |
# ── REPO-GOAT factorised log-prior ───────────────────────────────────
|
| 2716 |
# Independent of use_repo/use_repo_grape: it can be ablated with standard
|
|
@@ -3791,6 +4018,7 @@ class NeoLLMAttention(nn.Module):
|
|
| 3791 |
|
| 3792 |
# ── RoPE / REPO ───────────────────────────────────────────────────────
|
| 3793 |
cos, sin = position_embeddings
|
|
|
|
| 3794 |
if self.use_repo:
|
| 3795 |
# REPO path: f_ϕ predicts continuous per-head positions from the
|
| 3796 |
# residual stream, then cos/sin are built inline from those positions
|
|
@@ -3799,34 +4027,107 @@ class NeoLLMAttention(nn.Module):
|
|
| 3799 |
# directly from rotary_emb at forward time — no buffer on this module,
|
| 3800 |
# no meta-tensor issue on lm_eval / to(device) paths.
|
| 3801 |
# (Li et al., 2026, §3.2 — Eq. 6–7)
|
| 3802 |
-
z = self.repo_module(hidden_states) # [B, H, S]
|
| 3803 |
-
if self.use_repo_grape:
|
| 3804 |
-
z = self.repo_grape.transform_positions(z)
|
| 3805 |
inv_freq, attn_scaling = repo_rope_args
|
| 3806 |
-
|
| 3807 |
-
|
| 3808 |
-
|
| 3809 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3810 |
P = self.iha_P
|
| 3811 |
-
p_offsets = torch.arange(
|
| 3812 |
-
|
| 3813 |
-
z_expanded = (z.unsqueeze(-1) * P + p_offsets.view(1, 1, 1, P)).reshape(
|
| 3814 |
-
z.shape[0], z.shape[1], _S_orig * P
|
| 3815 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3816 |
if self.use_repo_grape:
|
| 3817 |
q, k = self.repo_grape.apply_multiplicative(
|
| 3818 |
-
q, k,
|
| 3819 |
)
|
| 3820 |
else:
|
| 3821 |
-
q, k = _apply_repo_rope(
|
| 3822 |
-
|
| 3823 |
-
|
|
|
|
|
|
|
|
|
|
| 3824 |
if self.use_repo_grape:
|
| 3825 |
q, k = self.repo_grape.apply_multiplicative(
|
| 3826 |
-
q, k,
|
| 3827 |
)
|
| 3828 |
else:
|
| 3829 |
-
q, k = _apply_repo_rope(
|
|
|
|
|
|
|
| 3830 |
elif _iha_seq_expand:
|
| 3831 |
# IHA seq-expand + standard integer RoPE:
|
| 3832 |
# Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
|
|
@@ -3848,7 +4149,8 @@ class NeoLLMAttention(nn.Module):
|
|
| 3848 |
# Standard path: integer positions pre-computed by NeoLLMModel.
|
| 3849 |
q, k = apply_rotary_pos_emb(q, k, cos, sin)
|
| 3850 |
|
| 3851 |
-
|
|
|
|
| 3852 |
# ── MEA head mixing ───────────────────────────────────────────────────
|
| 3853 |
# For IHA seq-expand (P>1): K/V are [B, H_kv, S·P, d] — same number of
|
| 3854 |
# heads as without IHA, just a longer sequence. Use the standard
|
|
@@ -4187,7 +4489,44 @@ class PolyNorm(nn.Module):
|
|
| 4187 |
def forward(
|
| 4188 |
self,
|
| 4189 |
x: torch.Tensor,
|
|
|
|
| 4190 |
) -> torch.Tensor:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4191 |
# Caché de potencias: x_sq reutilizado en x1 y x2; x_cu = x·x_sq evita pow(3)
|
| 4192 |
x_sq = x.pow(2)
|
| 4193 |
x_cu = x * x_sq
|
|
@@ -4213,6 +4552,9 @@ class PolyNorm(nn.Module):
|
|
| 4213 |
self.weight[0] * x3 + self.weight[1] * x2 + self.weight[2] * x1 + self.bias
|
| 4214 |
)
|
| 4215 |
|
|
|
|
|
|
|
|
|
|
| 4216 |
return output
|
| 4217 |
|
| 4218 |
|
|
@@ -4259,10 +4601,11 @@ class NeoLLMMLP(nn.Module):
|
|
| 4259 |
gate_out = self.gate_proj(x_fan)
|
| 4260 |
up_out = self.up_proj(x_fan)
|
| 4261 |
|
| 4262 |
-
|
|
|
|
| 4263 |
act_x_up = act_out * up_out
|
| 4264 |
|
| 4265 |
-
result = self.down_proj(
|
| 4266 |
|
| 4267 |
return result
|
| 4268 |
|
|
@@ -4997,6 +5340,9 @@ class SpellingBeeEmbedding(nn.Module):
|
|
| 4997 |
self,
|
| 4998 |
token_ids: torch.Tensor, # [B, S] or [N]
|
| 4999 |
token_embeds: torch.Tensor, # [B, S, d] or [N, d]
|
|
|
|
|
|
|
|
|
|
| 5000 |
) -> torch.Tensor:
|
| 5001 |
"""
|
| 5002 |
Args:
|
|
@@ -5021,7 +5367,22 @@ class SpellingBeeEmbedding(nn.Module):
|
|
| 5021 |
].mean(-2) # [..., d], fixed 16-position mean
|
| 5022 |
|
| 5023 |
# ── Step 3: mean with token embeddings ──────────────────────────────
|
| 5024 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5025 |
|
| 5026 |
# ── Inference utility ─────────────────────────────────────────────────────
|
| 5027 |
|
|
@@ -5117,11 +5478,33 @@ class NeoLLMPreTrainedModel(PreTrainedModel):
|
|
| 5117 |
|
| 5118 |
@classmethod
|
| 5119 |
def from_pretrained(cls, *args, **kwargs):
|
| 5120 |
-
"""
|
| 5121 |
-
|
| 5122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5123 |
if not isinstance(module, LeviathanGenerator):
|
| 5124 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5125 |
if module.knot_grid.device.type == "meta":
|
| 5126 |
module._buffers["knot_grid"] = torch.linspace(
|
| 5127 |
0.0,
|
|
@@ -5132,15 +5515,45 @@ class NeoLLMPreTrainedModel(PreTrainedModel):
|
|
| 5132 |
)
|
| 5133 |
else:
|
| 5134 |
with torch.no_grad():
|
|
|
|
|
|
|
| 5135 |
module.knot_grid.copy_(
|
| 5136 |
torch.linspace(
|
| 5137 |
0.0,
|
| 5138 |
1.0,
|
| 5139 |
module.num_knots,
|
| 5140 |
device=module.knot_grid.device,
|
| 5141 |
-
dtype=
|
| 5142 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5143 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5144 |
return model
|
| 5145 |
|
| 5146 |
def _init_weights(self, module):
|
|
@@ -5346,6 +5759,26 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5346 |
else:
|
| 5347 |
self.spelling_bee = None
|
| 5348 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5349 |
self.layers = nn.ModuleList(
|
| 5350 |
[
|
| 5351 |
NeoLLMDecoderLayer(config, layer_idx)
|
|
@@ -5422,6 +5855,7 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5422 |
output_hidden_states: Optional[bool] = None,
|
| 5423 |
output_attentions: Optional[bool] = None,
|
| 5424 |
output_tweo_activations: Optional[bool] = None,
|
|
|
|
| 5425 |
return_dict: Optional[bool] = None,
|
| 5426 |
**kwargs: Unpack[TransformersKwargs],
|
| 5427 |
) -> Tuple:
|
|
@@ -5447,9 +5881,35 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5447 |
raise ValueError("Specify exactly one of input_ids or inputs_embeds")
|
| 5448 |
|
| 5449 |
# ── Embedding stage ────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5450 |
if inputs_embeds is None:
|
| 5451 |
if self.config.use_token_generator:
|
| 5452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5453 |
else:
|
| 5454 |
inputs_embeds = self.embed_tokens(input_ids)
|
| 5455 |
|
|
@@ -5458,7 +5918,29 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5458 |
# caller; in that case SBE cannot run (no token_ids available) and is
|
| 5459 |
# silently skipped — consistent with the standard embedding bypass path.
|
| 5460 |
if self.spelling_bee is not None and input_ids is not None:
|
| 5461 |
-
inputs_embeds = self.spelling_bee(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5462 |
|
| 5463 |
if position_ids is None:
|
| 5464 |
if attention_mask is not None:
|
|
@@ -5789,6 +6271,7 @@ def compute_cce_loss(
|
|
| 5789 |
cce_impl="cce_kahan_full_c",
|
| 5790 |
use_mile_loss=False,
|
| 5791 |
mile_loss_gamma=1.0,
|
|
|
|
| 5792 |
use_mu_loss=False,
|
| 5793 |
mu_loss_lambda=1e-4,
|
| 5794 |
return_loss_metrics=False,
|
|
@@ -5817,6 +6300,9 @@ def compute_cce_loss(
|
|
| 5817 |
mile_enabled=True,
|
| 5818 |
mile_gamma=float(mile_loss_gamma),
|
| 5819 |
)
|
|
|
|
|
|
|
|
|
|
| 5820 |
if use_mu_loss:
|
| 5821 |
_require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
|
| 5822 |
extension_kwargs.update(
|
|
@@ -7502,19 +7988,9 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7502 |
_require_extended_cce_options("mile_enabled", "mile_gamma")
|
| 7503 |
if bool(getattr(config, "use_mu_loss", False)):
|
| 7504 |
_require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
|
| 7505 |
-
|
| 7506 |
-
|
| 7507 |
-
|
| 7508 |
-
f"Install it with `pip install --force-reinstall --no-deps "
|
| 7509 |
-
f"\"{_EXTENDED_CCE_INSTALL}\"`, or set `use_meap=False`."
|
| 7510 |
-
)
|
| 7511 |
-
if bool(getattr(config, "use_meap", False)) and (
|
| 7512 |
-
"return_metrics" not in inspect.signature(meap_mask_inputs).parameters
|
| 7513 |
-
):
|
| 7514 |
-
raise ImportError(
|
| 7515 |
-
"The installed cut-cross-entropy package does not provide kernel-side "
|
| 7516 |
-
"MEAP metrics. Reinstall the extended backend or set `use_meap=False`."
|
| 7517 |
-
)
|
| 7518 |
self._last_ntp_loss = None
|
| 7519 |
self._last_ntp_ce_unweighted = None
|
| 7520 |
self._last_mile_reweighting_delta = None
|
|
@@ -7609,6 +8085,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7609 |
and self.training
|
| 7610 |
and labels is not None
|
| 7611 |
)
|
|
|
|
| 7612 |
if meap_enabled:
|
| 7613 |
if input_ids is None or inputs_embeds is not None:
|
| 7614 |
raise ValueError(
|
|
@@ -7617,8 +8094,13 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7617 |
)
|
| 7618 |
if meap_mask_inputs is None:
|
| 7619 |
raise ImportError(
|
| 7620 |
-
"MEAP
|
| 7621 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7622 |
)
|
| 7623 |
mask_token_id = getattr(self.config, "meap_mask_token_id", None)
|
| 7624 |
if mask_token_id is None:
|
|
@@ -7642,7 +8124,8 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7642 |
)
|
| 7643 |
exclude_last = bool(getattr(self.config, "meap_exclude_last", True))
|
| 7644 |
mask_ratio = float(getattr(self.config, "meap_mask_ratio", 0.15))
|
| 7645 |
-
|
|
|
|
| 7646 |
input_ids,
|
| 7647 |
int(mask_token_id),
|
| 7648 |
enabled=True,
|
|
@@ -7650,12 +8133,16 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7650 |
eligible_mask=eligible_mask,
|
| 7651 |
seed=effective_meap_seed,
|
| 7652 |
exclude_last=exclude_last,
|
| 7653 |
-
return_mask=
|
| 7654 |
return_metrics=True,
|
| 7655 |
implementation=str(
|
| 7656 |
getattr(self.config, "meap_implementation", "triton")
|
| 7657 |
),
|
| 7658 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7659 |
eligible_count = meap_metrics[0]
|
| 7660 |
masked_count = meap_metrics[1]
|
| 7661 |
self._last_meap_mask_fraction = (
|
|
@@ -7706,6 +8193,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7706 |
inputs_embeds=inputs_embeds,
|
| 7707 |
output_hidden_states=model_output_hidden_states,
|
| 7708 |
output_tweo_activations=tweo_enabled,
|
|
|
|
| 7709 |
return_dict=return_dict,
|
| 7710 |
**kwargs,
|
| 7711 |
)
|
|
@@ -7810,6 +8298,12 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 7810 |
mile_loss_gamma=float(
|
| 7811 |
getattr(self.config, "mile_loss_gamma", 1.0)
|
| 7812 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7813 |
use_mu_loss=bool(getattr(self.config, "use_mu_loss", False)),
|
| 7814 |
mu_loss_lambda=float(
|
| 7815 |
getattr(self.config, "mu_loss_lambda", 1e-4)
|
|
|
|
| 81 |
import torch
|
| 82 |
import torch.nn.functional as F
|
| 83 |
from torch import nn
|
| 84 |
+
from torch.utils.checkpoint import checkpoint as _activation_checkpoint
|
| 85 |
|
| 86 |
try:
|
| 87 |
from cut_cross_entropy import linear_cross_entropy # type: ignore[import-not-found]
|
|
|
|
| 96 |
except ImportError:
|
| 97 |
meap_mask_inputs = None
|
| 98 |
|
| 99 |
+
try:
|
| 100 |
+
from cut_cross_entropy.polynorm import ( # type: ignore[import-not-found]
|
| 101 |
+
polynorm as _cce_polynorm,
|
| 102 |
+
)
|
| 103 |
+
except (ImportError, ModuleNotFoundError):
|
| 104 |
+
_cce_polynorm = None
|
| 105 |
+
|
| 106 |
+
try:
|
| 107 |
+
from cut_cross_entropy.polynorm import ( # type: ignore[import-not-found]
|
| 108 |
+
polynorm_uses_cute as _cce_polynorm_uses_cute,
|
| 109 |
+
)
|
| 110 |
+
except (ImportError, ModuleNotFoundError):
|
| 111 |
+
_cce_polynorm_uses_cute = None
|
| 112 |
+
|
| 113 |
+
_REPO_GRAPE_KERNEL_AVAILABLE = False
|
| 114 |
+
_cce_repo_grape = None
|
| 115 |
+
_cce_repo_grape_supported = None
|
| 116 |
+
try:
|
| 117 |
+
from cut_cross_entropy.repo_grape import ( # type: ignore[import-not-found]
|
| 118 |
+
repo_grape as _cce_repo_grape,
|
| 119 |
+
repo_grape_supported as _cce_repo_grape_supported,
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
_REPO_GRAPE_KERNEL_AVAILABLE = True
|
| 123 |
+
except (ImportError, ModuleNotFoundError, AttributeError):
|
| 124 |
+
# REPO-GRAPE remains fully functional through RepoGrapePositioning when the
|
| 125 |
+
# optional Triton package (or a new-enough CCE checkout) is unavailable.
|
| 126 |
+
pass
|
| 127 |
+
|
| 128 |
_LEV_KERNEL_AVAILABLE = False
|
| 129 |
_leviathan_embedding_compiler_safe = None
|
| 130 |
_leviathan_supports = None
|
|
|
|
| 527 |
# in the compiled graph while Inductor excludes only the unsafe Triton
|
| 528 |
# launch from CUDA-graph capture. A CUDA launch error is allowed to
|
| 529 |
# propagate instead of falling back to reference work on the same stream.
|
| 530 |
+
def forward(
|
| 531 |
+
self,
|
| 532 |
+
token_ids: torch.Tensor,
|
| 533 |
+
*,
|
| 534 |
+
meap_mask_embedding: Optional[torch.Tensor] = None,
|
| 535 |
+
meap_mask_token_id: Optional[int] = None,
|
| 536 |
+
) -> torch.Tensor:
|
| 537 |
"""
|
| 538 |
Generate embeddings from discrete token indices.
|
| 539 |
|
|
|
|
| 544 |
Returns:
|
| 545 |
embeddings [*token_ids.shape, hidden_size].
|
| 546 |
"""
|
| 547 |
+
has_meap = (
|
| 548 |
+
meap_mask_embedding is not None or meap_mask_token_id is not None
|
| 549 |
+
)
|
| 550 |
+
if has_meap:
|
| 551 |
+
if meap_mask_embedding is None or meap_mask_token_id is None:
|
| 552 |
+
raise ValueError(
|
| 553 |
+
"meap_mask_embedding and meap_mask_token_id must be "
|
| 554 |
+
"provided together"
|
| 555 |
+
)
|
| 556 |
+
if (
|
| 557 |
+
meap_mask_embedding.ndim != 1
|
| 558 |
+
or meap_mask_embedding.numel() != self.hidden_size
|
| 559 |
+
):
|
| 560 |
+
raise ValueError(
|
| 561 |
+
"meap_mask_embedding must have shape "
|
| 562 |
+
f"({self.hidden_size},)"
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
if (
|
| 566 |
self.use_leviathan_triton
|
| 567 |
and _LEV_KERNEL_AVAILABLE
|
|
|
|
| 595 |
params,
|
| 596 |
self.config,
|
| 597 |
self.knot_grid,
|
| 598 |
+
mask_embedding=meap_mask_embedding,
|
| 599 |
+
mask_token_id=meap_mask_token_id,
|
| 600 |
)
|
| 601 |
except (TypeError, ValueError, AttributeError):
|
| 602 |
# Only validation/configuration failures use the reference
|
|
|
|
| 648 |
|
| 649 |
e = e + modes.to(self.head_out_weight.dtype) @ self.head_out_weight[m]
|
| 650 |
|
| 651 |
+
e = e.reshape(*orig_shape, self.hidden_size)
|
| 652 |
+
if has_meap:
|
| 653 |
+
e = torch.where(
|
| 654 |
+
token_ids.eq(int(meap_mask_token_id)).unsqueeze(-1),
|
| 655 |
+
meap_mask_embedding.to(device=e.device, dtype=e.dtype),
|
| 656 |
+
e,
|
| 657 |
+
)
|
| 658 |
+
return e
|
| 659 |
|
| 660 |
|
| 661 |
|
|
|
|
| 933 |
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 934 |
self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
|
| 935 |
|
| 936 |
+
def _apply(self, fn):
|
| 937 |
+
# Positional frequencies are an FP32 numerical contract for both the
|
| 938 |
+
# PyTorch and fused REPO-GRAPE paths. Preserve that contract across
|
| 939 |
+
# model.to(bfloat16)/model.to(float16) instead of allocating a cast in
|
| 940 |
+
# every decoder layer. The buffers remain non-persistent as before.
|
| 941 |
+
super()._apply(fn)
|
| 942 |
+
for name in ("inv_freq", "original_inv_freq"):
|
| 943 |
+
value = self._buffers.get(name)
|
| 944 |
+
if value is not None:
|
| 945 |
+
self._buffers[name] = value.float()
|
| 946 |
+
return self
|
| 947 |
+
|
| 948 |
@staticmethod
|
| 949 |
def compute_default_rope_parameters(
|
| 950 |
config: NeoLLMConfig = None,
|
|
|
|
| 1772 |
where ``h_i ∈ R^d`` is the hidden state of token ``i`` entering the
|
| 1773 |
decoder layer (pre-FANLayer), and ``d_p = hidden_size // 8`` by default.
|
| 1774 |
|
| 1775 |
+
The resulting assignments ``z [B, H, S]`` are real-valued and
|
| 1776 |
+
unconstrained. In the original REPO-RoPE path they are used directly to
|
| 1777 |
+
compute per-head ``cos/sin`` embeddings. When REPO is composed with
|
| 1778 |
+
REPO-GRAPE, ``RepoGrapePositioning`` keeps ``z`` as the raw REPO output and
|
| 1779 |
+
constructs the final coordinate
|
| 1780 |
+
|
| 1781 |
+
u_i^(h) = i + alpha_h (z_i^(h) - i)
|
| 1782 |
+
|
| 1783 |
+
before the GRAPE phase is formed. This keeps the semantic distinction
|
| 1784 |
+
``REPO -> z`` versus ``REPO-GRAPE -> u`` explicit in the implementation.
|
| 1785 |
|
| 1786 |
Design notes:
|
| 1787 |
- ``W_g`` and ``W_c`` are shared across heads (parameter efficiency).
|
|
|
|
| 1951 |
|
| 1952 |
class RepoGrapePositioning(nn.Module):
|
| 1953 |
r"""
|
| 1954 |
+
Minimal REPO-GRAPE-M positional operator with linear-blend coordinates.
|
| 1955 |
+
|
| 1956 |
+
The implementation deliberately has no positional-mode flag. Whenever
|
| 1957 |
+
REPO-GRAPE is active it uses exactly one coordinate map, the
|
| 1958 |
+
``linear-blend-repo`` form from the accompanying derivation:
|
| 1959 |
+
|
| 1960 |
+
z_i^(h) = f_phi^(h)(h_i),
|
| 1961 |
+
u_i^(h) = (1 - alpha_h) i + alpha_h z_i^(h)
|
| 1962 |
+
= i + alpha_h (z_i^(h) - i).
|
| 1963 |
|
| 1964 |
+
``i`` is the actual runtime ``position_ids`` value, not an independently
|
| 1965 |
+
generated global arange. This matters for padding and packed/reset
|
| 1966 |
+
position streams. ``alpha_h`` is one learned scalar per query head. It
|
| 1967 |
+
is initialized to 1, so a newly constructed model starts exactly at the
|
| 1968 |
+
previous REPO coordinate u=z and can learn a linear anchor without an
|
| 1969 |
+
abrupt initialization change.
|
| 1970 |
|
| 1971 |
+
The parameter is intentionally stored directly rather than through a
|
| 1972 |
+
sigmoid/clamp. Therefore alpha_h is initialized inside [0,1] but is not
|
| 1973 |
+
constrained to stay there. This avoids an additional nonlinear operation,
|
| 1974 |
+
preserves the exact alpha_h=1 initialization, and lets the optimizer learn
|
| 1975 |
+
interpolation or extrapolation. Constraining alpha would be a different
|
| 1976 |
+
parameterization and is not silently introduced here.
|
| 1977 |
|
| 1978 |
+
Backward is standard autograd. If ubar_i^(h)=dL/du_i^(h), then for this
|
| 1979 |
+
active linear-blend coordinate
|
| 1980 |
|
| 1981 |
+
dL/dz_i^(h) = alpha_h * ubar_i^(h),
|
| 1982 |
+
dL/dalpha_h = sum_i ubar_i^(h) * (z_i^(h) - i).
|
| 1983 |
|
| 1984 |
+
For comparison only, the distinct ``linear-plus-repo`` residual map
|
| 1985 |
+
|
| 1986 |
+
u_i^(h) = i + alpha_h z_i^(h)
|
| 1987 |
+
|
| 1988 |
+
would instead give dL/dalpha_h = sum_i ubar_i^(h) z_i^(h). That formula
|
| 1989 |
+
is documented to avoid confusing the two maps, but it is not another
|
| 1990 |
+
executable mode and no flag is added for it.
|
| 1991 |
+
|
| 1992 |
+
Storage/lifetime contract:
|
| 1993 |
+
- ``position_blend_alpha`` is an FP32 ``nn.Parameter`` of shape [H_q].
|
| 1994 |
+
It is optimized normally and is saved/restored in ``state_dict``.
|
| 1995 |
+
- ``freq_log_scale`` remains an FP32 saved parameter of shape
|
| 1996 |
+
[H_q, head_dim/2].
|
| 1997 |
+
- ``position_ids`` is runtime metadata and is never copied into a buffer or
|
| 1998 |
+
checkpoint.
|
| 1999 |
+
- ``u`` and the optional IHA-expanded ``u_{i,p}`` are transient autograd
|
| 2000 |
+
intermediates only. The IHA tensor replaces the previous ``P*z+p``
|
| 2001 |
+
intermediate, so this change does not add another expanded coordinate
|
| 2002 |
+
tensor.
|
| 2003 |
+
|
| 2004 |
+
IHA is applied after the coordinate map. For P pseudo-slots,
|
| 2005 |
+
|
| 2006 |
+
u_{i,p}^(h) = P u_i^(h) + p, p in {0,...,P-1},
|
| 2007 |
+
|
| 2008 |
+
and GRAPE sees the phase
|
| 2009 |
+
|
| 2010 |
+
phi_{b,h,i,p,r} = u_{b,h,i,p} * theta_{h,r},
|
| 2011 |
+
theta_{h,r} = inv_freq_r * exp(s_{h,r}).
|
| 2012 |
|
| 2013 |
With one key per query head, the multiplicative action obeys the exact
|
| 2014 |
relative law
|
|
|
|
| 2019 |
GQA introduces a separate geometric restriction: several query heads
|
| 2020 |
share one key, although their phases phi_{b,h,i,r} can differ. A shared
|
| 2021 |
key cannot realize every head-specific rotation simultaneously. For a
|
| 2022 |
+
KV group H_m and rotary plane r, this implementation projects the requested
|
| 2023 |
+
rotations onto the single optimal shared SO(2) rotation:
|
| 2024 |
|
| 2025 |
Z_{b,m,i,r} = (1/g) * sum_{h in H_m} exp(i*phi_{b,h,i,r})
|
| 2026 |
= C_{b,m,i,r} + i*S_{b,m,i,r},
|
| 2027 |
|
| 2028 |
+
psi*_{b,m,i,r} = arg Z_{b,m,i,r}.
|
| 2029 |
|
| 2030 |
+
Equivalently, when |Z| > 0,
|
| 2031 |
|
| 2032 |
cos(psi*) = C/|Z|,
|
| 2033 |
sin(psi*) = S/|Z|.
|
| 2034 |
|
| 2035 |
+
If the circular resultant is numerically zero, the objective does not
|
| 2036 |
+
identify a preferred shared direction; the code uses the identity rotation
|
| 2037 |
+
as a deterministic finite convention. Under IHA, the projection is
|
| 2038 |
+
computed independently inside every pseudo-slot.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2039 |
"""
|
| 2040 |
|
| 2041 |
def __init__(
|
|
|
|
| 2054 |
self.head_dim = int(head_dim)
|
| 2055 |
self.max_rot_half = self.head_dim // 2
|
| 2056 |
|
| 2057 |
+
# Linear-blend coefficient alpha_h, one scalar per query head:
|
| 2058 |
+
#
|
| 2059 |
+
# u_i^(h) = i + alpha_h * (z_i^(h) - i)
|
| 2060 |
+
#
|
| 2061 |
+
# Initialize at alpha_h=1 so the first forward is exactly the previous
|
| 2062 |
+
# REPO behavior u=z. This is a real nn.Parameter, therefore it is
|
| 2063 |
+
# optimized by autograd/AdamW and stored in every state_dict checkpoint.
|
| 2064 |
+
# It is deliberately unconstrained: no sigmoid/clamp and no new flag.
|
| 2065 |
+
self.position_blend_alpha = nn.Parameter(
|
| 2066 |
+
torch.ones(self.num_attention_heads, dtype=torch.float32),
|
| 2067 |
+
requires_grad=True,
|
| 2068 |
+
)
|
| 2069 |
+
|
| 2070 |
+
# Learned log-scale over the base RoPE/GRAPE frequencies, per query
|
| 2071 |
+
# head and rotary plane: theta_{h,r}=theta^0_r*exp(s_{h,r}).
|
| 2072 |
self.freq_log_scale = nn.Parameter(
|
| 2073 |
torch.zeros(
|
| 2074 |
self.num_attention_heads, self.max_rot_half, dtype=torch.float32
|
|
|
|
| 2076 |
requires_grad=True,
|
| 2077 |
)
|
| 2078 |
|
| 2079 |
+
def _load_from_state_dict(
|
| 2080 |
+
self,
|
| 2081 |
+
state_dict,
|
| 2082 |
+
prefix,
|
| 2083 |
+
local_metadata,
|
| 2084 |
+
strict,
|
| 2085 |
+
missing_keys,
|
| 2086 |
+
unexpected_keys,
|
| 2087 |
+
error_msgs,
|
| 2088 |
+
):
|
| 2089 |
+
# Backward-compatible checkpoint migration. Older REPO-GRAPE
|
| 2090 |
+
# checkpoints do not contain alpha_h because they used u=z. Injecting
|
| 2091 |
+
# alpha_h=1 reproduces that old forward exactly at load time, while every
|
| 2092 |
+
# checkpoint saved by this version stores the learned parameter normally.
|
| 2093 |
+
alpha_key = prefix + "position_blend_alpha"
|
| 2094 |
+
local_state_dict = state_dict
|
| 2095 |
+
if alpha_key not in state_dict:
|
| 2096 |
+
# Use a local shallow copy so loading does not mutate the caller's
|
| 2097 |
+
# checkpoint mapping. CPU FP32 also works when the module was
|
| 2098 |
+
# constructed on meta before Hugging Face materializes parameters.
|
| 2099 |
+
local_state_dict = state_dict.copy()
|
| 2100 |
+
local_state_dict[alpha_key] = torch.ones(
|
| 2101 |
+
self.num_attention_heads, dtype=torch.float32
|
| 2102 |
+
)
|
| 2103 |
+
super()._load_from_state_dict(
|
| 2104 |
+
local_state_dict,
|
| 2105 |
+
prefix,
|
| 2106 |
+
local_metadata,
|
| 2107 |
+
strict,
|
| 2108 |
+
missing_keys,
|
| 2109 |
+
unexpected_keys,
|
| 2110 |
+
error_msgs,
|
| 2111 |
+
)
|
| 2112 |
+
|
| 2113 |
def _apply(self, fn):
|
| 2114 |
+
# Keep the tiny positional parameters in FP32 under model.to(dtype).
|
| 2115 |
+
# Both are checkpointed nn.Parameters; only runtime coordinates are
|
| 2116 |
+
# transient.
|
| 2117 |
super()._apply(fn)
|
| 2118 |
+
for p in (self.position_blend_alpha, self.freq_log_scale):
|
| 2119 |
+
p.data = p.data.float()
|
| 2120 |
+
if p.grad is not None:
|
| 2121 |
+
p.grad.data = p.grad.data.float()
|
| 2122 |
return self
|
| 2123 |
|
| 2124 |
def transform_positions(
|
| 2125 |
self,
|
| 2126 |
z: torch.Tensor,
|
| 2127 |
+
position_ids: Optional[torch.Tensor],
|
| 2128 |
) -> torch.Tensor:
|
| 2129 |
+
r"""Build the active linear-blend REPO-GRAPE coordinate.
|
| 2130 |
+
|
| 2131 |
+
The only executable map is
|
| 2132 |
+
|
| 2133 |
+
u_i^(h) = (1-alpha_h) i + alpha_h z_i^(h)
|
| 2134 |
+
= i + alpha_h (z_i^(h) - i).
|
| 2135 |
+
|
| 2136 |
+
The residualized second form is used because it needs only one
|
| 2137 |
+
subtraction, one multiplication and one addition. ``position_ids`` is
|
| 2138 |
+
broadcast as [B,1,S], so it is not materialized once per head.
|
| 2139 |
+
|
| 2140 |
+
``u`` is evaluated in FP32 because the next operation is the FP32 phase
|
| 2141 |
+
computation. It is not registered as a buffer and is not saved in the
|
| 2142 |
+
checkpoint. Standard autograd gives
|
| 2143 |
+
|
| 2144 |
+
dL/dalpha_h = sum_{b,i} ubar_{b,h,i} (z_{b,h,i} - i).
|
| 2145 |
+
"""
|
| 2146 |
+
B, H, S = z.shape
|
| 2147 |
+
if H != self.num_attention_heads:
|
| 2148 |
+
raise ValueError(
|
| 2149 |
+
f"REPO-GRAPE expected z heads={self.num_attention_heads}, got {H}."
|
| 2150 |
+
)
|
| 2151 |
+
|
| 2152 |
+
if position_ids is None:
|
| 2153 |
+
# Defensive fallback for direct attention calls. NeoLLMModel normally
|
| 2154 |
+
# constructs position_ids before entering the decoder layers.
|
| 2155 |
+
pid = torch.arange(S, device=z.device, dtype=torch.float32).view(1, S)
|
| 2156 |
+
else:
|
| 2157 |
+
pid = position_ids
|
| 2158 |
+
if pid.dim() == 1:
|
| 2159 |
+
pid = pid.unsqueeze(0)
|
| 2160 |
+
elif pid.dim() > 2:
|
| 2161 |
+
# Match the existing IHA RoPE convention: trainers may attach
|
| 2162 |
+
# metadata in the final axis; column 0 is the sequential index.
|
| 2163 |
+
pid = pid[..., 0]
|
| 2164 |
+
if pid.dim() != 2 or pid.shape[-1] != S:
|
| 2165 |
+
raise ValueError(
|
| 2166 |
+
"REPO-GRAPE position_ids must have shape [S] or [B,S] "
|
| 2167 |
+
f"matching z[...,S={S}], got {tuple(pid.shape)}."
|
| 2168 |
+
)
|
| 2169 |
+
if pid.shape[0] == 1 and B != 1:
|
| 2170 |
+
pid = pid.expand(B, -1)
|
| 2171 |
+
elif pid.shape[0] != B:
|
| 2172 |
+
raise ValueError(
|
| 2173 |
+
f"REPO-GRAPE position_ids batch={pid.shape[0]} does not match z batch={B}."
|
| 2174 |
+
)
|
| 2175 |
+
pid = pid.to(device=z.device, dtype=torch.float32)
|
| 2176 |
+
|
| 2177 |
+
# Views/broadcasting do not allocate [B,H,S] copies of pid or alpha.
|
| 2178 |
+
i = pid.unsqueeze(1)
|
| 2179 |
+
alpha = self.position_blend_alpha.view(1, H, 1)
|
| 2180 |
+
return i + alpha * (z.float() - i)
|
| 2181 |
|
| 2182 |
def _query_freq(
|
| 2183 |
self,
|
|
|
|
| 2205 |
self,
|
| 2206 |
q: torch.Tensor,
|
| 2207 |
k: torch.Tensor,
|
| 2208 |
+
u: torch.Tensor,
|
| 2209 |
inv_freq: torch.Tensor,
|
| 2210 |
attention_scaling: float,
|
| 2211 |
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 2212 |
r"""
|
| 2213 |
+
Apply REPO-GRAPE-M rotation to Q/K using final coordinates ``u``.
|
| 2214 |
|
| 2215 |
+
``u`` is already the linear-blend coordinate produced by
|
| 2216 |
+
``transform_positions`` (and, for IHA, already expanded as P*u+p).
|
| 2217 |
Query heads keep their individual phases
|
| 2218 |
|
| 2219 |
+
phi_{b,h,i,r} = u_{b,h,i} * theta_{h,r}.
|
| 2220 |
|
| 2221 |
For GQA, each KV head m serves a group H_m of query heads. Its shared
|
| 2222 |
rotation is the circular projection
|
|
|
|
| 2236 |
Args:
|
| 2237 |
q: [B, H_q_eff, S, head_dim]
|
| 2238 |
k: [B, H_k_eff, S, head_dim]
|
| 2239 |
+
u: [B, H_q_base, S] final REPO-GRAPE coordinates
|
| 2240 |
inv_freq: [rotary_dim/2]
|
| 2241 |
attention_scaling: scaling from NeoLLMRotaryEmbedding
|
| 2242 |
|
|
|
|
| 2244 |
Rotated ``(q, k)`` tensors with the same shapes and dtypes as the
|
| 2245 |
inputs. Channels beyond ``rotary_dim`` pass through unchanged.
|
| 2246 |
"""
|
| 2247 |
+
B, H_repo, S = u.shape
|
| 2248 |
H_q_eff = q.shape[1]
|
| 2249 |
H_k_eff = k.shape[1]
|
| 2250 |
|
| 2251 |
if H_q_eff % H_repo != 0:
|
| 2252 |
raise ValueError(
|
| 2253 |
+
f"REPO-GRAPE expected q heads divisible by coordinate heads, "
|
| 2254 |
f"got H_q={H_q_eff} vs H_repo={H_repo}."
|
| 2255 |
)
|
| 2256 |
P = H_q_eff // H_repo
|
|
|
|
| 2261 |
f"got H_k={H_k_eff} vs P={P}."
|
| 2262 |
)
|
| 2263 |
|
| 2264 |
+
u_q_struct = u.unsqueeze(2).expand(B, H_repo, P, S) # [B,H_q_base,P,S]
|
| 2265 |
H_k_base = H_k_eff // max(P, 1)
|
| 2266 |
if H_repo % H_k_base != 0:
|
| 2267 |
raise ValueError(
|
|
|
|
| 2270 |
)
|
| 2271 |
|
| 2272 |
q_per_k_base = H_repo // H_k_base
|
| 2273 |
+
u_q = u_q_struct.reshape(B, H_q_eff, S)
|
| 2274 |
|
| 2275 |
rot_half = int(inv_freq.shape[0])
|
| 2276 |
rotary_dim = rot_half * 2
|
|
|
|
| 2282 |
# Individual query phases are the fundamental quantity. They are
|
| 2283 |
# calculated in fp32 before trigonometric evaluation, as in the
|
| 2284 |
# original REPO-GRAPE path.
|
| 2285 |
+
phase_q = u_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
|
| 2286 |
1, H_q_eff, 1, rot_half
|
| 2287 |
)
|
| 2288 |
cos_q_half = phase_q.cos()
|
|
|
|
| 2899 |
# REPO-GRAPE reuses the REPO coordinate module f_phi and replaces only
|
| 2900 |
# the positional action applied to Q/K. Enabling use_repo_grape=True
|
| 2901 |
# therefore activates the REPO coordinate path even when use_repo=False.
|
| 2902 |
+
# There is no extra position-mode flag: active REPO-GRAPE always maps
|
| 2903 |
+
# raw z to u=i+alpha_h*(z-i) with one learned FP32 alpha_h per query
|
| 2904 |
+
# head before GRAPE-M (and before the IHA P*u+p expansion).
|
| 2905 |
_repo_requested = bool(getattr(config, "use_repo", False))
|
| 2906 |
_repo_grape_requested = bool(getattr(config, "use_repo_grape", False))
|
| 2907 |
_repo_start_layer = getattr(
|
|
|
|
| 2930 |
num_key_value_heads=config.num_key_value_heads,
|
| 2931 |
head_dim=self.head_dim,
|
| 2932 |
)
|
| 2933 |
+
# Availability is fixed when the module is constructed, so Dynamo
|
| 2934 |
+
# specializes one stable route. This is deliberately not a new
|
| 2935 |
+
# configuration flag: use_repo_grape still owns the feature, while
|
| 2936 |
+
# unsupported inputs retain the exact PyTorch implementation.
|
| 2937 |
+
self.use_repo_grape_triton = False
|
| 2938 |
else:
|
| 2939 |
self.repo_grape = None
|
| 2940 |
+
self.use_repo_grape_triton = False
|
| 2941 |
|
| 2942 |
# ── REPO-GOAT factorised log-prior ───────────────────────────────────
|
| 2943 |
# Independent of use_repo/use_repo_grape: it can be ablated with standard
|
|
|
|
| 4018 |
|
| 4019 |
# ── RoPE / REPO ───────────────────────────────────────────────────────
|
| 4020 |
cos, sin = position_embeddings
|
| 4021 |
+
_repo_grape_kernel_used = False
|
| 4022 |
if self.use_repo:
|
| 4023 |
# REPO path: f_ϕ predicts continuous per-head positions from the
|
| 4024 |
# residual stream, then cos/sin are built inline from those positions
|
|
|
|
| 4027 |
# directly from rotary_emb at forward time — no buffer on this module,
|
| 4028 |
# no meta-tensor issue on lm_eval / to(device) paths.
|
| 4029 |
# (Li et al., 2026, §3.2 — Eq. 6–7)
|
| 4030 |
+
z = self.repo_module(hidden_states) # [B, H, S], raw REPO assignment
|
|
|
|
|
|
|
| 4031 |
inv_freq, attn_scaling = repo_rope_args
|
| 4032 |
+
|
| 4033 |
+
# The optional kernel consumes raw REPO assignments and performs
|
| 4034 |
+
# the linear-blend coordinate, IHA P=2 sequence expansion, GRAPE
|
| 4035 |
+
# phases, GQA circular projection, and optional momentum in one
|
| 4036 |
+
# autograd-aware Triton boundary. Q/K are intentionally normalized
|
| 4037 |
+
# before this call: IHA mixes normalized heads, so moving RMSNorm
|
| 4038 |
+
# into the kernel after that mixing would change the model.
|
| 4039 |
+
_repo_grape_sequence_factor = self.iha_P if _iha_seq_expand else 1
|
| 4040 |
+
if (
|
| 4041 |
+
self.use_repo_grape
|
| 4042 |
+
and self.use_repo_grape_triton
|
| 4043 |
+
and _cce_repo_grape is not None
|
| 4044 |
+
and _cce_repo_grape_supported is not None
|
| 4045 |
+
and _repo_grape_sequence_factor in (1, 2)
|
| 4046 |
+
and _cce_repo_grape_supported(
|
| 4047 |
+
q,
|
| 4048 |
+
k,
|
| 4049 |
+
z,
|
| 4050 |
+
inv_freq,
|
| 4051 |
+
self.repo_grape.position_blend_alpha,
|
| 4052 |
+
self.repo_grape.freq_log_scale,
|
| 4053 |
+
sequence_pseudo_factor=_repo_grape_sequence_factor,
|
| 4054 |
+
)
|
| 4055 |
+
):
|
| 4056 |
+
_kernel_position_ids = position_ids
|
| 4057 |
+
if (
|
| 4058 |
+
_kernel_position_ids is not None
|
| 4059 |
+
and _kernel_position_ids.dim() > 2
|
| 4060 |
+
):
|
| 4061 |
+
# Match transform_positions and the IHA helpers: the first
|
| 4062 |
+
# metadata column is the runtime sequential position.
|
| 4063 |
+
_kernel_position_ids = _kernel_position_ids[..., 0]
|
| 4064 |
+
_kernel_gamma = (
|
| 4065 |
+
self.momentum_gamma if self.use_momentum_attention else 0.0
|
| 4066 |
+
)
|
| 4067 |
+
q, k = _cce_repo_grape(
|
| 4068 |
+
q,
|
| 4069 |
+
k,
|
| 4070 |
+
z,
|
| 4071 |
+
_kernel_position_ids,
|
| 4072 |
+
inv_freq,
|
| 4073 |
+
self.repo_grape.position_blend_alpha,
|
| 4074 |
+
self.repo_grape.freq_log_scale,
|
| 4075 |
+
attn_scaling,
|
| 4076 |
+
sequence_pseudo_factor=_repo_grape_sequence_factor,
|
| 4077 |
+
momentum_gamma=_kernel_gamma,
|
| 4078 |
+
output_dtype=q.dtype,
|
| 4079 |
+
)
|
| 4080 |
+
_repo_grape_kernel_used = True
|
| 4081 |
+
|
| 4082 |
+
# REPO-GRAPE consumes u, not raw z. There is intentionally no mode
|
| 4083 |
+
# flag: when REPO-GRAPE is active, transform_positions always applies
|
| 4084 |
+
# the learned linear blend
|
| 4085 |
+
#
|
| 4086 |
+
# u_i^(h) = i + alpha_h * (z_i^(h) - i).
|
| 4087 |
+
#
|
| 4088 |
+
# Pure REPO (without GRAPE) keeps the original paper behavior and
|
| 4089 |
+
# therefore continues to use z directly.
|
| 4090 |
+
if not _repo_grape_kernel_used:
|
| 4091 |
+
repo_coords = (
|
| 4092 |
+
self.repo_grape.transform_positions(z, position_ids)
|
| 4093 |
+
if self.use_repo_grape
|
| 4094 |
+
else z
|
| 4095 |
+
)
|
| 4096 |
+
|
| 4097 |
+
if not _repo_grape_kernel_used and _iha_seq_expand:
|
| 4098 |
+
# IHA is applied *after* the coordinate map. Thus REPO-GRAPE
|
| 4099 |
+
# uses u_{i,p}=P*u_i+p, while pure REPO keeps its previous
|
| 4100 |
+
# z_{i,p}=P*z_i+p behavior. ``repo_coords`` is transient and this
|
| 4101 |
+
# expanded tensor replaces, rather than accompanies, the old
|
| 4102 |
+
# z_expanded allocation.
|
| 4103 |
P = self.iha_P
|
| 4104 |
+
p_offsets = torch.arange(
|
| 4105 |
+
P, device=repo_coords.device, dtype=repo_coords.dtype
|
|
|
|
|
|
|
| 4106 |
)
|
| 4107 |
+
coords_expanded = (
|
| 4108 |
+
repo_coords.unsqueeze(-1) * P
|
| 4109 |
+
+ p_offsets.view(1, 1, 1, P)
|
| 4110 |
+
).reshape(repo_coords.shape[0], repo_coords.shape[1], _S_orig * P)
|
| 4111 |
+
|
| 4112 |
if self.use_repo_grape:
|
| 4113 |
q, k = self.repo_grape.apply_multiplicative(
|
| 4114 |
+
q, k, coords_expanded, inv_freq, attn_scaling
|
| 4115 |
)
|
| 4116 |
else:
|
| 4117 |
+
q, k = _apply_repo_rope(
|
| 4118 |
+
q, k, coords_expanded, inv_freq, attn_scaling
|
| 4119 |
+
)
|
| 4120 |
+
elif not _repo_grape_kernel_used:
|
| 4121 |
+
# P=1/no-IHA: ``repo_coords`` is u for REPO-GRAPE and z for
|
| 4122 |
+
# the original REPO-RoPE path.
|
| 4123 |
if self.use_repo_grape:
|
| 4124 |
q, k = self.repo_grape.apply_multiplicative(
|
| 4125 |
+
q, k, repo_coords, inv_freq, attn_scaling
|
| 4126 |
)
|
| 4127 |
else:
|
| 4128 |
+
q, k = _apply_repo_rope(
|
| 4129 |
+
q, k, repo_coords, inv_freq, attn_scaling
|
| 4130 |
+
)
|
| 4131 |
elif _iha_seq_expand:
|
| 4132 |
# IHA seq-expand + standard integer RoPE:
|
| 4133 |
# Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
|
|
|
|
| 4149 |
# Standard path: integer positions pre-computed by NeoLLMModel.
|
| 4150 |
q, k = apply_rotary_pos_emb(q, k, cos, sin)
|
| 4151 |
|
| 4152 |
+
if not _repo_grape_kernel_used:
|
| 4153 |
+
q, k = self._apply_momentum_attention(q, k)
|
| 4154 |
# ── MEA head mixing ───────────────────────────────────────────────────
|
| 4155 |
# For IHA seq-expand (P>1): K/V are [B, H_kv, S·P, d] — same number of
|
| 4156 |
# heads as without IHA, just a longer sequence. Use the standard
|
|
|
|
| 4489 |
def forward(
|
| 4490 |
self,
|
| 4491 |
x: torch.Tensor,
|
| 4492 |
+
dropout_p: float = 0.0,
|
| 4493 |
) -> torch.Tensor:
|
| 4494 |
+
if _cce_polynorm is not None:
|
| 4495 |
+
if (
|
| 4496 |
+
self.training
|
| 4497 |
+
and torch.is_grad_enabled()
|
| 4498 |
+
and torch.compiler.is_compiling()
|
| 4499 |
+
and _cce_polynorm_uses_cute is not None
|
| 4500 |
+
and _cce_polynorm_uses_cute(
|
| 4501 |
+
x,
|
| 4502 |
+
self.weight,
|
| 4503 |
+
self.bias,
|
| 4504 |
+
eps=float(self.eps),
|
| 4505 |
+
exclusive_logits=getattr(self, "exclusive_logits", None),
|
| 4506 |
+
dropout_p=dropout_p,
|
| 4507 |
+
)
|
| 4508 |
+
):
|
| 4509 |
+
return _activation_checkpoint(
|
| 4510 |
+
_cce_polynorm,
|
| 4511 |
+
x,
|
| 4512 |
+
self.weight,
|
| 4513 |
+
self.bias,
|
| 4514 |
+
use_reentrant=False,
|
| 4515 |
+
eps=float(self.eps),
|
| 4516 |
+
proj_eps=float(getattr(self, "proj_eps", 1e-6)),
|
| 4517 |
+
exclusive_logits=getattr(self, "exclusive_logits", None),
|
| 4518 |
+
dropout_p=dropout_p,
|
| 4519 |
+
)
|
| 4520 |
+
return _cce_polynorm(
|
| 4521 |
+
x,
|
| 4522 |
+
self.weight,
|
| 4523 |
+
self.bias,
|
| 4524 |
+
eps=float(self.eps),
|
| 4525 |
+
proj_eps=float(getattr(self, "proj_eps", 1e-6)),
|
| 4526 |
+
exclusive_logits=getattr(self, "exclusive_logits", None),
|
| 4527 |
+
dropout_p=dropout_p,
|
| 4528 |
+
)
|
| 4529 |
+
|
| 4530 |
# Caché de potencias: x_sq reutilizado en x1 y x2; x_cu = x·x_sq evita pow(3)
|
| 4531 |
x_sq = x.pow(2)
|
| 4532 |
x_cu = x * x_sq
|
|
|
|
| 4552 |
self.weight[0] * x3 + self.weight[1] * x2 + self.weight[2] * x1 + self.bias
|
| 4553 |
)
|
| 4554 |
|
| 4555 |
+
if dropout_p:
|
| 4556 |
+
output = F.dropout(output, p=dropout_p, training=True)
|
| 4557 |
+
|
| 4558 |
return output
|
| 4559 |
|
| 4560 |
|
|
|
|
| 4601 |
gate_out = self.gate_proj(x_fan)
|
| 4602 |
up_out = self.up_proj(x_fan)
|
| 4603 |
|
| 4604 |
+
dropout_p = float(self.dropout.p) if self.training else 0.0
|
| 4605 |
+
act_out = self.act_fn(gate_out, dropout_p=dropout_p)
|
| 4606 |
act_x_up = act_out * up_out
|
| 4607 |
|
| 4608 |
+
result = self.down_proj(act_x_up)
|
| 4609 |
|
| 4610 |
return result
|
| 4611 |
|
|
|
|
| 5340 |
self,
|
| 5341 |
token_ids: torch.Tensor, # [B, S] or [N]
|
| 5342 |
token_embeds: torch.Tensor, # [B, S, d] or [N, d]
|
| 5343 |
+
*,
|
| 5344 |
+
meap_mask_embedding: Optional[torch.Tensor] = None,
|
| 5345 |
+
meap_mask_token_id: Optional[int] = None,
|
| 5346 |
) -> torch.Tensor:
|
| 5347 |
"""
|
| 5348 |
Args:
|
|
|
|
| 5367 |
].mean(-2) # [..., d], fixed 16-position mean
|
| 5368 |
|
| 5369 |
# ── Step 3: mean with token embeddings ──────────────────────────────
|
| 5370 |
+
output = (token_embeds + e_chars) * 0.5
|
| 5371 |
+
has_meap = (
|
| 5372 |
+
meap_mask_embedding is not None or meap_mask_token_id is not None
|
| 5373 |
+
)
|
| 5374 |
+
if not has_meap:
|
| 5375 |
+
return output
|
| 5376 |
+
if meap_mask_embedding is None or meap_mask_token_id is None:
|
| 5377 |
+
raise ValueError(
|
| 5378 |
+
"meap_mask_embedding and meap_mask_token_id must be provided "
|
| 5379 |
+
"together"
|
| 5380 |
+
)
|
| 5381 |
+
return torch.where(
|
| 5382 |
+
token_ids.eq(int(meap_mask_token_id)).unsqueeze(-1),
|
| 5383 |
+
meap_mask_embedding.to(device=output.device, dtype=output.dtype),
|
| 5384 |
+
output,
|
| 5385 |
+
)
|
| 5386 |
|
| 5387 |
# ── Inference utility ─────────────────────────────────────────────────────
|
| 5388 |
|
|
|
|
| 5478 |
|
| 5479 |
@classmethod
|
| 5480 |
def from_pretrained(cls, *args, **kwargs):
|
| 5481 |
+
"""Initialize only a legacy checkpoint that lacks ``knot_grid``.
|
| 5482 |
+
|
| 5483 |
+
Current checkpoints persist this buffer and must retain its exact saved
|
| 5484 |
+
values. The previous compatibility path regenerated the grid after
|
| 5485 |
+
every successful load, silently overwriting a valid checkpoint (and
|
| 5486 |
+
BF16 direct-linspace rounding can differ from FP32-then-BF16 rounding).
|
| 5487 |
+
"""
|
| 5488 |
+
return_loading_info = bool(kwargs.pop("output_loading_info", False))
|
| 5489 |
+
requested_dtype = kwargs.get("dtype", kwargs.get("torch_dtype"))
|
| 5490 |
+
model, loading_info = super().from_pretrained(
|
| 5491 |
+
*args,
|
| 5492 |
+
output_loading_info=True,
|
| 5493 |
+
**kwargs,
|
| 5494 |
+
)
|
| 5495 |
+
missing_keys = set(loading_info.get("missing_keys", ()))
|
| 5496 |
+
for module_name, module in model.named_modules():
|
| 5497 |
if not isinstance(module, LeviathanGenerator):
|
| 5498 |
continue
|
| 5499 |
+
checkpoint_key = (
|
| 5500 |
+
f"{module_name}.knot_grid" if module_name else "knot_grid"
|
| 5501 |
+
)
|
| 5502 |
+
needs_legacy_initialization = (
|
| 5503 |
+
module.knot_grid.device.type == "meta"
|
| 5504 |
+
or checkpoint_key in missing_keys
|
| 5505 |
+
)
|
| 5506 |
+
if not needs_legacy_initialization:
|
| 5507 |
+
continue
|
| 5508 |
if module.knot_grid.device.type == "meta":
|
| 5509 |
module._buffers["knot_grid"] = torch.linspace(
|
| 5510 |
0.0,
|
|
|
|
| 5515 |
)
|
| 5516 |
else:
|
| 5517 |
with torch.no_grad():
|
| 5518 |
+
# Match training construction: create the fixed grid in
|
| 5519 |
+
# FP32 first, then let the explicit cast perform rounding.
|
| 5520 |
module.knot_grid.copy_(
|
| 5521 |
torch.linspace(
|
| 5522 |
0.0,
|
| 5523 |
1.0,
|
| 5524 |
module.num_knots,
|
| 5525 |
device=module.knot_grid.device,
|
| 5526 |
+
dtype=torch.float32,
|
| 5527 |
+
).to(dtype=module.knot_grid.dtype)
|
| 5528 |
+
)
|
| 5529 |
+
|
| 5530 |
+
# These SiameseNorm tensors are constructed explicitly in FP32 to make
|
| 5531 |
+
# direct ``NeoLLMForCausalLM(config)`` initialization stable. Training
|
| 5532 |
+
# then casts them with ``model.to(BF16)``. Transformers' low-memory
|
| 5533 |
+
# loader honors the explicit constructor dtype instead of ``dtype=``
|
| 5534 |
+
# when copying a checkpoint, so without this normalization a BF16 Hub
|
| 5535 |
+
# checkpoint silently resumes those 12 trainable vectors in FP32.
|
| 5536 |
+
if (
|
| 5537 |
+
isinstance(requested_dtype, torch.dtype)
|
| 5538 |
+
and requested_dtype.is_floating_point
|
| 5539 |
+
):
|
| 5540 |
+
for module in model.modules():
|
| 5541 |
+
siamese_scale = getattr(module, "siamese_attn_x_scale", None)
|
| 5542 |
+
if siamese_scale is not None:
|
| 5543 |
+
siamese_scale.data = siamese_scale.data.to(
|
| 5544 |
+
dtype=requested_dtype
|
| 5545 |
)
|
| 5546 |
+
stream_scale = getattr(
|
| 5547 |
+
module,
|
| 5548 |
+
"_siamese_stream_scale_value",
|
| 5549 |
+
None,
|
| 5550 |
+
)
|
| 5551 |
+
if stream_scale is not None:
|
| 5552 |
+
module._buffers["_siamese_stream_scale_value"] = (
|
| 5553 |
+
stream_scale.to(dtype=requested_dtype)
|
| 5554 |
+
)
|
| 5555 |
+
if return_loading_info:
|
| 5556 |
+
return model, loading_info
|
| 5557 |
return model
|
| 5558 |
|
| 5559 |
def _init_weights(self, module):
|
|
|
|
| 5759 |
else:
|
| 5760 |
self.spelling_bee = None
|
| 5761 |
|
| 5762 |
+
# MEAP owns one independent final embedding vector. It deliberately
|
| 5763 |
+
# lives in the model instead of the optional CCE package so checkpoints
|
| 5764 |
+
# remain loadable for inference when that package is absent. The last
|
| 5765 |
+
# active embedding producer owns the MEAP selection: Leviathan fuses it
|
| 5766 |
+
# into its output GEMM, while Spelling Bee owns it when byte features
|
| 5767 |
+
# follow. This prevents masked positions from updating dense token rows,
|
| 5768 |
+
# Leviathan codebooks, or Spelling Bee bytes.
|
| 5769 |
+
meap_mask_token_id = getattr(config, "meap_mask_token_id", None)
|
| 5770 |
+
if meap_mask_token_id is None:
|
| 5771 |
+
self.register_parameter("meap_mask_embedding", None)
|
| 5772 |
+
else:
|
| 5773 |
+
self.meap_mask_embedding = nn.Parameter(
|
| 5774 |
+
torch.empty(config.hidden_size)
|
| 5775 |
+
)
|
| 5776 |
+
nn.init.normal_(
|
| 5777 |
+
self.meap_mask_embedding,
|
| 5778 |
+
mean=0.0,
|
| 5779 |
+
std=config.initializer_range,
|
| 5780 |
+
)
|
| 5781 |
+
|
| 5782 |
self.layers = nn.ModuleList(
|
| 5783 |
[
|
| 5784 |
NeoLLMDecoderLayer(config, layer_idx)
|
|
|
|
| 5855 |
output_hidden_states: Optional[bool] = None,
|
| 5856 |
output_attentions: Optional[bool] = None,
|
| 5857 |
output_tweo_activations: Optional[bool] = None,
|
| 5858 |
+
meap_active: bool = False,
|
| 5859 |
return_dict: Optional[bool] = None,
|
| 5860 |
**kwargs: Unpack[TransformersKwargs],
|
| 5861 |
) -> Tuple:
|
|
|
|
| 5881 |
raise ValueError("Specify exactly one of input_ids or inputs_embeds")
|
| 5882 |
|
| 5883 |
# ── Embedding stage ────────────────────────────────────────────────
|
| 5884 |
+
meap_mask_embedding = None
|
| 5885 |
+
meap_mask_token_id = None
|
| 5886 |
+
if meap_active:
|
| 5887 |
+
if input_ids is None:
|
| 5888 |
+
raise ValueError("Active MEAP requires `input_ids`.")
|
| 5889 |
+
if self.meap_mask_embedding is None:
|
| 5890 |
+
raise ValueError(
|
| 5891 |
+
"Active MEAP requires a configured `meap_mask_token_id`."
|
| 5892 |
+
)
|
| 5893 |
+
meap_mask_embedding = self.meap_mask_embedding
|
| 5894 |
+
meap_mask_token_id = int(self.config.meap_mask_token_id)
|
| 5895 |
+
|
| 5896 |
+
meap_consumed = False
|
| 5897 |
if inputs_embeds is None:
|
| 5898 |
if self.config.use_token_generator:
|
| 5899 |
+
# Without a later Spelling Bee epilogue, Leviathan owns the
|
| 5900 |
+
# final embedding write and selects the MEAP vector inside its
|
| 5901 |
+
# GEMM store. If SBE is active, its epilogue must own the final
|
| 5902 |
+
# selection so byte features cannot alter masked positions.
|
| 5903 |
+
fuse_meap_in_leviathan = meap_active and self.spelling_bee is None
|
| 5904 |
+
if fuse_meap_in_leviathan:
|
| 5905 |
+
inputs_embeds = self.token_generator(
|
| 5906 |
+
input_ids,
|
| 5907 |
+
meap_mask_embedding=meap_mask_embedding,
|
| 5908 |
+
meap_mask_token_id=meap_mask_token_id,
|
| 5909 |
+
)
|
| 5910 |
+
else:
|
| 5911 |
+
inputs_embeds = self.token_generator(input_ids)
|
| 5912 |
+
meap_consumed = fuse_meap_in_leviathan
|
| 5913 |
else:
|
| 5914 |
inputs_embeds = self.embed_tokens(input_ids)
|
| 5915 |
|
|
|
|
| 5918 |
# caller; in that case SBE cannot run (no token_ids available) and is
|
| 5919 |
# silently skipped — consistent with the standard embedding bypass path.
|
| 5920 |
if self.spelling_bee is not None and input_ids is not None:
|
| 5921 |
+
inputs_embeds = self.spelling_bee(
|
| 5922 |
+
input_ids,
|
| 5923 |
+
inputs_embeds,
|
| 5924 |
+
meap_mask_embedding=(
|
| 5925 |
+
meap_mask_embedding if meap_active else None
|
| 5926 |
+
),
|
| 5927 |
+
meap_mask_token_id=(meap_mask_token_id if meap_active else None),
|
| 5928 |
+
)
|
| 5929 |
+
meap_consumed = meap_active
|
| 5930 |
+
|
| 5931 |
+
# This must be the final operation in the input-embedding pipeline.
|
| 5932 |
+
# The clean/inference path is unchanged and does not depend on CCE.
|
| 5933 |
+
if meap_active and not meap_consumed:
|
| 5934 |
+
meap_positions = input_ids.eq(meap_mask_token_id).unsqueeze(-1)
|
| 5935 |
+
mask_embedding = meap_mask_embedding.to(
|
| 5936 |
+
device=inputs_embeds.device,
|
| 5937 |
+
dtype=inputs_embeds.dtype,
|
| 5938 |
+
).view(1, 1, -1)
|
| 5939 |
+
inputs_embeds = torch.where(
|
| 5940 |
+
meap_positions,
|
| 5941 |
+
mask_embedding,
|
| 5942 |
+
inputs_embeds,
|
| 5943 |
+
)
|
| 5944 |
|
| 5945 |
if position_ids is None:
|
| 5946 |
if attention_mask is not None:
|
|
|
|
| 6271 |
cce_impl="cce_kahan_full_c",
|
| 6272 |
use_mile_loss=False,
|
| 6273 |
mile_loss_gamma=1.0,
|
| 6274 |
+
mile_group_mask=None,
|
| 6275 |
use_mu_loss=False,
|
| 6276 |
mu_loss_lambda=1e-4,
|
| 6277 |
return_loss_metrics=False,
|
|
|
|
| 6300 |
mile_enabled=True,
|
| 6301 |
mile_gamma=float(mile_loss_gamma),
|
| 6302 |
)
|
| 6303 |
+
if mile_group_mask is not None:
|
| 6304 |
+
_require_extended_cce_options("mile_group_mask")
|
| 6305 |
+
extension_kwargs["mile_group_mask"] = mile_group_mask
|
| 6306 |
if use_mu_loss:
|
| 6307 |
_require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
|
| 6308 |
extension_kwargs.update(
|
|
|
|
| 7988 |
_require_extended_cce_options("mile_enabled", "mile_gamma")
|
| 7989 |
if bool(getattr(config, "use_mu_loss", False)):
|
| 7990 |
_require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
|
| 7991 |
+
# The optional MEAP backend is checked only when training actually
|
| 7992 |
+
# applies corruption. Clean training, evaluation, and inference remain
|
| 7993 |
+
# usable without cut-cross-entropy even when loading a MEAP checkpoint.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7994 |
self._last_ntp_loss = None
|
| 7995 |
self._last_ntp_ce_unweighted = None
|
| 7996 |
self._last_mile_reweighting_delta = None
|
|
|
|
| 8085 |
and self.training
|
| 8086 |
and labels is not None
|
| 8087 |
)
|
| 8088 |
+
meap_selected_mask = None
|
| 8089 |
if meap_enabled:
|
| 8090 |
if input_ids is None or inputs_embeds is not None:
|
| 8091 |
raise ValueError(
|
|
|
|
| 8094 |
)
|
| 8095 |
if meap_mask_inputs is None:
|
| 8096 |
raise ImportError(
|
| 8097 |
+
"Active MEAP training requires the extended cut-cross-entropy "
|
| 8098 |
+
"package. Evaluation and inference do not require it."
|
| 8099 |
+
)
|
| 8100 |
+
if "return_metrics" not in inspect.signature(meap_mask_inputs).parameters:
|
| 8101 |
+
raise ImportError(
|
| 8102 |
+
"The installed cut-cross-entropy package lacks MEAP metrics. "
|
| 8103 |
+
"Install the extended backend before MEAP training."
|
| 8104 |
)
|
| 8105 |
mask_token_id = getattr(self.config, "meap_mask_token_id", None)
|
| 8106 |
if mask_token_id is None:
|
|
|
|
| 8124 |
)
|
| 8125 |
exclude_last = bool(getattr(self.config, "meap_exclude_last", True))
|
| 8126 |
mask_ratio = float(getattr(self.config, "meap_mask_ratio", 0.15))
|
| 8127 |
+
return_meap_mask = bool(getattr(self.config, "use_mile_loss", False))
|
| 8128 |
+
meap_result = meap_mask_inputs(
|
| 8129 |
input_ids,
|
| 8130 |
int(mask_token_id),
|
| 8131 |
enabled=True,
|
|
|
|
| 8133 |
eligible_mask=eligible_mask,
|
| 8134 |
seed=effective_meap_seed,
|
| 8135 |
exclude_last=exclude_last,
|
| 8136 |
+
return_mask=return_meap_mask,
|
| 8137 |
return_metrics=True,
|
| 8138 |
implementation=str(
|
| 8139 |
getattr(self.config, "meap_implementation", "triton")
|
| 8140 |
),
|
| 8141 |
)
|
| 8142 |
+
if return_meap_mask:
|
| 8143 |
+
input_ids, meap_selected_mask, meap_metrics = meap_result
|
| 8144 |
+
else:
|
| 8145 |
+
input_ids, meap_metrics = meap_result
|
| 8146 |
eligible_count = meap_metrics[0]
|
| 8147 |
masked_count = meap_metrics[1]
|
| 8148 |
self._last_meap_mask_fraction = (
|
|
|
|
| 8193 |
inputs_embeds=inputs_embeds,
|
| 8194 |
output_hidden_states=model_output_hidden_states,
|
| 8195 |
output_tweo_activations=tweo_enabled,
|
| 8196 |
+
meap_active=meap_enabled,
|
| 8197 |
return_dict=return_dict,
|
| 8198 |
**kwargs,
|
| 8199 |
)
|
|
|
|
| 8298 |
mile_loss_gamma=float(
|
| 8299 |
getattr(self.config, "mile_loss_gamma", 1.0)
|
| 8300 |
),
|
| 8301 |
+
mile_group_mask=(
|
| 8302 |
+
meap_selected_mask
|
| 8303 |
+
if meap_enabled
|
| 8304 |
+
and bool(getattr(self.config, "use_mile_loss", False))
|
| 8305 |
+
else None
|
| 8306 |
+
),
|
| 8307 |
use_mu_loss=bool(getattr(self.config, "use_mu_loss", False)),
|
| 8308 |
mu_loss_lambda=float(
|
| 8309 |
getattr(self.config, "mu_loss_lambda", 1e-4)
|