diff options
| author | Yuren Hao <yurenh2@illinois.edu> | 2026-07-09 22:28:07 -0500 |
|---|---|---|
| committer | Yuren Hao <yurenh2@illinois.edu> | 2026-07-09 22:28:07 -0500 |
| commit | 3605c2cd994643391ebfd0780e15397403dc4144 (patch) | |
| tree | af416d1d6b9cc68471b6a10ade381b2e2efd9832 /ep_run/muon.py | |
| parent | 654dfb94d727f7514470a7ca909fc865e34636d8 (diff) | |
A0.4 precision gate (TF32 harmless cos 0.9946==fp32; pure-bf16 cos 0.9427) + Muon hybrid optimizer (--opt muon) wired into both trainers; D1a flagship matrix launched (L12xC512, 3xBP + 3xEP + Muon arms)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FAPDWQ49M5Ye3NpTndTpn
Diffstat (limited to 'ep_run/muon.py')
| -rw-r--r-- | ep_run/muon.py | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/ep_run/muon.py b/ep_run/muon.py new file mode 100644 index 0000000..5ac6fc6 --- /dev/null +++ b/ep_run/muon.py @@ -0,0 +1,64 @@ +"""Muon optimizer (Newton-Schulz orthogonalized momentum) + hybrid helpers. +Convention: Muon on 2D hidden matrices, AdamW on everything else (emb/pos/LN/bias).""" +import torch + + +def newton_schulz(G, steps=5, eps=1e-7): + """approximate polar factor of G via the quintic NS iteration (Keller Jordan coefficients).""" + a, b, c = 3.4445, -4.7750, 2.0315 + X = G / (G.norm() + eps) + transposed = X.size(0) > X.size(1) + if transposed: X = X.T + for _ in range(steps): + A = X @ X.T + B = b * A + c * (A @ A) + X = a * X + B @ X + return X.T if transposed else X + + +class Muon(torch.optim.Optimizer): + def __init__(self, params, lr=0.02, momentum=0.95, ns_steps=5, nesterov=True): + super().__init__(params, dict(lr=lr, momentum=momentum, ns_steps=ns_steps, nesterov=nesterov)) + + @torch.no_grad() + def step(self, closure=None): + for group in self.param_groups: + for p in group['params']: + if p.grad is None: continue + g = p.grad + st = self.state[p] + if 'mom' not in st: st['mom'] = torch.zeros_like(g) + buf = st['mom'] + buf.mul_(group['momentum']).add_(g) + u = g.add(buf, alpha=group['momentum']) if group['nesterov'] else buf + if u.ndim == 2: + u = newton_schulz(u, group['ns_steps']) + u = u * max(1.0, u.size(0) / u.size(1)) ** 0.5 # rms-matched scaling + p.add_(u, alpha=-group['lr']) + + +class MultiOpt: + """duck-typed bundle of optimizers (step/zero_grad API-compatible).""" + def __init__(self, opts): self.optimizers = opts + def step(self): + for o in self.optimizers: o.step() + def zero_grad(self, set_to_none=True): + for o in self.optimizers: o.zero_grad(set_to_none=set_to_none) + + +class MultiSched: + def __init__(self, scheds): self.scheds = scheds + def step(self): + for s in self.scheds: s.step() + + +def build_hybrid(blocks, other_params, lr_adamw, lr_muon, warmup): + """Muon(2D block matrices) + AdamW(everything else), with linear-warmup scheds for both.""" + mats = [p for p in blocks.parameters() if p.ndim == 2] + mat_ids = {id(p) for p in mats} + rest = [p for p in other_params if id(p) not in mat_ids] + om = Muon(mats, lr=lr_muon) + oa = torch.optim.AdamW(rest, lr=lr_adamw, weight_decay=1e-4) + fn = lambda s: min(1.0, (s + 1) / max(warmup, 1)) + scheds = [torch.optim.lr_scheduler.LambdaLR(om, fn), torch.optim.lr_scheduler.LambdaLR(oa, fn)] + return MultiOpt([om, oa]), MultiSched(scheds) |
