从0训练LLM · 现代大模型体系 阅读 + 实验 ≈ 45 分钟 页内实验 ×3
C5 优化:参数怎么更新得又快又稳 第 7 课

Adam 把一个权重矩阵当成一堆独立的数,浪费了什么?

Muon:按矩阵的方向更新
浏览器里真训练的小模型,隐层 64×64 矩阵的一阶矩在第 100 步时,最大的一个奇异方向独占 57% 的能量——更新几乎是低秩的,多数方向几乎不动。Muon 用 5 步 Newton-Schulz 把更新的奇异值都拉到 1 附近(实际落在 0.68–1.13),让每个方向走差不多大的一步;embedding、输出头和归一化参数仍交给 AdamW。2025–2026 年,Kimi、GLM、DeepSeek 的旗舰都换成了这种分工。
❓

本课一问:第 2 课的旋转峡谷说明 Adam 只认坐标轴。一个权重矩阵被 Adam 当成一堆互不相干的数来更新——浪费了什么?

这一课先不碰:原版 Muon 下注意力 logit 失控、QK-Clip 怎么按住它(C8 兑现);大矩阵的 Newton-Schulz 怎么在多卡上算(本课程不展开,见 Kimi K3 与 DeepSeek-V4 报告的分布式 Muon 实现)。

从已有出发

「小明把苹果给了小王」里每个 token 的向量穿过 FFN 时,要乘一个 gate_proj 矩阵——Qwen3.8-27B 里它是 17,408 × 5,120,89,128,960 个数,每层 3 个这样的矩阵、64 层合计 17,112,760,320 个参数(与 safetensors 实测分项一致)。这个矩阵作为一个整体把输入空间映到输出空间,有它自己的「主方向」。前六课的优化器里,Adam 把这 89,128,960 个数当成 89,128,960 个独立的标量,各除各的 √v̂。

从已有出发 · 累计数据流 点已学环节看形状与参数
解码层
从已有出发 · 训练的一步(点任一环节看它在 Qwen/Qwen3.8-27B 上有多大) 本课本章后面的课

这一课用两个替身。矩阵更新长什么样,要在一个真实训练中的矩阵上看:浏览器里小模型的隐层矩阵 W1(64 × 64);矩阵「把圆变成椭圆」的几何,用一个 2 × 2 矩阵画出来。

先自己设计:继续用逐元素的 Adam

最省事的就是照旧:每个矩阵元素一个 m、一个 v,逐元素相除。先看看它给一个矩阵的更新长什么样——把更新矩阵做奇异值分解,64 个奇异值就是它在 64 个「主方向」上各走多大:

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案逐元素的 Adam:矩阵里每个数各除各的
隐层矩阵的更新长什么样:奇异值与 Muon 字级 MLP · 24,896 参数 · 《三國志演義》24,000 字 · 浏览器内真训练
等待开始(滚动到这里会自动开始)

数字来源:浏览器实时训练(纯 JS,Float64)。模型、语料、种子写在上方;同一组对照里所有 run 共用初始化和取批顺序,只改被比较的那一项。换种子重跑可以看到噪声有多大——差异小于种子间波动时不下结论。

🎯

试三件事(先看「朴素方案」):① 读表格第一列:最大方向占 57.0%、前 5 个占 82.8%;左边的条形图用对数纵轴,64 个奇异值从 1 一路掉到 10−3 附近。② 把「快照」切到第 50 步再到第 200 步:越早越集中(训练初期更新几乎只沿一个方向)。③ 看右边那张条形图:同一个矩阵送进 5 步 Newton-Schulz 之后,奇异值全部落在 0.41–1.19 之间——下面讲它怎么做到的。

改进:Muon——让每个方向走差不多大的一步

任何矩阵都可以写成 G = UΣVT:先转一下(VT),沿各主方向拉伸(Σ 的对角线就是奇异值),再转一下(U)。几何上,G 把单位圆变成一个椭圆,两条半轴就是两个奇异值。Muon(Keller Jordan 2024-12-08)的做法是把动量矩阵换成它的「正交化」版本 UVT:方向保留,奇异值全部变成 1——每个主方向走一样大的一步 。

