跳转至

DeepSeek 工程深读

更新日期:2026-04-26

DeepSeek 是公开技术诚意最高的实验室之一。从 V2 (MLA) → V3 (FP8 + DualPipe + 671B MoE) → V3.1 (混合 thinking) → V3.2 (Sparse Attention) → V4 (1.6T + 1M context) 一年走完三代架构突破。2025-02 Open Infra Week 一次性开源 7 个底层基础设施(FlashMLA、DeepEP、DeepGEMM、3FS、EPLB、profile-data、smallpond、DualPipe),把工程栈展开了。

本文按"读 paper 同时想能不能复现"的角度过一遍:架构 → 底层 infra → 训练 → 推理。

主要参考:


一、模型版本对照(V1 → V4)

每代关键架构数字。每 cell 一行,详细配置和 cite 见表下方。

模型 发布 总参数 激活 Hidden Attention Experts Shared Top-K Vocab Context
LLM-67B 2024-01 67B 67B 95 8192 GQA-8 102k 4k
MoE-16B 2024-01 16.4B 2.8B 28 2048 MHA 64 2 6 102k 4k
V2 2024-05 236B 21B 60 5120 MLA 160 2 6 102k 128k
V2.5 2024-09 236B 21B 60 5120 MLA 160 2 6 102k 128k
Coder-V2 2024-07 236B 21B 60 5120 MLA 160 2 6 102k 128k
V3 2024-12 671B 37B 61 7168 MLA 256 1 8 128k 128k
R1 2025-01 671B 37B 61 7168 MLA 256 1 8 128k 128k
V3.1 2025-08 671B 37B 61 7168 MLA 256 1 8 128k 128k
V3.2 2025-12 685B ~37B 61 7168 DSA 256 1 8 128k 128k
V4 Pro 2026-04 1.6T 49B 61 n/a CSA+HCA n/a n/a n/a n/a 1M
V4 Flash 2026-04 284B 13B n/a n/a CSA+HCA n/a n/a n/a n/a 1M

源:V2 paper · V3 paper · R1 paper · V3.2 HF card · V4-Pro HF card · V4 tech report PDF(58 页)

