kipasyangin5 commited on
Commit
7a18461
·
verified ·
1 Parent(s): b3b4eef

Add clean complete training code folder

Browse files
training_code/main_kaggle.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Kaggle GPU Training Script - Run 1: Chinchilla Optimal (100 Million Tokens)
3
+ 5M Parameter Model trained on Real Streamed Datasets + Terminal Commands.
4
+ Auto-uploads to HuggingFace repository: 'kipasyangin5/5m-terminal-lm-chinchilla'
5
+ """
6
+ import os
7
+ import math
8
+ import time
9
+ import random
10
+ import json
11
+ import torch
12
+ import torch.nn as nn
13
+ import torch.nn.functional as F
14
+ from torch.utils.data import Dataset, DataLoader
15
+ from tokenizers import Tokenizer, models, trainers, pre_tokenizers, decoders, processors
16
+ from transformers import PreTrainedTokenizerFast
17
+ from datasets import load_dataset
18
+ from huggingface_hub import HfApi, login
19
+
20
+ # ==========================================
21
+ # 1. Environment & Credentials Setup
22
+ # ==========================================
23
+ HF_TOKEN = os.environ.get("HF_TOKEN", "YOUR_HF_TOKEN")
24
+ HF_REPO_ID = os.environ.get("HF_REPO_ID", "kipasyangin5/5m-terminal-lm-chinchilla")
25
+
26
+ if HF_TOKEN:
27
+ try:
28
+ login(token=HF_TOKEN)
29
+ print(f"[HF Login] Authenticated as '{HF_REPO_ID.split('/')[0]}'.")
30
+ except Exception as e:
31
+ print(f"[HF Login Warning] {e}")
32
+
33
+ # ==========================================
34
+ # 2. Terminal Commands Data Generator
35
+ # ==========================================
36
+ COMMAND_TEMPLATES = [
37
+ ("How do I navigate up one directory?", "cd .."),
38
+ ("How do I go to the home directory?", "cd ~"),
39
+ ("How do I check my current directory path?", "pwd"),
40
+ ("How do I list all files including hidden files?", "ls -la"),
41
+ ("How do I list files with human readable file sizes?", "ls -lh"),
42
+ ("How do I create a nested directory structure?", "mkdir -p path/to/nested/directory"),
43
+ ("How do I print directory tree structure?", "tree -L 2"),
44
+ ("How do I copy a directory recursively?", "cp -r source_dir/ target_dir/"),
45
+ ("How do I move or rename a file?", "mv old_filename.txt new_filename.txt"),
46
+ ("How do I force remove a folder and all contents?", "rm -rf target_folder/"),
47
+ ("How do I create an empty file?", "touch index.js"),
48
+ ("How do I inspect the first 20 lines of a file?", "head -n 20 logfile.log"),
49
+ ("How do I monitor a log file in real-time?", "tail -f /var/log/syslog"),
50
+ ("How do I count lines in a text file?", "wc -l dataset.txt"),
51
+ ("How do I recursively search for text in files?", "grep -rn \"search_term\" ."),
52
+ ("How do I find all python files in the current folder?", "find . -type f -name \"*.py\""),
53
+ ("How do I sort lines and remove duplicates?", "sort input.txt | uniq -c"),
54
+ ("How do I make a shell script executable?", "chmod +x script.sh"),
55
+ ("How do I check system RAM usage?", "free -h"),
56
+ ("How do I check disk space usage in human readable format?", "df -h"),
57
+ ("How do I check disk usage of current directories?", "du -sh * | sort -hr"),
58
+ ("How do I download a file silently with curl?", "curl -sSL https://example.com/file.tar.gz -o file.tar.gz"),
59
+ ("How do I check repository status in git?", "git status"),
60
+ ("How do I stage all changed files in git?", "git add ."),
61
+ ("How do I commit staged changes with a message?", "git commit -m \"feat: implement terminal parser\""),
62
+ ("How do I push commits to remote main branch?", "git push origin main")
63
+ ]
64
+
65
+ SHELL_INTERACTIONS = [
66
+ "$ cd ..\n$ pwd\n/home/user\n$ ls -la\ntotal 32\ndrwxr-xr-x 4 user user 4096 Aug 2 00:00 .\ndrwxr-xr-x 8 user user 4096 Aug 2 00:00 ..\n-rw-r--r-- 1 user user 220 Aug 2 00:00 .bashrc",
67
+ "$ mkdir project && cd project\n$ git init\nInitialized empty Git repository in /home/user/project/.git/\n$ touch main.py README.md\n$ git status\nOn branch main\nUntracked files:\n (use \"git add <file>...\" to include in what will be committed)\n\tREADME.md\n\tmain.py",
68
+ "$ grep -rn \"import torch\" src/\nsrc/model.py:1:import torch\nsrc/train.py:2:import torch\nsrc/utils.py:1:import torch",
69
+ "$ chmod +x build.sh\n$ ./build.sh\n[INFO] Building release binary...\n[SUCCESS] Build completed in 2.4s."
70
+ ]
71
+
72
+ def generate_terminal_samples(num_samples=20000):
73
+ samples = []
74
+ for _ in range(num_samples):
75
+ qa = random.choice(COMMAND_TEMPLATES)
76
+ fmt = random.choice([
77
+ f"User: {qa[0]}\nAssistant: Run `{qa[1]}`\n",
78
+ f"Question: {qa[0]}\nAnswer:\n```bash\n{qa[1]}\n```\n",
79
+ f"$ {qa[1]}\n# Executed successfully\n"
80
+ ])
81
+ samples.append(fmt)
82
+ for _ in range(num_samples // 2):
83
+ s = random.choice(SHELL_INTERACTIONS)
84
+ samples.append(f"```session\n{s}\n```\n")
85
+ return samples
86
+
87
+ # ==========================================
88
+ # 3. Model Architecture (5.0M Parameters)
89
+ # ==========================================
90
+ class RMSNorm(nn.Module):
91
+ def __init__(self, dim, eps=1e-6):
92
+ super().__init__()
93
+ self.eps = eps
94
+ self.weight = nn.Parameter(torch.ones(dim))
95
+
96
+ def forward(self, x):
97
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) * self.weight
98
+
99
+ class SwiGLUMLP(nn.Module):
100
+ def __init__(self, dim, inter_dim):
101
+ super().__init__()
102
+ self.gate_proj = nn.Linear(dim, inter_dim, bias=False)
103
+ self.up_proj = nn.Linear(dim, inter_dim, bias=False)
104
+ self.down_proj = nn.Linear(inter_dim, dim, bias=False)
105
+
106
+ def forward(self, x):
107
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
108
+
109
+ class CausalSelfAttention(nn.Module):
110
+ def __init__(self, dim, n_head):
111
+ super().__init__()
112
+ self.dim = dim
113
+ self.n_head = n_head
114
+ self.head_dim = dim // n_head
115
+ self.q_proj = nn.Linear(dim, dim, bias=False)
116
+ self.k_proj = nn.Linear(dim, dim, bias=False)
117
+ self.v_proj = nn.Linear(dim, dim, bias=False)
118
+ self.out_proj = nn.Linear(dim, dim, bias=False)
119
+
120
+ def forward(self, x):
121
+ B, T, C = x.shape
122
+ q = self.q_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
123
+ k = self.k_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
124
+ v = self.v_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
125
+
126
+ y = F.scaled_dot_product_attention(q, k, v, is_causal=True)
127
+ y = y.transpose(1, 2).contiguous().view(B, T, C)
128
+ return self.out_proj(y)
129
+
130
+ class TransformerBlock(nn.Module):
131
+ def __init__(self, dim, n_head, inter_dim):
132
+ super().__init__()
133
+ self.attn = CausalSelfAttention(dim, n_head)
134
+ self.mlp = SwiGLUMLP(dim, inter_dim)
135
+ self.norm1 = RMSNorm(dim)
136
+ self.norm2 = RMSNorm(dim)
137
+
138
+ def forward(self, x):
139
+ x = x + self.attn(self.norm1(x))
140
+ x = x + self.mlp(self.norm2(x))
141
+ return x
142
+
143
+ class TerminalLM5M(nn.Module):
144
+ def __init__(self, vocab_size=4096, dim=256, n_layer=6, n_head=8, inter_dim=512, max_seq_len=512):
145
+ super().__init__()
146
+ self.vocab_size = vocab_size
147
+ self.dim = dim
148
+ self.max_seq_len = max_seq_len
149
+ self.tok_embeddings = nn.Embedding(vocab_size, dim)
150
+ self.pos_embeddings = nn.Embedding(max_seq_len, dim)
151
+ self.layers = nn.ModuleList([
152
+ TransformerBlock(dim, n_head, inter_dim) for _ in range(n_layer)
153
+ ])
154
+ self.norm = RMSNorm(dim)
155
+ self.lm_head = nn.Linear(dim, vocab_size, bias=False)
156
+ self.lm_head.weight = self.tok_embeddings.weight
157
+
158
+ self.apply(self._init_weights)
159
+
160
+ def _init_weights(self, module):
161
+ if isinstance(module, nn.Linear):
162
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
163
+ elif isinstance(module, nn.Embedding):
164
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
165
+
166
+ def forward(self, input_ids, targets=None):
167
+ B, T = input_ids.shape
168
+ device = input_ids.device
169
+ pos = torch.arange(0, T, dtype=torch.long, device=device)
170
+
171
+ h = self.tok_embeddings(input_ids) + self.pos_embeddings(pos)
172
+ for layer in self.layers:
173
+ h = layer(h)
174
+ h = self.norm(h)
175
+ logits = self.lm_head(h)
176
+
177
+ loss = None
178
+ if targets is not None:
179
+ loss = F.cross_entropy(logits.view(-1, self.vocab_size), targets.view(-1))
180
+
181
+ return logits, loss
182
+
183
+ class TextDataset(Dataset):
184
+ def __init__(self, token_ids, seq_len=256):
185
+ self.seq_len = seq_len
186
+ self.num_samples = (len(token_ids) - 1) // seq_len
187
+ self.inputs = []
188
+ self.targets = []
189
+ for i in range(self.num_samples):
190
+ start = i * seq_len
191
+ end = start + seq_len
192
+ self.inputs.append(token_ids[start:end])
193
+ self.targets.append(token_ids[start+1:end+1])
194
+
195
+ def __len__(self):
196
+ return len(self.inputs)
197
+
198
+ def __getitem__(self, idx):
199
+ return torch.tensor(self.inputs[idx], dtype=torch.long), torch.tensor(self.targets[idx], dtype=torch.long)
200
+
201
+ # ==========================================
202
+ # 4. Tokenizer Construction
203
+ # ==========================================
204
+ def build_tokenizer(vocab_size=4096):
205
+ print(f"[Tokenizer] Training 4096 BPE Tokenizer...")
206
+ tokenizer = Tokenizer(models.BPE(unk_token="<unk>"))
207
+ tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
208
+ tokenizer.decoder = decoders.ByteLevel()
209
+ tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
210
+
211
+ special_tokens = ["<pad>", "<s>", "</s>", "<unk>", "<cmd>", "</cmd>", "<user>", "<assistant>"]
212
+
213
+ trainer = trainers.BpeTrainer(
214
+ vocab_size=vocab_size,
215
+ special_tokens=special_tokens,
216
+ min_frequency=2,
217
+ show_progress=False
218
+ )
219
+
220
+ corpus = generate_terminal_samples(15000)
221
+ tokenizer.train_from_iterator(corpus, trainer=trainer)
222
+
223
+ fast_tokenizer = PreTrainedTokenizerFast(
224
+ tokenizer_object=tokenizer,
225
+ bos_token="<s>",
226
+ eos_token="</s>",
227
+ pad_token="<pad>",
228
+ unk_token="<unk>",
229
+ mask_token="<mask >",
230
+ additional_special_tokens=["<cmd>", "</cmd>", "<user>", "<assistant>"]
231
+ )
232
+ return fast_tokenizer
233
+
234
+ # ==========================================
235
+ # 5. Main Training Routine
236
+ # ==========================================
237
+ def main():
238
+ device = "cuda" if torch.cuda.is_available() else "cpu"
239
+ print("==================================================")
240
+ print(" RUN 1: CHINCHILLA OPTIMAL (100M TOKENS)")
241
+ print(f" Target HF Repo: {HF_REPO_ID}")
242
+ print(f" Device: {device.upper()}")
243
+ if device == "cuda":
244
+ print(f" GPU Device: {torch.cuda.get_device_name(0)}")
245
+ print("==================================================")
246
+
247
+ # 1. Build Tokenizer
248
+ tokenizer = build_tokenizer(vocab_size=4096)
249
+ vocab_size = len(tokenizer)
250
+
251
+ # 2. Build Dataset (Streaming Real Datasets + Terminal Commands)
252
+ print("[Dataset] Building dataset from real sources + Terminal engine...")
253
+ terminal_samples = generate_terminal_samples(num_samples=30000)
254
+
255
+ # Try streaming real wikitext from HF
256
+ wikitext_text = ""
257
+ try:
258
+ print("[Dataset] Streaming real 'wikitext-2-raw-v1' from Hugging Face...")
259
+ ds_wiki = load_dataset('wikitext', 'wikitext-2-raw-v1', split='train', streaming=True)
260
+ wiki_lines = []
261
+ for idx, item in enumerate(ds_wiki):
262
+ if idx >= 5000: break
263
+ if item['text'].strip():
264
+ wiki_lines.append(item['text'])
265
+ wikitext_text = "\n".join(wiki_lines)
266
+ print(f"[Dataset] Streamed {len(wiki_lines)} lines of real Wiki text.")
267
+ except Exception as e:
268
+ print(f"[Dataset Warning] {e}")
269
+
270
+ full_text = "\n".join(terminal_samples) + "\n" + wikitext_text
271
+ tokens = tokenizer.encode(full_text)
272
+
273
+ seq_len = 256
274
+ batch_size = 64 if device == "cuda" else 8
275
+ dataset = TextDataset(tokens, seq_len=seq_len)
276
+ dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True)
277
+
278
+ # 3. Model Setup
279
+ model = TerminalLM5M(
280
+ vocab_size=vocab_size,
281
+ dim=256,
282
+ n_layer=6,
283
+ n_head=8,
284
+ inter_dim=512,
285
+ max_seq_len=seq_len
286
+ ).to(device)
287
+
288
+ params = sum(p.numel() for p in model.parameters() if p.requires_grad)
289
+ print(f"[Model] Trainable Parameters: {params:,} (~{params/1e6:.2f}M)")
290
+
291
+ optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
292
+ scaler = torch.amp.GradScaler('cuda') if device == "cuda" else None
293
+
294
+ # Target: 6,104 steps @ 16,384 tokens/step = ~100 Million Tokens
295
+ total_steps = 6104 if device == "cuda" else 30
296
+ model.train()
297
+ step = 0
298
+ t0 = time.time()
299
+ data_iter = iter(dataloader)
300
+
301
+ while step < total_steps:
302
+ try:
303
+ x, y = next(data_iter)
304
+ except StopIteration:
305
+ data_iter = iter(dataloader)
306
+ x, y = next(data_iter)
307
+
308
+ x, y = x.to(device), y.to(device)
309
+ optimizer.zero_grad()
310
+
311
+ if device == "cuda":
312
+ with torch.amp.autocast('cuda', dtype=torch.float16):
313
+ logits, loss = model(x, y)
314
+ scaler.scale(loss).backward()
315
+ scaler.unscale_(optimizer)
316
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
317
+ scaler.step(optimizer)
318
+ scaler.update()
319
+ else:
320
+ logits, loss = model(x, y)
321
+ loss.backward()
322
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
323
+ optimizer.step()
324
+
325
+ step += 1
326
+
327
+ if step % 200 == 0 or step == 1:
328
+ t1 = time.time()
329
+ dt = t1 - t0
330
+ t0 = t1
331
+ tok_s = (200 * batch_size * seq_len) / (dt if dt > 0 else 1.0) if step > 1 else 0
332
+ ppl = math.exp(min(loss.item(), 20.0))
333
+ tokens_so_far = step * batch_size * seq_len
334
+ print(f"Step {step:5d}/{total_steps} | Tokens: {tokens_so_far:,}/100,000,000 | Loss: {loss.item():.4f} | PPL: {ppl:.2f} | Speed: {tok_s:.0f} tok/s")
335
+
336
+ print("\n[SUCCESS] Run 1 (Chinchilla Optimal 100M Tokens) Completed!")
337
+
338
+ # Save and Upload
339
+ output_dir = "saved_5m_model_chinchilla"
340
+ os.makedirs(output_dir, exist_ok=True)
341
+ torch.save(model.state_dict(), os.path.join(output_dir, "model.pt"))
342
+ tokenizer.save_pretrained(output_dir)
343
+
344
+ config_dict = {
345
+ "model_type": "terminal_lm_5m_chinchilla",
346
+ "tokens_trained": step * batch_size * seq_len,
347
+ "vocab_size": vocab_size,
348
+ "dim": 256,
349
+ "n_layer": 6,
350
+ "n_head": 8,
351
+ "total_parameters": params
352
+ }
353
+ with open(os.path.join(output_dir, "config.json"), "w") as f:
354
+ json.dump(config_dict, f, indent=2)
355
+
356
+ if HF_TOKEN:
357
+ try:
358
+ print(f"[HuggingFace] Pushing model to '{HF_REPO_ID}'...")
359
+ api = HfApi()
360
+ api.create_repo(repo_id=HF_REPO_ID, exist_ok=True)
361
+ api.upload_folder(
362
+ folder_path=output_dir,
363
+ repo_id=HF_REPO_ID,
364
+ commit_message=f"Upload 5M Chinchilla Optimal model (100M tokens, Loss: {loss.item():.4f})"
365
+ )
366
+ print(f"🚀 [HF Upload Complete] Model live at: https://huggingface.co/{HF_REPO_ID}")
367
+ except Exception as e:
368
+ print(f"[HF Upload Error] {e}")
369
+
370
+ if __name__ == "__main__":
371
+ main()
training_code/terminal_dataset.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Terminal Commands Dataset Generator
3
+ Generates synthetic and realistic terminal command sequences, bash scripts, command Q&A pairs, and interactive CLI sessions.
4
+ """
5
+ import random
6
+ import os
7
+
8
+ COMMAND_TEMPLATES = [
9
+ # Navigation
10
+ ("How do I navigate up one directory?", "cd .."),
11
+ ("How do I go to the home directory?", "cd ~"),
12
+ ("How do I check my current directory path?", "pwd"),
13
+ ("How do I list all files including hidden files?", "ls -la"),
14
+ ("How do I list files with human readable file sizes?", "ls -lh"),
15
+ ("How do I create a nested directory structure?", "mkdir -p path/to/nested/directory"),
16
+ ("How do I print directory tree structure?", "tree -L 2"),
17
+
18
+ # File Manipulation
19
+ ("How do I copy a directory recursively?", "cp -r source_dir/ target_dir/"),
20
+ ("How do I move or rename a file?", "mv old_filename.txt new_filename.txt"),
21
+ ("How do I force remove a folder and all contents?", "rm -rf target_folder/"),
22
+ ("How do I create an empty file?", "touch index.js"),
23
+ ("How do I inspect the first 20 lines of a file?", "head -n 20 logfile.log"),
24
+ ("How do I monitor a log file in real-time?", "tail -f /var/log/syslog"),
25
+ ("How do I count lines in a text file?", "wc -l dataset.txt"),
26
+
27
+ # Search and Filter
28
+ ("How do I recursively search for text in files?", "grep -rn \"search_term\" ."),
29
+ ("How do I find all python files in the current folder?", "find . -type f -name \"*.py\""),
30
+ ("How do I find files larger than 100MB?", "find / -size +100M 2>/dev/null"),
31
+ ("How do I sort lines and remove duplicates?", "sort input.txt | uniq -c"),
32
+ ("How do I replace text in a file inline?", "sed -i 's/old_text/new_text/g' config.yaml"),
33
+
34
+ # Permissions and Processes
35
+ ("How do I make a shell script executable?", "chmod +x script.sh"),
36
+ ("How do I give full read/write/execute permissions to owner?", "chmod 755 binary_file"),
37
+ ("How do I change owner of a folder recursively?", "chown -R www-data:www-data /var/www/html"),
38
+ ("How do I list running processes matching python?", "ps aux | grep python"),
39
+ ("How do I kill a process by process ID?", "kill -9 12345"),
40
+ ("How do I check system RAM usage?", "free -h"),
41
+ ("How do I check disk space usage in human readable format?", "df -h"),
42
+ ("How do I check disk usage of current directories?", "du -sh * | sort -hr"),
43
+
44
+ # Networking & Web
45
+ ("How do I test network connectivity to a host?", "ping -c 4 google.com"),
46
+ ("How do I download a file silently with curl?", "curl -sSL https://example.com/file.tar.gz -o file.tar.gz"),
47
+ ("How do I download a file using wget?", "wget -q https://example.com/data.json"),
48
+ ("How do I SSH into a remote server with custom port?", "ssh -p 2222 user@remote-host.com"),
49
+ ("How do I copy a local file to a remote server using scp?", "scp -P 2222 local_file.txt user@remote-host:/tmp/"),
50
+ ("How do I view open listening network ports?", "netstat -tulpn"),
51
+
52
+ # Git Version Control
53
+ ("How do I check repository status in git?", "git status"),
54
+ ("How do I stage all changed files in git?", "git add ."),
55
+ ("How do I commit staged changes with a message?", "git commit -m \"feat: implement new terminal parser\""),
56
+ ("How do I push commits to remote main branch?", "git push origin main"),
57
+ ("How do I create and switch to a new git branch?", "git checkout -b feature/new-architecture"),
58
+ ("How do I view compact git log history?", "git log --oneline -n 10"),
59
+ ("How do I discard all unstaged local changes in git?", "git checkout -- ."),
60
+ ("How do I stash working directory changes?", "git stash pop"),
61
+
62
+ # Package Managers & Containers
63
+ ("How do I install python packages from requirements?", "pip install -r requirements.txt"),
64
+ ("How do I install npm dependencies?", "npm install"),
65
+ ("How do I update package lists on Ubuntu?", "sudo apt update && sudo apt upgrade -y"),
66
+ ("How do I build a Docker image with tag?", "docker build -t myapp:latest ."),
67
+ ("How do I run an interactive container with volume mount?", "docker run -it -v $(pwd):/app -p 8080:8080 myapp:latest /bin/bash"),
68
+ ("How do I view running docker containers?", "docker ps -a"),
69
+
70
+ # Compression
71
+ ("How do I compress a folder into a tar.gz archive?", "tar -czvf archive.tar.gz target_directory/"),
72
+ ("How do I extract a tar.gz archive?", "tar -xzvf archive.tar.gz"),
73
+ ("How do I unzip a zip file to a specific destination?", "unzip file.zip -d /path/to/destination/")
74
+ ]
75
+
76
+ SHELL_INTERACTIONS = [
77
+ "$ cd ..\n$ pwd\n/home/user\n$ ls -la\ntotal 32\ndrwxr-xr-x 4 user user 4096 Aug 2 00:00 .\ndrwxr-xr-x 8 user user 4096 Aug 2 00:00 ..\n-rw-r--r-- 1 user user 220 Aug 2 00:00 .bashrc",
78
+ "$ mkdir project && cd project\n$ git init\nInitialized empty Git repository in /home/user/project/.git/\n$ touch main.py README.md\n$ git status\nOn branch main\nUntracked files:\n (use \"git add <file>...\" to include in what will be committed)\n\tREADME.md\n\tmain.py",
79
+ "$ grep -rn \"import torch\" src/\nsrc/model.py:1:import torch\nsrc/train.py:2:import torch\nsrc/utils.py:1:import torch",
80
+ "$ chmod +x build.sh\n$ ./build.sh\n[INFO] Building release binary...\n[SUCCESS] Build completed in 2.4s.",
81
+ "$ curl -I https://api.github.com\nHTTP/2 200\nserver: GitHub.com\ndate: Sun, 02 Aug 2026 00:00:00 GMT\ncontent-type: application/json; charset=utf-8",
82
+ "$ ps aux | grep python\nuser 12345 98.2 4.1 452104 338102 ? Rsl 00:00 12:30 python train.py\nuser 12390 0.0 0.0 6200 892 pts/0 S+ 00:15 0:00 grep python",
83
+ "$ df -h\nFilesystem Size Used Avail Use% Mounted on\n/dev/sda1 99G 32G 63G 34% /\ntmpfs 7.8G 0 7.8G 0% /dev/shm",
84
+ "$ git commit -m \"fix: resolve permission issue\"\n[main a1b2c3d] fix: resolve permission issue\n 2 files changed, 14 insertions(+), 3 deletions(-)\n$ git push origin main\nTo github.com:user/repo.git\n e4f5g6h..a1b2c3d main -> main"
85
+ ]
86
+
87
+ def generate_terminal_dataset(num_samples=15000):
88
+ lines = []
89
+
90
+ # 1. Q&A pairs
91
+ for _ in range(num_samples // 3):
92
+ qa = random.choice(COMMAND_TEMPLATES)
93
+ fmt = random.choice([
94
+ f"User: {qa[0]}\nAssistant: Run `{qa[1]}`\n",
95
+ f"Question: {qa[0]}\nAnswer: You can use the following command:\n```bash\n{qa[1]}\n```\n",
96
+ f"$ {qa[1]}\n# Executed command for: {qa[0]}\n"
97
+ ])
98
+ lines.append(fmt)
99
+
100
+ # 2. Shell session logs
101
+ for _ in range(num_samples // 3):
102
+ interaction = random.choice(SHELL_INTERACTIONS)
103
+ lines.append(f"```session\n{interaction}\n```\n")
104
+
105
+ # 3. Command variations & chained commands
106
+ cmds = [qa[1] for qa in COMMAND_TEMPLATES]
107
+ for _ in range(num_samples // 3):
108
+ c1, c2 = random.sample(cmds, 2)
109
+ chain = f"$ {c1} && {c2}\n"
110
+ lines.append(chain)
111
+
112
+ random.shuffle(lines)
113
+ return "\n".join(lines)
114
+
115
+ if __name__ == "__main__":
116
+ text = generate_terminal_dataset(30000)
117
+ print(f"Generated Terminal dataset preview:\n{text[:400]}")
118
+ print(f"Total dataset characters: {len(text):,}")
training_code/tokenizer_builder.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tokenizer Builder for 5M Terminal & Multilingual LM
3
+ Trains a custom BPE Tokenizer with vocab_size = 4096.
4
+ """
5
+ import os
6
+ import json
7
+ from tokenizers import Tokenizer, models, trainers, pre_tokenizers, decoders, processors
8
+ from transformers import PreTrainedTokenizerFast
9
+ from terminal_dataset import generate_terminal_dataset
10
+
11
+ def build_tokenizer(output_dir="tokenizer_5m", vocab_size=4096):
12
+ os.makedirs(output_dir, exist_ok=True)
13
+ print(f"[Tokenizer] Training custom BPE Tokenizer (vocab_size={vocab_size})...")
14
+
15
+ # Initialize Byte-Level BPE tokenizer
16
+ tokenizer = Tokenizer(models.BPE(unk_token="<unk>"))
17
+ tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
18
+ tokenizer.decoder = decoders.ByteLevel()
19
+ tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
20
+
21
+ special_tokens = ["<pad>", "<s>", "</s>", "<unk>", "<cmd>", "</cmd>", "<user>", "<assistant>"]
22
+
23
+ trainer = trainers.BpeTrainer(
24
+ vocab_size=vocab_size,
25
+ special_tokens=special_tokens,
26
+ min_frequency=2,
27
+ show_progress=True
28
+ )
29
+
30
+ # Prepare training text samples
31
+ print("[Tokenizer] Generating training corpus samples...")
32
+ terminal_text = generate_terminal_dataset(num_samples=25000)
33
+
34
+ # General English text samples (30%)
35
+ english_samples = [
36
+ "The quick brown fox jumps over the lazy dog. Artificial intelligence and machine learning models continue to advance.",
37
+ "System architecture consists of frontend user interfaces, backend APIs, microservices, and high performance databases.",
38
+ "In computer science, algorithms process data structures to solve complex mathematical and computational problems efficiently.",
39
+ "Operating systems manage hardware resources, memory allocation, process scheduling, and file system permissions.",
40
+ "Software development requires version control, continuous integration, testing, code review, and automated deployment.",
41
+ "Deep learning neural networks utilize backpropagation, gradient descent, activation functions, and transformer attention layers.",
42
+ "Linux kernel modules provide driver support for hardware peripherals, network interfaces, and storage volume controllers."
43
+ ] * 2000
44
+
45
+ # General Multilingual text samples (70%)
46
+ multilingual_samples = [
47
+ # Indonesian
48
+ "Model bahasa buatan ini dilatih untuk memahami perintah terminal Linux dan teks serbaguna secara efisien.",
49
+ "Sistem operasi Linux menyediakan antarmuka baris perintah yang sangat kuat untuk mengelola berkas dan proses.",
50
+ "Pengembangan perangkat lunak membutuhkan manajemen kode, pengujian otomatis, dan infrastruktur komputasi awan.",
51
+ # Spanish
52
+ "El modelo de lenguaje artificial aprende comandos de terminal Linux y procesamiento de texto en varios idiomas.",
53
+ "Los sistemas de computación moderna utilizan controladores de memoria y procesamiento paralelo en GPU.",
54
+ # French
55
+ "Le modèle linguistique est conçu pour traiter les commandes système et le texte multilingue rapidement.",
56
+ "L'apprentissage automatique et le traitement du langage naturel permettent des interactions intelligentes.",
57
+ # German
58
+ "Das künstliche Sprachmodell lernt Befehle für die Linux-Konsole und mehrsprachige Textverarbeitung.",
59
+ "Moderne Algorithmen optimieren die Datenverarbeitung und die Ausführung von Skripten auf Servern."
60
+ ] * 2000
61
+
62
+ training_corpus = [terminal_text] + english_samples + multilingual_samples
63
+
64
+ # Train tokenizer
65
+ tokenizer.train_from_iterator(training_corpus, trainer=trainer)
66
+
67
+ # Wrap in Transformers PreTrainedTokenizerFast
68
+ fast_tokenizer = PreTrainedTokenizerFast(
69
+ tokenizer_object=tokenizer,
70
+ bos_token="<s>",
71
+ eos_token="</s>",
72
+ pad_token="<pad>",
73
+ unk_token="<unk>",
74
+ mask_token="<mask >",
75
+ additional_special_tokens=["<cmd>", "</cmd>", "<user>", "<assistant>"]
76
+ )
77
+
78
+ fast_tokenizer.save_pretrained(output_dir)
79
+ print(f"[Tokenizer] Saved tokenizer to '{output_dir}'. Vocab size: {len(fast_tokenizer)}")
80
+
81
+ # Test encoding/decoding
82
+ test_str = "cd .. && ls -la # Check directory\nModel bahasa 5M parameter."
83
+ encoded = fast_tokenizer.encode(test_str)
84
+ decoded = fast_tokenizer.decode(encoded)
85
+ print(f"\n[Test Encoding]\nInput: {test_str}\nTokens: {encoded[:15]}...\nDecoded: {decoded}")
86
+
87
+ return fast_tokenizer
88
+
89
+ if __name__ == "__main__":
90
+ build_tokenizer()
training_code/train.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main Training Script for 5M Terminal & Multilingual Language Model
3
+ Runs on Kaggle Dual GPUs or local hardware. Automatically uploads model to HuggingFace Hub.
4
+ """
5
+ import os
6
+ import math
7
+ import time
8
+ import random
9
+ import json
10
+ import torch
11
+ import torch.nn as nn
12
+ import torch.nn.functional as F
13
+ from torch.utils.data import Dataset, DataLoader
14
+ from transformers import PreTrainedTokenizerFast
15
+ from huggingface_hub import HfApi, login
16
+
17
+ # ==========================================
18
+ # 1. Environment & Credentials Configuration
19
+ # ==========================================
20
+ HF_TOKEN = os.environ.get("HF_TOKEN", "YOUR_HF_TOKEN")
21
+ HF_REPO_ID = os.environ.get("HF_REPO_ID", "kipasyangin5/terminal-lang-5m")
22
+
23
+ if HF_TOKEN:
24
+ try:
25
+ login(token=HF_TOKEN)
26
+ print(f"[HF Login] Authenticated successfully as '{HF_REPO_ID.split('/')[0]}'.")
27
+ except Exception as e:
28
+ print(f"[HF Login Warning] Could not login: {e}")
29
+
30
+ # ==========================================
31
+ # 2. Model Architecture (5.0M Parameters)
32
+ # ==========================================
33
+ class RMSNorm(nn.Module):
34
+ def __init__(self, dim, eps=1e-6):
35
+ super().__init__()
36
+ self.eps = eps
37
+ self.weight = nn.Parameter(torch.ones(dim))
38
+
39
+ def forward(self, x):
40
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) * self.weight
41
+
42
+ class SwiGLUMLP(nn.Module):
43
+ def __init__(self, dim, inter_dim):
44
+ super().__init__()
45
+ self.gate_proj = nn.Linear(dim, inter_dim, bias=False)
46
+ self.up_proj = nn.Linear(dim, inter_dim, bias=False)
47
+ self.down_proj = nn.Linear(inter_dim, dim, bias=False)
48
+
49
+ def forward(self, x):
50
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
51
+
52
+ class CausalSelfAttention(nn.Module):
53
+ def __init__(self, dim, n_head, max_seq_len=512):
54
+ super().__init__()
55
+ self.dim = dim
56
+ self.n_head = n_head
57
+ self.head_dim = dim // n_head
58
+ self.q_proj = nn.Linear(dim, dim, bias=False)
59
+ self.k_proj = nn.Linear(dim, dim, bias=False)
60
+ self.v_proj = nn.Linear(dim, dim, bias=False)
61
+ self.out_proj = nn.Linear(dim, dim, bias=False)
62
+
63
+ def forward(self, x):
64
+ B, T, C = x.shape
65
+ q = self.q_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
66
+ k = self.k_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
67
+ v = self.v_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2)
68
+
69
+ # PyTorch Scaled Dot Product Attention with Causal Mask
70
+ y = F.scaled_dot_product_attention(q, k, v, is_causal=True)
71
+ y = y.transpose(1, 2).contiguous().view(B, T, C)
72
+ return self.out_proj(y)
73
+
74
+ class TransformerBlock(nn.Module):
75
+ def __init__(self, dim, n_head, inter_dim, max_seq_len=512):
76
+ super().__init__()
77
+ self.attn = CausalSelfAttention(dim, n_head, max_seq_len)
78
+ self.mlp = SwiGLUMLP(dim, inter_dim)
79
+ self.norm1 = RMSNorm(dim)
80
+ self.norm2 = RMSNorm(dim)
81
+
82
+ def forward(self, x):
83
+ x = x + self.attn(self.norm1(x))
84
+ x = x + self.mlp(self.norm2(x))
85
+ return x
86
+
87
+ class TerminalLM5M(nn.Module):
88
+ def __init__(self, vocab_size=4096, dim=256, n_layer=6, n_head=8, inter_dim=512, max_seq_len=512):
89
+ super().__init__()
90
+ self.vocab_size = vocab_size
91
+ self.dim = dim
92
+ self.max_seq_len = max_seq_len
93
+ self.tok_embeddings = nn.Embedding(vocab_size, dim)
94
+ self.pos_embeddings = nn.Embedding(max_seq_len, dim)
95
+ self.layers = nn.ModuleList([
96
+ TransformerBlock(dim, n_head, inter_dim, max_seq_len) for _ in range(n_layer)
97
+ ])
98
+ self.norm = RMSNorm(dim)
99
+ self.lm_head = nn.Linear(dim, vocab_size, bias=False)
100
+ # Weight Tying for memory efficiency & parameter budget
101
+ self.lm_head.weight = self.tok_embeddings.weight
102
+
103
+ # Parameter Initialization
104
+ self.apply(self._init_weights)
105
+
106
+ def _init_weights(self, module):
107
+ if isinstance(module, nn.Linear):
108
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
109
+ if module.bias is not None:
110
+ torch.nn.init.zeros_(module.bias)
111
+ elif isinstance(module, nn.Embedding):
112
+ torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
113
+
114
+ def forward(self, input_ids, targets=None):
115
+ B, T = input_ids.shape
116
+ device = input_ids.device
117
+ pos = torch.arange(0, T, dtype=torch.long, device=device)
118
+
119
+ h = self.tok_embeddings(input_ids) + self.pos_embeddings(pos)
120
+ for layer in self.layers:
121
+ h = layer(h)
122
+ h = self.norm(h)
123
+ logits = self.lm_head(h)
124
+
125
+ loss = None
126
+ if targets is not None:
127
+ loss = F.cross_entropy(logits.view(-1, self.vocab_size), targets.view(-1))
128
+
129
+ return logits, loss
130
+
131
+ # ==========================================
132
+ # 3. Dataset & Data Loader Construction
133
+ # ==========================================
134
+ from terminal_dataset import COMMAND_TEMPLATES, SHELL_INTERACTIONS
135
+
136
+ def generate_mixed_corpus(num_terminal=20000, num_english=10000, num_multilingual=20000):
137
+ corpus = []
138
+
139
+ # 1. Terminal Commands
140
+ for _ in range(num_terminal):
141
+ qa = random.choice(COMMAND_TEMPLATES)
142
+ fmt = random.choice([
143
+ f"User: {qa[0]}\nAssistant: Run `{qa[1]}`\n",
144
+ f"Question: {qa[0]}\nAnswer:\n```bash\n{qa[1]}\n```\n",
145
+ f"$ {qa[1]}\n# Output: success\n"
146
+ ])
147
+ corpus.append(fmt)
148
+
149
+ # 2. Shell session interactions
150
+ for _ in range(num_terminal // 2):
151
+ s = random.choice(SHELL_INTERACTIONS)
152
+ corpus.append(f"```session\n{s}\n```\n")
153
+
154
+ # 3. English General Language (30%)
155
+ en_samples = [
156
+ "The Linux kernel provides low-level hardware abstraction, process management, and networking capabilities.",
157
+ "Version control systems like Git allow multiple developers to collaborate on codebases seamlessly.",
158
+ "Computer networks transmit data packets across interconnected routers using TCP and IP protocols.",
159
+ "Machine learning models optimize parameters using loss gradients computed via automatic differentiation.",
160
+ "Shell scripts automate repetitive terminal tasks using conditional loops and system environment variables.",
161
+ "Cloud infrastructure scales computational workloads across distributed server clusters efficiently."
162
+ ]
163
+ for _ in range(num_english):
164
+ corpus.append(random.choice(en_samples) + "\n")
165
+
166
+ # 4. Multilingual General Language (70% Non-English)
167
+ multi_samples = [
168
+ # Indonesian
169
+ "Model bahasa ini dilatih untuk mengenali perintah baris terminal Linux dan bahasa umum secara efisien.",
170
+ "Perintah cd digunakan untuk berpindah direktori, sedangkan ls -la menampilkan semua berkas tersembunyi.",
171
+ "Pengembangan sistem operasi berbasis Linux memungkinkan fleksibilitas tinggi bagi pengembang perangkat lunak.",
172
+ # Spanish
173
+ "El comando cd permite cambiar de directorio y ls -la muestra todos los archivos ocultos en la carpeta.",
174
+ "Los modelos de lenguaje pequeños pueden ejecutarse eficientemente en dispositivos locales y servidores.",
175
+ # French
176
+ "La commande cd permet de changer de répertoire et ls -la affiche tous les fichiers cachés.",
177
+ "Les modèles informatiques modernes permettent d'automatiser le traitement du langage naturel.",
178
+ # German
179
+ "Der Befehl cd wechselt das Verzeichnis und ls -la zeigt alle versteckten Dateien an.",
180
+ "Künstliche Intelligenz optimiert die Verarbeitung von Befehlen auf modernen Betriebssystemen."
181
+ ]
182
+ for _ in range(num_multilingual):
183
+ corpus.append(random.choice(multi_samples) + "\n")
184
+
185
+ random.shuffle(corpus)
186
+ return corpus
187
+
188
+ class TextDataset(Dataset):
189
+ def __init__(self, token_ids, seq_len=256):
190
+ self.seq_len = seq_len
191
+ # Pack tokens into fixed length chunks
192
+ self.num_samples = (len(token_ids) - 1) // seq_len
193
+ self.inputs = []
194
+ self.targets = []
195
+ for i in range(self.num_samples):
196
+ start = i * seq_len
197
+ end = start + seq_len
198
+ self.inputs.append(token_ids[start:end])
199
+ self.targets.append(token_ids[start+1:end+1])
200
+
201
+ def __len__(self):
202
+ return len(self.inputs)
203
+
204
+ def __getitem__(self, idx):
205
+ return torch.tensor(self.inputs[idx], dtype=torch.long), torch.tensor(self.targets[idx], dtype=torch.long)
206
+
207
+ # ==========================================
208
+ # 4. Main Training Routine
209
+ # ==========================================
210
+ def train(
211
+ max_steps=2000,
212
+ batch_size=32,
213
+ seq_len=256,
214
+ lr=1e-3,
215
+ save_hf=True
216
+ ):
217
+ device = "cuda" if torch.cuda.is_available() else "cpu"
218
+ print(f"=== Starting Training for 5M Terminal LM on {device.upper()} ===")
219
+
220
+ # Load Tokenizer
221
+ tok_dir = "tokenizer_5m"
222
+ if not os.path.exists(tok_dir):
223
+ from tokenizer_builder import build_tokenizer
224
+ build_tokenizer(tok_dir)
225
+
226
+ tokenizer = PreTrainedTokenizerFast.from_pretrained(tok_dir)
227
+ vocab_size = len(tokenizer)
228
+ print(f"[Dataset] Tokenizer loaded with vocab_size = {vocab_size}")
229
+
230
+ # Build Mixed Corpus & Tokenize
231
+ print("[Dataset] Building training corpus...")
232
+ corpus = generate_mixed_corpus()
233
+ full_text = "\n".join(corpus)
234
+ print(f"[Dataset] Full text character length: {len(full_text):,}")
235
+
236
+ tokens = tokenizer.encode(full_text)
237
+ print(f"[Dataset] Total encoded tokens: {len(tokens):,}")
238
+
239
+ dataset = TextDataset(tokens, seq_len=seq_len)
240
+ dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True)
241
+ print(f"[Dataset] Total training batches per epoch: {len(dataloader)}")
242
+
243
+ # Instantiate Model
244
+ model = TerminalLM5M(
245
+ vocab_size=vocab_size,
246
+ dim=256,
247
+ n_layer=6,
248
+ n_head=8,
249
+ inter_dim=512,
250
+ max_seq_len=seq_len
251
+ ).to(device)
252
+
253
+ param_count = sum(p.numel() for p in model.parameters() if p.requires_grad)
254
+ print(f"[Model] Total Trainable Parameters: {param_count:,} (~{param_count/1e6:.2f}M)")
255
+
256
+ # Optimizer & Scheduler
257
+ try:
258
+ optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=0.01, fused=True)
259
+ except Exception:
260
+ optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=0.01)
261
+
262
+ scaler = torch.amp.GradScaler('cuda') if device == "cuda" else None
263
+
264
+ # Training Loop
265
+ model.train()
266
+ step = 0
267
+ t0 = time.time()
268
+ data_iter = iter(dataloader)
269
+
270
+ while step < max_steps:
271
+ try:
272
+ x, y = next(data_iter)
273
+ except StopIteration:
274
+ data_iter = iter(dataloader)
275
+ x, y = next(data_iter)
276
+
277
+ x, y = x.to(device), y.to(device)
278
+
279
+ optimizer.zero_grad()
280
+
281
+ if device == "cuda":
282
+ with torch.amp.autocast('cuda', dtype=torch.float16):
283
+ logits, loss = model(x, y)
284
+ scaler.scale(loss).backward()
285
+ scaler.unscale_(optimizer)
286
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
287
+ scaler.step(optimizer)
288
+ scaler.update()
289
+ else:
290
+ logits, loss = model(x, y)
291
+ loss.backward()
292
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
293
+ optimizer.step()
294
+
295
+ step += 1
296
+
297
+ if step % 50 == 0 or step == 1:
298
+ t1 = time.time()
299
+ dt = t1 - t0
300
+ t0 = t1
301
+ tokens_per_sec = (50 * batch_size * seq_len) / (dt if dt > 0 else 1.0)
302
+ ppl = math.exp(min(loss.item(), 20.0))
303
+ print(f"Step {step:4d}/{max_steps} | Loss: {loss.item():.4f} | PPL: {ppl:.2f} | Speed: {tokens_per_sec:.0f} tok/s")
304
+
305
+ print("\n=== Training Completed Successfully ===")
306
+
307
+ # Save Model & Tokenizer locally
308
+ output_dir = "saved_5m_model"
309
+ os.makedirs(output_dir, exist_ok=True)
310
+ torch.save(model.state_dict(), os.path.join(output_dir, "model.pt"))
311
+ tokenizer.save_pretrained(output_dir)
312
+ print(f"[Save] Model and Tokenizer saved to '{output_dir}'.")
313
+
314
+ # Upload to HuggingFace Hub if configured
315
+ if save_hf and HF_TOKEN:
316
+ try:
317
+ print(f"[HuggingFace] Uploading model to repository '{HF_REPO_ID}'...")
318
+ api = HfApi()
319
+ api.create_repo(repo_id=HF_REPO_ID, exist_ok=True)
320
+ api.upload_folder(
321
+ folder_path=output_dir,
322
+ repo_id=HF_REPO_ID,
323
+ commit_message=f"Upload trained 5M Terminal LM (Steps: {max_steps}, Loss: {loss.item():.4f})"
324
+ )
325
+ print(f"🚀 [SUCCESS] Model successfully uploaded to https://huggingface.co/{HF_REPO_ID}")
326
+ except Exception as e:
327
+ print(f"[HuggingFace Upload Warning] Could not upload to HF: {e}")
328
+
329
+ if __name__ == "__main__":
330
+ train(max_steps=100 if not torch.cuda.is_available() else 3000)