DeepSeek V4 源码剖析

第17章 Muon 优化器与 33T 预训练

作者 杨艺韬 · 8,336 字 · 发布于 · 更新于

AdamW 统治了上一个十年。Muon 是这个十年的候选者之一——注意是"之一"。

V4 的训练栈用 Muon 取代部分 AdamW——一个看似细节的选择,背后是 1.6T MoE 训练成本与稳定性的极致权衡。


17.1 引子:AdamW 在 1.6T 规模上的局限

AdamW(Adam with weight decay)从 2017 年问世后,统治了大模型训练近十年。它的工作原理是为每个参数维护两个 moment:

  • 一阶 moment(动量):梯度的指数移动平均
  • 二阶 moment(方差):梯度平方的指数移动平均

更新公式:

θt+1=θt−η⋅m^tv^t+ϵ\theta_{t+1} = \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}

AdamW 在小模型上极稳定。技术报告第 2.4 节给 V4 换用 Muon 的理由只有一句:收敛更快、训练更稳定。下面几条是本专栏的归纳,不是报告原话:

问题 1:状态显存翻 2 倍

AdamW 的 m / v 与参数同形——对 1.6T 参数意味着多 3.2T 个 32-bit 状态 ≈ 12.8 TB(即便 8-bit 优化器如 bitsandbytes,也要约 3.2 TB)。ZeRO 分片只是把这份总量摊到各 rank,总量不变。

问题 2:逐元素更新,看不到矩阵结构

AdamW 对每个元素单独做 m/sqrt(v) 归一化,不关心参数是一个矩阵。Keller Jordan 在 Muon 的 blog 里给出的动机是:Transformer 里 2D 参数的 SGD-momentum / Adam 更新条件数很高、近乎低秩,少数主方向压住了其余方向。Muon 就是冲着这一点设计的(见 §17.2)。

顺带澄清一个常见误解:精度不是换优化器的理由。V4 预训练走的是 FP8 混合精度,优化器维护的是 FP32 主权重;routed expert 的 FP4(e2m1,16 个编码、15 个不同值)是后训练阶段才引入的 QAT(技术报告第 3.4 节),和选 AdamW 还是 Muon 无关。

问题 3:超参敏感

AdamW 的 β1 / β2 / ε / weight decay 都要调,大规模训练里每试一组的代价都很高。不过 V4 并没有甩掉这些超参:它的 embedding 等模块仍用 AdamW(β1 = 0.9、β2 = 0.95、ε = 10⁻²⁰、weight decay 0.1,技术报告第 4.2.2 节),Muon 这边也特意把更新的 RMS 缩放到能复用 AdamW 学习率的量级(见 §17.8)。

问题 4:对 spectral norm 的隐性约束弱

AdamW 通过 weight decay 间接控制 weight 范数,对单步更新的谱范数没有直接约束。

Muon 通过完全不同的数学路径回应了第 1、2、4 条。


17.2 Muon 的核心思想

Muon (Momentum Orthogonalized by Newton-Schulz) 是 Keller Jordan 在 2024 年通过 blog post + GitHub repo(github.com/KellerJordan/Muon)提出的优化器;后续 Moonshot AI 团队的扩展研究 "Muon is Scalable for LLM Training"(arXiv:2502.16982,2025-02)把它推到大模型规模的工业验证。它的核心思想:先把动量矩阵正交化,再拿它当更新方向——每步更新的谱范数被约束在 1 附近(再乘学习率与形状缩放),而不是像 AdamW 那样逐元素自适应缩放。

Muon 的更新规则可以简化成三步:

1. 算梯度的动量 m = β · m + g        (Keller Jordan 的实现默认用 Nesterov:送去正交化的是 g + β·m;V4 同样用 Nesterov)
2. 把 m 正交化:
   m_orth = newton_schulz_5(m)      # 逼近 m 的正交极分解因子 U V^T(V4 用 8+2 共 10 步的混合迭代,见 §17.11)
3. 更新参数:θ_{t+1} = θ_t - lr · s · m_orth
                                     # s 是与形状有关的缩放,原实现取 max(1, out/in)^0.5;
                                     # V4 取 sqrt(max(n, m)) · 0.18,并先做 θ ← θ·(1 − lr·λ) 的 weight decay

第 2 步最容易被说错,这里写准:Newton-Schulz 迭代逼近的不是 m/∥m∥2m / \lVert m \rVert_2。如果只是除以谱范数,那不过是一次标量缩放,奇异值之间的比例一点没变。NS 迭代做的是把 m=UΣV⊤m = U\Sigma V^\top 里的 Σ\Sigma 整个拉平成单位阵,输出 UV⊤UV^\top——奇异值都被推向 1。(严格说,Keller Jordan 那组系数为了少迭代几步,只把奇异值推到 1 附近的一个区间,并不精确收敛到 1;V4 的混合迭代最后两步专门负责把它们稳定到 1,§17.11 有实测。)所以它不只是"限制最大奇异值",而是"抹掉动量矩阵各方向之间的量级差异",让那些被主方向压住的小奇异值方向也拿到同等的更新步长。这正是 Muon 与"梯度裁剪 / 归一化"这一类做法的本质区别。

