maxffarrell's picture
Publish validated AMOS Core AI conversion with source provenance and runtime adapter
0415692 verified
Raw History Blame Contribute Delete
7.82 kB
"""ModernBERT-architecture encoder (Ettin weights), written from scratch.
Attention is controlled by per-token vectors instead of a precomputed mask:
position_ids: RoPE positions (branches restart after the state).
segment_ids: 0 = state, k > 0 = question branch k, -1 = padding.
option_ids: 0 = not an option, m > 0 = option m of its question.
A token attends to state tokens, its question's own tokens, and (for option tokens)
its own option. Local layers additionally restrict attention to
|pos_i - pos_j| <= window // 2, measured in position ids (not sequence index) so
packed and separate requests match.
Comparison mode lets each question and its options attend to the whole question branch.
"""
import json
from dataclasses import dataclass
import torch
import torch.nn.functional as F
from huggingface_hub import hf_hub_download
from torch import nn
@dataclass
class Config:
vocab_size: int
hidden_size: int
intermediate_size: int
num_hidden_layers: int
num_attention_heads: int
global_attn_every_n_layers: int = 3
local_attention: int = 128
global_rope_theta: float = 160000.0
local_rope_theta: float = 10000.0
norm_eps: float = 1e-5
pad_token_id: int = 50283
cls_token_id: int = 50281
sep_token_id: int = 50282
model_type: str = "modernbert"
compare_options: bool = False
date_facts: bool = False # an input-format setting: facts.date_facts runs on every state (model.pack_request)
@classmethod
def from_hf(cls, d: dict) -> "Config":
"""Reads a Hugging Face config or a run's saved config, ignoring fields this class does not define."""
return cls(**{k: d[k] for k in cls.__dataclass_fields__ if k in d})
def rope(x: torch.Tensor, pos: torch.Tensor, theta: float) -> torch.Tensor:
"""x: [B, H, L, D], pos: [B, L]. Half-split rotation (rotate_half convention)."""
d = x.shape[-1]
inv_freq = 1.0 / (theta ** (torch.arange(0, d, 2, device=x.device, dtype=torch.float32) / d))
freqs = pos[:, None, :, None].float() * inv_freq # [B, 1, L, D/2]
cos, sin = freqs.cos().to(x.dtype), freqs.sin().to(x.dtype)
x1, x2 = x[..., : d // 2], x[..., d // 2 :]
return torch.cat((x1 * cos - x2 * sin, x2 * cos + x1 * sin), dim=-1)
class Layer(nn.Module):
def __init__(self, c: Config, idx: int):
super().__init__()
self.is_global = idx % c.global_attn_every_n_layers == 0
self.theta = c.global_rope_theta if self.is_global else c.local_rope_theta
self.heads = c.num_attention_heads
self.attn_norm = nn.LayerNorm(c.hidden_size, eps=c.norm_eps, bias=False) if idx else nn.Identity()
self.Wqkv = nn.Linear(c.hidden_size, 3 * c.hidden_size, bias=False)
self.Wo = nn.Linear(c.hidden_size, c.hidden_size, bias=False)
self.mlp_norm = nn.LayerNorm(c.hidden_size, eps=c.norm_eps, bias=False)
self.Wi = nn.Linear(c.hidden_size, 2 * c.intermediate_size, bias=False)
self.Wo_mlp = nn.Linear(c.intermediate_size, c.hidden_size, bias=False)
def forward(self, h, pos, global_mask, local_mask, prefix=None):
"""prefix: cached (k, v) of earlier tokens, prepended to this call's keys and values."""
B, L, _ = h.shape
q, k, v = self.Wqkv(self.attn_norm(h)).view(B, L, 3, self.heads, -1).permute(2, 0, 3, 1, 4)
q, k = rope(q, pos, self.theta), rope(k, pos, self.theta)
kv = (k, v)
if prefix is not None:
k, v = torch.cat([prefix[0], k], 2), torch.cat([prefix[1], v], 2)
mask = global_mask if self.is_global else local_mask
a = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
h = h + self.Wo(a.transpose(1, 2).reshape(B, L, -1))
x, gate = self.Wi(self.mlp_norm(h)).chunk(2, dim=-1)
return h + self.Wo_mlp(F.gelu(x) * gate), kv
class Encoder(nn.Module):
def __init__(self, c: Config):
super().__init__()
self.config = c
self.tok_embeddings = nn.Embedding(c.vocab_size, c.hidden_size)
self.emb_norm = nn.LayerNorm(c.hidden_size, eps=c.norm_eps, bias=False)
self.layers = nn.ModuleList(Layer(c, i) for i in range(c.num_hidden_layers))
self.final_norm = nn.LayerNorm(c.hidden_size, eps=c.norm_eps, bias=False)
@staticmethod
def masks(pos, seg, opt, window, pos_k=None, seg_k=None, opt_k=None, *, compare_options=False):
"""Boolean [B, 1, Lq, Lk] masks, True = may attend. Global and local variants.
Keys default to the queries; pass *_k to attend over a longer key sequence (cached state + queries).
The masks are dense, so their memory grows with Lq * Lk.
"""
pos_k, seg_k, opt_k = (x if x is not None else y for x, y in ((pos_k, pos), (seg_k, seg), (opt_k, opt)))
si, sj, oi, oj = seg[:, :, None], seg_k[:, None, :], opt[:, :, None], opt_k[:, None, :]
within = (sj == si) & ((si > 0) if compare_options else ((oj == 0) | (oj == oi)))
allowed = (sj != -1) & ((sj == 0) | within)
local = allowed & ((pos[:, :, None] - pos_k[:, None, :]).abs() <= window // 2)
return allowed[:, None], local[:, None]
def forward(self, input_ids, position_ids=None, segment_ids=None, option_ids=None, inputs_embeds=None):
B, L = input_ids.shape
if position_ids is None:
position_ids = torch.arange(L, device=input_ids.device).expand(B, L)
if segment_ids is None:
segment_ids = torch.zeros_like(input_ids)
if option_ids is None:
option_ids = torch.zeros_like(input_ids)
emb = self.tok_embeddings(input_ids) if inputs_embeds is None else inputs_embeds
return self.run(emb, position_ids, segment_ids, option_ids)[0]
def run(self, emb, pos, seg, opt, cache=None):
"""Encode embeddings, optionally on top of a cached prefix. Returns (hidden, cache for these tokens).
A cache holds each layer's rotated keys and values plus the pos/seg/opt ids of its tokens. The state
never attends to questions, so a state encoded once can serve any number of later questions.
"""
if cache is None:
g, l = self.masks(pos, seg, opt, self.config.local_attention, compare_options=self.config.compare_options)
else:
g, l = self.masks(pos, seg, opt, self.config.local_attention,
torch.cat([cache["pos"], pos], 1), torch.cat([cache["seg"], seg], 1), torch.cat([cache["opt"], opt], 1),
compare_options=self.config.compare_options)
h, kvs = self.emb_norm(emb), []
for i, layer in enumerate(self.layers):
h, kv = layer(h, pos, g, l, None if cache is None else cache["kv"][i])
kvs.append(kv)
return self.final_norm(h), {"kv": kvs, "pos": pos, "seg": seg, "opt": opt}
@classmethod
def from_pretrained(cls, repo: str) -> "Encoder":
with open(hf_hub_download(repo, "config.json")) as f:
model = cls(Config.from_hf(json.load(f)))
sd = torch.load(hf_hub_download(repo, "pytorch_model.bin"), map_location="cpu", weights_only=True)
rename = {
"model.embeddings.tok_embeddings.": "tok_embeddings.",
"model.embeddings.norm.": "emb_norm.",
"model.final_norm.": "final_norm.",
".attn.Wqkv.": ".Wqkv.",
".attn.Wo.": ".Wo.",
".mlp.Wi.": ".Wi.",
".mlp.Wo.": ".Wo_mlp.",
"model.layers.": "layers.",
}
out = {}
for k, v in sd.items():
if not k.startswith("model."): # MLM head, unused
continue
for a, b in rename.items():
k = k.replace(a, b)
out[k] = v
model.load_state_dict(out, strict=True)
return model.eval()