精确的 UVT 要做 SVD,又慢,在 BF16 下也不稳。Muon 改用 Newton-Schulz 迭代:先除以 Frobenius 范数(保证最大奇异值 ≤ 1),再做 5 步 X ← aX + b(XXT)X + c(XXT)²X。这一步对每个奇异值恰好作用同一个五次多项式 φ(σ) = aσ + bσ³ + cσ⁵,奇异向量不变 。系数 (a, b, c) = (3.4445, −4.7750, 2.0315) 是调出来的:让 φ 在 0 附近尽量陡(小奇异值每步放大 3.4 倍),代价是 1 不再是不动点。

🎯

试三件事(「改进方案」里的 NewtonSchulzViz):① 默认的 diag(3, 0.1):归一化后是 (0.9994, 0.0333),把 k 拖到 5,变成 (0.697, 1.129)——两个都被拉进了 0.7–1.2 的带里,虚线椭圆变成接近圆的实线。② 读下方「[0.01, 1] 经 5 步映到 [0.682, 1.134]」:不是精确的 1。Jordan 的实验是这点误差(约 ±0.3)不伤 loss。③ 系数切到「基线五次」,k = 5:[0.312, 1.002]——它能精确收敛到 1,但太慢;再切到「DeepSeek-V4」:前 8 步用 Jordan 系数快速拉起来,后 2 步换基线系数钉在 1 上,10 步后是 [1.0000, 1.0005]。

Muon 的一步(对一个隐藏层权重矩阵 W)
← βM + G; = NS(βM + G); W ← W·(1 − ) − η · · O
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
NS 只作用于二维矩阵。embedding、输出头、归一化增益、偏置仍用 AdamW——Jordan 的原话是「应当用 AdamW 优化 embedding 和最终的分类头」,并说输出层这条是经验结论,不是理论推出来的。

把 Newton-Schulz 放到锚点模型的真实形状上,看一步要做什么运算:

Newton-Schulz 的一次迭代(锚点 Qwen3.8-27B 的 gate_proj 真实形状)
X ← aX + (b·A + c·A2)·X, A = X·XT, X0 = GT ÷ ‖G‖F
输入输出中间结果
边长按对数比例缩放,块边上的数字是真实值;同一维度符号的边在全图等长。
维度含义 真实规模 小规模
F FFN 中间维 intermediate_size 17,408 6
D 隐藏维度 hidden_size 5120 4
每次迭代约 4D²F + 2D³ 次浮点运算,5 次约 1.05e+13;这个矩阵前向 + 反向处理一个 token 约 6DF = 5.35e+8,所以一次 NS 相当于它处理约 19,576 个 token。一步的 batch 有几百万 token 时,这点开销不到 1%(Jordan 的估算:上界 T·m ÷ B)。锚点模型实际用的优化器没有公开,这里只算形状和开销。
🔍 为什么是「正交化」?有没有比「经验观察」更硬的理由?

有一条理论线索:把「每一步在谱范数(最大奇异值)约束下让 loss 下降最多」写成优化问题,解正好是 UVᵀ 的方向——正交化更新等价于谱范数下的最速下降(Bernstein 等人的系列工作;R3 §3.9 摘录)。它解释了「为什么是 UVᵀ」,但没有证明「谱范数是对的那把尺子」,所以仍是理论动机。另一个性质是可以证明的:对任意正交矩阵 Q₁、Q₂,Ortho(Q₁GQ₂) = Q₁·Ortho(G)·Q₂——整个矩阵在输入、输出两侧怎么转,正交化都跟着转。这正是第 2 课旋转峡谷里 Adam 做不到的事:逐元素的 √v̂ 只认坐标轴。

真训练对比:结论随设置翻转

浏览器内真训练 · AdamW vs Muon(W1 用 Muon,其余 AdamW)
隐层矩阵的更新长什么样:奇异值与 Muon 字级 MLP · 24,896 参数 · 《三國志演義》24,000 字 · 浏览器内真训练
等待开始(滚动到这里会自动开始)

数字来源:浏览器实时训练(纯 JS,Float64)。模型、语料、种子写在上方;同一组对照里所有 run 共用初始化和取批顺序,只改被比较的那一项。换种子重跑可以看到噪声有多大——差异小于种子间波动时不下结论。

🎯