这个看似奇怪的设计有几个好处:

  • 不需要二阶 moment:Muon 只需要一阶 m,状态显存少一半
  • 每步更新的谱范数有界:正交化后的更新方向奇异值都在 1 附近,单步不会沿某个方向走得过猛。但它不能替代 weight decay——Moonshot 论文正是靠给 Muon 加回 weight decay 才把它放大到大规模,V4 也照做(weight decay 0.1)
  • 超参可复用:把更新 RMS 缩放到与 AdamW 相当后,可以沿用 AdamW 的学习率与 weight decay 设置(Moonshot 论文与 V4 都这么做)
  • 低精度下可用:技术报告第 3.5.1 节观察到 Newton-Schulz 用 BF16 矩阵乘就能稳定运行;参数本身仍由 FP32 主权重承载

在 V4 之前,Moonshot 的 Kimi K2(1T 总参、32B 激活,arXiv:2507.20534)已经用 Muon 的改进版 MuonClip 训完了万亿参数级 MoE;V4-Pro 把 Muon 用到了 1.6T 总参。


17.3 Muon 单步更新的数据流

把 Muon 一次 step 的核心计算画清楚:

flowchart LR
  G["梯度 g: [out, in]"] --> Mom["更新动量<br/>m = β·m + g"]
  Mom --> Norm["M = g + β·m (Nesterov)<br/>M / ‖M‖_F(BF16,保证最大奇异值 ≤ 1)"]
  Norm --> NS["Newton-Schulz 迭代<br/>原版 5 步 / V4 混合 10 步"]
  NS --> Spectral["m_orth ≈ U·Vᵀ<br/>奇异值被推向 1"]
  Spectral --> Update["weight ← weight − lr × s × m_orth<br/>原版 s = max(1, out/in)^0.5"]
  M_state["持久 momentum buffer<br/>(只保 m, 不保 v)"] -.每步读写.-> Mom
  classDef compute fill:#312e81,stroke:#a78bfa
  classDef state fill:#1f2937,stroke:#475569
  class NS,Spectral compute
  class M_state state

Muon 与 AdamW 的核心差别一目了然:只维护一阶 m,没有二阶 v——状态显存减半。Newton-Schulz 正交化承担了二阶 moment 那种"按方向调整步长"的角色,只是调整的对象是整个矩阵的奇异值,而不是逐个元素。


17.4 Muon 与 AdamW 的对比

把 Muon 与 AdamW 在 V4 训练场景下对比:

维度 AdamW Muon
状态显存 2× 参数(m, v) 1× 参数(仅 m)
关键超参 lr, β1, β2, ε, wd lr, momentum, wd, 更新 RMS 缩放(V4:0.95 / 0.1 / 0.18)
spectral 约束 间接(weight decay) 直接(Newton-Schulz)
低精度 逐元素运算,无特殊要求 NS 用 BF16 矩阵乘即稳定(技术报告第 3.5.1 节)
收敛速度 基线 更快,但两种口径别混(见下)
训练稳定性 基线 技术报告称更稳定(第 2.4 节)
数学复杂度 简单 中(Newton-Schulz 迭代)

V4 选 Muon 的工程账:

  • 显存节省:Muon 只留动量 m(按 FP32 计,1.6T 参数 × 4 bytes = 6.4 TB),比 AdamW 少一份同样大的 v——省下约 6.4 TB 优化器状态(embedding、预测 head 等少数模块仍用 AdamW,实际略少于此)
  • 训练加速:两个来源的口径不同,别合并成一个区间。Keller Jordan 的 blog 报告的是 NanoGPT 速跑(FineWeb 上训到 val loss 3.28)的速度纪录提升约 1.35 倍;Moonshot 的 Muon is Scalable for LLM Training 给的是算力口径——在 compute-optimal 训练下达到 AdamW 约 2 倍的计算效率(该文同时给出两个让 Muon 能放大到大规模的关键改动:加 weight decay、逐参数调整更新尺度)
  • 超参复用:更新 RMS 对齐 AdamW 后可以沿用 AdamW 的学习率设置,省去为 Muon 单独扫参(见 §17.8)

代价:Newton-Schulz 迭代的额外计算。Keller Jordan 给的估算是额外 FLOPs 至多为 T·m/B(T 为 NS 步数,m 为模型宽度,B 为以 token 计的 batch size),他算出 NanoGPT 速跑约 0.7%、Llama 405B 规模约 0.5%。V4 的 NS 步数翻倍(10 步),但 batch 高达数千万 token(§17.14),按这个式子额外 FLOPs 仍很小。真正的工程麻烦不在 FLOPs,而在 Muon 需要完整的梯度矩阵,和 ZeRO 分片冲突(§17.11)。


