| """ |
| Muon Optimizer — Keller Jordan, NanoGPT speedrun. |
| |
| Newton-Schulz iterasyonu ile orthogonalize edilmiş momentum. |
| 2D ağırlıklara (Linear weight) uygulanır. 1D parametreler (norm weight, |
| bias, embedding) AdamW'ye verilir. |
| |
| Referans: |
| https://github.com/KellerJordan/modded-nanogpt |
| https://kellerjordan.github.io/posts/muon/ |
| |
| Kullanim: |
| # Param ayri: |
| muon_params = [p for p in model.parameters() if p.dim() >= 2 and p.requires_grad] |
| other_params = [p for p in model.parameters() if p.dim() < 2 and p.requires_grad] |
| |
| # Embedding ve lm_head'i muon'dan ayir (yaygin best practice) |
| embed_params = [model.wte.weight] # tied ise lm_head dahil |
| muon_params = [p for p in muon_params if not any(p is e for e in embed_params)] |
| other_params = other_params + embed_params |
| |
| optimizer_muon = Muon(muon_params, lr=2e-2, momentum=0.95) |
| optimizer_adam = torch.optim.AdamW(other_params, lr=3e-4, ...) |
| """ |
|
|
| import torch |
|
|
|
|
| @torch.no_grad() |
| def newton_schulz(G: torch.Tensor, steps: int = 5) -> torch.Tensor: |
| """G matrisini orthogonalize et (yaklasik USV^T -> UV^T). |
| |
| Newton-Schulz quintic iteration. bf16'da kararli, hizli. |
| """ |
| assert G.ndim == 2 |
| a, b, c = (3.4445, -4.7750, 2.0315) |
| X = G.to(torch.bfloat16) |
| |
| if X.size(0) > X.size(1): |
| X = X.T |
| |
| X = X / (X.norm() + 1e-7) |
| for _ in range(steps): |
| A = X @ X.T |
| B = b * A + c * (A @ A) |
| X = a * X + B @ X |
| if G.size(0) > G.size(1): |
| X = X.T |
| return X.to(G.dtype) |
|
|
|
|
| class Muon(torch.optim.Optimizer): |
| """Muon: Momentum + orthogonalize edilmiş update. |
| |
| Sadece 2D parametreler için. 1D'leri AdamW ile ayrı eğit. |
| |
| Args: |
| params: 2D parametreler iterable |
| lr: 0.02 (AdamW'nin ~50x'i, çünkü update'ler ortonormal) |
| momentum: 0.95 |
| nesterov: True (genelde daha iyi) |
| ns_steps: Newton-Schulz iter sayisi (5 default) |
| """ |
| def __init__(self, params, lr=0.02, momentum=0.95, nesterov=True, ns_steps=5): |
| defaults = dict(lr=lr, momentum=momentum, nesterov=nesterov, ns_steps=ns_steps) |
| super().__init__(params, defaults) |
|
|
| @torch.no_grad() |
| def step(self, closure=None): |
| loss = None |
| if closure is not None: |
| with torch.enable_grad(): |
| loss = closure() |
|
|
| for group in self.param_groups: |
| lr = group["lr"] |
| momentum = group["momentum"] |
| nesterov = group["nesterov"] |
| ns_steps = group["ns_steps"] |
|
|
| for p in group["params"]: |
| if p.grad is None: |
| continue |
| if p.ndim < 2: |
| raise ValueError( |
| f"Muon sadece >=2D param destekler, {p.ndim}D bulundu. " |
| "1D paramları AdamW'ye ver.") |
|
|
| g = p.grad |
| state = self.state[p] |
| if "momentum_buffer" not in state: |
| state["momentum_buffer"] = torch.zeros_like(g) |
|
|
| buf = state["momentum_buffer"] |
| buf.mul_(momentum).add_(g) |
|
|
| |
| if nesterov: |
| g = g.add(buf, alpha=momentum) |
| else: |
| g = buf |
|
|
| |
| original_shape = g.shape |
| if g.ndim > 2: |
| g = g.view(g.size(0), -1) |
|
|
| |
| g_orth = newton_schulz(g, steps=ns_steps) |
|
|
| |
| |
| |
| scale = max(1.0, g_orth.size(0) / g_orth.size(1)) ** 0.5 |
|
|
| |
| g_orth = g_orth.view(original_shape) |
|
|
| p.add_(g_orth, alpha=-lr * scale) |
|
|
| return loss |
|
|