试三件事:① 默认(cosine、weight decay 0.1)读最后一行:AdamW 3.691、Muon 3.656。② 把调度切到「常数」、weight decay 切到 0 再训练:AdamW 3.677、Muon 3.704——Muon 反而更差。原因和第 5 课一样:正交化后每一步都是「满幅」的更新,步子不会随梯度变小而变小,不降学习率就停不稳。③ 回到默认,再到实验课用三个学习率、两个种子去比:差距要大于种子间的波动才算数。

这个翻转本身就是本课最值得带走的证据课:同一个模型、同一份数据,优化器的比较结论会随调度和衰减翻转。Moonlight 把「加 weight decay」和「调整每个参数的更新尺度」列为 Muon 扩展到大模型的两个关键 ,和上面的现象方向一致;但我们的小模型不能替它作证:2.5 万参数对 30 亿激活、160 亿总参数,规模差了五个数量级以上。

现代做法:Muon 管矩阵,AdamW 管其余

模型优化器分工出处
NanoGPT speedrun(Jordan)Muon 把达到 3.28 验证 loss 的速度记录提高到 1.35 倍Jordan 博客
Moonlight 3B/16B MoEMuon + weight decay + 更新 RMS 0.2;报告约 2 倍算力效率arXiv 2502.16982
Kimi K2MuonClip:原版 Muon 在中等规模 run 中 max logit 超过 1,000,于是加 QK-Clip(τ = 100);15.5T token 全程无 loss spikearXiv 2507.20534(R4 摘录)
Kimi K3Per-Head Muon:Q、K、V 的动量按头切开各自正交化,并沿用 K2 的权重裁剪K3 报告 §2.5(本地 PDF)
GLM-4.5 / GLM-5除 embedding、bias、RMSNorm 外全用 Muon(NS 5 步、μ = 0.95、RMS 0.2);GLM-5 起按头拆开(Muon Split)R2 摘录
DeepSeek-V4多数模块用 Muon;embedding、预测头、mHC 的静态偏置与门控、RMSNorm 用 AdamW;混合 NS 10 步(8 + 2);动量 0.95、wd 0.1、更新 RMS 0.18;Q 与 KV 上的 RMSNorm 按住 logit,不用 QK-Clip报告 §2.4、§4.2.2(本地 PDF)
Qwen3.8-Flash-NextMuon 负责主体;embedding、lm_head、MoE router、输出门等用 AdamW;NS 8 步;融合矩阵先按头或 gate/up 拆开技术报告(R2 摘录,官方自报)
Llama 3、OLMo 2AdamWR4 摘录
Qwen3.8-27B(本课锚点)未公开R2 §1.5.1

对我新 ≠ 对领域新:Newton-Schulz 迭代本身是数值分析里的老方法(Jordan 引用了 Kovarik 1970、Björck & Bowie 1971、Higham 2008),更早的矩阵预条件优化器有 Shampoo(2018);「Q、K、V 分开正交化更好」在 Jordan 2024 年的博客里就写过,GLM-5 的 Muon Split、K3 的 Per-Head Muon 是各自的工程版本,彼此没有互相引用(R2)。

🔎

一个证据三问的反面例子。Qwen3.8-Flash-Next 报告做过压力测试:学习率固定在最优值的 4 倍,「AdamW 加 Qwen3.5 结构」每万步 183 次 loss spike,「Muon 加 Gated Residual」为 0 次(官方自报)。这组对比同时换了优化器和残差结构,不能单独归功于 Muon——引用时要把两个变量一起说出来。

项目里的同一个观察(同一步、同一个隐层矩阵,AdamW 一阶矩、Muon 动量、Newton-Schulz 输出的奇异值):

项目真实日志 · 隐层矩阵的奇异值快照

⚠ 常见误解

⚠

「Muon 可以替换所有参数的优化器。」它只处理二维的隐藏层矩阵。embedding、输出头、归一化增益、偏置仍用 AdamW(Jordan 的建议,DeepSeek-V4、GLM、Qwen 的实际做法);Qwen3.8-Flash-Next 还专门把 MoE router 留给 AdamW,理由是用 Muon 会让 router 早期不稳定。

⚠

「正交化之后,奇异值严格等于 1。」Jordan 系数 5 步后落在 0.68–1.13,1 甚至不是不动点(φ(1) = 0.701);要精确到 1,得像 DeepSeek-V4 那样最后换系数多迭代两步。上面的组件可以直接读出这两组范围。

