Upload folder using huggingface_hub
Browse files- README.md +3 -0
- config.json +1 -0
- flybrain.py +73 -0
- model.safetensors +3 -0
- tokenizer.json +0 -0
README.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FlyBrain LM — Drosophila whole-brain language model
|
| 2 |
+
|
| 3 |
+
Recurrent core = real adult fruit-fly connectome (FlyWire v783, top-48k neurons, 6.65M synapses). See `flybrain.py` for standalone inference.
|
config.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"architecture": "FlyBrainLM3", "vocab": 4096, "n_fly": 48000, "e_in": 160, "leak": 0.3, "connectome": "FlyWire v783 top-48k by out-degree", "tokenizer": "bpe_4k"}
|
flybrain.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# standalone inference for the public Hub repo (torch + safetensors + tokenizers only)
|
| 2 |
+
import json, torch
|
| 3 |
+
from safetensors.torch import load_file
|
| 4 |
+
from tokenizers import Tokenizer
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class FlyBrainLM(torch.nn.Module):
|
| 8 |
+
def __init__(self, vocab=4096, n_fly=48000, e_in=160, leak=0.3):
|
| 9 |
+
super().__init__()
|
| 10 |
+
self.n, self.leak = n_fly, leak
|
| 11 |
+
self.register_buffer("Widx", torch.zeros(2, 1, dtype=torch.long))
|
| 12 |
+
self.Wval = torch.nn.Parameter(torch.zeros(1))
|
| 13 |
+
self.Win = torch.nn.Parameter(torch.zeros(n_fly, e_in), requires_grad=False)
|
| 14 |
+
self.emb = torch.nn.Embedding(vocab, e_in)
|
| 15 |
+
self.lens1 = torch.nn.Linear(n_fly, 1024)
|
| 16 |
+
self.lens2 = torch.nn.Linear(1024, vocab)
|
| 17 |
+
|
| 18 |
+
def forward(self, idx):
|
| 19 |
+
W = torch.sparse_coo_tensor(self.Widx, self.Wval, (self.n, self.n)).coalesce()
|
| 20 |
+
e = self.emb(idx)
|
| 21 |
+
s = torch.zeros(idx.shape[0], self.n, device=idx.device)
|
| 22 |
+
outs = []
|
| 23 |
+
for t in range(idx.shape[1]):
|
| 24 |
+
s = (1 - self.leak) * s + self.leak * torch.tanh(
|
| 25 |
+
e[:, t, :] @ self.Win.T + torch.sparse.mm(W, s.T).T)
|
| 26 |
+
outs.append(s)
|
| 27 |
+
return self.lens2(torch.tanh(self.lens1(torch.stack(outs, 1))))
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def load_model(repo_or_dir, device="cuda"):
|
| 31 |
+
try:
|
| 32 |
+
from huggingface_hub import snapshot_download
|
| 33 |
+
import os
|
| 34 |
+
d = repo_or_dir if os.path.isdir(repo_or_dir) else snapshot_download(repo_or_dir)
|
| 35 |
+
except Exception:
|
| 36 |
+
d = repo_or_dir
|
| 37 |
+
with open(f"{d}/config.json") as f:
|
| 38 |
+
cfg = json.load(f)
|
| 39 |
+
tok = Tokenizer.from_file(f"{d}/tokenizer.json")
|
| 40 |
+
m = FlyBrainLM(cfg["vocab"], cfg["n_fly"], cfg["e_in"], cfg.get("leak", 0.3))
|
| 41 |
+
m.load_state_dict(load_file(f"{d}/model.safetensors", device="cpu"))
|
| 42 |
+
return m.to(device).eval(), tok, cfg
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def generate(model, tok, prompt, max_new=120, temp=0.8, ctx_win=160):
|
| 46 |
+
dev = next(model.parameters()).device
|
| 47 |
+
ids = tok.encode(prompt).ids[-ctx_win:]
|
| 48 |
+
W = torch.sparse_coo_tensor(model.Widx, model.Wval, (model.n, model.n)).coalesce()
|
| 49 |
+
with torch.no_grad():
|
| 50 |
+
e = model.emb(torch.tensor([ids], device=dev))
|
| 51 |
+
s = torch.zeros(1, model.n, device=dev)
|
| 52 |
+
for t in range(len(ids)):
|
| 53 |
+
s = 0.7 * s + 0.3 * torch.tanh(e[:, t, :] @ model.Win.T + torch.sparse.mm(W, s.T).T)
|
| 54 |
+
last, out = ids[-1], []
|
| 55 |
+
for _ in range(max_new):
|
| 56 |
+
e1 = model.emb(torch.tensor([[last]], device=dev))
|
| 57 |
+
s = 0.7 * s + 0.3 * torch.tanh(e1[:, 0, :] @ model.Win.T + torch.sparse.mm(W, s.T).T)
|
| 58 |
+
p = torch.softmax(model.lens2(torch.tanh(model.lens1(s)))[0] / temp, -1)
|
| 59 |
+
nx = int(torch.multinomial(p, 1))
|
| 60 |
+
if nx == tok.encode("[EOS]").ids[0]:
|
| 61 |
+
break
|
| 62 |
+
out.append(nx)
|
| 63 |
+
last = nx
|
| 64 |
+
txt = tok.decode(out)
|
| 65 |
+
cut = txt.find("\nHuman:")
|
| 66 |
+
return txt[:cut].strip() if cut >= 0 else txt.strip()
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if __name__ == "__main__":
|
| 70 |
+
import sys
|
| 71 |
+
repo = sys.argv[1] if len(sys.argv) > 1 else "."
|
| 72 |
+
m, tok, cfg = load_model(repo)
|
| 73 |
+
print(generate(m, tok, "Human: Hi Fly! Who are you?\nFly:"))
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:324e8ce9c796e8f4f33246c35c7ad14f345b409f53dd0bf2ed491cbb6b3c8c45
|
| 3 |
+
size 243109934
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|