""" SCUGNIZZ - Hugging Face Jobs edition NOTE: - Configurato per training su Hugging Face Jobs. - Usa FineWeb in streaming. - Parametri modello aumentati (12L / 768D / 12H). - TARGET_TOKENS rappresenta un obiettivo logico di training. - Per usare l'intero FineWeb รจ consigliabile eliminare il memmap e passare a un DataLoader streaming. Questa versione mantiene la struttura originale per ridurre le modifiche. """ !pip -q install datasets transformers huggingface_hub import os import math import time import random import numpy as np import torch import torch.nn as nn import torch.nn.functional as F from contextlib import nullcontext from datasets import load_dataset from transformers import GPT2TokenizerFast seed = 42 random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) device = "cuda" if torch.cuda.is_available() else "cpu" print("Device:", device) if torch.cuda.is_available(): torch.backends.cuda.matmul.allow_tf32 = True torch.backends.cudnn.allow_tf32 = True # ========================== # DATASET / TRAINING # ========================== TARGET_TOKENS = 10_000_000_000 # logical target for long-running HF Jobs DATA_FILE = "fineweb_full_uint32.dat" CKPT_FILE = "pcs_fineweb_checkpoint_last.pt" BEST_FILE = "pcs_fineweb_checkpoint_best.pt" FINAL_FILE = "pcs_fineweb_final.pt" batch_size = 16 block_size = 1024 # circa 1 epoca sui 90M token di training TOKENS_PER_STEP = batch_size * block_size TRAIN_TOKENS = int(TARGET_TOKENS * 0.9) max_iters = (TRAIN_TOKENS + TOKENS_PER_STEP - 1) // TOKENS_PER_STEP print(f"Training tokens : {TRAIN_TOKENS:,}") print(f"Token/step : {TOKENS_PER_STEP:,}") print(f"Iterazioni : {max_iters:,} (~1 epoca)") eval_interval = 500 save_interval = 1000 eval_iters = 20 learning_rate = 3e-4 min_lr = 3e-5 warmup_iters = 1000 weight_decay = 0.1 beta1 = 0.9 beta2 = 0.95 grad_clip = 1.0 n_embd = 768 n_head = 12 n_layer = 12 dropout = 0.1 bias = False pcs_a = 0.8309193524478643 pcs_b = 0.0 tokenizer = GPT2TokenizerFast.from_pretrained("gpt2") tokenizer.pad_token = tokenizer.eos_token vocab_size = tokenizer.vocab_size eos_id = tokenizer.eos_token_id print("Vocab size:", vocab_size) if not os.path.exists(DATA_FILE): print("Creating FineWeb memmap...") arr = np.memmap( DATA_FILE, dtype=np.uint32, mode="w+", shape=(TARGET_TOKENS,) ) ds = load_dataset( "HuggingFaceFW/fineweb", name="CC-MAIN-2024-10", split="train", streaming=True ) pos = 0 last_print_million = -1 for row in ds: txt = row["text"] if txt and len(txt) > 100: ids = tokenizer.encode(txt + tokenizer.eos_token) n = min(len(ids), TARGET_TOKENS - pos) if n > 0: arr[pos:pos+n] = np.array(ids[:n], dtype=np.uint32) pos += n cur_million = pos // 1_000_000 if cur_million != last_print_million: print("Saved tokens:", pos) last_print_million = cur_million if pos >= TARGET_TOKENS: break arr.flush() print("Memmap created. Tokens written:", pos) else: print("Memmap already exists:", DATA_FILE) data = np.memmap( DATA_FILE, dtype=np.uint32, mode="r", shape=(TARGET_TOKENS,) ) split_idx = int(0.9 * TARGET_TOKENS) train_len = split_idx val_len = TARGET_TOKENS - split_idx print("Train tokens:", train_len) print("Val tokens:", val_len) def get_batch(split_name): if split_name == "train": lo = 0 hi = train_len - block_size - 1 else: lo = train_len hi = TARGET_TOKENS - block_size - 1 ix = np.random.randint(lo, hi, size=(batch_size,)) x = np.stack([data[i:i+block_size] for i in ix]) y = np.stack([data[i+1:i+block_size+1] for i in ix]) x = torch.tensor(x, dtype=torch.long, device=device) y = torch.tensor(y, dtype=torch.long, device=device) return x, y def get_lr(it): if it < warmup_iters: return learning_rate * (it + 1) / warmup_iters if it > max_iters: return min_lr decay_ratio = (it - warmup_iters) / (max_iters - warmup_iters) coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio)) return min_lr + coeff * (learning_rate - min_lr) class PCS(nn.Module): def __init__(self, a=pcs_a, b=pcs_b): super().__init__() self.a = a self.b = b def forward(self, x): return x * torch.sin(self.a * x) + self.b * torch.cos(x) class CausalSelfAttention(nn.Module): def __init__(self, n_embd, n_head, block_size, dropout, bias=False): super().__init__() assert n_embd % n_head == 0 self.n_head = n_head self.head_dim = n_embd // n_head self.q_proj = nn.Linear(n_embd, n_embd, bias=bias) self.k_proj = nn.Linear(n_embd, n_embd, bias=bias) self.v_proj = nn.Linear(n_embd, n_embd, bias=bias) self.out_proj = nn.Linear(n_embd, n_embd, bias=bias) self.attn_dropout = nn.Dropout(dropout) self.resid_dropout = nn.Dropout(dropout) mask = torch.tril(torch.ones(block_size, block_size)) self.register_buffer("mask", mask.view(1, 1, block_size, block_size)) def forward(self, x): B, T, C = x.shape q = self.q_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2) k = self.k_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2) v = self.v_proj(x).view(B, T, self.n_head, self.head_dim).transpose(1, 2) att = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim) att = att.masked_fill(self.mask[:, :, :T, :T] == 0, float("-inf")) att = F.softmax(att, dim=-1) att = self.attn_dropout(att) y = att @ v y = y.transpose(1, 2).contiguous().view(B, T, C) y = self.resid_dropout(self.out_proj(y)) return y class MLP(nn.Module): def __init__(self, n_embd, dropout, bias=False): super().__init__() self.fc1 = nn.Linear(n_embd, 4 * n_embd, bias=bias) self.act = PCS() self.fc2 = nn.Linear(4 * n_embd, n_embd, bias=bias) self.dropout = nn.Dropout(dropout) def forward(self, x): x = self.fc1(x) x = self.act(x) x = self.fc2(x) x = self.dropout(x) return x class Block(nn.Module): def __init__(self, n_embd, n_head, block_size, dropout, bias=False): super().__init__() self.ln1 = nn.LayerNorm(n_embd) self.attn = CausalSelfAttention(n_embd, n_head, block_size, dropout, bias=bias) self.ln2 = nn.LayerNorm(n_embd) self.mlp = MLP(n_embd, dropout, bias=bias) def forward(self, x): x = x + self.attn(self.ln1(x)) x = x + self.mlp(self.ln2(x)) return x class GPT(nn.Module): def __init__(self): super().__init__() self.tok_emb = nn.Embedding(vocab_size, n_embd) self.pos_emb = nn.Embedding(block_size, n_embd) self.drop = nn.Dropout(dropout) self.blocks = nn.ModuleList([ Block(n_embd, n_head, block_size, dropout, bias=bias) for _ in range(n_layer) ]) self.ln_f = nn.LayerNorm(n_embd) self.lm_head = nn.Linear(n_embd, vocab_size, bias=False) self.tok_emb.weight = self.lm_head.weight self.apply(self._init_weights) def _init_weights(self, module): if isinstance(module, nn.Linear): nn.init.normal_(module.weight, mean=0.0, std=0.02) if module.bias is not None: nn.init.zeros_(module.bias) elif isinstance(module, nn.Embedding): nn.init.normal_(module.weight, mean=0.0, std=0.02) def forward(self, idx, targets=None): B, T = idx.shape assert T <= block_size pos = torch.arange(0, T, device=idx.device, dtype=torch.long) x = self.tok_emb(idx) + self.pos_emb(pos) x = self.drop(x) for block in self.blocks: x = block(x) x = self.ln_f(x) logits = self.lm_head(x) loss = None if targets is not None: loss = F.cross_entropy( logits.reshape(B * T, logits.size(-1)), targets.reshape(B * T) ) return logits, loss model = GPT().to(device) print("Parameters (M):", sum(p.numel() for p in model.parameters()) / 1e6) optimizer = torch.optim.AdamW( model.parameters(), lr=learning_rate, betas=(beta1, beta2), weight_decay=weight_decay ) use_amp = (device == "cuda") scaler = torch.cuda.amp.GradScaler(enabled=use_amp) start_iter = 0 best_val = float("inf") if os.path.exists(CKPT_FILE): print("Loading checkpoint...") ckpt = torch.load(CKPT_FILE, map_location=device) model.load_state_dict(ckpt["model"]) optimizer.load_state_dict(ckpt["optimizer"]) start_iter = ckpt["iter"] + 1 best_val = ckpt.get("best_val", float("inf")) print("Resume from iter:", start_iter) print("Best val:", best_val) @torch.no_grad() def estimate_loss(): out = {} model.eval() for split in ["train", "val"]: losses = torch.zeros(eval_iters) for _ in range(eval_iters): x, y = get_batch(split) ctx = torch.cuda.amp.autocast() if use_amp else nullcontext() with ctx: _, loss = model(x, y) losses[_] = loss.item() out[split] = losses.mean().item() model.train() return out print("Starting training...") t0 = time.time() for it in range(start_iter, max_iters + 1): lr = get_lr(it) for param_group in optimizer.param_groups: param_group["lr"] = lr xb, yb = get_batch("train") ctx = torch.cuda.amp.autocast() if use_amp else nullcontext() with ctx: _, loss = model(xb, yb) optimizer.zero_grad(set_to_none=True) scaler.scale(loss).backward() scaler.unscale_(optimizer) torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip) scaler.step(optimizer) scaler.update() if it % eval_interval == 0: losses = estimate_loss() train_loss = losses["train"] val_loss = losses["val"] ppl = math.exp(val_loss) print("Iter", f"{it:06d}", "|", "LR", f"{lr:.6e}", "|", "Train", f"{train_loss:.4f}", "|", "Val", f"{val_loss:.4f}", "|", "PPL", f"{ppl:.2f}") if val_loss < best_val: best_val = val_loss torch.save( { "iter": it, "model": model.state_dict(), "optimizer": optimizer.state_dict(), "best_val": best_val }, BEST_FILE ) print("New best checkpoint saved:", BEST_FILE) if it % save_interval == 0 and it > 0: torch.save( { "iter": it, "model": model.state_dict(), "optimizer": optimizer.state_dict(), "best_val": best_val }, CKPT_FILE ) print("Checkpoint saved:", CKPT_FILE) elapsed = (time.time() - t0) / 60 print("Training finished in", round(elapsed, 2), "minutes") torch.save(model.state_dict(), FINAL_FILE) print("Final model saved:", FINAL_FILE) @torch.no_grad() def generate(prompt, max_new_tokens=150, temperature=0.8, top_k=50, top_p=0.95, repetition_penalty=1.10): model.eval() ids = tokenizer.encode(prompt) x = torch.tensor(ids, dtype=torch.long, device=device).unsqueeze(0) for _ in range(max_new_tokens): x_cond = x[:, -block_size:] ctx = torch.cuda.amp.autocast() if use_amp else nullcontext() with ctx: logits, _ = model(x_cond) logits = logits[:, -1, :] if repetition_penalty != 1.0: used_tokens = torch.unique(x[0]) for token_id in used_tokens: token_id = token_id.item() if logits[0, token_id] < 0: logits[0, token_id] *= repetition_penalty else: logits[0, token_id] /= repetition_penalty logits = logits / temperature if top_k is not None and top_k > 0: v, _ = torch.topk(logits, min(top_k, logits.size(-1))) logits[logits < v[:, [-1]]] = float("-inf") if top_p is not None and top_p < 1.0: sorted_logits, sorted_indices = torch.sort(logits, descending=True) sorted_probs = F.softmax(sorted_logits, dim=-1) cumulative_probs = torch.cumsum(sorted_probs, dim=-1) sorted_indices_to_remove = cumulative_probs > top_p sorted_indices_to_remove[:, 1:] = sorted_indices_to_remove[:, :-1].clone() sorted_indices_to_remove[:, 0] = False indices_to_remove = sorted_indices_to_remove.scatter( 1, sorted_indices, sorted_indices_to_remove ) logits = logits.masked_fill(indices_to_remove, float("-inf")) probs = F.softmax(logits, dim=-1) next_id = torch.multinomial(probs, num_samples=1) x = torch.cat((x, next_id), dim=1) if next_id.item() == eos_id: break return tokenizer.decode(x[0].tolist()) print("============================================================") print("Quick generation test") print("============================================================") print(generate("Artificial intelligence is", max_new_tokens=120)) print("============================================================") print("PCS GPT - Chat") print("Scrivi exit per uscire.") print("============================================================") while True: user_in = input("Tu: ").strip() if user_in.lower() == "exit": break out = generate( prompt=user_in, max_new_tokens=120, temperature=0.8, top_k=40, top_p=0.9, repetition_penalty=1.12 ) print("PCS:") print(out)