Optimizer
Mixed Muon + AdamW Optimizer
Overview
flaxchat uses a mixed optimizer matching nanochat’s design: Muon for 2D matrix parameters (attention/MLP weights) and AdamW for everything else.
graph TD
PARAMS[Model Parameters] --> CLASSIFY{Parameter Type?}
CLASSIFY --> |"2D matrices<br/>(attn Q/K/V/O, MLP)"| MUON[Muon Optimizer<br/>Polar Express + NorMuon]
CLASSIFY --> |"Embeddings"| ADAMW_EMB[AdamW<br/>β1=0.8, β2=0.995]
CLASSIFY --> |"LM Head"| ADAMW_LM[AdamW<br/>β1=0.8, β2=0.96]
CLASSIFY --> |"Value Embeds"| ADAMW_VE[AdamW<br/>0.5x embedding LR]
CLASSIFY --> |"Scalars<br/>(lambdas)"| ADAMW_SC[AdamW<br/>separate β1/β2]
CLASSIFY --> |"Smear/Backout"| ADAMW_SM[AdamW<br/>fixed LR=0.2]
MUON --> UPDATE[Parameter Update]
ADAMW_EMB --> UPDATE
ADAMW_LM --> UPDATE
ADAMW_VE --> UPDATE
ADAMW_SC --> UPDATE
ADAMW_SM --> UPDATE
Muon Algorithm
For each 2D matrix parameter:
1. Nesterov momentum:
m ← m * β + g * (1-β)
g ← g * (1-β) + m * β
2. Polar Express orthogonalization (5 iterations):
X ← g / (‖g‖ * 1.01)
for (a,b,c) in coefficients:
if tall: A = Xᵀ X; X = a*X + X*(b*A + c*A²)
if wide: A = X Xᵀ; X = a*X + (b*A + c*A²)*X
g ← X
3. NorMuon variance reduction:
v_mean = mean(g², dim=reduction_dim)
second_moment ← lerp(second_moment, v_mean, 1-β2)
scale = rsqrt(second_moment) * normalize
g ← g * scale
4. Cautious weight decay + update:
mask = (g * param) ≥ 0
param -= lr * (g + wd * param * mask)
LR Scaling
All learning rates scale automatically:
| Scaling | Formula | Source |
|---|---|---|
| Batch size | η ∝ √(B/B_ref) |
AdamW standard |
| Model dim | η ∝ 1/√(d/768) |
μP-inspired |
| Weight decay | λ ∝ √(B/B_ref) · (D_ref/D) |
T_epoch framework |
LR Schedule
LR
↑
│ ┌─────────────────────┐
│ /│ │\
│ / │ constant │ \ warmdown
│ / │ │ \ (cosine to final_lr_frac)
│ / │ │ \
│/ │ │ \____
└─────┴─────────────────────┴────────→ steps
warmup 65% 35%
- Warmup: Linear, 40 steps
- Constant: Peak LR
- Warmdown: Cosine decay to 5% of peak over last 65% of training