关键配置详解(每列 < 80 字塞不下的内容放这里):

  • MLA(V2+)\(d_c=512\)(latent dim),\(d_h=128\)(per-head),V3 用 \(n_h=128\) heads;V2 用 \(n_h=128\) 但 latent KV head 数不同
  • DSA(V3.2):DeepSeek Sparse Attention,fine-grained sparse 选择,长 context 显著提速;保留 MLA 底层
  • CSA + HCA hybrid(V4):双引擎 attention
  • CSA:Compressed Sparse Attention,小窗口 \(m=4\),sparse top-k 选择
  • HCA:Heavily Compressed Attention,大块 \(m'=128\),dense attention
  • 效果:1M context 下 KV cache 仅为 BF16 GQA8 baseline 的 2%,FLOPs 仅为 V3.2 的 27%
  • V4 训练:32T tokens,Muon optimizer(Newton-Schulz orthogonalize),FP4 master quant + FP8 compute 原生低精度训练
  • V4 layer 数:61(与 V3 同),其他细节(hidden / expert 数 / top-k)官方 tech report 还在分析中
  • V3 训练:14.8T tokens,AdamW + ZeRO-1,FP8 + DualPipe

几个 pattern

  • MoE 经济化:V2 → V3,总参数 ×2.8 但激活 ×1.7,expert 数 ×1.6
  • Attention 演化:MHA(67B) → GQA(67B) → MLA(V2-V3.1) → DSA(V3.2) → CSA+HCA(V4)
  • Context 增长:4k(V1) → 128k(V2 YaRN) → 1M(V4 hybrid attention)
  • 训练 token:V2 8.1T → V3 14.8T → V4 32T+
  • 训练精度:BF16(V2) → FP8 fine-grained(V3) → FP4 master + FP8 compute(V4)
flowchart LR
    in["Token IDs<br/>[B, S]"] --> emb["Embedding<br/>[B, S, 7168]"]
    emb --> dense["Dense Block × 3"]
    dense --> moe["MoE Block × 58<br/>(MLA + Routed-MoE + Shared-Expert)"]
    moe --> norm[RMSNorm]
    norm --> head["LM Head + MTP heads<br/>(deep n=1 lookahead)"]

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    classDef io fill:#f5f3eb,stroke:#bdb9ab,color:#1a1a1a;
    class in,emb,norm io
    class dense,moe,head stage

为什么前 3 层 dense:MoE 早期路由不稳定,dense warm-up 让 token rep 先聚焦再被分到 expert。这跟 Mixtral 的"全 MoE"策略不同,是 V3 的工程经验取舍。


二、MLA — Multi-Head Latent Attention

MLA 是 V3 在 KV Cache 维度上的核心创新。从 V2 开始引入,V3 沿用并扩展。

2.1 出发点

标准 MHA 推理时 KV Cache 大小:\(2 \cdot n_{kv} \cdot d_h \cdot S \cdot \text{layers}\)。70B 模型 128k context 单 sequence KV 占几十 GB,是 long-context 推理的最大瓶颈。

GQA / MQA 通过减少 \(n_{kv}\)(共享 K/V 头)来压缩,但表达能力下降。MLA 走另一路:把 K/V 投到一个低维 latent,再 up-project 出多头,等价于把 KV Cache 压成 latent 向量。

MHA / GQA / MLA 三种 attention 的 KV cache 结构对比

┌─────────────────────────────────────────────────────────────────┐
│  MHA: 每个 head 独立 K/V                                          │
│                                                                  │
│   h ──┬─→ W_K^1 ──→ k¹ ┐                                         │
│       ├─→ W_V^1 ──→ v¹ │                                         │
│       │                 │                                         │
│       ├─→ W_K^2 ──→ k² │  cache 全部 n_h 个                       │
│       ├─→ W_V^2 ──→ v² ├─→ KV size = 2 · n_h · d_h                │
│       │                 │     = 2 × 128 × 128 = 32768/token       │
│       ├─→ W_K^h ──→ kʰ │                                         │
│       └─→ W_V^h ──→ vʰ ┘                                         │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│  GQA: n_kv 个 head 共享 K/V                                       │
│                                                                  │
│   h ──┬─→ W_K^g ──→ k_g (n_kv 组)                                │
│       │   W_V^g ──→ v_g                                          │
│       │                                                          │
│       └─→ broadcast 到 n_h 个 query head                          │
│                                                                  │
│       cache size = 2 · n_kv · d_h                                │
│                  = 2 × 8 × 128 = 2048/token  (GQA-8)             │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│  MLA: down-project to latent, up-project on-the-fly              │
│                                                                  │
│   h ──→ W_DKV ──→ c_KV (d_c=512)  ← cache 这个                   │
│                    │                                              │
│                    ├──→ W_UK ──→ k¹..kʰ  (推理时实时算)           │
│                    └──→ W_UV ──→ v¹..vʰ                          │
│                                                                  │
│       cache size = d_c + d_R = 512 + 64 = 576/token (V3)         │
│                                                                  │
│       压缩比 vs MHA: 576/32768 = 1.7%   (32×)                    │
└─────────────────────────────────────────────────────────────────┘

直观区别: - MHA:每个 head 自己一份 K/V → 全量缓存,但表达力最强 - GQA:n_kv 组共享 → 缓存压缩,但表达力损失 - MLA:缓存的是 latent,K/V 推理时再展开 → 缓存极小,且 K/V 展开是矩阵乘可融合到 Q 路径(§2.5),等价无开销

2.2 数学结构

记 hidden state \(h \in \mathbb{R}^{D}\),要拿到 \(n_h\) 个 head 的 K/V:

Down-projection(共享 latent)

\[ c^{KV} = W^{DKV} h \quad \in \mathbb{R}^{d_c} \]

其中 \(d_c \ll n_h \cdot d_h\)。V3 取 \(d_c = 512\),对比标准 MHA 的 \(128 \times 128 = 16384\),KV 维度压缩 32×

Up-projection(per-head K/V)

\[ k_i^C = W^{UK}_i c^{KV}, \quad v_i = W^{UV}_i c^{KV} \]

注意:缓存里只存 \(c^{KV}\)(512 维),不存 \(k_i^C, v_i\)。推理时实时 up-project。

2.3 RoPE 的处理

RoPE 是位置依赖的旋转矩阵 \(R(m)\) 应用在 \(q\)\(k\) 上。如果 K 是从 latent up-project 出来的,那 RoPE 只能加到 up-project 之后;但缓存的是 latent,意味着每次 attention 都要重算 RoPE(contradicting 缓存的初衷)。

V3 的解法 — decoupled RoPE

\[ k_i = [k_i^C; k_i^R], \quad q_i = [q_i^C; q_i^R] \]

其中 \(k_i^R, q_i^R\)专门承载位置信息的"位置头",从 \(h\) 单独投影出来 + 应用 RoPE:

\[ k^R = R(m) \cdot W^{KR} h \]

\(k^R\) 在所有 head 间共享(类似 MQA),所以缓存 \(k^R\) 的代价小。注意力分数:

\[ \text{score}_i = q_i^C \cdot k_i^C + q_i^R \cdot k^R \]

第一项可以用预乘技巧(吸收 \(W^{UK}\)\(W^Q\) 里)做到与缓存 \(c^{KV}\) 同级别效率,第二项用缓存的 \(k^R\)

MLA 完整数据流(含 decoupled RoPE):

flowchart LR
    h["hidden h<br/>[B, S, D=7168]"]
    h --> Wq["W^Q<br/>(已吸收 W^UK)"]
    h --> Wdkv["W^DKV"]
    h --> Wkr["W^KR<br/>(共享位置头)"]
    h --> Wqr["W^QR"]

    Wdkv --> ckv["c^KV<br/>[B, S, d_c=512]<br/><b>★ 缓存这个</b>"]
    Wkr --> kr_pre["k^R pre-rotate<br/>[B, S, d_R=64]"]
    kr_pre --> rope1["RoPE"]
    rope1 --> kr["k^R<br/><b>★ 也缓存</b>"]

    Wq --> qC["q^C<br/>[B, S, n_h, d_c]"]
    Wqr --> qr_pre["q^R pre"]
    qr_pre --> rope2["RoPE"]
    rope2 --> qR["q^R"]

    ckv --> dot1["q^C · c^KV<br/>(content score)"]
    qC --> dot1
    kr --> dot2["q^R · k^R<br/>(position score)"]
    qR --> dot2

    dot1 --> sum["+"]
    dot2 --> sum
    sum --> sm["softmax"]
    sm --> attn["attn weights"]

    ckv --> Wuv["W^UV"]
    Wuv --> v["v"]
    attn --> out["attn · v"]
    v --> out
    out --> Wo["W^O"]
    Wo --> y["output"]

    classDef cache fill:#fff5e8,stroke:#cc785c,color:#1a1a1a;
    classDef op fill:#fff,stroke:#bdb9ab,color:#1a1a1a;
    class ckv,kr cache
    class h,Wq,Wdkv,Wkr,Wqr,kr_pre,qr_pre,rope1,rope2,qC,qR,dot1,dot2,sum,sm,attn,Wuv,v,out,Wo,y op

橙色框是唯一缓存的两个张量\(c^{KV}\)\(k^R\)),其它都是 on-the-fly 计算。

关键工程窍门:左下角 q^C · c^KV —— 朴素实现是 q 先 up-project 成 k,再点积;但因为 \(W^{UK}\) 是线性的,可以吸收到 W^Q 里

原: score = (W^Q h)^T · (W^UK c^KV)
等价: score = ((W^UK)^T W^Q h)^T · c^KV
让 W^Q' = (W^UK)^T W^Q —— 离线预乘一次

此时 attention 等价于直接在 \(d_c\) 维点积,跟正常 attention 算 \(d_h\) 维点积是一回事,没多余 cost。这是 MLA"省 cache 不费 compute"的核心。

2.4 实际 KV 占用对比

128k context、70B 模型、bf16:

方法 KV per token per layer 70B/61 layers/128k
MHA \(2 \cdot 128 \cdot 128 \cdot 2\) B = 64 KB ~500 GB
GQA(8) \(2 \cdot 8 \cdot 128 \cdot 2\) B = 4 KB ~31 GB
MLA(V3) \((512 + 64) \cdot 2\) B = 1.15 KB ~9 GB

KV 缓存数字是单序列估算;batched serving 时 prefill / decode 分离架构(PD 分离)会进一步降低有效占用。