17.5 V4 的混合优化器分工

V4 的训练源码不公开,但技术报告第 2.4 节把分工写明了:

  • AdamW:embedding 模块、预测 head 模块、mHC 的静态偏置与门控因子(源码 hc_*_base、hc_*_scale)、所有 RMSNorm 的权重
  • Muon:其余所有模块,按"逻辑上独立的矩阵"逐个正交化(算法 1)

也就是 Muon + AdamW 混合:Muon 只对矩阵有意义,向量参数(RMSNorm 权重就是 1D)交给 AdamW;embedding 与输出 head 留在 AdamW,也与 Keller Jordan 最初的建议一致。mHC 的投影矩阵 hc_*_fn 是 2D,走 Muon(第 10 章)。Flash 与 Pro 的优化器超参相同,见技术报告第 4.2.2 节:AdamW 为 β1 = 0.9、β2 = 0.95、ε = 10⁻²⁰、weight decay 0.1;Muon 为 momentum 0.95、weight decay 0.1、更新 RMS 缩放到 0.18。

报告没有逐一交代的是零散的小参数:例如 inference/model.py 里每头一个标量的 attn_sink(1D),以及压缩器的位置偏置 ape(形状 [compress_ratio, coff × head_dim],虽是 2D 却不是线性变换的权重)。它们具体归哪个优化器,报告没说,本专栏不做猜测。路由的 Gate.bias 则不靠梯度更新,而是按无辅助损失均衡规则调整(更新速度 0.001,第 7 章)。


17.6 33T tokens 预训练:数据规模的几何

V4 的 README 原文是 "We pre-train both models on more than 32T diverse and high-quality tokens"。这是两个模型的合称,技术报告第 1 节给出的分别是 V4-Flash 32T、V4-Pro 33T。以 Pro 的 33T 为准,这个数字背后的几何含义:

维度 33T 是什么
总数据量 33 × 10^12 tokens
与 V3 对比 V3 用 14.8T, V4-Pro 翻倍多
文本量 假设平均 1.5 byte/token,约 50 TB 原始文本
训练时长 报告未公开;§17.12 按公开数据做了量级估算
数据成本 报告未公开

V4 为什么需要 33T 而不是 V3 的 14.8T?技术报告没有给出归因,下面两条是第 1 章 §1.12 的推测:

  1. 384 expert + top-6 比 256 + top-8 更稀疏:每个 expert 见到的 token 比例下降,需要更多总 token 让每个 expert 训练充分
  2. HC 把残差流拓宽到 4 路:残差通道更宽,可能也需要更多 token 才能把它用满

FP4 精度不在归因之列:FP4 QAT 只在后训练引入(技术报告第 3.4 节),预训练走 FP8 混合精度,与预训练的 token 量无关(第 1 章 §1.12 也已说明)。


17.7 数据组成的"五大支柱"

V4 没公开具体数据配比。技术报告第 4.1 节只说预训练语料在 V3 数据的基础上扩充,包含数学、代码、网页、长文档及其他高质量类别,并交代了几条方向性的改动。按这些类别归成 5 个支柱:

支柱一:网页 报告提到对网页数据新加了过滤策略,去掉批量自动生成和模板化的内容,以降低"模型崩溃"(model collapse)的风险。

支柱二:代码 报告称数学与代码语料"仍是核心组成",并在 mid-training 阶段加入 agentic 数据来增强代码能力。

支柱三:数学 与代码并列为核心组成,报告没有更多细节。

支柱四:多语言 报告称为 V4 构建了更大的多语言语料,以更好地覆盖不同文化里的长尾知识。

支柱五:长文档 这是 V4 特别强调的一类:优先收录科研论文、技术报告等有独特学术价值的长文档,对应报告开头说的"更长的有效上下文"。

预处理基本沿用 V3:词表仍是 128K,沿用 token-splitting 与 FIM(Fill-in-Middle);不同来源的文档打包成序列以减少截断;与 V3 不同的是预训练改用样本级注意力掩码。各支柱的具体配比不公开。


17.8 学习率与训练阶段

技术报告第 4.2.2 节公开了学习率调度,分 3 个阶段:

flowchart LR
  S1["阶段 1: warm-up<br/>前 2000 步<br/>lr 线性升到峰值"] --> S2["阶段 2: stable<br/>大部分训练<br/>lr 保持峰值"]
  S2 --> S3["阶段 3: decay<br/>临近结束<br/>cosine 衰减到峰值的 1/10"]
模型 峰值 lr 结束 lr
V4-Flash 2.7 × 10⁻⁴ 2.7 × 10⁻⁵
V4-Pro 2.0 × 10⁻⁴ 2.0 × 10⁻⁵

阶段 1 (warm-up):前 2000 步从 0 线性升到峰值。这是为了避免训练初期的不稳定(随机初始化 + 大 lr = 灾难)。

