Spaces:
Sleeping
Sleeping
Commit ·
78c28fc
0
Parent(s):
Super-squash branch 'main' using huggingface_hub
Browse files- .gitattributes +35 -0
- README.md +24 -0
- app.py +56 -0
- modeling_flash.py +302 -0
- requirements.txt +5 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: NodeNestor
|
| 3 |
+
emoji: 🌉
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: gradio
|
| 7 |
+
python_version: "3.12"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: true
|
| 10 |
+
short_description: The agent hub — open weights & open tools for agent fleets.
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# NodeNestor
|
| 14 |
+
|
| 15 |
+
### The agent hub.
|
| 16 |
+
|
| 17 |
+
Open weights and open tools for AI agent fleets.
|
| 18 |
+
|
| 19 |
+
🌉 **Bifrost** — open Nordic ↔ English translation (sv · da · nb · nn · fi · is ↔ en):
|
| 20 |
+
a 1.2B teacher + a 430M distilled flash. *Weights in private preview; public release soon.*
|
| 21 |
+
|
| 22 |
+
## 🌉 [▶ Try the live translator →](https://huggingface.co/spaces/NodeNestor/README)
|
| 23 |
+
|
| 24 |
+
→ [nodenestor.com](https://nodenestor.com) · [GitHub](https://github.com/NodeNestor)
|
app.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import sentencepiece as spm
|
| 5 |
+
from huggingface_hub import hf_hub_download
|
| 6 |
+
from modeling_flash import NordicFlash
|
| 7 |
+
|
| 8 |
+
REPO = "NodeNestor/bifrost-flash-430m" # private — pulled with the HF_TOKEN space secret
|
| 9 |
+
TOKEN = os.environ.get("HF_TOKEN")
|
| 10 |
+
|
| 11 |
+
print("Downloading Bifrost Flash assets…", flush=True)
|
| 12 |
+
weights = hf_hub_download(REPO, "model.safetensors", token=TOKEN)
|
| 13 |
+
spm_path = hf_hub_download(REPO, "nordic_unigram_65k.model", token=TOKEN)
|
| 14 |
+
sp = spm.SentencePieceProcessor(); sp.load(spm_path)
|
| 15 |
+
model = NordicFlash.from_checkpoint(weights, device="cpu", dtype=torch.float32)
|
| 16 |
+
print("Model ready.", flush=True)
|
| 17 |
+
|
| 18 |
+
TARGETS = {
|
| 19 |
+
"Swedish": "sv", "Danish": "da", "Norwegian Bokmål": "nb",
|
| 20 |
+
"Norwegian Nynorsk": "nn", "Finnish": "fi", "Icelandic": "is", "English": "en",
|
| 21 |
+
}
|
| 22 |
+
LID = {"en": 65000, "sv": 65001, "da": 65002, "nb": 65003, "nn": 65004, "fi": 65005, "is": 65006}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def translate(text, target):
|
| 26 |
+
text = (text or "").strip()
|
| 27 |
+
if not text:
|
| 28 |
+
return ""
|
| 29 |
+
ids = sp.encode(text, out_type=int)[:1000]
|
| 30 |
+
out = model.translate(ids, LID[TARGETS[target]], max_new=128)
|
| 31 |
+
return sp.decode(out)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
with gr.Blocks(title="Bifrost — Nordic translation") as demo:
|
| 35 |
+
gr.Markdown(
|
| 36 |
+
"# 🌉 Bifrost Flash 430M\n"
|
| 37 |
+
"Open **Nordic ↔ English** translation — Swedish · Danish · Norwegian (Bokmål/Nynorsk) · "
|
| 38 |
+
"Finnish · Icelandic ↔ English. Source language is auto-detected; pick a target.\n\n"
|
| 39 |
+
"*The 430M distilled model, running on CPU. Part of [NodeNestor](https://nodenestor.com).*"
|
| 40 |
+
)
|
| 41 |
+
with gr.Row():
|
| 42 |
+
inp = gr.Textbox(label="Text", lines=4, placeholder="Hello, how are you today?")
|
| 43 |
+
out = gr.Textbox(label="Translation", lines=4)
|
| 44 |
+
target = gr.Dropdown(list(TARGETS), value="Swedish", label="Translate into")
|
| 45 |
+
btn = gr.Button("Translate", variant="primary")
|
| 46 |
+
btn.click(translate, [inp, target], out)
|
| 47 |
+
inp.submit(translate, [inp, target], out)
|
| 48 |
+
gr.Examples(
|
| 49 |
+
[["Hello, how are you today?", "Swedish"],
|
| 50 |
+
["The weather is nice and the sun is shining.", "Danish"],
|
| 51 |
+
["Vädret är fint idag.", "English"],
|
| 52 |
+
["God morgen, min venn.", "Finnish"]],
|
| 53 |
+
[inp, target],
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
demo.queue(max_size=16).launch(server_name="0.0.0.0", server_port=7860)
|
modeling_flash.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Standalone inference for Nordic Flash 430M (Bifrost family) — pure PyTorch,
|
| 3 |
+
depends only on `torch`. Independent re-implementation of the forward pass;
|
| 4 |
+
nothing imported from the training stack.
|
| 5 |
+
|
| 6 |
+
Hybrid architecture (~430M):
|
| 7 |
+
* 18 layers in a [dynamic_conv, dynamic_conv, gqa] x6 pattern.
|
| 8 |
+
* DynaConv layers: per-token data-dependent causal depthwise conv —
|
| 9 |
+
kernel_proj(x) -> (head_dim 80, K 14) softmax-over-taps weights, each kernel
|
| 10 |
+
shared by 16 channels; out = out_proj(causal_conv(in_proj(x)) * silu(gate_proj(x))).
|
| 11 |
+
* GQA layers: grouped-query attention (16/4 heads, head_dim 80, fused QKV),
|
| 12 |
+
partial rotary (first 25% of head dim).
|
| 13 |
+
* SwiGLU FFN, RMSNorm, parallel residual (one norm), tied embeddings.
|
| 14 |
+
"""
|
| 15 |
+
from __future__ import annotations
|
| 16 |
+
from dataclasses import dataclass, field
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
import torch.nn.functional as F
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _default_layer_types():
|
| 23 |
+
return ["dynamic_conv", "dynamic_conv", "gqa"] * 6
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class FlashConfig:
|
| 28 |
+
vocab_size: int = 65008
|
| 29 |
+
hidden_dim: int = 1280
|
| 30 |
+
num_layers: int = 18
|
| 31 |
+
num_attention_heads: int = 16
|
| 32 |
+
num_kv_heads: int = 4
|
| 33 |
+
head_dim: int = 80
|
| 34 |
+
ffn_intermediate: int = 3584
|
| 35 |
+
rope_theta: float = 500000.0
|
| 36 |
+
rope_partial_factor: float = 0.25
|
| 37 |
+
rms_norm_eps: float = 1e-6
|
| 38 |
+
max_position: int = 4096
|
| 39 |
+
dynaconv_kernel: int = 14 # K taps
|
| 40 |
+
dynaconv_head_dim: int = 80 # number of distinct per-token kernels
|
| 41 |
+
dynaconv_num_heads: int = 16 # channels sharing each kernel (head_dim*num_heads = hidden)
|
| 42 |
+
layer_types: list = field(default_factory=_default_layer_types)
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def rotary_dim(self) -> int:
|
| 46 |
+
rd = max(2, int(self.head_dim * self.rope_partial_factor))
|
| 47 |
+
return rd - (rd % 2)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class RMSNorm(nn.Module):
|
| 51 |
+
def __init__(self, dim: int, eps: float = 1e-6) -> None:
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.eps = eps
|
| 54 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 55 |
+
|
| 56 |
+
def forward(self, x):
|
| 57 |
+
dtype = x.dtype
|
| 58 |
+
x32 = x.float()
|
| 59 |
+
x32 = x32 * torch.rsqrt(x32.pow(2).mean(-1, keepdim=True) + self.eps)
|
| 60 |
+
return (x32 * self.weight.float()).to(dtype)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _rotate_half(x):
|
| 64 |
+
d = x.shape[-1]
|
| 65 |
+
return torch.cat([-x[..., d // 2:], x[..., : d // 2]], dim=-1)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class RoPE(nn.Module):
|
| 69 |
+
def __init__(self, head_dim, rotary_dim, theta):
|
| 70 |
+
super().__init__()
|
| 71 |
+
self.rotary_dim = rotary_dim
|
| 72 |
+
idx = torch.arange(0, rotary_dim, 2, dtype=torch.float32)
|
| 73 |
+
self.register_buffer("inv_freq", 1.0 / (theta ** (idx / rotary_dim)), persistent=False)
|
| 74 |
+
|
| 75 |
+
def forward(self, q, k):
|
| 76 |
+
rot = self.rotary_dim
|
| 77 |
+
t = torch.arange(q.shape[-2], device=q.device, dtype=torch.float32)
|
| 78 |
+
freqs = torch.outer(t, self.inv_freq.to(q.device))
|
| 79 |
+
emb = torch.cat([freqs, freqs], dim=-1)
|
| 80 |
+
cos, sin = emb.cos().to(q.dtype)[None, None], emb.sin().to(q.dtype)[None, None]
|
| 81 |
+
qr, qp = q[..., :rot], q[..., rot:]
|
| 82 |
+
kr, kp = k[..., :rot], k[..., rot:]
|
| 83 |
+
qr = qr * cos + _rotate_half(qr) * sin
|
| 84 |
+
kr = kr * cos + _rotate_half(kr) * sin
|
| 85 |
+
return torch.cat([qr, qp], -1), torch.cat([kr, kp], -1)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class Attention(nn.Module):
|
| 89 |
+
def __init__(self, cfg: FlashConfig):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.nq, self.nkv, self.hd = cfg.num_attention_heads, cfg.num_kv_heads, cfg.head_dim
|
| 92 |
+
self.groups = self.nq // self.nkv
|
| 93 |
+
q, kv = self.nq * self.hd, self.nkv * self.hd
|
| 94 |
+
self.qkv_proj = nn.Linear(cfg.hidden_dim, q + 2 * kv, bias=False)
|
| 95 |
+
self.o_proj = nn.Linear(q, cfg.hidden_dim, bias=False)
|
| 96 |
+
self._split = (q, kv, kv)
|
| 97 |
+
self.rope = RoPE(self.hd, cfg.rotary_dim, cfg.rope_theta)
|
| 98 |
+
|
| 99 |
+
def forward(self, x):
|
| 100 |
+
b, s, _ = x.shape
|
| 101 |
+
qf, kf, vf = self.qkv_proj(x).split(self._split, dim=-1)
|
| 102 |
+
q = qf.view(b, s, self.nq, self.hd).transpose(1, 2)
|
| 103 |
+
k = kf.view(b, s, self.nkv, self.hd).transpose(1, 2)
|
| 104 |
+
v = vf.view(b, s, self.nkv, self.hd).transpose(1, 2)
|
| 105 |
+
q, k = self.rope(q, k)
|
| 106 |
+
if self.groups > 1:
|
| 107 |
+
k = k.repeat_interleave(self.groups, dim=1)
|
| 108 |
+
v = v.repeat_interleave(self.groups, dim=1)
|
| 109 |
+
out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
|
| 110 |
+
return self.o_proj(out.transpose(1, 2).reshape(b, s, self.nq * self.hd))
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class DynaConv(nn.Module):
|
| 114 |
+
"""Per-token data-dependent causal depthwise conv with a silu gate.
|
| 115 |
+
|
| 116 |
+
kernel_proj(x) -> (head_dim, K) softmax-over-taps weights, each kernel shared
|
| 117 |
+
by num_heads channels; causal depthwise conv of in_proj(x); gated by silu(gate).
|
| 118 |
+
out = out_proj( causal_conv(in_proj(x), softmax_kernel) * silu(gate_proj(x)) ).
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
def __init__(self, cfg: FlashConfig):
|
| 122 |
+
super().__init__()
|
| 123 |
+
D = cfg.hidden_dim
|
| 124 |
+
self.K = cfg.dynaconv_kernel
|
| 125 |
+
self.hdc = cfg.dynaconv_head_dim # distinct kernels
|
| 126 |
+
self.nh = cfg.dynaconv_num_heads # channels per kernel
|
| 127 |
+
assert self.hdc * self.nh == D
|
| 128 |
+
self.in_proj = nn.Linear(D, D, bias=False)
|
| 129 |
+
self.gate_proj = nn.Linear(D, D, bias=False)
|
| 130 |
+
self.out_proj = nn.Linear(D, D, bias=False)
|
| 131 |
+
self.kernel_proj = nn.Linear(D, self.hdc * self.K, bias=False)
|
| 132 |
+
|
| 133 |
+
def forward(self, x):
|
| 134 |
+
B, S, D = x.shape
|
| 135 |
+
K = self.K
|
| 136 |
+
k = self.kernel_proj(x).view(B, S, self.hdc, K) # (B,S,head_dim,K)
|
| 137 |
+
k = F.softmax(k.float(), dim=-1).to(x.dtype) # softmax over taps
|
| 138 |
+
w = k.repeat_interleave(self.nh, dim=2) # (B,S,D,K); channel c -> kernel c//nh
|
| 139 |
+
bx = self.in_proj(x)
|
| 140 |
+
g = self.gate_proj(x)
|
| 141 |
+
xp = F.pad(bx, (0, 0, K - 1, 0)) # left-pad seq by K-1
|
| 142 |
+
xu = xp.unfold(1, K, 1) # (B,S,D,K); xu[...,K-1]=bx[t] (current)
|
| 143 |
+
y = (xu * w).sum(-1) * F.silu(g) # (B,S,D)
|
| 144 |
+
return self.out_proj(y)
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
class SwiGLU(nn.Module):
|
| 148 |
+
def __init__(self, cfg: FlashConfig):
|
| 149 |
+
super().__init__()
|
| 150 |
+
self.gate_proj = nn.Linear(cfg.hidden_dim, cfg.ffn_intermediate, bias=False)
|
| 151 |
+
self.up_proj = nn.Linear(cfg.hidden_dim, cfg.ffn_intermediate, bias=False)
|
| 152 |
+
self.down_proj = nn.Linear(cfg.ffn_intermediate, cfg.hidden_dim, bias=False)
|
| 153 |
+
|
| 154 |
+
def forward(self, x):
|
| 155 |
+
return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
class DecoderBlock(nn.Module):
|
| 159 |
+
def __init__(self, cfg: FlashConfig, layer_type: str):
|
| 160 |
+
super().__init__()
|
| 161 |
+
self.input_norm = RMSNorm(cfg.hidden_dim, cfg.rms_norm_eps)
|
| 162 |
+
self.attention = Attention(cfg) if layer_type == "gqa" else DynaConv(cfg)
|
| 163 |
+
self.ffn = SwiGLU(cfg)
|
| 164 |
+
|
| 165 |
+
def forward(self, x):
|
| 166 |
+
h = self.input_norm(x)
|
| 167 |
+
return x + self.attention(h) + self.ffn(h)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
class NordicFlash(nn.Module):
|
| 171 |
+
def __init__(self, cfg: FlashConfig):
|
| 172 |
+
super().__init__()
|
| 173 |
+
self.cfg = cfg
|
| 174 |
+
self.embed_tokens = nn.Embedding(cfg.vocab_size, cfg.hidden_dim)
|
| 175 |
+
self.layers = nn.ModuleList(DecoderBlock(cfg, t) for t in cfg.layer_types)
|
| 176 |
+
self.final_norm = RMSNorm(cfg.hidden_dim, cfg.rms_norm_eps)
|
| 177 |
+
self.lm_head = nn.Linear(cfg.hidden_dim, cfg.vocab_size, bias=False)
|
| 178 |
+
|
| 179 |
+
def forward(self, input_ids):
|
| 180 |
+
x = self.embed_tokens(input_ids)
|
| 181 |
+
for layer in self.layers:
|
| 182 |
+
x = layer(x)
|
| 183 |
+
return self.lm_head(self.final_norm(x))
|
| 184 |
+
|
| 185 |
+
@torch.no_grad()
|
| 186 |
+
def translate(self, src_ids, tgt_lang_id, *, bos=1, eos=2, eos_src=65007,
|
| 187 |
+
max_new=256, vocab_text_limit=65000):
|
| 188 |
+
dev = self.embed_tokens.weight.device
|
| 189 |
+
ids = [bos, tgt_lang_id] + list(src_ids) + [eos_src]
|
| 190 |
+
out = []
|
| 191 |
+
for _ in range(max_new):
|
| 192 |
+
logits = self(torch.tensor([ids], device=dev)) # full recompute (local conv + attn)
|
| 193 |
+
nxt = int(logits[0, -1].argmax())
|
| 194 |
+
if nxt == eos:
|
| 195 |
+
break
|
| 196 |
+
out.append(nxt); ids.append(nxt)
|
| 197 |
+
return [t for t in out if t < vocab_text_limit]
|
| 198 |
+
|
| 199 |
+
@classmethod
|
| 200 |
+
def from_checkpoint(cls, path, device="cuda", dtype=torch.bfloat16, cfg=None):
|
| 201 |
+
cfg = cfg or FlashConfig()
|
| 202 |
+
model = cls(cfg)
|
| 203 |
+
if path.endswith(".safetensors"):
|
| 204 |
+
from safetensors.torch import load_file
|
| 205 |
+
sd = load_file(path)
|
| 206 |
+
else:
|
| 207 |
+
sd = torch.load(path, map_location="cpu", weights_only=False)
|
| 208 |
+
sd = sd.get("model", sd)
|
| 209 |
+
sd = {k.replace("._orig_mod", ""): v for k, v in sd.items()}
|
| 210 |
+
if "lm_head.weight" not in sd and "embed_tokens.weight" in sd:
|
| 211 |
+
sd["lm_head.weight"] = sd["embed_tokens.weight"] # tied
|
| 212 |
+
missing, unexpected = model.load_state_dict(sd, strict=False)
|
| 213 |
+
missing = [m for m in missing if "inv_freq" not in m]
|
| 214 |
+
unexpected = [u for u in unexpected if "inv_freq" not in u]
|
| 215 |
+
if missing or unexpected:
|
| 216 |
+
raise RuntimeError(f"state_dict mismatch:\n missing={missing}\n unexpected={unexpected}")
|
| 217 |
+
return model.to(device=device, dtype=dtype).eval()
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
# --------------------------------------------------------------------------- #
|
| 221 |
+
# Optional HuggingFace wrapper — AutoModelForCausalLM.from_pretrained + #
|
| 222 |
+
# model.generate() (trust_remote_code=True). Cache-free recompute for #
|
| 223 |
+
# version-robustness; NordicFlash.translate() is the fast standalone path. #
|
| 224 |
+
# --------------------------------------------------------------------------- #
|
| 225 |
+
try:
|
| 226 |
+
from transformers import PretrainedConfig, PreTrainedModel
|
| 227 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 228 |
+
|
| 229 |
+
class NordicFlashConfig(PretrainedConfig):
|
| 230 |
+
model_type = "nordic_flash"
|
| 231 |
+
|
| 232 |
+
def __init__(self, vocab_size=65008, hidden_dim=1280, num_layers=18,
|
| 233 |
+
num_attention_heads=16, num_kv_heads=4, head_dim=80,
|
| 234 |
+
ffn_intermediate=3584, rope_theta=500000.0,
|
| 235 |
+
rope_partial_factor=0.25, rms_norm_eps=1e-6, max_position=4096,
|
| 236 |
+
dynaconv_kernel=14, dynaconv_head_dim=80, dynaconv_num_heads=16,
|
| 237 |
+
layer_types=None, **kwargs):
|
| 238 |
+
self.vocab_size = vocab_size
|
| 239 |
+
self.hidden_dim = hidden_dim
|
| 240 |
+
self.num_layers = num_layers
|
| 241 |
+
self.num_attention_heads = num_attention_heads
|
| 242 |
+
self.num_kv_heads = num_kv_heads
|
| 243 |
+
self.head_dim = head_dim
|
| 244 |
+
self.ffn_intermediate = ffn_intermediate
|
| 245 |
+
self.rope_theta = rope_theta
|
| 246 |
+
self.rope_partial_factor = rope_partial_factor
|
| 247 |
+
self.rms_norm_eps = rms_norm_eps
|
| 248 |
+
self.max_position = max_position
|
| 249 |
+
self.dynaconv_kernel = dynaconv_kernel
|
| 250 |
+
self.dynaconv_head_dim = dynaconv_head_dim
|
| 251 |
+
self.dynaconv_num_heads = dynaconv_num_heads
|
| 252 |
+
self.layer_types = layer_types or _default_layer_types()
|
| 253 |
+
self.num_hidden_layers = num_layers
|
| 254 |
+
self.hidden_size = hidden_dim
|
| 255 |
+
self.num_key_value_heads = num_kv_heads
|
| 256 |
+
self.max_position_embeddings = max_position
|
| 257 |
+
kwargs.setdefault("tie_word_embeddings", True)
|
| 258 |
+
kwargs.setdefault("use_cache", False)
|
| 259 |
+
super().__init__(**kwargs)
|
| 260 |
+
|
| 261 |
+
def to_flash(self):
|
| 262 |
+
return FlashConfig(
|
| 263 |
+
vocab_size=self.vocab_size, hidden_dim=self.hidden_dim,
|
| 264 |
+
num_layers=self.num_layers, num_attention_heads=self.num_attention_heads,
|
| 265 |
+
num_kv_heads=self.num_kv_heads, head_dim=self.head_dim,
|
| 266 |
+
ffn_intermediate=self.ffn_intermediate, rope_theta=self.rope_theta,
|
| 267 |
+
rope_partial_factor=self.rope_partial_factor, rms_norm_eps=self.rms_norm_eps,
|
| 268 |
+
max_position=self.max_position, dynaconv_kernel=self.dynaconv_kernel,
|
| 269 |
+
dynaconv_head_dim=self.dynaconv_head_dim, dynaconv_num_heads=self.dynaconv_num_heads,
|
| 270 |
+
layer_types=list(self.layer_types))
|
| 271 |
+
|
| 272 |
+
class NordicFlashForCausalLM(PreTrainedModel):
|
| 273 |
+
config_class = NordicFlashConfig
|
| 274 |
+
_supports_cache_class = False
|
| 275 |
+
_no_split_modules = ["DecoderBlock"]
|
| 276 |
+
|
| 277 |
+
def __init__(self, config: "NordicFlashConfig"):
|
| 278 |
+
super().__init__(config)
|
| 279 |
+
cfg = config.to_flash()
|
| 280 |
+
self.embed_tokens = nn.Embedding(cfg.vocab_size, cfg.hidden_dim)
|
| 281 |
+
self.layers = nn.ModuleList(DecoderBlock(cfg, t) for t in cfg.layer_types)
|
| 282 |
+
self.final_norm = RMSNorm(cfg.hidden_dim, cfg.rms_norm_eps)
|
| 283 |
+
self.lm_head = nn.Linear(cfg.hidden_dim, cfg.vocab_size, bias=False)
|
| 284 |
+
self.post_init()
|
| 285 |
+
|
| 286 |
+
def get_input_embeddings(self): return self.embed_tokens
|
| 287 |
+
def set_input_embeddings(self, v): self.embed_tokens = v
|
| 288 |
+
def get_output_embeddings(self): return self.lm_head
|
| 289 |
+
|
| 290 |
+
def forward(self, input_ids=None, attention_mask=None, past_key_values=None,
|
| 291 |
+
use_cache=None, labels=None, return_dict=True, **kwargs):
|
| 292 |
+
x = self.embed_tokens(input_ids)
|
| 293 |
+
for layer in self.layers:
|
| 294 |
+
x = layer(x)
|
| 295 |
+
logits = self.lm_head(self.final_norm(x))
|
| 296 |
+
return CausalLMOutputWithPast(logits=logits)
|
| 297 |
+
|
| 298 |
+
def prepare_inputs_for_generation(self, input_ids, **kwargs):
|
| 299 |
+
return {"input_ids": input_ids, "use_cache": False}
|
| 300 |
+
|
| 301 |
+
except ImportError:
|
| 302 |
+
pass
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--extra-index-url https://download.pytorch.org/whl/cpu
|
| 2 |
+
torch
|
| 3 |
+
sentencepiece
|
| 4 |
+
safetensors
|
| 5 |
+
huggingface_hub
|