amer224 commited on
Commit
4637ed0
·
verified ·
1 Parent(s): d712ccd

Add brains/sovereign-nano/distill_nano.py

Browse files
Files changed (1) hide show
  1. brains/sovereign-nano/distill_nano.py +106 -0
brains/sovereign-nano/distill_nano.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ distill_nano.py — wake-sleep distillation INTO SOVEREIGN-nano.
3
+
4
+ The v3 teacher (or any contract-compatible model: forward(rgb, nums,
5
+ rtg=None, cell=None, return_all=True) -> dict with cell_logits/
6
+ kind_logits/value/win_logits/econ/pct_params) distills into nano via:
7
+
8
+ KL(cell_logits) + KL(kind) + KL(win) + MSE(value) + MSE(pct_params)
9
+ + MSE(econ) + hard-cell CE + optional search-target KL
10
+
11
+ Nano's contract is identical to the teacher's, so every term is a direct
12
+ op — no adapters, no shape translation. search_targets (B,3,g*g) are the
13
+ teacher's MCTS visit distributions when available (AlphaZero-lite policy
14
+ improvement flowing into the cheap policy).
15
+
16
+ torch only. The smoke uses two nano-sized nets (no 290M import needed).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import Iterator, Optional
22
+
23
+ import torch
24
+ import torch.nn.functional as F
25
+
26
+
27
+ def distill_nano(teacher, student, batches: Iterator[dict], epochs: int = 1,
28
+ lr: float = 3e-4, kl_cell: float = 1.0,
29
+ kl_kind: float = 1.0, kl_win: float = 0.5,
30
+ mse_val: float = 1.0, mse_pct: float = 0.5,
31
+ mse_econ: float = 0.1, ce_cell: float = 0.25,
32
+ search_w: float = 0.5, device: str = "cuda") -> None:
33
+ teacher.to(device).eval()
34
+ student.to(device).train()
35
+ dev = torch.device(device)
36
+ opt = torch.optim.AdamW(student.parameters(), lr=lr)
37
+
38
+ for ep in range(epochs):
39
+ tot, nb = 0.0, 0
40
+ for raw in batches:
41
+ b = {k: v.to(dev) for k, v in raw.items()
42
+ if hasattr(v, "to") and k != "search_targets"}
43
+ rgb, nums = b["rgb"], b["nums"]
44
+ rtg = b.get("rtg")
45
+ cell = b["cell"]
46
+ with torch.no_grad():
47
+ to = teacher(rgb, nums, rtg=rtg, cell=cell,
48
+ return_all=True)
49
+ so = student(rgb, nums, rtg=rtg, cell=cell, return_all=True)
50
+
51
+ loss = kl_cell * F.kl_div(
52
+ F.log_softmax(so["cell_logits"], 1),
53
+ F.softmax(to["cell_logits"], 1), reduction="batchmean")
54
+ loss = loss + kl_kind * F.kl_div(
55
+ F.log_softmax(so["kind_logits"], 1),
56
+ F.softmax(to["kind_logits"], 1), reduction="batchmean")
57
+ loss = loss + kl_win * F.kl_div(
58
+ F.log_softmax(so["win_logits"], 1),
59
+ F.softmax(to["win_logits"], 1), reduction="batchmean")
60
+ loss = loss + mse_val * F.mse_loss(so["value"], to["value"])
61
+ loss = loss + mse_pct * (
62
+ F.mse_loss(so["pct_params"][0], to["pct_params"][0]) +
63
+ F.mse_loss(so["pct_params"][1], to["pct_params"][1]))
64
+ if so["econ"] is not None and to["econ"] is not None:
65
+ loss = loss + mse_econ * F.mse_loss(so["econ"], to["econ"])
66
+ loss = loss + ce_cell * F.cross_entropy(so["cell_logits"], cell)
67
+ if "search_targets" in raw:
68
+ loss = loss + search_w * F.kl_div(
69
+ F.log_softmax(so["cell_logits"], 1),
70
+ raw["search_targets"].to(dev).clamp_min(1e-6),
71
+ reduction="batchmean")
72
+
73
+ opt.zero_grad()
74
+ loss.backward()
75
+ opt.step()
76
+ tot += loss.item()
77
+ nb += 1
78
+ print(f"distill_nano ep {ep+1}: loss={tot/max(nb,1):.4f}",
79
+ flush=True)
80
+
81
+
82
+ def save_student(student, path: str) -> None:
83
+ torch.save({"config": student.cfg, "state_dict": student.state_dict()},
84
+ path)
85
+ print(f"[distill_nano] saved {path}")
86
+
87
+
88
+ if __name__ == "__main__":
89
+ # smoke: teacher = nano-width net, student = thinner nano — the real
90
+ # teacher swaps in without code changes (same contract).
91
+ from model_nano import NanoConfig, make_nano
92
+
93
+ tcfg = NanoConfig(map_size=64)
94
+ scfg = NanoConfig(map_size=64)
95
+ scfg.enc_ch = (12, 24, 32, 64, 96)
96
+ scfg.dec_plan = ((64, 64, 1), (32, 32, 1), (24, 24, 1),
97
+ (20, None, 1), (20, None, 0))
98
+ scfg.dec_out_ch = 20
99
+ scfg.head_hidden = 96
100
+ teacher = make_nano(tcfg, seed=0)
101
+ student = make_nano(scfg, seed=1)
102
+ batch = [dict(rgb=torch.rand(2, 3, 64, 64), nums=torch.rand(2, 8),
103
+ rtg=torch.rand(2, 1), cell=torch.tensor([10, 40]),
104
+ search_targets=torch.rand(2, 3 * 16 * 16))]
105
+ distill_nano(teacher, student, iter(batch), epochs=1, device="cpu")
106
+ print("distill_nano.py smoke OK")