阶段 2 (stable):lr 保持在峰值,做主要训练。这是最长的阶段——大部分参数学习发生在这里。

阶段 3 (decay):临近训练结束按 cosine 衰减到峰值的 1/10。这个阶段让模型在"低 lr 下精修"——loss 进一步下降。MTP loss 权重也在这时从 0.3 降到 0.1(第 11 章)。

报告只给了这一套 lr,Muon 与 AdamW 两部分参数都按它走。这之所以可行,是因为 V4 把 Muon 每个更新矩阵的 RMS 缩放到 0.18,目的正是"复用 AdamW 的学习率"(技术报告原话 for reutilization of the AdamW learning rate)。原版 Muon 的更新尺度由正交化决定、与 AdamW 的 m/sqrt(v) 不是一回事,不做这步 RMS 对齐,两边的 lr 是不能直接互换的。

与学习率并行推进的还有序列长度:训练从 4K 开始,逐步扩展到 16K、64K,最后到 1M。注意力也分阶段切换:Flash 先用稠密注意力热身前 1T tokens(Pro 的稠密阶段更长),到 64K 长度时引入稀疏注意力,此后一直保持;引入时先单独热身一小段 lightning indexer,再进入稀疏注意力训练。序列长度这条线与 1M 位置编码的关系见第 6 章。


17.9 训练监控的关键指标

大规模 LLM 训练需要监控的关键指标(基于通用训练经验整理,不是 V4 公开的监控清单;危险信号的具体阈值都要在自己的配置上标定):

指标 目的 危险信号
Per-token loss 主优化目标 突刺(spike)/ 上升 / 不收敛
Gradient norm 训练稳定性 突然跳高数倍 / 塌到接近零
Per-layer activation norm 检测层间数值稳定 某层异常大 / 小
Expert load (per-layer MoE) 检测路由均衡 某 expert 长期 0 load
Routing entropy gate 是否塌陷 接近 0 (塌陷) 或 ln(n_experts) (退化)
长上下文评测(eval) 长序列阶段是否有效 长度增加后检索类得分骤降
MTP loss / 预测准确率 MTP head 与主 head 一致度 相对主 loss 异常偏高或持续上升
HC pre/post weights HC 是否正常学 comb 矩阵不接近双随机
FP4 quant error (proxy) 后训练 QAT 是否有效 量化误差大幅增长

表里第一行 loss 的突刺(loss spike)是 V4 实际遇到过的问题:技术报告第 4.2.3 节说 spike 总与 MoE 层的离群值相关(§17.15 介绍报告给出的两项对策)。


17.10 动手实验:Muon 的 mini 实现

import torch

def newton_schulz_5(G, steps=5):
    """5 步 Newton-Schulz 迭代逼近 G 的正交化"""
    a, b, c = (3.4445, -4.7750, 2.0315)  # Keller Jordan 原始 Muon 给的 NS 系数
    X = G.bfloat16() / (G.norm() + 1e-7)
    for _ in range(steps):
        A = X @ X.transpose(-2, -1)
        B = b * A + c * (A @ A)
        X = a * X + B @ X
    return X.float()

class MuonOptimizer(torch.optim.Optimizer):
    def __init__(self, params, lr=0.01, momentum=0.95):
        defaults = dict(lr=lr, momentum=momentum)
        super().__init__(params, defaults)

    def step(self):
        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue
                state = self.state[p]
                if 'momentum_buffer' not in state:
                    state['momentum_buffer'] = torch.zeros_like(p.data)

                buf = state['momentum_buffer']
                buf.mul_(group['momentum']).add_(p.grad)

                # 仅对 2D weight 做 Newton-Schulz(向量参数走普通 SGD)
                if p.dim() == 2:
                    update = newton_schulz_5(buf)
                    # 原实现还有一个与形状有关的步长缩放,非方阵时不能省
                    scale = max(1.0, p.size(0) / p.size(1)) ** 0.5
                else:
                    update = buf
                    scale = 1.0

                p.data.add_(update, alpha=-group['lr'] * scale)


# 测试:用 Muon 训练一个简单 Linear 拟合
torch.manual_seed(0)
W_target = torch.randn(64, 32)
data_x = torch.randn(1000, 32)
data_y = data_x @ W_target.T

model = torch.nn.Linear(32, 64, bias=False)
opt = MuonOptimizer(model.parameters(), lr=0.05)

for step in range(50):
    opt.zero_grad()
    pred = model(data_x)
    loss = (pred - data_y).pow(2).mean()
    loss.backward()
    opt.step()
    if step % 10 == 0:
        print(f"Step {step}: loss = {loss.item():.4f}")

