Download conversion/vela3_export.py from FluidInference/vela-3.0-coreml: direct link, hf CLI and curl.
- Browser
- Download file 3.45 kB
-
https://huggingface.co/FluidInference/vela-3.0-coreml/resolve/main/conversion/vela3_export.py
- Command line
-
hf download hf://FluidInference/vela-3.0-coreml/conversion/vela3_export.py
-
curl -L -o vela3_export.py https://huggingface.co/FluidInference/vela-3.0-coreml/resolve/main/conversion/vela3_export.py
3.45 kB
| """vela3-fast UltraBERT-380M encoder as a fixed-length, eager-attention graph (ids + mask -> final-norm hidden states). | |
| Same contract as the CPU path of modeling_ultrabert.UltrabertModel.forward: local layers attend to keys with |i - j| <= 128, global | |
| layers to all keys, padding masks keys only; RoPE theta per layer (10k local, 2.56M global). Masks use -1e4 instead of finfo.min | |
| (fp16-safe); cos / sin and the two masks are precomputed for the bucket length.""" | |
| import sys | |
| import torch | |
| from torch import nn | |
| MASK_VALUE = -1e4 | |
| sys.path.insert(0, "src") | |
| def load_decision(path="src"): | |
| from vela3_encoder import DecisionEncoder | |
| return DecisionEncoder.load(path, device="cpu") | |
| class Vela3EncoderExport(nn.Module): | |
| def __init__(self, enc, length): | |
| super().__init__() | |
| cfg = enc.config | |
| cfg._attn_implementation = "eager" | |
| self.length, self.layers, self.embed, self.norm = length, enc.layers, enc.embed_tokens, enc.norm | |
| self.windows, self.thetas = list(cfg.attention_windows), list(cfg.rope_theta) | |
| pos = torch.arange(length).unsqueeze(0) | |
| probe = torch.zeros(1, length, cfg.hidden_size) | |
| self.theta_key = {} | |
| for i, base in enumerate(sorted(set(self.thetas))): | |
| cos, sin = enc.rotary_emb[str(base).replace(".", "_")](probe, pos) | |
| self.register_buffer(f"cos{i}", cos.detach().clone()); self.register_buffer(f"sin{i}", sin.detach().clone()) | |
| self.theta_key[base] = i | |
| idx = torch.arange(length) | |
| dist = (idx[:, None] - idx[None, :]).abs() | |
| self.window_key = {} | |
| for i, w in enumerate(sorted({w for w in self.windows if w is not None})): | |
| self.register_buffer(f"band{i}", torch.where(dist <= w, 0.0, MASK_VALUE).view(1, 1, length, length)) | |
| self.window_key[w] = i | |
| def forward(self, input_ids, attention_mask): | |
| pad = (1.0 - attention_mask.float()).view(1, 1, 1, self.length) * MASK_VALUE | |
| bands = {w: getattr(self, f"band{i}") + pad for w, i in self.window_key.items()} | |
| x = self.embed(input_ids.long()) | |
| for layer, w, t in zip(self.layers, self.windows, self.thetas): | |
| k = self.theta_key[t] | |
| x = layer(x, attention_mask=pad if w is None else bands[w], | |
| position_embeddings=(getattr(self, f"cos{k}"), getattr(self, f"sin{k}"))) | |
| return self.norm(x) | |
| if __name__ == "__main__": # parity of the export graph vs the release encoder (sdpa, fp32 CPU) on a real decision input | |
| import json | |
| import pyarrow.parquet as pq | |
| m = load_decision() | |
| m.encoder.config._attn_implementation = "eager" | |
| r = pq.read_table("data/typed/all/test-00000-of-00001.parquet").to_pylist()[0] | |
| state, qs = json.loads(r["state"]), json.loads(r["questions"]) | |
| q = next(q for q in qs.values() if q["type"] == "choice") | |
| ids = m.encode(state, q)["ids"]; n = len(ids) | |
| with torch.no_grad(): | |
| ref = m.encoder(input_ids=torch.tensor([ids]), attention_mask=torch.ones(1, n, dtype=torch.long)).last_hidden_state[0] | |
| for L in (n, 512): | |
| g = Vela3EncoderExport(m.encoder, L).eval() | |
| x = torch.zeros(1, L, dtype=torch.int32); x[0, :n] = torch.tensor(ids); mk = torch.zeros(1, L, dtype=torch.int32); mk[0, :n] = 1 | |
| h = g(x, mk)[0, :n] | |
| print(f"n={n} L={L} max|dh|={(h - ref).abs().max():.2e} min cos={nn.functional.cosine_similarity(h, ref, dim=-1).min():.6f}") | |