Add self-contained loader + TinyStoriesGPT class
Browse files- load_model.py +96 -0
load_model.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load Compactbot/tinystories-50m.
|
| 2 |
+
|
| 3 |
+
Custom small GPT (not a transformers model). Weights are plain state-dict
|
| 4 |
+
tensors in model.safetensors. Tokenizer is a HuggingFace `tokenizers` BPE
|
| 5 |
+
file (tokenizer.json).
|
| 6 |
+
|
| 7 |
+
from load_model import TinyStoriesGPT, load
|
| 8 |
+
model, tok = load()
|
| 9 |
+
ids = tok.encode("Once upon a time,")
|
| 10 |
+
...
|
| 11 |
+
"""
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
import torch.nn.functional as F
|
| 15 |
+
from tokenizers import Tokenizer
|
| 16 |
+
|
| 17 |
+
D, L, H, FFN, VOCAB, SEQ = 512, 16, 8, 2048, 8192, 512
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class RMSNorm(nn.Module):
|
| 21 |
+
def __init__(self, d):
|
| 22 |
+
super().__init__()
|
| 23 |
+
self.w = nn.Parameter(torch.ones(d))
|
| 24 |
+
def forward(self, x):
|
| 25 |
+
return self.w * x * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + 1e-6)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class Block(nn.Module):
|
| 29 |
+
def __init__(self, d, h):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.ln1 = RMSNorm(d); self.ln2 = RMSNorm(d)
|
| 32 |
+
self.qkv = nn.Linear(d, 3 * d, bias=False)
|
| 33 |
+
self.proj = nn.Linear(d, d, bias=False)
|
| 34 |
+
self.fc1 = nn.Linear(d, FFN, bias=False)
|
| 35 |
+
self.fc2 = nn.Linear(FFN, d, bias=False)
|
| 36 |
+
self.h, self.d = h, d
|
| 37 |
+
def forward(self, x):
|
| 38 |
+
B, T, Dd = x.shape
|
| 39 |
+
h = self.ln1(x)
|
| 40 |
+
qkv = self.qkv(h).view(B, T, 3, self.h, Dd // self.h).transpose(2, 1)
|
| 41 |
+
q, k, v = qkv[:, 0], qkv[:, 1], qkv[:, 2]
|
| 42 |
+
q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
|
| 43 |
+
att = F.scaled_dot_product_attention(q, k, v, is_causal=True)
|
| 44 |
+
att = att.transpose(1, 2).reshape(B, T, Dd)
|
| 45 |
+
x = x + self.proj(att)
|
| 46 |
+
x = x + self.fc2(F.gelu(self.fc1(self.ln2(x))))
|
| 47 |
+
return x
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class TinyStoriesGPT(nn.Module):
|
| 51 |
+
def __init__(self):
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.tok = nn.Embedding(VOCAB, D)
|
| 54 |
+
self.pos = nn.Embedding(SEQ, D)
|
| 55 |
+
self.blocks = nn.ModuleList([Block(D, H) for _ in range(L)])
|
| 56 |
+
self.ln_f = RMSNorm(D)
|
| 57 |
+
self.lm_head = self.tok # weight-tied
|
| 58 |
+
def forward(self, idx, targets=None):
|
| 59 |
+
B, T = idx.shape
|
| 60 |
+
x = self.tok(idx) + self.pos(torch.arange(T, device=idx.device))
|
| 61 |
+
for b in self.blocks:
|
| 62 |
+
x = b(x)
|
| 63 |
+
x = self.ln_f(x)
|
| 64 |
+
logits = self.lm_head(x)
|
| 65 |
+
loss = None
|
| 66 |
+
if targets is not None:
|
| 67 |
+
loss = F.cross_entropy(logits.view(-1, VOCAB), targets.view(-1))
|
| 68 |
+
return logits, loss
|
| 69 |
+
def generate(self, idx, max_new, temp=0.8, top_k=40):
|
| 70 |
+
for _ in range(max_new):
|
| 71 |
+
logits, _ = self(idx)
|
| 72 |
+
logits = logits[:, -1] / temp
|
| 73 |
+
if top_k:
|
| 74 |
+
v, _ = torch.topk(logits, top_k)
|
| 75 |
+
logits[logits < v[:, [-1]]] = float("-inf")
|
| 76 |
+
nxt = torch.multinomial(F.softmax(logits, -1), 1)
|
| 77 |
+
idx = torch.cat([idx, nxt], 1)
|
| 78 |
+
return idx
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def load(weights="model.safetensors", tokenizer="tokenizer.json", device="cuda"):
|
| 82 |
+
from safetensors.torch import load_file
|
| 83 |
+
m = TinyStoriesGPT().to(device)
|
| 84 |
+
sd = load_file(weights)
|
| 85 |
+
m.load_state_dict(sd)
|
| 86 |
+
m.eval()
|
| 87 |
+
tok = Tokenizer.from_file(tokenizer)
|
| 88 |
+
return m, tok
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
if __name__ == "__main__":
|
| 92 |
+
m, tok = load()
|
| 93 |
+
ids = tok.encode("Once upon a time,")
|
| 94 |
+
ids = torch.tensor([ids], device="cuda")
|
| 95 |
+
out = m.generate(ids, 100, temp=0.8, top_k=40)
|
| 96 |
+
print(tok.decode(out[0].tolist(), skip_special_tokens=True))
|