Compactbot commited on
Commit
2c168a1
·
verified ·
1 Parent(s): b40aa9c

Add model class (TinyStoriesGPT, trust_remote_code)

Browse files
Files changed (1) hide show
  1. modeling_tinystories.py +183 -0
modeling_tinystories.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Transformers-compatible loading module for the TinyStoriesGPT model.
2
+
3
+ Architecture (matches the exported model.safetensors exactly):
4
+ - LLaMA-style decoder: RMSNorm + RoPE(theta=1e4) + SwiGLU + GQA
5
+ - Tied input embedding / LM head (weight shared, no separate lm_head)
6
+ - Fused qkv projection (one Linear producing q,k,v)
7
+
8
+ Tensor names in the safetensors (must match these attribute names):
9
+ tok.weight, layers.N.attn.qkv.weight, layers.N.attn.proj.weight,
10
+ layers.N.ln1.w, layers.N.ln2.w, layers.N.mlp.{gate,up,down}.weight, ln_f.w
11
+ """
12
+ import math
13
+ import torch
14
+ import torch.nn as nn
15
+ import torch.nn.functional as F
16
+ from dataclasses import dataclass
17
+ from typing import Optional, Tuple
18
+ from transformers import PreTrainedModel
19
+
20
+
21
+ class RMSNorm(nn.Module):
22
+ def __init__(self, dim, eps=1e-6):
23
+ super().__init__()
24
+ self.w = nn.Parameter(torch.ones(dim))
25
+ self.eps = eps
26
+
27
+ def forward(self, x):
28
+ return self.w * (x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + self.eps)).type_as(x)
29
+
30
+
31
+ class CausalSelfAttn(nn.Module):
32
+ def __init__(self, c):
33
+ super().__init__()
34
+ self.nq = c["n_head_q"]
35
+ self.nkv = c["n_head_kv"]
36
+ self.hd = c["head_dim"]
37
+ self.qkv = nn.Linear(c["n_embd"], (self.nq + 2 * self.nkv) * self.hd, bias=False)
38
+ self.proj = nn.Linear(self.nq * self.hd, c["n_embd"], bias=False)
39
+
40
+ def forward(self, x, cos, sin, kv_cache=None):
41
+ B, T, _ = x.shape
42
+ cos = cos[:, :, :T]
43
+ sin = sin[:, :, :T]
44
+ qkv = self.qkv(x).view(B, T, self.nq + 2 * self.nkv, self.hd).transpose(1, 2)
45
+ q, k, v = qkv.split([self.nq, self.nkv, self.nkv], dim=1)
46
+
47
+ def _rope(t):
48
+ t1 = t[..., 0::2]
49
+ t2 = t[..., 1::2]
50
+ return torch.stack([t1 * cos - t2 * sin, t2 * cos + t1 * sin], dim=-1).reshape(t.shape)
51
+
52
+ q = _rope(q)
53
+ k = _rope(k)
54
+ if kv_cache is not None:
55
+ k = torch.cat([kv_cache[0], k], dim=2)
56
+ v = torch.cat([kv_cache[1], v], dim=2)
57
+ y = F.scaled_dot_product_attention(q, k, v, is_causal=kv_cache is None, enable_gqa=True)
58
+ y = y.transpose(1, 2).contiguous().view(B, T, -1)
59
+ return self.proj(y), (k, v)
60
+
61
+
62
+ class MLP(nn.Module):
63
+ def __init__(self, c):
64
+ super().__init__()
65
+ self.gate = nn.Linear(c["n_embd"], c["ffn"], bias=False)
66
+ self.down = nn.Linear(c["ffn"], c["n_embd"], bias=False)
67
+ self.up = nn.Linear(c["n_embd"], c["ffn"], bias=False)
68
+
69
+ def forward(self, x):
70
+ return self.down(F.silu(self.gate(x)) * self.up(x))
71
+
72
+
73
+ class Block(nn.Module):
74
+ def __init__(self, c):
75
+ super().__init__()
76
+ self.ln1 = RMSNorm(c["n_embd"])
77
+ self.attn = CausalSelfAttn(c)
78
+ self.ln2 = RMSNorm(c["n_embd"])
79
+ self.mlp = MLP(c)
80
+
81
+ def forward(self, x, cos, sin, kv_cache=None):
82
+ a, kv = self.attn(self.ln1(x), cos, sin, kv_cache)
83
+ x = x + a
84
+ x = x + self.mlp(self.ln2(x))
85
+ return x, kv
86
+
87
+
88
+ @dataclass
89
+ class Output:
90
+ logits: torch.Tensor
91
+ past_key_values: Optional[Tuple[Tuple[torch.Tensor, torch.Tensor], ...]] = None
92
+
93
+
94
+ class TinyStoriesGPT(PreTrainedModel):
95
+ """Decoder-only GQA LLaMA with tied embedding/head.
96
+
97
+ Reads the standard transformers config attributes from config.json:
98
+ num_hidden_layers, hidden_size, num_attention_heads, num_key_value_heads,
99
+ head_dim, intermediate_size, vocab_size, rope_theta, max_position_embeddings
100
+ """
101
+
102
+ def __init__(self, config):
103
+ super().__init__(config)
104
+ # rope_theta: top-level attr, or nested under rope_scaling (newer transformers)
105
+ if hasattr(config, "rope_theta") and config.rope_theta is not None:
106
+ rope_theta = config.rope_theta
107
+ else:
108
+ rs = getattr(config, "rope_scaling", None) or {}
109
+ rope_theta = rs.get("rope_theta", 10000.0)
110
+ c = dict(
111
+ n_layer=config.num_hidden_layers,
112
+ n_embd=config.hidden_size,
113
+ n_head_q=config.num_attention_heads,
114
+ n_head_kv=config.num_key_value_heads,
115
+ head_dim=config.head_dim,
116
+ ffn=config.intermediate_size,
117
+ rope_theta=rope_theta,
118
+ vocab=config.vocab_size,
119
+ )
120
+ self.c = c
121
+ self.config = config
122
+ self.tok = nn.Embedding(c["vocab"], c["n_embd"])
123
+ self.layers = nn.ModuleList([Block(c) for _ in range(c["n_layer"])])
124
+ self.ln_f = RMSNorm(c["n_embd"])
125
+ # No separate LM head: logits are computed directly from the input
126
+ # embedding (weight tying). This keeps the parameter set identical to
127
+ # the exported model.safetensors (which has no head.weight key).
128
+ self.max_seq = config.max_position_embeddings
129
+ self._rope_cache = {}
130
+ # No tied weights in this model (logits come directly from the input
131
+ # embedding). Make sure the transformers 5.x tied-weight machinery sees
132
+ # an empty mapping so from_pretrained finalization does not trip.
133
+ self._tied_weights_keys = {}
134
+ self.all_tied_weights_keys = {}
135
+
136
+ def _rope(self, T, device):
137
+ key = (T, device)
138
+ if key not in self._rope_cache:
139
+ c = self.c
140
+ freqs = 1.0 / (c["rope_theta"] ** (torch.arange(0, c["head_dim"], 2, device=device).float() / c["head_dim"]))
141
+ t = torch.arange(T, device=device).float()
142
+ ang = torch.outer(t, freqs)
143
+ self._rope_cache[key] = (ang.cos()[None, None], ang.sin()[None, None])
144
+ return self._rope_cache[key]
145
+
146
+ def forward(self, input_ids, attention_mask=None, past_key_values=None, use_cache=False):
147
+ B = input_ids.shape[0]
148
+ T = input_ids.shape[1]
149
+ start = 0 if past_key_values is None else past_key_values[0][0].shape[2]
150
+ cos, sin = self._rope(start + T, input_ids.device)
151
+ x = self.tok(input_ids)
152
+ kvs = []
153
+ for i, blk in enumerate(self.layers):
154
+ kc = past_key_values[i] if past_key_values else None
155
+ x, kv = blk(x, cos, sin, kc)
156
+ kvs.append(kv)
157
+ x = self.ln_f(x)
158
+ logits = F.linear(x, self.tok.weight) # tied head: embedding as output projection
159
+ return Output(logits=logits, past_key_values=tuple(kvs) if use_cache else None)
160
+
161
+ @torch.no_grad()
162
+ def generate(self, input_ids, max_new_tokens=100, temperature=0.8, top_k=0, eos_token_id=None):
163
+ if eos_token_id is None:
164
+ eos_token_id = 2 # </s>
165
+ device = input_ids.shape[0] and input_ids.device
166
+ B = input_ids.shape[0]
167
+ ids = input_ids
168
+ past = None
169
+ for _ in range(max_new_tokens):
170
+ out = self(ids, past_key_values=past, use_cache=True)
171
+ past = out.past_key_values
172
+ logits = out.logits[:, -1, :]
173
+ if temperature and temperature != 1.0:
174
+ logits = logits / temperature
175
+ if top_k and top_k > 0:
176
+ v, _ = torch.topk(logits, top_k)
177
+ logits[logits < v[:, [-1]]] = float("-inf")
178
+ probs = F.softmax(logits, dim=-1)
179
+ nxt = torch.multinomial(probs, 1)
180
+ ids = torch.cat([ids, nxt], dim=1)
181
+ if (nxt.item() == eos_token_id) or (B == 1 and nxt.item() == eos_token_id):
182
+ break
183
+ return ids