在 CPU torch 2.11.0 上实跑,loss 从 33.65 降到第 40 步的 18.21,下降几乎是匀速的——正交化后的更新谱范数固定,每步走的"距离"只由 lr 决定,与梯度大小无关。和 torch.optim.SGD 比(同样 lr = 0.05,第 49 步):不带动量的 SGD 只降到 28.74,Muon 为 15.50;但带 0.95 动量的 SGD 降到 2.21,反而更快。所以这个玩具实验说明的是 Muon 的步长行为,不能用来证明它比 SGD 收敛快:单个小矩阵的凸回归问题上,Muon 的优势没有用武之地。

生产代码不必手写:本专栏对照的 PyTorch 2.11.0 已自带 torch.optim.Muon(torch/optim/_muon.py),默认 5 步 NS、同一组系数 (3.4445, -4.7750, 2.0315)、momentum 0.95、nesterov=True,只接受 2D 参数;adjust_lr_fn="match_rms_adamw" 时按 0.2·sqrt(max(A, B)) 缩放学习率,就是 Moonshot 论文那种对齐 AdamW 更新 RMS 的做法(V4 取 0.18)。


17.11 Muon 在 1.6T 规模的工程挑战

Muon 最早是 Keller Jordan 在 2024 年发布的 blog + GitHub repo,主要在小规模(NanoGPT 速跑记录,blog 里最大的实验是 1.5B)上验证;Moonshot 2025-02 的扩展论文把它推到了 MoE 规模(16B 总参的 Moonlight),2025-07 的 Kimi K2 又推到 1T。V4 把它用到 1.6T,下面几处工程问题中,前四处技术报告第 2.4、3.4、3.5.1 节都有交代:

挑战 1:Newton-Schulz 在大矩阵上的数值稳定

迭代前先把矩阵除以 Frobenius 范数(Frobenius 范数不小于谱范数,除完后最大奇异值不超过 1),这是 NS 能收敛的前提。V4 没有用原版的 5 步,而是混合 10 步:前 8 步用 Keller Jordan 的系数 (3.4445, -4.7750, 2.0315) 快速把奇异值推到 1 附近,最后 2 步换成 (2, -1.5, 0.5),把奇异值精确稳定在 1。报告还观察到 NS 用 BF16 矩阵乘就能稳定运行。

在 CPU 上用 BF16 实跑对比(随机高斯矩阵):原版 5 步后,64×32 矩阵的奇异值落在 0.68~1.07,3072×7168(Pro 的 expert 形状)落在 0.68~1.13;换成 8+2 混合迭代后,64×32 落在 0.996~1.004。随机方阵(1024×1024)的最小奇异值很小,5 步后最小的只到 0.008,混合迭代后也收到 0.994~1.007。

挑战 2:Muon 与 ZeRO 优化器分片的协调

ZeRO 是为 AdamW 这类逐元素优化器设计的,一个参数矩阵可以切到多个 rank 上各自更新;Muon 却需要完整的梯度矩阵才能做正交化。V4 的办法是混合的 ZeRO 分桶策略(技术报告第 3.5.1 节):

  • 稠密参数:限制 ZeRO 并行度上限,用背包算法把整块参数矩阵分给各 rank,使负载大致均衡;每个 rank 的桶补齐到最大桶的大小以便 reduce-scatter,这份 padding 在他们的配置下(每 rank 至多 5 个矩阵)一般不到 10% 显存。数据并行规模超过 ZeRO 上限时,多出来的组冗余地重复计算 Muon 更新,用计算换桶内存。
  • MoE 参数:每个 expert 单独优化。把所有层所有 expert 的 down / up / gate 投影矩阵依次展平再 padding,保证能均分到各 rank 又不切开任何一个矩阵;expert 数量多,不设 ZeRO 上限,padding 开销可忽略。
  • 同一 rank 上相邻的同形状参数自动合并,批量跑 NS 迭代以提高硬件利用率。

挑战 3:低精度与随机舍入

预训练里没有 FP4 权重需要更新。优化器维护的是 FP32 主权重,预训练走 FP8 混合精度;FP4 只在后训练 QAT 阶段进入(下一条)。报告里真正用到随机舍入(stochastic rounding)的地方在通信上:既然 NS 用 BF16 就稳定,V4 把要跨数据并行 rank 同步的 MoE 梯度以随机舍入方式量化到 BF16,通信量减半;为避免低精度加法器累积误差,不用树形或环形 reduce-scatter,而是先 all-to-all 交换本地梯度,再由各 rank 在 FP32 下本地求和。

挑战 4:与 QAT 的协同

FP4 QAT 在后训练阶段才引入(技术报告第 3.4 节)。做法是常规 QAT:优化器维护的 FP32 主权重先量化到 FP4(MXFP4),再反量化到 FP8 参与计算——FP8(E4M3)比 FP4(E2M1)多 2 个指数位,只要每个 128×128 的 FP8 块内各 1×32 子块 scale 的比值不超过阈值,这步反量化是无损的,整条 QAT 流程可以直接复用现有 FP8 训练框架。反向时梯度对同一份 FP8 权重求出,直接回传给 FP32 主权重,相当于对量化操作用直通估计(STE)。优化器(包括 Muon)始终作用在 FP32 主权重上,从不直接更新 FP4 值(第 14 章)。

