VADRK155 commited on
Commit
edceaee
·
verified ·
1 Parent(s): 569556a

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. Cortex_3.1_Story.pt +3 -0
  2. chat.py +326 -0
  3. config.json +24 -0
  4. requirements.txt +2 -0
Cortex_3.1_Story.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bcc29fff3337e03afb062ac4adc1111597e4c49c3ef93a0a321507903f81333d
3
+ size 250612938
chat.py ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Cortex_2 Chat — just put this script in the same folder as your model files and run:
3
+ python chat.py
4
+
5
+ Required files in the same folder:
6
+ - best_model.pt (or any .pt model file)
7
+ - tokenizer.json
8
+ - config.json
9
+
10
+ Or if your model has tokenizer+config baked in (new format):
11
+ - best_model.pt (only this one file needed!)
12
+ """
13
+
14
+ import torch
15
+ import torch.nn.functional as F
16
+ import json
17
+ import sys
18
+ import math
19
+ from pathlib import Path
20
+
21
+ class CausalSelfAttention(torch.nn.Module):
22
+ def __init__(self, d_model, n_heads, dropout, context_length):
23
+ super().__init__()
24
+ self.n_heads = n_heads
25
+ self.head_dim = d_model // n_heads
26
+ self.qkv = torch.nn.Linear(d_model, 3 * d_model)
27
+ self.proj = torch.nn.Linear(d_model, d_model)
28
+ self.attn_dropout = torch.nn.Dropout(dropout)
29
+ self.resid_dropout = torch.nn.Dropout(dropout)
30
+ self.register_buffer("mask", torch.tril(torch.ones(context_length, context_length)).unsqueeze(0).unsqueeze(0))
31
+
32
+ def forward(self, x):
33
+ B, T, C = x.shape
34
+ qkv = self.qkv(x)
35
+ q, k, v = qkv.chunk(3, dim=-1)
36
+ q = q.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
37
+ k = k.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
38
+ v = v.view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
39
+ attn = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim))
40
+ attn = attn.masked_fill(self.mask[:, :, :T, :T] == 0, float("-inf"))
41
+ attn = F.softmax(attn, dim=-1)
42
+ attn = self.attn_dropout(attn)
43
+ out = attn @ v
44
+ out = out.transpose(1, 2).contiguous().view(B, T, C)
45
+ out = self.proj(out)
46
+ out = self.resid_dropout(out)
47
+ return out
48
+
49
+
50
+ class MLP(torch.nn.Module):
51
+ def __init__(self, d_model, d_ff, dropout):
52
+ super().__init__()
53
+ self.net = torch.nn.Sequential(
54
+ torch.nn.Linear(d_model, d_ff),
55
+ torch.nn.GELU(),
56
+ torch.nn.Linear(d_ff, d_model),
57
+ torch.nn.Dropout(dropout),
58
+ )
59
+
60
+ def forward(self, x):
61
+ return self.net(x)
62
+
63
+
64
+ class TransformerBlock(torch.nn.Module):
65
+ def __init__(self, d_model, n_heads, d_ff, dropout, context_length):
66
+ super().__init__()
67
+ self.ln1 = torch.nn.LayerNorm(d_model)
68
+ self.attn = CausalSelfAttention(d_model, n_heads, dropout, context_length)
69
+ self.ln2 = torch.nn.LayerNorm(d_model)
70
+ self.mlp = MLP(d_model, d_ff, dropout)
71
+
72
+ def forward(self, x):
73
+ x = x + self.attn(self.ln1(x))
74
+ x = x + self.mlp(self.ln2(x))
75
+ return x
76
+
77
+
78
+ class TinyGPT(torch.nn.Module):
79
+ def __init__(self, config):
80
+ super().__init__()
81
+ self.config = config
82
+ vocab_size = config["tokenizer_vocab_size"] + 10
83
+ self.token_emb = torch.nn.Embedding(vocab_size, config["d_model"])
84
+ self.pos_emb = torch.nn.Embedding(config["context_length"], config["d_model"])
85
+ self.drop = torch.nn.Dropout(config["dropout"])
86
+ self.blocks = torch.nn.ModuleList([
87
+ TransformerBlock(config["d_model"], config["n_heads"], config["d_ff"], config["dropout"], config["context_length"])
88
+ for _ in range(config["n_layers"])
89
+ ])
90
+ self.ln_f = torch.nn.LayerNorm(config["d_model"])
91
+ self.head = torch.nn.Linear(config["d_model"], vocab_size, bias=False)
92
+ self.token_emb.weight = self.head.weight
93
+
94
+ def forward(self, idx, targets=None):
95
+ B, T = idx.shape
96
+ pos = torch.arange(0, T, device=idx.device).unsqueeze(0)
97
+ x = self.token_emb(idx) + self.pos_emb(pos)
98
+ x = self.drop(x)
99
+ for block in self.blocks:
100
+ x = block(x)
101
+ x = self.ln_f(x)
102
+ logits = self.head(x)
103
+ loss = None
104
+ if targets is not None:
105
+ loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=0)
106
+ return logits, loss
107
+
108
+
109
+ def find_model_file():
110
+ """Find the model file in current directory."""
111
+ here = Path(".")
112
+
113
+ # Check for .pt files
114
+ pt_files = list(here.glob("*.pt"))
115
+
116
+ # Priority: best_model.pt > final_model.pt > any other .pt
117
+ for name in ["best_model.pt", "final_model.pt"]:
118
+ if name in [f.name for f in pt_files]:
119
+ return here / name
120
+
121
+ # Any .pt file
122
+ if pt_files:
123
+ return pt_files[0]
124
+
125
+ return None
126
+
127
+
128
+ def main():
129
+ device = torch.device("cpu")
130
+
131
+ # Find model file
132
+ model_path = find_model_file()
133
+ if model_path is None:
134
+ print("No .pt model file found! Put this script in the same folder as your model.")
135
+ sys.exit(1)
136
+
137
+ # Allow override via command line
138
+ if len(sys.argv) > 1:
139
+ model_path = Path(sys.argv[1])
140
+
141
+ print(f"Loading model from: {model_path.name}")
142
+
143
+ # Load checkpoint
144
+ ckpt = torch.load(model_path, map_location=device, weights_only=False)
145
+
146
+ # Load config & tokenizer
147
+ if "config" in ckpt and "tokenizer" in ckpt:
148
+ # New format: everything in one file
149
+ config = ckpt["config"]
150
+ from tokenizers import Tokenizer
151
+ tokenizer = Tokenizer.from_str(ckpt["tokenizer"])
152
+ print("Loaded config + tokenizer from checkpoint")
153
+ else:
154
+ # Old format: separate files
155
+ here = model_path.parent
156
+ config_path = here / "config.json"
157
+ tokenizer_path = here / "tokenizer.json"
158
+
159
+ if not config_path.exists():
160
+ print("config.json not found next to model!")
161
+ sys.exit(1)
162
+ if not tokenizer_path.exists():
163
+ print("tokenizer.json not found next to model!")
164
+ sys.exit(1)
165
+
166
+ with open(config_path) as f:
167
+ config = json.load(f)
168
+ from tokenizers import Tokenizer
169
+ tokenizer = Tokenizer.from_file(str(tokenizer_path))
170
+ print("Loaded config + tokenizer from separate files")
171
+
172
+ # Build and load model
173
+ model = TinyGPT(config).to(device)
174
+ model.load_state_dict(ckpt["model"])
175
+ model.eval()
176
+
177
+ n_params = sum(p.numel() for p in model.parameters())
178
+ step = ckpt.get("step", "?")
179
+ val_loss = ckpt.get("val_loss", "?")
180
+ if isinstance(val_loss, float):
181
+ val_loss = f"{val_loss:.4f}"
182
+
183
+ print("Cortex_2 loaded!")
184
+ print(f" Parameters: {n_params / 1e6:.1f}M")
185
+ print(f" Step: {step}")
186
+ print(f" Val loss: {val_loss}")
187
+ print(f" Device: {device}")
188
+
189
+ dataset_mode = config.get("dataset_mode", "stories")
190
+ is_chat_model = dataset_mode == "chat"
191
+
192
+ if is_chat_model:
193
+ print(" Mode: conversational (dataset_mode=chat)")
194
+ else:
195
+ print(" Mode: story completion (dataset_mode=stories)")
196
+
197
+ print()
198
+ print("Type a prompt and press Enter. Type 'quit' to exit.")
199
+ if is_chat_model:
200
+ print(" (type 'reset' to clear conversation history)")
201
+ print(" (type 'temp 0.9' to change temperature, current default: 0.8)")
202
+ print("=" * 50)
203
+
204
+ bos_id = tokenizer.token_to_id("<bos>")
205
+ eos_id = tokenizer.token_to_id("<eos>")
206
+ context_length = config["context_length"]
207
+
208
+ # For the chat model we keep the full conversation history as text,
209
+ # in the same "User: ...\nBot: ..." format used during training.
210
+ history_lines = []
211
+ temperature = 0.8
212
+
213
+ # Chat loop
214
+ while True:
215
+ try:
216
+ prompt = input("\nYou: ").strip()
217
+ except (EOFError, KeyboardInterrupt):
218
+ print("\nBye!")
219
+ break
220
+
221
+ if prompt.lower() == "quit":
222
+ print("Bye!")
223
+ break
224
+ if is_chat_model and prompt.lower() == "reset":
225
+ history_lines = []
226
+ print("Conversation history cleared.")
227
+ continue
228
+ if is_chat_model and prompt.lower().startswith("temp"):
229
+ parts = prompt.split()
230
+ if len(parts) == 2:
231
+ try:
232
+ new_temp = float(parts[1])
233
+ if new_temp <= 0:
234
+ print("Temperature must be greater than 0.")
235
+ else:
236
+ temperature = new_temp
237
+ print(f"Temperature set to: {temperature}")
238
+ except ValueError:
239
+ print("Could not parse number. Example: temp 0.9")
240
+ else:
241
+ print(f"Current temperature: {temperature} (example to change: temp 0.9)")
242
+ continue
243
+ if not prompt:
244
+ continue
245
+
246
+ if is_chat_model:
247
+ # Build the full dialogue text: entire history + new turn + "Bot:"
248
+ history_lines.append(f"User: {prompt}")
249
+ history_lines.append("Bot:")
250
+ full_text = "\n".join(history_lines)
251
+
252
+ ids = tokenizer.encode(full_text).ids
253
+ idx = torch.tensor([[bos_id] + ids], dtype=torch.long, device=device)
254
+
255
+ # How many history tokens actually fit in context (before truncation)
256
+ tokens_before_gen = idx.shape[1]
257
+
258
+ # Truncate from the left if history doesn't fit in the model's context
259
+ if idx.shape[1] > context_length:
260
+ idx = idx[:, -context_length:]
261
+
262
+ generated_ids = []
263
+ with torch.no_grad():
264
+ for _ in range(200):
265
+ idx_cond = idx[:, -context_length:]
266
+ logits, _ = model(idx_cond)
267
+ logits = logits[:, -1, :]
268
+ probs = F.softmax(logits / temperature, dim=-1)
269
+ next_id = torch.multinomial(probs, num_samples=1)
270
+ idx = torch.cat([idx, next_id], dim=1)
271
+ generated_ids.append(next_id.item())
272
+
273
+ if next_id.item() == eos_id:
274
+ break
275
+
276
+ # The tokenizer decodes "User:" as "User :" (a space before
277
+ # the colon — an artifact of the Whitespace pre-tokenizer),
278
+ # so we check against the normalized form.
279
+ partial_text = tokenizer.decode(generated_ids)
280
+ normalized = partial_text.replace(" :", ":").replace(" ,", ",")
281
+ if "User:" in normalized:
282
+ break
283
+
284
+ reply_text = tokenizer.decode(generated_ids)
285
+ # Trim off anything the model "made up" on behalf of the user.
286
+ # Normalize the space before ":" and cut on the normalized string,
287
+ # applying the same cut to both versions.
288
+ normalized_reply = reply_text.replace(" :", ":")
289
+ if "User:" in normalized_reply:
290
+ # Simplest approach: cut on the raw text, also matching "User :".
291
+ reply_text = reply_text.split("User :")[0].split("User:")[0].strip()
292
+ else:
293
+ reply_text = reply_text.strip()
294
+
295
+ print(f"Cortex_2: {reply_text}")
296
+
297
+ # Add the model's reply to history for the next turn
298
+ history_lines[-1] = f"Bot: {reply_text}"
299
+
300
+ # Show how much of the context window is used (history + generated reply)
301
+ tokens_used = min(tokens_before_gen + len(generated_ids), context_length)
302
+ pct = tokens_used / context_length * 100
303
+ print(f"Context: {tokens_used}/{context_length} tokens ({pct:.1f}%)")
304
+
305
+ else:
306
+ # Legacy mode — plain text continuation (story generation)
307
+ ids = tokenizer.encode(prompt).ids
308
+ idx = torch.tensor([[bos_id] + ids], dtype=torch.long, device=device)
309
+
310
+ with torch.no_grad():
311
+ for _ in range(750):
312
+ idx_cond = idx[:, -context_length:]
313
+ logits, _ = model(idx_cond)
314
+ logits = logits[:, -1, :]
315
+ probs = F.softmax(logits / 0.8, dim=-1) # temperature 0.8
316
+ next_id = torch.multinomial(probs, num_samples=1)
317
+ idx = torch.cat([idx, next_id], dim=1)
318
+ if next_id.item() == eos_id:
319
+ break
320
+
321
+ text = tokenizer.decode(idx[0].tolist())
322
+ print(f"Cortex_2: {text}")
323
+
324
+
325
+ if __name__ == "__main__":
326
+ main()
config.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dataset_name": "roneneldan/TinyStories",
3
+ "max_dataset_samples": 1500000,
4
+ "tokenizer_vocab_size": 8000,
5
+ "context_length": 256,
6
+ "d_model": 744,
7
+ "n_heads": 4,
8
+ "n_layers": 9,
9
+ "d_ff": 2676,
10
+ "dropout": 0.1,
11
+ "batch_size": 32,
12
+ "learning_rate": 0.0003,
13
+ "weight_decay": 0.1,
14
+ "warmup_steps": 2000,
15
+ "max_steps": 58000,
16
+ "eval_interval": 2000,
17
+ "eval_steps": 100,
18
+ "save_interval": 5000,
19
+ "grad_clip": 1.0,
20
+ "device": "cuda",
21
+ "fp16": true,
22
+ "seed": 42,
23
+ "output_dir": "cortex_54m_output"
24
+ }
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ torch
2
+ tokenizers