2.5 实现陷阱

  1. Up-projection 矩阵融合:朴素实现每次 attention 都要做 \(c^{KV} \to k_i^C, v_i\) 的乘法,一头一矩阵。V3 把 \(W^{UK}_i\) 吸收到 \(W^Q_i\) 里:\(W^{Q,\text{new}}_i = (W^{UK}_i)^\top W^Q_i\)。这样 attention 等价为 \(q\) 直接和 \(c^{KV}\) 做点积(dim \(d_c\)),prefill 后只算 \(d_c\) 维内积,不再 up-project。
  2. 量化兼容性:吸收后的权重矩阵更大,per-channel quantization 的"channel"定义需重审视。现存 INT4/FP8 算子库(vLLM、TensorRT-LLM 早期版本)需要打补丁。
  3. 训练梯度路径:MLA 的 down-projection 是 bottleneck,如果 \(d_c\) 太小 accumulation 不够会把信息压坏。V3 选 \(d_c=512\) 是经验上限。

2.6 PyTorch 参考实现

下面是 MLA 的最小可运行 PyTorch 实现(教学用,不含 decoupled RoPE 优化)。改编自 rasbt/LLMs-from-scratch ch04/05_mla

import torch
import torch.nn as nn
import math

class MultiHeadLatentAttention(nn.Module):
    """简化版 MLA:down-project to latent, up-project per-head K/V.
    Production 版要加 decoupled RoPE + W^UK 吸收到 W^Q(见 §2.5)。"""

    def __init__(self, d_in, d_out, num_heads, latent_dim=None):
        super().__init__()
        assert d_out % num_heads == 0
        self.num_heads = num_heads
        self.head_dim = d_out // num_heads
        # latent_dim = d_c; V3 实测 d_c=512 是经验上限
        self.latent_dim = latent_dim if latent_dim else max(16, d_out // 8)

        self.W_query = nn.Linear(d_in, d_out)         # Q 不压缩
        self.W_DKV   = nn.Linear(d_in, self.latent_dim)  # 共享 down-project
        self.W_UK    = nn.Linear(self.latent_dim, d_out) # K up-project (per-head)
        self.W_UV    = nn.Linear(self.latent_dim, d_out) # V up-project (per-head)
        self.out_proj = nn.Linear(d_out, d_out)

        self.register_buffer("cache_c_kv", None, persistent=False)
        self.cache_pos = 0

    def reset_cache(self):
        self.cache_c_kv = None
        self.cache_pos = 0

    def forward(self, x, use_cache=False):
        B, S, _ = x.shape
        Q = self.W_query(x)                  # (B, S, d_out)
        c_kv = self.W_DKV(x)                 # (B, S, d_c)  ← 缓存这个

        if use_cache:
            if self.cache_c_kv is None:
                c_total = c_kv
            else:
                c_total = torch.cat([self.cache_c_kv, c_kv], dim=1)
            self.cache_c_kv = c_total
        else:
            c_total = c_kv

        K = self.W_UK(c_total)               # 推理时实时 up-project
        V = self.W_UV(c_total)

        Q = Q.view(B, S, self.num_heads, self.head_dim).transpose(1, 2)
        K = K.view(B, -1, self.num_heads, self.head_dim).transpose(1, 2)
        V = V.view(B, -1, self.num_heads, self.head_dim).transpose(1, 2)

        scores = (Q @ K.transpose(-2, -1)) / math.sqrt(self.head_dim)
        # causal mask
        s_q, s_k = Q.shape[-2], K.shape[-2]
        q_pos = torch.arange(self.cache_pos, self.cache_pos + s_q, device=x.device)
        k_pos = torch.arange(s_k, device=x.device)
        scores = scores.masked_fill(q_pos[:, None] < k_pos[None, :], float('-inf'))
        if use_cache:
            self.cache_pos += s_q

        attn = torch.softmax(scores, dim=-1)
        out = (attn @ V).transpose(1, 2).contiguous().view(B, S, -1)
        return self.out_proj(out)

KV cache 大小:朴素 MHA 缓存 (n_h × head_dim × 2),MLA 只缓存 d_c → 压缩比 ≈ d_c / (n_h × head_dim × 2)。V3 配置:512 / (128×128×2) = 1.5%,对应 §2.4 的 64 KB → 1.15 KB per token per layer。

Production-grade MLA

  • 上面 reference 是教学版,真 production 用 FlashMLA kernel:H800 上 BF16 580 TFLOPS / 3000 GB/s 内存 bound
  • vLLM 0.6+ / SGLang 0.3+ / TensorRT-LLM 都已合并 MLA 支持
  • V3 实际跑的 attention 等价于 \(q\)\(c^{KV}\) 直接做内积(吸收 \(W^{UK}\)\(W^Q\) 后),decoupled RoPE 走单独 head

三、MoE 架构

flowchart LR
    h["token h<br/>[B, S, 7168]"]
    gate["Gate W_r<br/>logits [256]"]
    bias["+ bias b_i<br/>(load balance)"]
    topk["Top-8 routed<br/>3.1% 稀疏率"]
    shared["Shared Expert<br/>always on"]
    e1["Routed E_1"]
    e2["Routed E_2"]
    edot["…"]
    e8["Routed E_8"]
    sum["加权求和<br/>(softmax score × FFN out)"]
    out["输出 [B, S, 7168]"]

    h --> gate --> bias --> topk
    h --> shared --> sum
    topk --> e1 --> sum
    topk --> e2 --> sum
    topk --> edot --> sum
    topk --> e8 --> sum
    sum --> out

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class h,gate,bias,topk,shared,e1,e2,edot,e8,sum,out stage

3.1 256 expert × top-8 + 1 shared

V3 MoE 设计:

  • 每层 256 个 routed expert + 1 个 shared expert
  • 每个 token 选 top-8 routed + always-on shared = 9 个 expert 算
  • 路由 score:\(g_i = \text{softmax}(W_r h)_i\),取 top-8 indices
  • 稀疏率:8/256 = 3.1%(V3 设计哲学是"足够稀疏 + 足够多 expert")

跟其他实验室对比:

模型 总 expert top-k shared 稀疏率 总/激活
Mixtral 8x7B 8 2 0 25% 47B / 13B
Mixtral 8x22B 8 2 0 25% 141B / 39B
DeepSeek-V3 256 8 1 3.1% 671B / 37B
Qwen3-MoE 235B 128 8 0 6.3% 235B / 22B
Kimi K2 384 8 1 2.1% 1.04T / 32B

DeepSeek 在 V2 已经验证了细粒度路由(更多更小的 expert)泛化更好;V3 拉到 256 是该思想的 scale-up。

3.2 Auxiliary-loss-free load balancing

经典 MoE 训练用 auxiliary loss 强制路由均匀(鼓励所有 expert 被调用)。这个 loss 跟主 loss 之间需要权重调整,难调还会损害模型容量。V3 用bias term update

\[ g_i^{\text{biased}} = g_i + b_i \]

\(b_i\) 是每个 expert 的 bias,按以下规则更新(不参与梯度,纯 EMA 控制)。完整 reference 实现

import torch
import torch.nn as nn
import torch.nn.functional as F

class DeepSeekV3MoEGate(nn.Module):
    """V3 的 auxiliary-loss-free routing gate.

    关键: bias b_i 不进 backward,每 step 后按 expert load 累计更新.
    """
    def __init__(self, hidden_dim, n_experts=256, top_k=8, bias_lr=1e-3):
        super().__init__()
        self.n_experts = n_experts
        self.top_k = top_k
        self.bias_lr = bias_lr  # γ in paper

        # 路由权重 (这个进 backward)
        self.router = nn.Linear(hidden_dim, n_experts, bias=False)

        # Expert bias (不进 backward)
        self.register_buffer("expert_bias", torch.zeros(n_experts))

        # Tracking expert 调用频率(用于 update bias)
        self.register_buffer("expert_load", torch.zeros(n_experts))
        self.register_buffer("step_count", torch.zeros(1))

    def forward(self, x):  # x: [B, S, D]
        # 1. compute routing logits
        logits = self.router(x)  # [B, S, n_experts]

        # 2. add bias for routing (但 bias 不进 grad)
        biased_logits = logits + self.expert_bias.detach()

        # 3. top-k select
        top_k_logits, top_k_idx = biased_logits.topk(self.top_k, dim=-1)
        # softmax 只在 top-k 上做(关键:用 unbiased logits 算 weight)
        unbiased_top_k = logits.gather(-1, top_k_idx)
        gates = F.softmax(unbiased_top_k, dim=-1)  # [B, S, top_k]

        # 4. 累计 expert 调用次数(推理时不更新)
        if self.training:
            with torch.no_grad():
                # 统计每个 expert 被选了多少次
                flat_idx = top_k_idx.flatten()
                load = torch.bincount(flat_idx, minlength=self.n_experts).float()
                self.expert_load += load
                self.step_count += 1

        return gates, top_k_idx  # [B, S, top_k] each

    @torch.no_grad()
    def update_bias(self):
        """每 N step 后调一次。让"火"的 expert bias 降低,"冷"的升。"""
        avg_load = self.expert_load.mean()
        # 每个 expert: 若高于均值 → bias 降;低于均值 → bias 升
        delta = torch.where(
            self.expert_load > avg_load,
            -self.bias_lr,
            self.bias_lr,
        )
        self.expert_bias += delta
        # reset load tracker
        self.expert_load.zero_()
        self.step_count.zero_()

为什么这样比 aux loss 好

维度 Aux loss(GShard / Switch) V3 bias-only
Backward 干扰 ★★★ 有,与主 loss 权重打架 无(bias 不进 grad)
Hyperparameter 1-2(loss 权重 + capacity factor) 1(bias_lr γ)
Capacity 损失 强制均匀 → 难学专家专精 软推动 → 允许 expert 适度专精
Cold-start 需要 warm-up 阶段 bias=0 初始即可

陷阱 / 已知问题

  • 推理 deploy 时仍然用 biased logits(router_logits + bias),不是 raw logits —— 因为 expert 已经按 biased 分布学专精
  • domain shift 时 bias 收敛慢(数千 step 才稳定),跨 domain CPT 时需重置 bias
  • \(\gamma\)(bias_lr)取太大震荡,太小不收敛 —— V3 paper Section 4.2 给的是 1e-3(每 step)

3.3 通信成本

MoE 训练的瓶颈往往是 all-to-all:每个 token 选 top-8 expert,分散到 8 张不同 GPU 跑,结果再汇总。V3 用 EP(Expert Parallel)+ DualPipe(见 §五)把通信掩盖在计算后面。

通信量估算(per token):

  • Forward dispatch:\(2 \cdot d_c \cdot \text{topk}\) bytes(用 fp16)
  • Backward gather:同上

671B / 37B activation 实际 forward + backward all-to-all 单步通信量约 1-2 MB/token(待核实,跟具体 batch shape 有关)。


四、FP8 训练 stack

V3 是首个把 FP8 训练验证到 671B 规模的开源工作。FP8 有两种格式:E4M3(高精度,小动态范围)、E5M2(大范围,低精度)。V3 选 E4M3 全场景(训练 forward / backward / 权重 / 激活)。

4.1 为什么不用 E5M2

NVIDIA Transformer Engine 默认 forward 用 E4M3、backward 用 E5M2(FP8 paper 推荐)。V3 全 E4M3 的理由:

  1. fine-grained scaling(见 §4.2)让动态范围问题被 tile-level scale 吸收
  2. E4M3 的精度(mantissa=3)比 E5M2(mantissa=2)多一倍有效 bit
  3. 反向传播的 gradient 量级用 tile-level scaling 控制,不需要 E5M2 的大范围

4.2 Per-tile fine-grained scaling

朴素 FP8:每个 tensor 一个 scale factor。问题:tensor 内部不同位置的数值范围可能差几个数量级,单一 scale 必然牺牲 outlier 或 small value。

V3:

  • 激活 (act):每 1×128 tile 一个 scale
  • 权重 (W):每 128×128 tile 一个 scale
flowchart LR
    subgraph act["激活 act [S, H]"]
        a1["1×128 tile<br/>scale_a1"]
        a2["1×128 tile<br/>scale_a2"]
        a3["… 沿 hidden 切"]
    end
    subgraph wt["权重 W [H, H']"]
        w1["128×128<br/>scale_w1"]
        w2["128×128<br/>scale_w2"]
        w3["… 块状切"]
    end
    matmul["FP8 matmul<br/>FP8 × FP8"]
    accum["BF16 / FP32<br/>accumulator"]
    out["BF16 输出"]

    act --> matmul
    wt --> matmul
    matmul --> accum --> out

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class a1,a2,a3,w1,w2,w3,matmul,accum,out stage
# 概念伪代码(V3 paper Section 3.3)
def fp8_quantize(x, tile_h, tile_w):
    """x: [H, W] tensor"""
    scales = []
    quantized = torch.zeros_like(x, dtype=torch.float8_e4m3fn)
    for i in range(0, x.shape[0], tile_h):
        for j in range(0, x.shape[1], tile_w):
            tile = x[i:i+tile_h, j:j+tile_w]
            # tile-level absmax → scale
            scale = tile.abs().amax() / FP8_MAX  # FP8_MAX = 448 for E4M3
            quantized[i:i+tile_h, j:j+tile_w] = (tile / scale).to(torch.float8_e4m3fn)
            scales.append(scale)
    return quantized, scales

关键点:1×128 vs 128×128 不对称是有理由的。激活是 sequence × hidden,sequence 维度上的位置往往有长程相关,用 1×128 在 hidden 维度切;权重是各向同性,128×128 块状切更省 scale 数量。

4.3 高精度累加

FP8 矩阵乘 → BF16 / FP32 累加器。这是 NVIDIA Hopper Tensor Core 原生支持:

FMA pipeline:
  fp8 × fp8 → fp16 partial
  fp16 partial → fp32 accumulator

V3 在 TileLang / 自研 kernel 里强制累加用 BF16(FP32 太慢),关键 reduction(如 softmax 分母)用 FP32。

4.4 Outlier 控制

V3 没有 SmoothQuant 那种"激活 → 权重难度转移",因为 fine-grained scaling 已经把 outlier 局部化到单个 tile。但报告里提到一些经验:

  • Embedding / LM head 不量化(保持 BF16)
  • 训练初期 1k step 用 BF16 warm-up,避免 FP8 噪音破坏初始化
  • Loss spike 检测时回退到 BF16 重训该 batch

4.5 复现度评估

组件 公开度 是否能直接用
FP8 算子 部分(FlashMLA 公开,但端到端 stack 没全开) 需要拼
Per-tile scaling 描述充分 可实现,需写 CUDA / Triton
训练 hyperparam 给了 LR schedule, batch 可复现规模化
Outlier 检测策略 没细节 需自己摸

五、DualPipe — 双向 pipeline 并行

5.1 为什么需要新 schedule

Megatron 1F1B (one-forward-one-backward) 在 P stages 上 bubble = \((P-1)/(\text{micro-batches})\)。256 expert MoE 训练时 micro-batch 受限于 expert 显存,bubble 占总时间 5-10%。V3 用 DualPipe 把这个 bubble 压到接近 0。

5.2 核心思想

把 forward 和 backward 看成两条数据流(一条往前,一条往后),让它们在 pipeline 里对穿

flowchart LR
    subgraph t1["时刻 t"]
        g0t1["GPU0<br/>F4 + B1"]
        g1t1["GPU1<br/>F3 + B2"]
        g2t1["GPU2<br/>F2 + B3"]
        g3t1["GPU3<br/>F1 + B4"]
    end
    subgraph t2["时刻 t+1"]
        g0t2["GPU0<br/>F5 + B2"]
        g1t2["GPU1<br/>F4 + B3"]
        g2t2["GPU2<br/>F3 + B4"]
        g3t2["GPU3<br/>F2 + B5"]
    end

    g0t1 -.send act.-> g1t1
    g1t1 -.send act.-> g2t1
    g2t1 -.send act.-> g3t1
    g3t1 -.send grad.-> g2t1
    g2t1 -.send grad.-> g1t1
    g1t1 -.send grad.-> g0t1

    t1 ==> t2

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class g0t1,g1t1,g2t1,g3t1,g0t2,g1t2,g2t2,g3t2 stage

每个 GPU 同时承担 forward 的某 micro-batch 和 backward 的另一 micro-batch,bubble ≈ 0。

每个 GPU 在任意时刻同时跑 forward 的某个 micro-batch 和 backward 的另一个 micro-batch,计算 + 通信通过精心安排时序完全重叠。

5.3 关键工程点

  1. 流水线 stage 数 = expert 通信周期的整数倍 —— 让 dispatch / gather 完美对齐
  2. 激活内存 :DualPipe 需要存的 activation 比 1F1B 多(两条流),但 V3 把 last layer 激活重计算(recompute)省回来
  3. PTX 手工 kernel:通信 + 计算重叠用普通 cuBLAS / NCCL 做不到精细时序,V3 直接写 PTX 控制 SM 调度(见 V3 paper Acknowledgements 提及)

5.4 vs Megatron interleaved 1F1B

维度 1F1B Interleaved 1F1B DualPipe
Bubble \((P-1)/M\) \((P-1)/(M \cdot V)\) ~0
激活内存 \(V \times\)
通信复杂度 简单
适用规模 小到中 极大(千卡 +)

\(V\) = virtual stage 数;\(M\) = micro-batch 数;\(P\) = pipeline stages。


六、跨节点通信优化

6.1 All-to-All 是瓶颈

MoE 训练最大的通信开销是expert 路由的 all-to-all:每个 token 要发到 8 个不同 expert(在不同 GPU 上)跑 FFN,然后把结果收回。standard NCCL all-to-all 在 IB 网络上 latency 高、吞吐低。

flowchart LR
    subgraph node1["Node 1 (NVLink 域)"]
        g0["GPU0<br/>tokens A,B,C"]
        g1["GPU1<br/>tokens D,E,F"]
    end
    subgraph node2["Node 2 (NVLink 域)"]
        g2["GPU2<br/>experts 0-63"]
        g3["GPU3<br/>experts 64-127"]
    end

    g0 -.NVLink<br/>900GB/s.-> g1
    g0 ==IB 400Gb/s==> g2
    g0 ==IB 400Gb/s==> g3
    g1 ==IB==> g2
    g1 ==IB==> g3

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class g0,g1,g2,g3 stage

V3 优化点:尽量让热门 expert 留在同一 NVLink 域;dispatch 用 FP8、gather 用 BF16;用 PTX 让 all-to-all 与 attention 重叠(部分 SM 跑通信,剩余 SM 跑算)。

6.2 V3 的优化

手写 PTX kernel:直接控制 SM 切片,让 all-to-all 通信占用一部分 SM,剩余 SM 同时跑 attention 计算。绕开 cuBLAS / NCCL 调度开销。

topology-aware routing:尽量把热门 expert 放在同一 NVLink 域内,跨 IB 流量减半(待核实,paper 提及但细节不足)。

FP8 通信:dispatch 数据用 FP8 传,gather 结果用 BF16 传(梯度精度需要)。

6.3 集群拓扑假设

V3 训练集群(公开声明):

  • 2048 H800 GPU(H100 中国阉割版,NVLink 受限)
  • NVLink 8-way 节点内
  • IB 200/400 GbE 节点间

H800 vs H100 的 NVLink 减半(300 GB/s vs 600 GB/s),DualPipe + 手写 PTX 的核心动机就是把这个瓶颈干掉。


七、Multi-Token Prediction (MTP)

V3 在每层 LM head 之外加了一个 MTP head

flowchart LR
    backbone["Backbone<br/>h_t (主隐藏态)"]
    norm1["RMSNorm(h_t)"]
    norm2["RMSNorm(Emb(x_{t+1}))"]
    cat["concat"]
    trm["TRM⁽¹⁾<br/>1 层 transformer block"]
    h1["h_t⁽¹⁾"]
    main_head["LM Head<br/>→ token_{t+1}"]
    mtp_head["MTP Head⁽¹⁾<br/>→ token_{t+2}"]
    loss["L = L_main + λ·L_MTP<br/>λ=0.3"]

    backbone --> main_head
    backbone --> norm1
    norm1 --> cat
    norm2 --> cat
    cat --> trm --> h1 --> mtp_head
    main_head --> loss
    mtp_head --> loss

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class backbone,norm1,norm2,cat,trm,h1,main_head,mtp_head,loss stage
原始 head:    h_t → token_t+1
MTP head:     h_t → token_t+2 (next-next token)

训练时同时预测 \(t+1\)\(t+2\),loss 加权求和。两个动机:

  1. 稠密化训练信号 —— 每个 token 提供 2 个预测目标,等效 batch 翻倍
  2. 天然 speculative decoding 信号 —— 推理时 MTP head 的输出可以做 1-step 投机(类似 EAGLE-style),加速 decode

V3 报告里 MTP 让 token efficiency 提升约 1.8×(上下文相关,待核实具体数)。

7.1 MTP 数学结构

V3 的 MTP 不是独立预测每个未来位置,而是串行依赖 —— 第 \(k\) 个 MTP head 的输入是第 \(k-1\) 个 head 的隐藏状态 + 当前 token embedding:

\[ \begin{aligned} h_t^{(0)} &= h_t \quad \text{(主 backbone 输出)} \\ h_t^{(k)} &= \text{TRM}^{(k)}\left( \text{RMSNorm}(h_t^{(k-1)}) \;\Vert\; \text{RMSNorm}(\text{Emb}(x_{t+k}))\right) \\ \hat{p}_t^{(k)} &= \text{LMHead}(h_t^{(k)}) \end{aligned} \]

其中 TRM 是单层小 transformer block(自带 attention + FFN),\(\Vert\) 是 concat。

V3 用 D=1(只看 t+1 一个未来位置)。Loss:

\[ \mathcal{L}_\text{MTP} = \frac{\lambda}{D} \sum_{k=1}^{D} \mathcal{L}_\text{CE}(\hat{p}^{(k)}, x^{(k)}) \]

\(\lambda\) 是 MTP loss 权重(V3 用 0.3,比 main loss 小)。

7.2 MTP PyTorch 参考实现

import torch
import torch.nn as nn

class MTPHead(nn.Module):
    """V3 single-depth MTP head.
    Input:
      h_main:        [B, S, D]  主 backbone 输出
      next_embed:    [B, S, D]  下一个 token 的 embedding (训练时已知)
    Output:
      logits:        [B, S, V]  对 t+1 位置的预测
    """
    def __init__(self, hidden_dim, vocab_size, lm_head=None):
        super().__init__()
        # RMSNorm 前置
        self.norm_h = nn.RMSNorm(hidden_dim)
        self.norm_e = nn.RMSNorm(hidden_dim)
        # 拼接后 dim = 2D,用 W_combined 压回 D
        self.W_combined = nn.Linear(2 * hidden_dim, hidden_dim)
        # 单层 transformer block (attention + FFN)
        self.block = TransformerBlock(hidden_dim)  # 复用主网络的 block
        # LM head 通常和主网络共享(weight tying,省 V*D 参数)
        self.lm_head = lm_head if lm_head else nn.Linear(hidden_dim, vocab_size, bias=False)

    def forward(self, h_main, next_embed):
        # 1. norm + concat
        x = torch.cat([self.norm_h(h_main), self.norm_e(next_embed)], dim=-1)
        # 2. project back to D
        x = self.W_combined(x)
        # 3. 走一层 transformer
        x = self.block(x)
        # 4. LM head
        return self.lm_head(x)


class TransformerWithMTP(nn.Module):
    def __init__(self, base_model, mtp_depth=1, mtp_loss_weight=0.3):
        super().__init__()
        self.base = base_model
        self.mtp_depth = mtp_depth
        self.lambda_mtp = mtp_loss_weight
        # 共享 lm_head + embed
        self.mtp_heads = nn.ModuleList([
            MTPHead(base_model.hidden_dim, base_model.vocab_size, base_model.lm_head)
            for _ in range(mtp_depth)
        ])

    def forward(self, ids):
        # base forward
        h = self.base.embed(ids)
        h = self.base.transformer(h)         # [B, S, D]
        main_logits = self.base.lm_head(h)   # [B, S, V]

        # MTP forward (只在训练时)
        mtp_logits_list = []
        if self.training:
            h_prev = h
            for k, head in enumerate(self.mtp_heads):
                # 第 k 个 head 看 t+k+1 位置的 embed
                shift = k + 1
                next_emb = self.base.embed(ids[:, shift:])  # [B, S-shift, D]
                # h_prev 也要对齐
                h_aligned = h_prev[:, :-shift] if shift > 0 else h_prev
                logits_k = head(h_aligned, next_emb)
                mtp_logits_list.append(logits_k)
                h_prev = h_aligned  # 串行:下一 head 用这个的输入(粗略)

        return main_logits, mtp_logits_list

    def compute_loss(self, ids, main_logits, mtp_logits_list):
        # main loss: predict t+1
        main_loss = F.cross_entropy(
            main_logits[:, :-1].reshape(-1, main_logits.size(-1)),
            ids[:, 1:].reshape(-1),
        )
        # MTP loss: predict t+k+1 for each head
        mtp_loss = 0
        for k, mtp_logits in enumerate(mtp_logits_list):
            shift = k + 2  # t+k+1, prediction shift = k+2
            mtp_loss += F.cross_entropy(
                mtp_logits.reshape(-1, mtp_logits.size(-1)),
                ids[:, shift:].reshape(-1),
            )
        mtp_loss = mtp_loss / len(mtp_logits_list)
        return main_loss + self.lambda_mtp * mtp_loss

7.3 推理时如何用 MTP head 做 speculative decoding

# Decode 阶段:主模型生成 t+1,MTP head 同时给 t+2 候选
def speculative_decode_step(model, prompt_ids, n_speculate=1):
    # 1. 主模型 forward 一步,拿到 h_t 和 logits_t
    h, main_logits = model.base.forward_step(prompt_ids)
    next_token = main_logits.argmax(-1)

    # 2. MTP head 用 h_t + next_token 的 embed 预测 t+2
    next_embed = model.base.embed(next_token)
    mtp_logits = model.mtp_heads[0](h, next_embed)
    speculated = mtp_logits.argmax(-1)  # 候选 t+2

    # 3. 用主模型 verify:拿 [next_token, speculated] 跑 forward,
    #    看主模型对 t+2 位置的预测是不是 speculated
    candidate_ids = torch.cat([prompt_ids, next_token.unsqueeze(0), speculated.unsqueeze(0)], -1)
    h_v, main_v = model.base.forward(candidate_ids)
    verified = main_v[:, -1].argmax(-1)

    if verified == speculated:
        return [next_token, speculated]  # 一步输出 2 token
    else:
        return [next_token, verified]    # MTP 错了,用主模型预测

V3 实测:MTP head 接受率 ~85-90%(V3 paper Section 4.1),等效 1.8× decode 加速。


八、Inference engineering

8.1 PD 分离

V3 推理时 prefill / decode 分离部署:

  • Prefill workers:H200 / 计算密集型集群,专跑长 prompt 的 attention
  • Decode workers:MoE 专用,bandwidth 敏感,跑 routed expert
  • KV transfer:prefill 产出的 KV cache 经 RDMA 传给 decode worker

8.2 Decode 优化

256 expert × top-8 在 decode 时是高度稀疏的:每 batch 不同 user 选不同 expert,expert utilization 可能只有 30-50%。V3 在 inference 阶段:

  • expert 静态调度:根据 prompt 类别(代码 / 数学 / 普通对话)pre-load 高频 expert
  • MoE batch packing:把同 expert 的 token 打包跑(类似 vLLM PagedAttention 但 expert 维度)

8.3 速度数字

公开 benchmark(V3, FP8, 128k context):

阶段 吞吐 备注
Prefill ~10k tokens/s/GPU H100
Decode (single user) ~60-80 tokens/s H100
Decode (batched) ~3000 tokens/s 总 待核实

九、Open Infra Index(2025-02 开源周)

DeepSeek 在 V3 / R1 之后,2025-02 用一周时间开源了整套训推 infra,是公开技术诚意最高的一批 release。Index 在 github.com/deepseek-ai/open-infra-index

flowchart LR
    subgraph storage["存储层"]
        fs["3FS<br/>6.6 TiB/s"]
        sp["Smallpond<br/>数据处理"]
    end
    subgraph compute["计算层"]
        gemm["DeepGEMM<br/>FP8 1350+ TFLOPS"]
        mla["FlashMLA<br/>3000 GB/s"]
    end
    subgraph comm["通信层"]
        eep["DeepEP<br/>FP8 dispatch"]
        eplb["EPLB<br/>负载均衡"]
    end
    subgraph orch["编排层"]
        dual["DualPipe<br/>bubble≈0"]
        prof["Profile-data<br/>真实 trace"]
    end

    storage --> compute --> comm --> orch

    classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class fs,sp,gemm,mla,eep,eplb,dual,prof stage

按层次叠起来就是一套可复现的 671B-MoE 训推栈,几乎没有"黑盒"——除了真实训练数据本身。

9.1 全清单

项目 类型 性能 用途
FlashMLA Hopper attention kernel 3000 GB/s mem / 580 TFLOPS BF16 MLA 推理加速(取代 vLLM 默认)
DeepEP EP 通信库 NVLink + RDMA 双 path,FP8 dispatch MoE all-to-all(解 V3 §六瓶颈)
DeepGEMM FP8 GEMM 库 1350+ TFLOPS,core ~300 行 V3/R1 训推全用
DualPipe Pipeline schedule 双向 + 通信掩盖 V3 § 五完整开源
EPLB Expert Parallel Load Balancer 减 EP 长尾 跟 §3.2 配合使用
Profile-Data Profiling 数据集 真实 V3/R1 trace 复现优化的参考
3FS 分布式文件系统 6.6 TiB/s read,180 节点 训练数据 + KVCache 共享存储
Smallpond 数据处理框架 基于 3FS DataLoader / dedup / mix

生产数据(V3/R1 在线服务):

  • 每 H800 节点:input 73.7K tokens/s,output 14.8K tokens/s
  • cost-profit margin: 545%(按理论最大 token throughput 计费 vs 实际成本)

9.2 FlashMLA(Day 1,attention kernel)

V3 inference 的关键瓶颈是 MLA 在 Hopper 上的 fused kernel。FlashMLA 是 DeepSeek 自家版的 FlashAttention 等价物,专门为 MLA 优化

  • Variable-length sequence:batch 内不同 seq 长度不需要 padding,省 KV cache
  • Paged KV cache:block size = 64,跟 vLLM PagedAttention 类似
  • BF16 / FP8 双精度
  • H800 实测:3000 GB/s 内存带宽 bound(≈ HBM3 理论上限 95%);580 TFLOPS BF16 计算 bound

工程上:vLLM / SGLang 在 0.6+ / 0.4+ 都已经 vendor 进 FlashMLA,是当前 V3 部署的事实标准。

9.3 DeepEP(Day 2,MoE 通信)

MoE 训推的 all-to-all 是最大瓶颈。DeepEP 是第一个开源的 EP 专用通信库

  • 训练版 kernel:高吞吐,掩盖在 backward 计算下
  • 推理版 kernel:低延迟,专为 decode 优化
  • NVLink + RDMA dual-path:单机内走 NVLink,跨机走 RDMA,不用 NCCL 通用 path
  • Native FP8 dispatch:dispatch 阶段 token activations 直接 FP8,省 2× 带宽
  • Flexible GPU resource control:可以指定多少 SM 给通信,不与计算抢 SM

实测(DeepSeek 自报):相比 NCCL 默认 all-to-all,DeepEP MoE 通信延迟降 30-50%。

9.4 DeepGEMM(Day 3,FP8 矩阵乘)

V3 训练的 90% 算力在 GEMM 上。DeepGEMM 是 V3/R1 production 用的 FP8 GEMM 库:

  • Core 仅 ~300 行 CUDA 代码 —— 极简
  • JIT 编译:根据矩阵 shape 自动生成 kernel
  • Dense + MoE GEMM 双支持(MoE GEMM 是不规则 shape,单独优化)
  • 1350+ TFLOPS on H800(接近 NVIDIA 自家 cuBLAS 上限)

为什么短:DeepSeek 的策略是只在 V3 实际用到的 shape 上极致优化,不做通用库。300 行覆盖 ~95% 训练时间。

参考实现:github.com/deepseek-ai/DeepGEMM。读 source code 是工程艺术品。

9.5 EPLB(Day 4,专家负载均衡)

§3.2 讲的 auxiliary-loss-free 是算法层面的 LB;EPLB 是通信层面的:

  • Hierarchical balancing:跨节点 vs 节点内不同策略
  • Replicated experts:高频 expert 复制到多 GPU,降低单 GPU 拥塞
  • Dynamic placement:根据 traffic profile 重新分布 expert 到 GPU

实操上 EPLB 跟 DeepEP 配合用:DeepEP 负责 token dispatch,EPLB 负责 expert placement。

9.6 3FS(Day 5,存储层)

训练超大模型的隐性瓶颈:数据 IO + checkpoint IO + KV cache 共享。3FS 把这三个场景统一:

  • 聚合 read 6.6 TiB/s(180 节点)—— 数据 loader 不再阻塞
  • GraySort 3.66 TiB/min —— 数据预处理加速
  • 40+ GiB/s per-client KVCache lookup —— inference 时跨实例共享 KV cache
  • Disaggregated 架构:compute / storage 解耦,扩展性好

为啥不用现成的 Lustre / GPFS:训推混合 workload + RDMA full-utilization 需要专设计。3FS 跟 NVIDIA Magnum IO / Storage Fabric 是同类工具但更贴 LLM 场景。

9.7 Smallpond(Day 5,数据处理)

基于 3FS 的数据处理框架,做 LLM pretrain data 的 dedup / mix / shuffle / shard。比 Spark 更窄但更快。

9.8 复现优先级建议

不必全套 adopt。最有价值的 3 个:

  1. FlashMLA:直接 vendor 进 vLLM/SGLang,零成本免费提速
  2. DeepGEMM:H100/H800 训练栈插入,自家 cuBLAS / TE 替换
  3. DeepEP:MoE 训练 > 100B 才有显著收益

TileKernels(疑似命名):DeepSeek 没单独 release 一个叫 "TileKernels" 的项目,社区有时这样称呼可能是指 TileLang(Microsoft)—— DeepGEMM 部分思路与 TileLang 类似(tile-level scaling + JIT)。可以读 DeepGEMM source code 学。


十、跟 Kimi K2 / Qwen-3 / LLaMA 的横向比较

V3 是 2024.12 发布,2025 一年内开源 MoE 生态的新参考:

设计选择 DeepSeek-V3 Kimi K2 Qwen3-MoE 235B 设计取舍
总参数 671B 1.04T 235B 越大越能 scale,但 serve 成本
激活参数 37B 32B 22B 推理成本主因
Attention MLA MLA GQA MLA 长 context 优势,GQA 简单
路由 256 ex × top-8 384 × top-8 128 × top-8 越细粒度越泛化
Optimizer AdamW + ZeRO-1 MuonClip AdamW MuonClip 是 K2 创新
FP8 训练 ✅ 全栈 部分(Hybrid) V3 验证可行性后通用
Pipeline DualPipe 待核实 Megatron 1F1B DualPipe H800 优化
数据 14.8T 公开声明 15.5T 36T 数据量级正在指数增

K2 用 MLA + 更激进的 384 expert + MuonClip 优化器 —— 在 V3 设计基础上每个维度都加重;Qwen3 选保守但够用的 GQA + 1F1B,赌训练稳定。


十一、复现度自评("是否可实现")

组件 公开材料 复现难度 替代方案
MLA 论文 + FlashMLA 中(需写 kernel) GQA 已经够用
Aux-loss-free LB 描述充分 易(10 行 EMA) aux loss 老办法
FP8 + per-tile scaling 描述够 中(需自写 Triton) TorchAO FP8 / TE 现成
DualPipe 高层描述 难(需 PTX 调度) Megatron 1F1B 退而求其次
MTP 描述充分 不做也能训
跨节点 all-to-all PTX 框架描述 极难 NCCL + 接受 bubble

实操建议(如果要 mini-DeepSeek):

  1. 先复现 MLA + aux-loss-free LB(这两个改动小、收益大)
  2. FP8 用 TorchAO 起步,per-tile scaling 第二阶段做
  3. DualPipe / PTX kernel 是规模化优化,small-scale 训练 1F1B 够了
  4. MTP 几行代码加上,免费提速 1.5-2×

参考文献

  1. DeepSeek-AI. DeepSeek-V3 Technical Report. 2024. arXiv:2412.19437
  2. DeepSeek-AI. DeepSeek-V2: A Strong, Economical, and Efficient MoE. 2024. arXiv:2405.04434
  3. DeepSeek-AI. DeepSeek-R1. 2025. arXiv:2501.12948
  4. Shao et al. DeepSeekMath: GRPO. 2024. arXiv:2402.03300
  5. NVIDIA. Transformer Engine Documentation(FP8 训练参考实现)
  6. FlashMLA — DeepSeek 公开的 MLA Hopper kernel
  7. Micikevicius et al. FP8 Formats for Deep Learning. 2022. arXiv:2209.05433

上级 · DeepSeek