挑战 5:与 HC / Sinkhorn 的协同

HC 的 Sinkhorn 是个归一化迭代,Muon 的 Newton-Schulz 也是——两者叠加会不会"过约束"?从作用对象看不太会:Sinkhorn 作用在每个 token 每一层现算出来的 4×4 混合矩阵上(是 forward 的一部分),Muon 作用在权重矩阵的动量上(是 optimizer 的一部分),层级完全不同。V4 能跑通本身说明这两者没打架,但公开材料里没有针对这一点的 ablation——"实测表明没有冲突"这种说法没有出处,本节只能给出结构上的理由。

另外一点值得记下:Kimi K2 的 MuonClip 用 QK-Clip 防止注意力 logits 爆炸,V4 没用——它的注意力结构允许直接对 query 和 KV 条目做 RMSNorm,已能压住 logits(技术报告第 2.4 节)。训练源码没有开放,但上述做法报告里都写了;想复制"1.6T + Muon + FP4 QAT + mHC"这条路线,这几节是最直接的参考。


17.12 33T tokens 的硬件投入估算

把 33T tokens 训练 V4 Pro 的硬件投入做个粗略估算(基于公开数据 + 工程惯例,下面的 MFU、卡数、电价、卡价都是假设值):

单 token 训练 FLOPs:按 6N 惯例,49B 激活参 × 6(前向 2 + 反向 4)≈ 300G FLOPs / token(粗略)

总 FLOPs:33 × 10^12 × 300 × 10^9 ≈ 9.9 × 10^24 FLOPs(约 9900 ZettaFLOPs)

硬件:H100 SXM 的 FP8 稠密峰值约 2 PFLOPS(第 13 章那个 1550 TFLOPS 是 DeepGEMM 在 H800 上的实测吞吐,不是硬件峰值,别混用)。大规模训练要扣掉通信、反向、气泡与非 GEMM 算子,假设 30%-35% 的 MFU,有效算力 ~650 TFLOPS——这一档假设针对的是1.6T 规模 MoE 的大规模预训练,换成稠密模型训练、推理 prefill 或 decode 都是完全不同的数,MFU 不带场景和模型规模就没有可比性。

总 GPU-hour:9.9 × 10^24 / (650 × 10^12) / 3600 ≈ 420 万 GPU-hour

集群规模:如果用 5000 张 H100 持续训:420 万 / 5000 ≈ 850 小时 ≈ 35 天。如果 10000 张:~18 天。

电费:每张 H100 ~700W,5000 张 × 850 小时 ≈ 300 万 kWh,约 300 万元人民币电费(按 1 元/kWh,只算 GPU 功耗)。

硬件折旧:5000 张 H100 总价按 15 亿元计,按 3 年折旧、训练 35 天占用:~5000 万元折旧成本。

总训练成本估算:硬件折旧 + 电费 + 数据处理 + 工程师工资 ≈ 5000 万 - 1 亿元人民币。

拿 V3 的一手数字校验一下:V3 技术报告给的正式训练用量是 278.8 万 H800 GPU 小时(其中预训练 266.4 万),按每小时 2 美元的租价折合 $557.6 万(不含前期研究与消融实验)。这个口径是 GPU 租金,和上面"折旧 + 电费 + 数据 + 人力"的口径不同,不能直接比金额,但可以比算力:按 6N 估算,V4-Pro(49B 激活 × 33T)的预训练 FLOPs 约为 V3(37B 激活 × 14.8T)的 2.95 倍。按 6N 反推,V3 预训练实际折合的有效算力约 340 TFLOPS/卡,只有上面假设的 650 TFLOPS 的一半左右;若 V4 的效率与 V3 相当,预训练 GPU 小时数会是 266.4 万 × 2.95 ≈ 790 万,按同样租价约 $1570 万。所以 420 万~790 万 GPU 小时是更稳妥的量级区间,技术报告没有公开 V4 的实际用量。

V4 公开了训练方法(Muon、混合 ZeRO、FP4 QAT、两阶段后训练等写在技术报告里),但没有开源训练代码,这种规模的算力 + 数据也是大多数公司复制不了的。这是为什么"V4 即便开源权重,也不会让小公司从零训练 V4 类模型"——开源的最大价值是 fine-tune 与部署,不是 from-scratch 复刻。


17.13 Muon 在社区中的现状与争议

Muon 是 2024 年提出的新优化器,社区对它的接受度还在演化中。把当前现状梳理一下(截至 2026 年 4 月):

支持方观点:

  • 收敛快、状态显存少(V4 技术报告称其收敛更快、训练更稳定)
  • NS 用 BF16 矩阵乘即可稳定运行(技术报告第 3.5.1 节)
  • Newton-Schulz 数学优雅,spectral 约束有理论基础
  • Keller Jordan 的 blog 解释了 NS 系数的选取思路,Moonshot 扩展论文给出了放大到大规模所需的改动与 scaling 实验