⚠

「Muon 更稳,就不需要别的稳定手段。」Kimi K2 用原版 Muon 时注意力 max logit 很快超过 1,000,才加了 QK-Clip;DeepSeek-V4 靠 Q 与 KV 上的 RMSNorm。优化器只管「怎么走」,模型内部的数值会不会失控是另一回事(C8)。

在演进地图上的位置

优化的演进:每一代都被上一代的某个真数字逼出来(点名字去那一课;完整版在章首页)
  1. 全量梯度下降
  2. 小批量随机梯度下降 SGD
  3. 动量(重球法)
  4. Adam:每个参数一把尺子
  5. AdamW:解耦权重衰减
  6. 学习率 warmup
  7. 学习率衰减:cosine → WSD
  8. batch 大小:渐增,还是一开始就大
  9. 全局梯度范数裁剪
  10. Muon:按矩阵的奇异方向更新
第五代Muon:按矩阵的奇异方向更新Keller Jordan 2024 → Moonlight 2025你在这里
解决了Newton-Schulz 把更新的奇异值拉进 [0.68, 1.13];Moonlight 报告在算力最优训练下约为 AdamW 的2 倍算力效率。
卡在Kimi K2 在中等规模 run 中用原版 Muon,注意力 max logit 很快超过1,000——稳定性问题留到 C8 兑现。
数字口径:峡谷步数、Newton-Schulz 范围、DeepSeek-V3 与 WSD 的步数账在构建时现算;标「浏览器实训」的来自本章 MicroTrainer 的默认设置(种子 1,Chrome 实测);DeepSeek-V3 数字出自其报告 §4.2(本地 PDF);Moonlight「约 2 倍」出自 arXiv 2502.16982 摘要;Kimi K2「max logit > 1000」出自 arXiv 2507.20534;Qwen3.8-Flash-Next 为官方技术报告自报。

证据三问

问本课的回答
① 它解决什么问题?逐元素的更新让矩阵的更新几乎低秩:少数奇异方向独占大部分步长,其余方向几乎不动
② 数学上为什么可能有效?NS 迭代对奇异值作用同一个多项式、不改奇异向量;正交化对两侧旋转等变 ;「正交化 = 谱范数下的最速下降」是理论动机
③ 实验上为什么最后选了它?NanoGPT speedrun 1.35 倍、Moonlight 约 2 倍算力效率;Kimi、GLM、DeepSeek 的旗舰先后采用

一句话

📌

Adam 看坐标,Muon 看矩阵的奇异方向:把更新的奇异值都拉到 1 附近。

🚦

先停在这里:记住「Muon 管隐藏层矩阵、AdamW 管其余」和「NS 只是近似正交化」,去实验课把三种优化器放在一起比一次。
再深一层:打开 chunhuizhang/llm_rl 的 muon_basics notebook(公开),把 NS5 与精确 SVD 的差在自己机器上算一遍。

下一个缺口

「参数怎么更新得又快又稳」这一章到此结束:每一步怎么走(SGD → 动量 → Adam → AdamW → Muon)、学习率怎么随时间变(warmup、cosine / WSD)、异常怎么挡(裁剪)。下一个问题换了一个尺度:优化器定了,该训多大的模型、喂多少数据? → C6 规模。先去实验课把本章的优化器在同一个模型上比一遍。

回扣先记下:Kimi K2「max logit 超过 1,000」会在 C8(稳定性)兑现。

你可能会问

这一课还没有收到真实提问;你的提问会记到 05-问答记录/C5-L7.md。

🔍 Muon 省显存吗?

Muon 对每个矩阵参数只存一份动量,Adam 存 m、v 两份,所以矩阵部分的优化器状态少一半。但 embedding、输出头这些仍用 AdamW 的参数照旧是两份;Qwen3.8-27B 的 embedding 与 lm_head 合计 2,542,796,800 个参数,这部分省不了。代价在另一头:NS 需要完整的矩阵,分布式训练时要先把切到各卡上的矩阵拼回来(Kimi K3、DeepSeek-V4 都专门写了这部分的工程实现;本课程不展开,C10 讲的是切法本身)。