Upload train_fast.py
Browse files- train_fast.py +106 -0
train_fast.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, random, json, sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np, torch
|
| 4 |
+
from torch.utils.data import Dataset, DataLoader
|
| 5 |
+
|
| 6 |
+
HUB_DIR = Path(__file__).parent.resolve()
|
| 7 |
+
sys.path.insert(0, str(HUB_DIR / "src"))
|
| 8 |
+
sys.path.insert(0, str(HUB_DIR / "src" / "models"))
|
| 9 |
+
|
| 10 |
+
from models.grid_jepa import GridJEPA
|
| 11 |
+
import trackio
|
| 12 |
+
|
| 13 |
+
MAX_GRID = 30
|
| 14 |
+
|
| 15 |
+
def load_tasks(d):
|
| 16 |
+
tasks = []
|
| 17 |
+
for p in sorted(Path(d).glob("*.json")):
|
| 18 |
+
with open(p) as f: tasks.append(json.load(f))
|
| 19 |
+
return tasks
|
| 20 |
+
|
| 21 |
+
def to_tensor(grid):
|
| 22 |
+
arr = np.array(grid, dtype=np.int64)
|
| 23 |
+
H, W = arr.shape
|
| 24 |
+
t = torch.zeros(MAX_GRID, MAX_GRID, dtype=torch.long)
|
| 25 |
+
t[:H, :W] = torch.from_numpy(arr)
|
| 26 |
+
return t
|
| 27 |
+
|
| 28 |
+
class ARCDataset(Dataset):
|
| 29 |
+
def __init__(self, d):
|
| 30 |
+
self.samples = []
|
| 31 |
+
for task in load_tasks(d):
|
| 32 |
+
for pair in task.get("train", []):
|
| 33 |
+
self.samples.append({"input": to_tensor(pair["input"]), "output": to_tensor(pair["output"])})
|
| 34 |
+
def __len__(self): return len(self.samples)
|
| 35 |
+
def __getitem__(self, idx):
|
| 36 |
+
s = self.samples[idx]
|
| 37 |
+
return {"context_grid": s["input"], "target_grid": s["output"]}
|
| 38 |
+
|
| 39 |
+
def collate(batch):
|
| 40 |
+
return {"context_grid": torch.stack([b["context_grid"] for b in batch]), "target_grid": torch.stack([b["target_grid"] for b in batch])}
|
| 41 |
+
|
| 42 |
+
def sample_masks(B, H, W, ratio=0.4, device="cpu"):
|
| 43 |
+
N = H * W; nt = max(1, int(N * ratio))
|
| 44 |
+
ctx = torch.zeros(B, N, dtype=torch.bool, device=device)
|
| 45 |
+
tgt = torch.zeros(B, N, dtype=torch.bool, device=device)
|
| 46 |
+
for b in range(B):
|
| 47 |
+
idx = list(range(N)); random.shuffle(idx)
|
| 48 |
+
tgt[b, idx[:nt]] = True; ctx[b, idx[nt:]] = True
|
| 49 |
+
return ctx, tgt
|
| 50 |
+
|
| 51 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 52 |
+
print(f"[TRAIN] Device: {device}", flush=True)
|
| 53 |
+
run = trackio.init(project="arc-agi-3", name="grid-jepa-fast", group="pretrain")
|
| 54 |
+
print("[TRAIN] Trackio OK", flush=True)
|
| 55 |
+
|
| 56 |
+
model = GridJEPA(num_colors=10, embed_dim=192, encoder_depth=6, predictor_depth=6, num_heads=6, max_grid_size=MAX_GRID, ema_decay=0.996).to(device)
|
| 57 |
+
print(f"[TRAIN] Params: {sum(p.numel() for p in model.parameters()):,}", flush=True)
|
| 58 |
+
|
| 59 |
+
data_dir = HUB_DIR / "data" / "training"
|
| 60 |
+
if not data_dir.exists(): data_dir = Path("/app/arc_data_source/data/training")
|
| 61 |
+
ds = ARCDataset(str(data_dir))
|
| 62 |
+
print(f"[TRAIN] Samples: {len(ds)}", flush=True)
|
| 63 |
+
|
| 64 |
+
loader = DataLoader(ds, batch_size=8, shuffle=True, collate_fn=collate, num_workers=0)
|
| 65 |
+
opt = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.05)
|
| 66 |
+
|
| 67 |
+
save_dir = Path("/app/arc-jepa-hf/checkpoints")
|
| 68 |
+
save_dir.mkdir(exist_ok=True)
|
| 69 |
+
|
| 70 |
+
for epoch in range(1, 51):
|
| 71 |
+
model.train(); epoch_loss = 0.0; n = 0
|
| 72 |
+
for batch in loader:
|
| 73 |
+
ctx_g = batch["context_grid"].to(device)
|
| 74 |
+
tgt_g = batch["target_grid"].to(device)
|
| 75 |
+
B = ctx_g.shape[0]
|
| 76 |
+
ctx_mask, target_mask = sample_masks(B, MAX_GRID, MAX_GRID, ratio=0.5, device=device)
|
| 77 |
+
a_key = torch.zeros(B, dtype=torch.long, device=device)
|
| 78 |
+
a_pos = torch.zeros(B, dtype=torch.long, device=device)
|
| 79 |
+
opt.zero_grad()
|
| 80 |
+
loss, _ = model(tgt_g, ctx_mask, target_mask, a_key, a_pos)
|
| 81 |
+
if torch.isnan(loss) or loss.item() == 0: continue
|
| 82 |
+
loss.backward()
|
| 83 |
+
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
|
| 84 |
+
opt.step(); model.update_ema()
|
| 85 |
+
epoch_loss += loss.item(); n += 1
|
| 86 |
+
avg = epoch_loss / max(n, 1)
|
| 87 |
+
print(f"[TRAIN] Epoch {epoch}/50: loss={avg:.4f}", flush=True)
|
| 88 |
+
run.log({"epoch_loss": avg, "epoch": epoch})
|
| 89 |
+
if epoch % 10 == 0 or epoch == 50:
|
| 90 |
+
ck = save_dir / f"ckpt_{epoch}.pt"
|
| 91 |
+
torch.save({"epoch": epoch, "model": model.state_dict()}, ck)
|
| 92 |
+
print(f"[TRAIN] Saved {ck}", flush=True)
|
| 93 |
+
|
| 94 |
+
final = save_dir / "final.pt"
|
| 95 |
+
torch.save({"model": model.state_dict(), "epoch": 50}, final)
|
| 96 |
+
print(f"[TRAIN] Final: {final}", flush=True)
|
| 97 |
+
|
| 98 |
+
from huggingface_hub import HfApi
|
| 99 |
+
try:
|
| 100 |
+
api = HfApi()
|
| 101 |
+
api.upload_file(path_or_fileobj=str(final), path_in_repo="checkpoints/final.pt", repo_id="guychuk/arc-agi-3-grid-jepa", repo_type="model")
|
| 102 |
+
print("[TRAIN] Pushed to hub", flush=True)
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f"[TRAIN] Push failed: {e}", flush=True)
|
| 105 |
+
run.finish()
|
| 106 |
+
print("[TRAIN] Done", flush=True)
|