反对方观点:

  • Newton-Schulz 系数(3.4445, -4.7750, 2.0315)对不同矩阵形状的鲁棒性未充分验证
  • spectral 约束可能让某些任务(如 fine-tune)不灵活
  • 社区可复现实验还不够多——大多数实验在小规模上
  • 与 ZeRO / FSDP 等优化器分片框架的协同需要专门设计(V4 为此做了混合 ZeRO 分桶,见 §17.11)

中立观察:

继 Kimi K2(1T)之后,V4-Pro 把 Muon 用到了 1.6T 规模——它的成功 / 失败都会被社区作为重要 data point。以下是预测:如果 V4 GA 后社区独立复现实验显示 Muon 在多个任务上稳定,Muon 有望取代 AdamW 成为新标准;如果出现 V4 训练曲线后期问题归因到 Muon,Muon 的接受度会停滞。值得注意的是,V4 报告把训练中的 loss spike 归因于 MoE 层离群值与路由,而不是优化器(§17.15)。

对工程师的建议:

如果你做的是 < 100B 规模的训练,AdamW 仍然是稳妥选择——成熟、社区支持广。Muon 适合 100B+ 规模 + 显存敏感 + 可承担"踩坑成本"的场景。等 6-12 个月看 V4 GA 后的社区反馈,再决定是否切换。


17.14 Muon 与"超大 batch size" 训练的关系

技术报告第 4.2.2 节公开了 batch size:与 V3 一样采用 batch size 调度,从较小值逐步增大,V4-Flash 升到 7550 万(75.5M)tokens 后保持到训练大部分阶段,V4-Pro 的最大 batch 为 9440 万(94.4M)tokens。作为对照,V3 报告第 4.2 节的调度是在前 469B tokens 内从 3072 条增到 15360 条 4K 序列,约 6300 万 tokens,用的还是 AdamW。把 Muon 与超大 batch 的关系梳理一下。

经典训练理论(Adam / AdamW):

batch size 大 → 梯度估计准 → 可以用更大 lr。但收益不是无限的:batch 超过某个"临界 batch size"后,再增大 batch 带来的步数节省迅速递减。这个临界值随训练进程和 loss 水平变化,不是一个固定的 token 数——V3 用 AdamW 就跑到了约 6300 万 tokens 的 batch。

Muon 的特点:

有观点认为,大 batch 下梯度噪声小、动量方向更准,Muon 的正交化更能发挥作用。这是一种推测,本专栏没有找到 V4 报告或 Moonshot 论文对"Muon 在大 batch 下优于 AdamW"的直接实验结论。

V4 的实战暗示:

V4-Pro 的最大 batch(94.4M)比 V3(约 63M)大约 1.5 倍,33T tokens 按这个 batch 大致是 35 万步量级(前期 batch 较小,实际步数更多)。但报告没有把 batch 变大归功于 Muon,V3 用 AdamW 已经在同一个量级,所以"Muon 解锁了大 batch"这种说法缺乏证据。

对工程师的意义:

如果你做的是大模型训练(10B+),切到 Muon 时更实际的做法是像 Moonshot 论文和 V4 那样对齐更新 RMS,沿用已调好的 AdamW 学习率与 batch 调度,再在自己的规模上做小范围扫参;batch size 与 lr 的最优组合仍要按自己的模型规模实验确定。

如果你做的是 fine-tune(小数据),batch size 受限于数据量。另需留意:Moonshot 论文报告过预训练与微调优化器不一致(如 AdamW 预训练的模型用 Muon 微调)时效果不是最好,换优化器前先确认基座是用什么训出来的。

换 Muon 不只是"换个优化器",更新尺度、weight decay、学习率都要一起对齐。


17.15 Muon 与"训练稳定性"的工程指标

V4 用 Muon 训练 1.6T 模型,训练稳定性是工程纪律的核心。下面先列出训练时值得监控的稳定性指标,再看 V4 报告里实际用到的对策。

指标 1:gradient norm

每 step 算梯度的 L2 norm。健康的训练里它应该在一个相对窄的区间内平稳波动;突然跳高几倍或塌到接近零,都说明出了问题(学习率太大、某层数值崩溃、数据里混进了异常样本)。具体的告警阈值必须在你自己的模型和训练配置上标定——绝对数值高度依赖初始化、参数规模与 loss 尺度,没有可以照抄的区间。

指标 2:Newton-Schulz 迭代误差

Muon 内部的 NS 迭代逼近正交化。原版 5 步是否够收敛取决于输入矩阵的条件数(V4 干脆用了 10 步混合迭代,见 §17.11)——所以监控"NS 输出离正交还差多少"是有意义的。收敛不好时的处理是增加步数或收紧输入归一化。(具体阈值同上,要自己标定。)

指标 3:weight 范数稳定性

