| """Trening GPT od zera na korpusie ABC (L03/L08 w praktyce). |
| Batche -> strata cross-entropy -> backprop -> AdamW -> val loss -> checkpoint. |
| """ |
| import os, time, math, sys |
| from contextlib import nullcontext |
| import torch |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| from core.gpt import GPT, GPTConfig |
|
|
| |
| block_size = 128 |
| batch_size = 32 |
| n_layer = 4 |
| n_head = 4 |
| n_embd = 128 |
| dropout = 0.1 |
| lr = 3e-4 |
| max_iters = 2000 |
| eval_interval = 200 |
| eval_iters = 100 |
| warmup = 100 |
|
|
| torch.manual_seed(20260620) |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| use_bf16 = device == "cuda" and torch.cuda.is_bf16_supported() |
| ctx = torch.autocast(device_type="cuda", dtype=torch.bfloat16) if use_bf16 else nullcontext() |
| sys.stdout.reconfigure(encoding="utf-8") |
| print(f"urządzenie: {device} | bf16: {use_bf16}") |
|
|
| |
| DATA = sys.argv[1] if len(sys.argv) > 1 else "data/jigs.abc" |
| CKPT = sys.argv[2] if len(sys.argv) > 2 else "data/models/jig_ckpt.pt" |
| LOSSLOG = sys.argv[3] if len(sys.argv) > 3 else "data/models/jig_loss_log.csv" |
| VOCAB_FROM = sys.argv[4] if len(sys.argv) > 4 else None |
| print(f"dane: {DATA} -> checkpoint: {CKPT}") |
|
|
| |
| text = open(DATA, encoding="utf-8").read() |
| if VOCAB_FROM: |
| vck = torch.load(VOCAB_FROM, map_location="cpu", weights_only=False) |
| stoi, itos = vck["stoi"], vck["itos"] |
| chars = [itos[i] for i in range(len(itos))] |
| print(f"wspólny słownik z {VOCAB_FROM}: {len(chars)} znaków") |
| else: |
| chars = sorted(set(text)) |
| stoi = {c: i for i, c in enumerate(chars)} |
| itos = {i: c for i, c in enumerate(chars)} |
| data = torch.tensor([stoi[c] for c in text], dtype=torch.long) |
| n = int(0.9 * len(data)) |
| train_data, val_data = data[:n], data[n:] |
| print(f"słownik: {len(chars)} | tokeny: train {len(train_data):,} / val {len(val_data):,}") |
|
|
| def get_batch(split): |
| d = train_data if split == "train" else val_data |
| ix = torch.randint(len(d) - block_size, (batch_size,)) |
| x = torch.stack([d[i:i+block_size] for i in ix]) |
| y = torch.stack([d[i+1:i+1+block_size] for i in ix]) |
| return x.to(device), y.to(device) |
|
|
| @torch.no_grad() |
| def estimate_loss(): |
| model.eval() |
| out = {} |
| for split in ("train", "val"): |
| losses = torch.zeros(eval_iters) |
| for k in range(eval_iters): |
| x, y = get_batch(split) |
| with ctx: |
| _, loss = model(x, y) |
| losses[k] = loss.item() |
| out[split] = losses.mean().item() |
| model.train() |
| return out |
|
|
| def lr_at(it): |
| if it < warmup: |
| return lr * it / warmup |
| r = (it - warmup) / (max_iters - warmup) |
| return lr * 0.1 + 0.5 * lr * 0.9 * (1 + math.cos(math.pi * r)) |
|
|
| cfg = GPTConfig(vocab_size=len(chars), block_size=block_size, |
| n_layer=n_layer, n_head=n_head, n_embd=n_embd, dropout=dropout) |
| model = GPT(cfg).to(device) |
| print(f"parametry modelu: {model.num_params():,}") |
| opt = torch.optim.AdamW(model.parameters(), lr=lr, betas=(0.9, 0.99), weight_decay=0.1) |
|
|
| best_val = float("inf") |
| log = [("iter", "train_loss", "val_loss")] |
| t0 = time.time() |
| for it in range(max_iters + 1): |
| if it % eval_interval == 0 or it == max_iters: |
| L = estimate_loss() |
| ppl = math.exp(L["val"]) |
| print(f"iter {it:4d} | train {L['train']:.3f} | val {L['val']:.3f} | ppl {ppl:.2f} | {time.time()-t0:.0f}s") |
| log.append((it, round(L["train"], 4), round(L["val"], 4))) |
| if L["val"] < best_val: |
| best_val = L["val"] |
| torch.save({"model": model.state_dict(), "config": cfg, |
| "stoi": stoi, "itos": itos, "val_loss": best_val}, |
| CKPT) |
| if it == max_iters: |
| break |
| for g in opt.param_groups: |
| g["lr"] = lr_at(it) |
| x, y = get_batch("train") |
| with ctx: |
| _, loss = model(x, y) |
| opt.zero_grad(set_to_none=True) |
| loss.backward() |
| torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) |
| opt.step() |
|
|
| with open(LOSSLOG, "w", encoding="utf-8") as f: |
| f.write("\n".join(",".join(map(str, row)) for row in log)) |
| print(f"\ngotowe. best val loss: {best_val:.3f} (ppl {math.exp(best_val):.2f})") |
| print(f"checkpoint -> {CKPT} | krzywa -> {LOSSLOG}") |
|
|