每层 weight 的 L2 norm 在训练中应该缓慢变化。如果某层 weight norm 突跳(如某 step 内增加 50%),说明更新失控或该层数值出了问题——Muon 约束的是单步更新的谱范数,并不直接约束 weight 本身。

指标 4:每层 activation 范数

forward 过程中每层 activation 的 norm 应该稳定(V4 用 Sinkhorn 约束残差混合、用 RMSNorm 做归一化,但这并不能杜绝离群值,见下文)。某层 activation norm 异常(变成预期 10x 或 0.1x),说明该层数值出现问题。

指标 5:路由 bias 的漂移速度

V4 用 topk_method = "noaux_tc"——靠一个逐 expert 的 bias(Gate.bias,fp32)做负载均衡,而不是辅助 loss(详见第 7 章)。这个 bias 每步只该微调;如果某步的调整幅度突然放大一个数量级,说明 routing 严重失衡,要去查输入数据分布。(幅度阈值取决于 bias 的更新规则与量级,需自行标定。)

这些指标的实时监控:

任何数周到数月量级的训练都需要实时追踪这些指标,并在异常时自动 checkpoint + 暂停 + 告警。以上 5 条是通用工程实践,不是对 V4 监控系统的描述。

V4 实际用到的两项稳定手段:

技术报告第 4.2.3 节承认训练中出现过明显的不稳定:单纯回滚只能暂时恢复,loss spike 会反复出现;spike 总与 MoE 层的离群值相关,而路由机制本身似乎会放大离群值。报告给出两项对策:

  • Anticipatory Routing(预判路由):把主干网络与路由网络的同步更新解耦——第 t 步用当前参数 θ_t 计算特征,路由索引却用历史参数 θ_{t−Δt} 算。实现上提前在 t−Δt 步取来第 t 步的数据,预先算好路由索引缓存起来。开启时额外墙钟开销约 20%,所以只在自动检测到 loss spike 时短暂回滚并开启,跑一段后恢复常规训练,整体开销可以忽略。
  • SwiGLU Clamping:Flash 与 Pro 全程把 SwiGLU 线性分量截断在 [−10, 10]、门控分量上限截断在 10,消除离群值且不损性能(推理代码里的 swiglu_limit 就是它,第 9 章)。

报告也坦言两者的原理尚未被充分理解。注意这两项针对的是 MoE 路由与激活离群值,和优化器选 Muon 还是 AdamW 没有直接关系。

理解这些指标让你在自己的项目里部署"Muon 友好的训练监控"——避免在长训练(数周到数月)后期才发现问题,浪费几百万 GPU-hour。


17.16 延伸阅读

  • Muon 原始工作(Keller Jordan,2024):github.com/KellerJordan/Muon blog post + repo
  • Muon 大规模扩展研究(Moonshot AI,2025-02):"Muon is Scalable for LLM Training" (arXiv:2502.16982):weight decay 与更新 RMS 对齐的出处
  • DeepSeek-V4 技术报告第 2.4 节(Muon 算法与混合 NS 迭代)、第 3.5.1 节(Muon 的 ZeRO 实现)、第 4.2.2 节(训练超参)、第 4.2.3 节(训练稳定性)—— 本章主要参考
  • Kimi K2 技术报告(arXiv:2507.20534):MuonClip 在 1T 规模的应用
  • AdamW 论文(arXiv:1711.05101):AdamW 的源头
  • DeepSeek-V3 报告(arXiv:2412.19437):V3 的训练配置
  • Spectral Normalization GAN(arXiv:1802.05957):spectral norm 在训练中的早期应用
  • 本专栏第 18 章:两阶段后训练 pipeline

17.17 本章小结

  • V4 对大部分模块用 Muon,embedding、预测 head、RMSNorm 权重与 mHC 的静态偏置 / 门控因子仍用 AdamW;Muon 部分的优化器状态比 AdamW 少一半
  • Muon 用 Newton-Schulz 迭代把动量正交化(奇异值推向 1);V4 用 8+2 共 10 步的混合迭代,加 weight decay 0.1、Nesterov,并把更新 RMS 缩放到 0.18 以复用 AdamW 学习率
  • 优化器始终作用在 FP32 主权重上:预训练走 FP8 混合精度,FP4 QAT 只在后训练引入
  • 预训练规模:V4-Pro 33T、V4-Flash 32T tokens(README 合称 32T+),序列长度 4K→16K→64K→1M 逐步扩展
  • 数据按类别分为网页 / 代码 / 数学 / 多语言 / 长文档,具体配比未公开
  • 学习率:2000 步 warm-up,峰值保持(Pro 2.0 × 10⁻⁴),临近结束 cosine 衰减到 1/10;Pro 最大 batch 94.4M tokens
  • 训练稳定性靠 Anticipatory Routing 与 SwiGLU Clamping 压制 MoE 离群值引起的 loss spike

第 18 章:两阶段后训练——领域专家培养 + on-policy 蒸馏。