Files
2026-07-13 13:37:02 +08:00

969 lines
82 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MoE (Mixture-of-Experts) 面试 Cheat Sheet</title>
<meta name="generator" content="ARIS render-html (academic, v1)">
<meta name="aris:source-path" content="docs/tutorials/moe_tutorial.md">
<meta name="aris:source-sha256" content="e860589270ed447981c868a7e0f0ba51ac8917a8908580825403af8205baf3b1">
<meta name="aris:generated-at" content="2026-05-19 10:10 UTC">
<!-- MathJax 3 -->
<script>
window.MathJax = {
tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], displayMath: [['$$', '$$'], ['\\[', '\\]']], processEscapes: true },
options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] }
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
<!-- highlight.js -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-light.min.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
<script>document.addEventListener('DOMContentLoaded', () => hljs.highlightAll());</script>
<style>
:root {
--bg: #fdfcf7;
--bg-soft: #f4f1ea;
--bg-code: #f8f5ec;
--ink: #1a1a1a;
--ink-soft: #4a4a4a;
--ink-muted: #6b6b6b;
--primary: #1a4a8c;
--primary-soft: #2d6cb8;
--accent: #b8390e;
--warn: #b45309;
--warn-bg: #fef3c7;
--info-bg: #dbeafe;
--good-bg: #d1fae5;
--good: #065f46;
--bad-bg: #fee2e2;
--bad: #991b1b;
--border: #d6d0c0;
--border-soft: #e8e3d5;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: "Source Serif Pro", "Source Serif 4", "Crimson Pro", "Georgia", "Songti SC", "STSong", serif;
line-height: 1.65;
color: var(--ink);
background: var(--bg);
margin: 0;
padding: 0;
font-size: 16px;
}
.layout {
max-width: 1280px;
margin: 0 auto;
display: grid;
grid-template-columns: 260px 1fr;
gap: 48px;
padding: 40px 32px;
}
nav.toc {
position: sticky;
top: 24px;
align-self: start;
font-size: 13px;
max-height: calc(100vh - 48px);
overflow-y: auto;
border-right: 1px solid var(--border-soft);
padding-right: 16px;
}
nav.toc h3 {
margin: 0 0 12px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-muted);
font-weight: 600;
}
nav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: toc; }
nav.toc ol li { margin: 5px 0; counter-increment: toc; }
nav.toc ol li::before { content: counter(toc) ". "; color: var(--ink-muted); margin-right: 4px; }
nav.toc a {
color: var(--ink-soft);
text-decoration: none;
border-bottom: 1px dotted transparent;
}
nav.toc a:hover { color: var(--primary); border-bottom-color: var(--primary); }
nav.toc ul { list-style: none; padding-left: 14px; margin: 3px 0; font-size: 12px; }
nav.toc ul li::before { content: "→ "; color: var(--border); }
main { min-width: 0; }
header.hero {
border-bottom: 3px double var(--primary);
padding-bottom: 24px;
margin-bottom: 32px;
}
header.hero .eyebrow {
color: var(--accent);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.12em;
font-weight: 600;
margin-bottom: 8px;
}
header.hero h1 {
font-size: 32px;
line-height: 1.2;
margin: 0 0 12px;
color: var(--ink);
font-weight: 700;
letter-spacing: -0.01em;
}
header.hero .subtitle {
font-size: 16px;
color: var(--ink-soft);
margin: 0 0 8px;
font-style: italic;
}
header.hero .byline {
font-size: 14px;
color: var(--ink-soft);
margin: 0 0 20px;
}
header.hero .byline strong {
color: var(--ink);
font-weight: 600;
}
header.hero .meta {
display: flex;
gap: 20px;
flex-wrap: wrap;
font-size: 12px;
color: var(--ink-muted);
border-top: 1px solid var(--border-soft);
padding-top: 14px;
}
header.hero .meta span strong { color: var(--ink-soft); }
header.hero .meta code {
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
font-size: 11px;
background: var(--bg-soft);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--border-soft);
}
h2 {
font-size: 24px;
margin: 44px 0 14px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border);
color: var(--ink);
font-weight: 700;
}
h2 .num { color: var(--primary); font-weight: 600; margin-right: 8px; }
h3 { font-size: 19px; margin: 28px 0 10px; color: var(--primary); font-weight: 600; }
h4 { font-size: 16px; margin: 20px 0 8px; color: var(--ink); font-weight: 600; }
p { margin: 10px 0; }
ul, ol { padding-left: 22px; margin: 10px 0; }
ul li, ol li { margin: 4px 0; }
ul li::marker { color: var(--primary); }
strong { color: var(--accent); font-weight: 600; }
em { color: var(--ink-soft); }
a { color: var(--primary); }
a:hover { color: var(--accent); }
code:not(.hljs) {
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
font-size: 0.86em;
background: var(--bg-code);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--border-soft);
color: var(--accent);
}
pre {
background: #fafaf6;
border: 1px solid var(--border);
border-left: 4px solid var(--primary);
padding: 0;
overflow-x: auto;
border-radius: 4px;
margin: 14px 0;
}
pre code, pre code.hljs {
background: transparent !important;
display: block;
padding: 14px 18px !important;
font-size: 13px;
line-height: 1.55;
font-family: "JetBrains Mono", "SF Mono", "Menlo", monospace;
color: var(--ink);
}
pre.diagram {
background: #f9f6ed;
border-left: 4px solid var(--accent);
font-size: 12.5px;
line-height: 1.4;
}
.callout {
margin: 16px 0;
padding: 12px 16px;
border-radius: 4px;
border-left: 4px solid;
font-size: 15px;
}
.callout-title {
font-weight: 600;
margin-bottom: 6px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.callout-info { background: var(--info-bg); border-left-color: var(--primary); }
.callout-info .callout-title { color: var(--primary); }
.callout-warn { background: var(--warn-bg); border-left-color: var(--warn); }
.callout-warn .callout-title { color: var(--warn); }
.callout-good { background: var(--good-bg); border-left-color: var(--good); }
.callout-good .callout-title { color: var(--good); }
.callout-bad { background: var(--bad-bg); border-left-color: var(--bad); }
.callout-bad .callout-title { color: var(--bad); }
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 14px;
border: 1px solid var(--border);
border-radius: 4px;
overflow: hidden;
}
thead { background: var(--primary); color: white; }
th, td {
text-align: left;
padding: 9px 12px;
border-bottom: 1px solid var(--border-soft);
vertical-align: top;
}
th { font-weight: 600; font-size: 13px; letter-spacing: 0.02em; }
tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) { background: var(--bg-soft); }
details.qa, details {
background: white;
border: 1px solid var(--border-soft);
border-radius: 6px;
margin: 10px 0;
padding: 0;
}
details summary {
cursor: pointer;
padding: 10px 14px;
font-weight: 600;
font-size: 14px;
color: var(--primary);
list-style: none;
user-select: none;
}
details summary::-webkit-details-marker { display: none; }
details summary::before {
content: "▸ ";
margin-right: 4px;
display: inline-block;
transition: transform 0.15s;
}
details[open] summary::before { transform: rotate(90deg); }
details[open] summary { border-bottom: 1px solid var(--border-soft); }
details > :not(summary) { padding: 10px 14px; }
details p:first-of-type { margin-top: 8px; }
mjx-container[display="true"] { margin: 12px 0 !important; }
footer.aris-footer {
margin-top: 60px;
padding-top: 20px;
border-top: 1px solid var(--border);
font-size: 12px;
color: var(--ink-muted);
}
footer.aris-footer a { color: var(--ink-muted); border-bottom: 1px dotted var(--border); }
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; gap: 20px; padding: 20px 16px; }
nav.toc {
position: static;
max-height: none;
border-right: none;
border-bottom: 1px solid var(--border-soft);
padding-right: 0;
padding-bottom: 14px;
}
header.hero h1 { font-size: 24px; }
h2 { font-size: 20px; }
}
@media print {
nav.toc { display: none; }
.layout { grid-template-columns: 1fr; padding: 0; }
body { background: white; }
header.hero { border-bottom-color: var(--ink); }
}
</style>
</head>
<body>
<div class="layout">
<nav class="toc">
<h3>Contents</h3>
<ol>
<li><a href="#0-tldr-cheat-sheet">§0 TL;DR Cheat Sheet</a>
</li>
<li><a href="#1-直觉稀疏激活--用参数容量换计算量">§1 直觉:稀疏激活 = 用&quot;参数容量&quot;&quot;计算量&quot;</a>
</li>
<li><a href="#2-路由从-top-k-token-choice-到-expert-choice-到-loss-free">§2 路由:从 Top-k Token-Choice 到 Expert Choice 到 Loss-Free</a>
<ul>
<li><a href="#21-token-choice-top-kgshard--switch--mixtral--deepseek">2.1 Token-Choice Top-kGShard / Switch / Mixtral / DeepSeek</a></li>
<li><a href="#22-auxiliary-load-balancing-lossswitch-公式必背">2.2 Auxiliary Load Balancing LossSwitch 公式,必背)</a></li>
<li><a href="#23-expert-choice-routingzhou-et-al-2022neurips">2.3 Expert Choice RoutingZhou et al. 2022NeurIPS</a></li>
<li><a href="#24-auxiliary-loss-free-balancedeepseek-v3-招牌必考">2.4 Auxiliary-Loss-Free BalanceDeepSeek-V3 招牌,必考)</a></li>
<li><a href="#25-noisy-top-kshazeer-2017-原始方案已较少用">2.5 Noisy Top-kShazeer 2017 原始方案,已较少用)</a></li>
</ul>
</li>
<li><a href="#3-实现细节核心-60-行-pytorch">§3 实现细节:核心 60 行 PyTorch</a>
<ul>
<li><a href="#31-aux-loss-free-bias-updatedeepseek-v3-风格">3.1 Aux-Loss-Free Bias UpdateDeepSeek-V3 风格)</a></li>
<li><a href="#32-fine-grained--shared-expert-layerdeepseekmoe-风格">3.2 Fine-Grained + Shared Expert LayerDeepSeekMoE 风格)</a></li>
</ul>
</li>
<li><a href="#4-容量token-droppingrouting-collapse">§4 容量、Token Dropping、Routing Collapse</a>
<ul>
<li><a href="#41-capacity-factor必背工程细节">4.1 Capacity Factor(必背工程细节)</a></li>
<li><a href="#42-token-dropping-vs-residual-bypass">4.2 Token Dropping vs Residual Bypass</a></li>
<li><a href="#43-routing-collapse-经典-bug">4.3 Routing Collapse(💣 经典 bug</a></li>
<li><a href="#44-top-k-的硬选择如何反传梯度">4.4 Top-k 的&quot;&quot;选择如何反传梯度?</a></li>
</ul>
</li>
<li><a href="#5-经典模型谱系从-shazeer-到-deepseek-v3">§5 经典模型谱系:从 Shazeer 到 DeepSeek-V3</a>
<ul>
<li><a href="#51-主流开源-moe-横向对比2026-面试常考">5.1 主流开源 MoE 横向对比(2026 面试常考)</a></li>
</ul>
</li>
<li><a href="#6-deepseek-v3-全景必背重点">§6 DeepSeek-V3 全景(必背重点)</a>
<ul>
<li><a href="#61-架构层面">6.1 架构层面</a></li>
<li><a href="#62-auxiliary-loss-free-balance详见-24">6.2 Auxiliary-Loss-Free Balance(详见 §2.4</a></li>
<li><a href="#63-node-limited-routing">6.3 Node-Limited Routing</a></li>
<li><a href="#64-systemdualpipe--deepep">6.4 SystemDualPipe + DeepEP</a></li>
</ul>
</li>
<li><a href="#7-复杂度显存推理算账">§7 复杂度、显存、推理算账</a>
<ul>
<li><a href="#71-训练时-flops--参数对比">7.1 训练时 FLOPs / 参数对比</a></li>
<li><a href="#72-推理显存必考">7.2 推理显存(必考!)</a></li>
<li><a href="#73-kv-cache">7.3 KV Cache</a></li>
</ul>
</li>
<li><a href="#8-ep--tp--pp--dpmoe-并行的-4-维交织">§8 EP / TP / PP / DPMoE 并行的 4 维交织</a>
<ul>
<li><a href="#81-expert-parallelism">8.1 Expert Parallelism</a></li>
<li><a href="#82-ep--dp--tp--pp">8.2 EP × DP × TP × PP</a></li>
<li><a href="#83-ep-通信量算账">8.3 EP 通信量算账</a></li>
</ul>
</li>
<li><a href="#9-与相关方法对比">§9 与相关方法对比</a>
<ul>
<li><a href="#91-moe-vs-dense同-flops--同参数">9.1 MoE vs Dense(同 FLOPs / 同参数)</a></li>
<li><a href="#92-moe-vs-mixture-of-depths-mod">9.2 MoE vs Mixture-of-Depths (MoD)</a></li>
<li><a href="#93-moe-微调esft-和它的同类">9.3 MoE 微调:ESFT 和它的同类</a></li>
<li><a href="#94-moe-vs-mqagqa">9.4 MoE vs MQA/GQA</a></li>
</ul>
</li>
<li><a href="#10-25-高频面试题">§10 25 高频面试题</a>
<ul>
<li><a href="#l1必会题任何-ml-工程岗都会问">L1必会题(任何 ML 工程岗都会问)</a></li>
<li><a href="#l2进阶题research-oriented-岗位">L2进阶题(research-oriented 岗位)</a></li>
<li><a href="#l3顶级-lab--research-方向">L3顶级 lab / Research 方向</a></li>
</ul>
</li>
<li><a href="#a-附录参考文献清单按时间">§A 附录:参考文献清单(按时间)</a>
</li>
</ol>
</nav>
<main>
<header class="hero">
<div class="eyebrow">Interview Prep · Mixture-of-Experts Architectures</div>
<h1>MoE (Mixture-of-Experts) 面试 Cheat Sheet</h1>
<p class="subtitle">DeepSeek-V3 / Mixtral / Llama 4 + 25 高频题(L1 必会 · L2 进阶 · L3 顶级 lab)</p>
<p class="byline">By <strong>Ruofeng Yang (杨若峰), Shanghai Jiao Tong University</strong></p>
<div class="meta">
<span><strong>Source:</strong> <code>docs/tutorials/moe_tutorial.md</code></span>
<span><strong>SHA256:</strong> <code>e860589270ed</code></span>
<span><strong>Rendered:</strong> 2026-05-19 10:10 UTC</span>
</div>
</header>
<h2 id="0-tldr-cheat-sheet">§0 TL;DR Cheat Sheet</h2>
<div class="callout callout-info"><div class="callout-title">8 句话搞定 MoE</div><p>一页拿下 2026 秋招核心要点(详见后文 §1–§9 推导)。</p></div>
<ol><li><strong>核心思想</strong>:把单个 FFN 换成 $N$ 个 expert + 一个 router,每个 token 只走 $k \ll N$ 个 expert<strong>总参数 ↑、激活参数不变</strong>sparse activation)。计算量约等于 $k/N$ × dense,但内存 / 显存按总参数计。</li><li><strong>路由公式</strong>Token-Choice top-k):$g_i(x) = \text{softmax}(W_g x)_i$,选 $\mathcal{T}_k(x) = \text{TopK}_i\, g_i(x)$,输出 $y = \sum_{i \in \mathcal{T}_k(x)} g_i(x) \cdot E_i(x)$。Gate 概率作为 <strong>soft 权重</strong>乘到 expert 输出上,反传时让 router 可微。</li><li><strong>历史脉络(必背 5 篇)</strong>Shazeer 2017(首个深度学习里可工作的 MoE 层)→ GShard 2020top-2 + capacity factor + all-to-all)→ Switch Transformer 2021top-1 极简,aux loss + load balance)→ Mixtral 8x7B/8x22B 2024(首个开源主流 MoEtop-2)→ DeepSeek-V3 2024671B/37B<strong>aux-loss-free</strong>fine-grained + 1 shared)。</li><li><strong>DeepSeek 路线(2026 面试热点)</strong>DeepSeekMoE 2024 提出 <strong>fine-grained experts</strong>(拆细,$mN$ 个小 expert 选 $mK$ 个)+ <strong>shared experts</strong>(少数 expert 给所有 token,吸收通用知识)。V2 用 MLA + DeepSeekMoEV3 把路由专家做到 256 + 1 shared<strong>取消 aux loss</strong>,改用 expert bias 在线更新。</li><li><strong>Aux-loss-free balance</strong>:在 router score 上加每 expert 的偏置 $b_i$<strong>只用于 top-k 选择</strong>,不进梯度(也不进最终 gate 权重);每个 step 按"实际 load - 期望 load"反向更新 $b_i$(多负载 expert 减偏置)。<strong>不破坏 sparse gradient,不引入干扰梯度</strong></li><li><strong>容量与丢 token</strong>:每个 expert 的容量 $C = \lceil \alpha \cdot T \cdot k / N \rceil$$\alpha$ 是 capacity factor,常用 1.0-1.25)。超过容量的 token 走 <strong>residual bypass</strong>(直接跳过 expert,原始残差通过),或被 drop。Switch Transformer 论文里这是核心工程细节。</li><li><strong>并行</strong>MoE 几乎必须用 <strong>Expert Parallelism (EP)</strong>——把不同 expert 放到不同 GPUtoken 路由后做 <strong>all-to-all</strong>dispatch)→ expert 算 → all-to-allcombine)。DeepEP 与 DualPipe 是 DeepSeek-V3 在 H800 上把 EP 通信和计算 overlap 的两大工程武器。</li><li><strong>常见 bug</strong>:①routing collapse(某些 expert 被全部 token 选、其他 expert 饿死);②fp16 下 router logit overflow;③推理时 <strong>all experts 必须全部加载到显存</strong>——只是激活参数少,<strong>显存依然按总参数算</strong>;④小 batch 时 EP 通信占满,吞吐反而比 dense 差。</li></ol>
<h2 id="1-直觉稀疏激活--用参数容量换计算量">§1 直觉:稀疏激活 = 用"参数容量"换"计算量"</h2>
<p>Dense FFN 长这样:每个 token 都过同一个 $D \times 4D \to 4D \times D$ 大矩阵,参数全部参与计算。</p>
<p>MoE 把 FFN 换成 $N$ 个独立 expert(每个本质仍是 FFN),但每个 token 由 router 决定<strong>只走 $k$ 个</strong></p>
<pre><code>
Token x [B, L, D]
Router W_g ∈ R^{D × N} (gate)
scores g(x) ∈ R^N
TopK + softmax-renormalize
┌─────┬─────┼─────┬─────┐
↓ ↓ ↓ ↓ ↓
E_1 E_2 ... E_{N-1} E_N (每个 expert = FFN)
│ │ │ │
└─ × g_1 ─ ... ─ × g_N ─┘ (只有 k 个 g_i ≠ 0)
Sum → output y [B, L, D]</code></pre>
<p>为什么这是好主意?三个层次的回答:</p>
<ul><li><strong>容量论</strong>:模型容量主要来自参数量,泛化主要来自数据 × 容量。Sparse activation 允许把总参数推到上千 B,而每 token 计算仍像 30B dense——<strong>参数容量 ≫ 计算量</strong></li><li><strong>专精论</strong>:不同 expert 在训练中天然分工——code / math / 多语言 / 通用语义。Switch Transformer 给过 t-SNE 可视化,DeepSeekMoE 用 fine-grained 进一步加剧这种专精。</li><li><strong>推理效率</strong>:固定 active 参数 $\approx$ 固定 FLOPs,可与 dense 同台比,但参数容量大很多——这是 Mixtral 8x7B 用 13B active 干过 LLaMA-2 70B 的根本原因。</li></ul>
<div class="callout callout-warn"><div class="callout-title">MoE 不是免费午餐</div><p>sparse activation 减的是 <strong>FLOPs</strong><strong>不减显存</strong>(推理时所有 expert 必须常驻 GPU);EP 训练时 all-to-all 通信能轻易吃掉一半时间;负载不均会让多数 GPU 空转。下面 §3-§7 是这些工程现实的全部展开。</p></div>
<h2 id="2-路由从-top-k-token-choice-到-expert-choice-到-loss-free">§2 路由:从 Top-k Token-Choice 到 Expert Choice 到 Loss-Free</h2>
<h3 id="21-token-choice-top-kgshard--switch--mixtral--deepseek">2.1 Token-Choice Top-kGShard / Switch / Mixtral / DeepSeek</h3>
<p>最经典也最常考。Gate 计算每个 (token, expert) 对的 affinitytoken 选 top-k expert</p>
<p>$$\boxed{\;s(x) = W_g x \in \mathbb{R}^N, \quad g(x) = \text{softmax}(s(x)), \quad \mathcal{T}_k(x) = \text{TopK}_i\, g_i(x)\;}$$</p>
<p>输出(routing weight 重新归一化版本,Mixtral / DeepSeek 默认这种):</p>
<p>$$y = \sum_{i \in \mathcal{T}_k(x)} \tilde{g}_i(x) \cdot E_i(x), \quad \tilde{g}_i(x) = \frac{g_i(x)}{\sum_{j \in \mathcal{T}_k(x)} g_j(x)}$$</p>
<div class="callout callout-info"><div class="callout-title">重归一 vs 直用 softmax 概率</div><p>两种工程实现都见过:Switch 直接用 $g_i$top-1 时 $g_i$ 直接乘);Mixtral / DeepSeek 在 top-k 上再 renormalize 让权重和 = 1。renormalize 让输出尺度稳定,不依赖 softmax 在 top-k 之外的"漏出"概率质量。</p></div>
<p><strong>几个变体的 $k$</strong></p>
<table><thead><tr><th>模型</th><th style="text-align:center">$k$</th><th>备注</th></tr></thead><tbody><tr><td>Switch Transformer</td><td style="text-align:center">1</td><td>极简,被证明也能 work</td></tr><tr><td>GShard / Mixtral / Qwen3-MoE</td><td style="text-align:center">2</td><td>主流;2 个 expert 足够"集成"</td></tr><tr><td>DeepSeek-V2 / V3</td><td style="text-align:center">6 / 8routed+ 1 shared</td><td>配合 fine-grained 拆细</td></tr><tr><td>Llama 4 Scout</td><td style="text-align:center">1(仅 1 个 routed+ 1 shared</td><td>16 experts1 shared</td></tr><tr><td>Llama 4 Maverick</td><td style="text-align:center">1 routed + 1 shared</td><td>128 experts</td></tr></tbody></table>
<h3 id="22-auxiliary-load-balancing-lossswitch-公式必背">2.2 Auxiliary Load Balancing LossSwitch 公式,必背)</h3>
<p>问题:纯 top-k 没有任何机制让 router <strong>均衡使用</strong>所有 expert——很容易 collapse 成"只用 1-2 个 expert"。</p>
<p>Switch Transformer 引入 differentiable load balancing loss</p>
<p>$$\boxed{\;\mathcal{L}_\text{aux} = \alpha \cdot N \sum_{i=1}^{N} f_i \cdot P_i\;}$$</p>
<p>其中</p>
<ul><li>$f_i = \dfrac{1}{T} \sum_{t=1}^{T} \mathbb{1}\{\arg\max_j s_j(x_t) = i\}$ ——分配给 expert $i$ 的 token 比例(不可微,sample-level 估计)</li><li>$P_i = \dfrac{1}{T} \sum_{t=1}^{T} g_i(x_t)$ ——expert $i$ 的平均 gate 概率(可微,softmax 输出)</li><li>$\alpha$ 是 loss 权重(常用 $10^{-2}$</li><li>$N$ 是 expert 数量;$T$ 是 batch 中 token 数</li></ul>
<p><strong>关键点 / 易混淆</strong></p>
<ol><li>$f_i$ 给"实际频率"$P_i$ 给"梯度通路"。乘起来 $\sum_i f_i P_i$ 当两者都集中在同一组 expert 时最大;均匀分布时最小($= 1/N$,乘以 $N$ 后 = 1)。</li><li>这是 <strong>encourage 均匀分布</strong>,不是硬约束。极端 collapse 会被惩罚,但日常小幅不均不会被强力惩罚。</li><li>Switch top-1 公式直接如此;top-k 时把 $f_i$ 改成"top-k 命中频率"(更准确写法见 GShard 论文)。</li><li><strong>$\alpha$ 太大会干扰主任务梯度</strong>——这是 DeepSeek 改 aux-loss-free 的根本动机。</li></ol>
<h3 id="23-expert-choice-routingzhou-et-al-2022neurips">2.3 Expert Choice RoutingZhou et al. 2022NeurIPS</h3>
<p>反向思维:不让 token 挑 expert,让 <strong>expert 挑 token</strong>。每个 expert 根据 capacity 选 top-$M$ 个 token$M = (T \cdot k) / N$(每个 expert 平均分到 token 数)。</p>
<p>$$s(x) \in \mathbb{R}^{T \times N}, \quad \mathcal{T}_M^{(i)} = \text{TopM}_{t}\, s_{t,i}, \quad y_t = \sum_{i: t \in \mathcal{T}_M^{(i)}} g_{t,i} \cdot E_i(x_t)$$</p>
<p><strong>优点</strong></p>
<ul><li><strong>天然平衡</strong>:每个 expert 严格选 $M$ 个 token,不需要 aux loss</li><li><strong>不丢 token</strong>:理论上不会有"溢出"capacity 是 hard 给定)</li></ul>
<p><strong>缺点 / 限制</strong></p>
<ul><li><strong>不是 causal</strong>:第 $t$ 个 token 的归属依赖 $T$ 个 token 全部的 score——decoder 推理时<strong>无法 token-by-token 生成</strong>,需要 batch-level 全局视角(autoregressive 不友好)</li><li>每个 token 实际激活的 expert 数 <strong>不固定</strong>(可能没被选 / 被多个选)</li><li>主要在 encoder(如 T5、BERT-style)或 vision 用</li></ul>
<div class="callout callout-warn"><div class="callout-title">面试陷阱</div><p>被问"Expert Choice 解决了 routing collapse,那 Mixtral / DeepSeek 为什么还用 token-choice" 答案:autoregressive decoder + 每 token 严格走固定 $k$ expert 的需求让 expert-choice 用不了;DeepSeek-V3 改用 aux-loss-free 在 <strong>token-choice 框架内</strong>做平衡。</p></div>
<h3 id="24-auxiliary-loss-free-balancedeepseek-v3-招牌必考">2.4 Auxiliary-Loss-Free BalanceDeepSeek-V3 招牌,必考)</h3>
<p>DeepSeekWang et al., arXiv 2408.15664, 2024)提出的方案,被 V3 全面采纳。核心一句话:<strong>给每个 expert 加一个偏置项 $b_i$,只用于 top-k 选择,不进梯度</strong></p>
<p>具体地,原 score 是 $s_i(x)$,做 top-k 选择时用 <strong>biased score</strong></p>
<p>$$\tilde{s}_i(x) = s_i(x) + b_i$$</p>
<p>$$\mathcal{T}_k(x) = \text{TopK}_i\, \tilde{s}_i(x)$$</p>
<p><strong>最终 gating weight</strong> 仍用原始 $s_i$ 的 softmax(或 sigmoid 后 renormalizeDeepSeek-V3 用 sigmoid):</p>
<p>$$g_i(x) = \frac{\sigma(s_i(x))}{\sum_{j \in \mathcal{T}_k(x)} \sigma(s_j(x))}, \quad i \in \mathcal{T}_k(x)$$</p>
<p>$b_i$ 的更新规则(每 step 一次,<strong>out-of-graph</strong>,非梯度更新):</p>
<p>$$b_i \leftarrow b_i + u \cdot \text{sign}(\bar{c}_i - c_i)$$</p>
<ul><li>$c_i$ = 本 step 中 expert $i$ 实际接到的 token 数</li><li>$\bar{c}_i = T k / N$ = 均匀分布下每个 expert 应接到的期望 token 数</li><li>若 $c_i > \bar{c}_i$(过载)→ $b_i$ 减小 → 下次更难被选</li><li>若 $c_i < \bar{c}_i$(欠载)→ $b_i$ 增大 → 下次更易被选</li><li>$u$ 是固定步长(V3 报告里很小,量级 $10^{-3}$</li></ul>
<div class="callout callout-good"><div class="callout-title">为什么这是非平凡的胜利</div><p>Aux loss 的问题是它把"平衡"作为 <strong>梯度信号</strong> 注入 router → 干扰主任务梯度,模型为了平衡可能牺牲质量。Loss-free 把平衡作为 <strong>离线控制信号</strong>PID 风味的 sign update),完全不进入计算图——router 仍然只为"语言建模 loss"求梯度,但选 expert 时被 $b_i$ 推着走。<strong>不破坏 sparse routing 的语义、不污染主梯度、不引入需要 tune 的 $\alpha$</strong></p></div>
<h3 id="25-noisy-top-kshazeer-2017-原始方案已较少用">2.5 Noisy Top-kShazeer 2017 原始方案,已较少用)</h3>
<p>Shazeer 等 2017 论文里在 score 上加可学习的 Gaussian noise</p>
<p>$$s_i(x) = (W_g x)_i + \text{StandardNormal}() \cdot \text{Softplus}((W_\text{noise} x)_i)$$</p>
<p>噪声为了让 top-k 选择"软化"、避免 collapse。后被 GShard / Switch 用更明确的 load balance loss 取代——但概念("top-k 不可微 → 需要某种 stochastic 软化")仍有教学价值。</p>
<h2 id="3-实现细节核心-60-行-pytorch">§3 实现细节:核心 60 行 PyTorch</h2>
<p>最小可跑实现:token-choice top-k MoE 层 + Switch aux loss。不考虑 EP,单 GPU 教学用。</p>
<pre><code class="language-python">import torch
import torch.nn as nn
import torch.nn.functional as F
class Expert(nn.Module):
&quot;&quot;&quot;每个 expert 就是一个标准 FFNSwiGLU 风格也可,这里用 GELU 简化)&quot;&quot;&quot;
def __init__(self, d_model, d_ff):
super().__init__()
self.w1 = nn.Linear(d_model, d_ff, bias=False)
self.w2 = nn.Linear(d_ff, d_model, bias=False)
def forward(self, x):
return self.w2(F.gelu(self.w1(x)))
class MoELayer(nn.Module):
&quot;&quot;&quot;
Token-choice top-k MoE layer.
输入 x: [B, L, D]
输出 y: [B, L, D], aux_loss: scalar (Switch load-balance loss)
&quot;&quot;&quot;
def __init__(self, d_model, d_ff, num_experts, top_k=2, capacity_factor=1.25, aux_loss_coef=0.01):
super().__init__()
assert top_k &lt;= num_experts
self.d_model = d_model
self.num_experts = num_experts
self.top_k = top_k
self.capacity_factor = capacity_factor
self.aux_loss_coef = aux_loss_coef
# Router: D -&gt; N, no bias (Switch / Mixtral 都不用 bias)
self.router = nn.Linear(d_model, num_experts, bias=False)
# N 个 expert
self.experts = nn.ModuleList([Expert(d_model, d_ff) for _ in range(num_experts)])
def forward(self, x):
B, L, D = x.shape
T = B * L
x_flat = x.view(T, D) # [T, D]
# ----- 1. Router 计算 score + top-k -----
logits = self.router(x_flat) # [T, N]
probs = F.softmax(logits, dim=-1) # [T, N]
top_probs, top_idx = probs.topk(self.top_k, dim=-1) # [T, k], [T, k]
# Mixtral / DeepSeek 风格:在 top-k 上重归一
top_probs = top_probs / (top_probs.sum(dim=-1, keepdim=True) + 1e-9) # [T, k]
# ----- 2. Aux load-balance loss (Switch 公式) -----
# f_i = 分给 expert i 的 token 比例 (每 token 算 top_k 次命中, 与 top_k 一致)
with torch.no_grad():
one_hot = F.one_hot(top_idx, num_classes=self.num_experts).float() # [T, k, N]
f = one_hot.sum(dim=(0, 1)) / (T * self.top_k) # [N]
P = probs.mean(dim=0) # [N], 可微
aux_loss = self.aux_loss_coef * self.num_experts * (f * P).sum()
# ----- 3. Capacity 计算 + token dispatch -----
# 与正文公式一致:ceil(capacity_factor * T * top_k / N_E),避免小 batch 截断为 0
import math as _math
capacity = max(1, _math.ceil(self.capacity_factor * T * self.top_k / self.num_experts))
# 把 [T, k] 展开成 [T*k] 的 (token_idx, expert_idx) pair
flat_expert = top_idx.view(-1) # [T*k]
flat_weight = top_probs.view(-1) # [T*k]
flat_token = torch.arange(T, device=x.device).repeat_interleave(self.top_k) # [T*k]
# ----- 4. Expert forward (capacity-aware) -----
y_flat = torch.zeros_like(x_flat) # [T, D]
for e in range(self.num_experts):
mask_e = (flat_expert == e) # [T*k]
if mask_e.sum() == 0:
continue
tok_e = flat_token[mask_e] # 要给 expert e 处理的 token 全局 idx
w_e = flat_weight[mask_e] # 对应 gate weight
# 容量截断:超过 capacity 的 token drop (Switch residual bypass)
if tok_e.numel() &gt; capacity:
tok_e = tok_e[:capacity]
w_e = w_e[:capacity]
inp_e = x_flat[tok_e] # [≤cap, D]
out_e = self.experts[e](inp_e) # [≤cap, D]
# 按 token idx scatter-add (一个 token 可能被多个 expert 命中,所以是 add)
y_flat.index_add_(0, tok_e, out_e * w_e.unsqueeze(-1))
return y_flat.view(B, L, D), aux_loss</code></pre>
<div class="callout callout-warn"><div class="callout-title">生产实现关键差异</div><p>教学版用 Python <code>for e in range(num_experts)</code> 串行调每个 expert<strong>生产 (Megatron / vLLM) 都是 grouped GEMM 或 fused kernel</strong>,把所有 expert 的计算 batch 到一起,否则 N 大时 GPU 大部分时间在 launch kernel。MoE 真正难的是这一层。</p></div>
<h3 id="31-aux-loss-free-bias-updatedeepseek-v3-风格">3.1 Aux-Loss-Free Bias UpdateDeepSeek-V3 风格)</h3>
<p>把上面 §3 中的 aux loss 删掉,改用 expert bias 更新:</p>
<pre><code class="language-python">class MoEAuxFree(nn.Module):
def __init__(self, d_model, d_ff, num_experts, top_k=2, bias_step=1e-3):
super().__init__()
self.num_experts = num_experts
self.top_k = top_k
self.bias_step = bias_step
self.router = nn.Linear(d_model, num_experts, bias=False)
# b_i: 非参数, 不进梯度图 (buffer)
self.register_buffer(&#x27;expert_bias&#x27;, torch.zeros(num_experts))
self.experts = nn.ModuleList([Expert(d_model, d_ff) for _ in range(num_experts)])
def forward(self, x):
B, L, D = x.shape
T = B * L
x_flat = x.view(T, D)
# Router score (V3 用 sigmoid 不是 softmax, 但 top-k 逻辑一样)
logits = self.router(x_flat) # [T, N]
# ★ Top-k 用 biased score, 但 gating weight 用原始 score
biased_logits = logits + self.expert_bias.unsqueeze(0)
_, top_idx = biased_logits.topk(self.top_k, dim=-1) # [T, k]
# 取出原始 score 对应位置, sigmoid 后 renormalize
gate_raw = torch.sigmoid(logits).gather(-1, top_idx) # [T, k]
top_weights = gate_raw / (gate_raw.sum(dim=-1, keepdim=True) + 1e-9)
# ----- 在线更新 expert_bias (无梯度, 论文 sign 更新) -----
if self.training:
with torch.no_grad():
one_hot = F.one_hot(top_idx, num_classes=self.num_experts).float() # [T, k, N]
c = one_hot.sum(dim=(0, 1)) # [N], 实际 load
c_bar = T * self.top_k / self.num_experts # 期望 load (scalar)
# 过载 -&gt; 减偏置; 欠载 -&gt; 加偏置
self.expert_bias -= self.bias_step * torch.sign(c - c_bar)
# Dispatch + expert forward (与 §3 同, 这里省略)
y_flat = torch.zeros_like(x_flat)
flat_expert = top_idx.view(-1)
flat_weight = top_weights.view(-1)
flat_token = torch.arange(T, device=x.device).repeat_interleave(self.top_k)
for e in range(self.num_experts):
mask = (flat_expert == e)
if mask.sum() == 0: continue
tok_e = flat_token[mask]
w_e = flat_weight[mask]
out_e = self.experts[e](x_flat[tok_e]) * w_e.unsqueeze(-1)
y_flat.index_add_(0, tok_e, out_e)
return y_flat.view(B, L, D)</code></pre>
<p>注意:</p>
<ol><li><code>expert_bias</code> 是 buffer 不是 parameter<strong>不进梯度图、不被 optimizer step</strong>——只被 §forward 末尾的"sign 更新"修改。</li><li><strong>biased_logits 只用于选 top-kgating weight 来自原始 logits</strong>(这是 V3 论文反复强调的点)。如果连 gating weight 都用 biased,相当于把控制信号污染到主梯度路径,aux-loss-free 的好处就丢了。</li><li>V3 实际是 sigmoid + 256 experts + 9 选(8 routed + 1 shared 在 §6 讲),这里简化成 softmax + top-k 演示原理。</li></ol>
<h3 id="32-fine-grained--shared-expert-layerdeepseekmoe-风格">3.2 Fine-Grained + Shared Expert LayerDeepSeekMoE 风格)</h3>
<pre><code class="language-python">class DeepSeekMoELayer(nn.Module):
&quot;&quot;&quot;
Fine-grained + shared expert:
- 总共 N 个 routed expert (拆细, 每个比标准 expert 小 m 倍)
- 加 N_shared 个 shared expert (所有 token 都走)
- 每个 token 路由到 K 个 routed expert (K = m × baseline_top_k)
论文 (Dai et al. 2024, arXiv 2401.06066): 把 N 个原始 expert 拆成 mN 个,
每个 expert 的 d_ff 缩到 d_ff/m, 路由数从 K 提到 mK, 总计算 / 参数量不变,
但组合数 C(mN, mK) &gt;&gt; C(N, K) -&gt; 专业化粒度提升.
&quot;&quot;&quot;
def __init__(self, d_model, d_ff_per_expert, num_routed_experts, num_shared_experts, top_k):
super().__init__()
self.num_routed = num_routed_experts
self.num_shared = num_shared_experts
self.top_k = top_k
self.router = nn.Linear(d_model, num_routed_experts, bias=False)
# 拆细后的 routed experts (每个比 baseline 小 m 倍)
self.routed_experts = nn.ModuleList(
[Expert(d_model, d_ff_per_expert) for _ in range(num_routed_experts)]
)
# Shared experts: 永远激活, 不进 router
self.shared_experts = nn.ModuleList(
[Expert(d_model, d_ff_per_expert) for _ in range(num_shared_experts)]
)
def forward(self, x):
# 1. Shared expert: 直接累加, 不路由
shared_out = sum(e(x) for e in self.shared_experts)
# 2. Routed top-k (这里简化省略 aux loss / capacity)
B, L, D = x.shape
T = B * L
x_flat = x.view(T, D)
logits = self.router(x_flat)
probs = F.softmax(logits, dim=-1)
top_p, top_i = probs.topk(self.top_k, dim=-1)
top_p = top_p / (top_p.sum(dim=-1, keepdim=True) + 1e-9)
routed_out = torch.zeros_like(x_flat)
flat_e = top_i.view(-1)
flat_w = top_p.view(-1)
flat_t = torch.arange(T, device=x.device).repeat_interleave(self.top_k)
for e in range(self.num_routed):
mask = (flat_e == e)
if mask.sum() == 0: continue
tok = flat_t[mask]
w = flat_w[mask]
routed_out.index_add_(0, tok, self.routed_experts[e](x_flat[tok]) * w.unsqueeze(-1))
return shared_out + routed_out.view(B, L, D)</code></pre>
<div class="callout callout-info"><div class="callout-title">Fine-grained 的&quot;组合爆炸&quot;直觉</div><p>标准 8 选 2 → 28 种组合。拆细成 64 选 16(每个 expert 缩到 1/8,路由数 ×8),组合 $\binom{64}{16} \approx 4.9 \times 10^{14}$。同样的参数/算力预算下,<strong>每个 token 可表达的"专家组合身份"指数级丰富</strong>,是 DeepSeekMoE 的核心论点之一。</p></div>
<h2 id="4-容量token-droppingrouting-collapse">§4 容量、Token Dropping、Routing Collapse</h2>
<h3 id="41-capacity-factor必背工程细节">4.1 Capacity Factor(必背工程细节)</h3>
<p>每个 expert 不可能接受任意多 token——GPU memory + EP 通信都需要静态 buffer。所以预先给每个 expert 设容量 $C$:</p>
<p>$$\boxed{\;C = \left\lceil \alpha \cdot \frac{T \cdot k}{N} \right\rceil\;}$$</p>
<ul><li>$T$batch 中 token 总数</li><li>$k$:每 token 走的 expert 数</li><li>$N$expert 总数</li><li>$\alpha$capacity factor(常用 1.0、1.25、2.0</li></ul>
<p><strong>$\alpha = 1$</strong>:均匀分布下刚好够用,但稍微不均就 drop;<strong>$\alpha = 1.25$</strong>Switch / GShard 默认):留 25% 余量缓冲不均匀;<strong>$\alpha > 2$</strong>:基本不 drop 但浪费显存。</p>
<h3 id="42-token-dropping-vs-residual-bypass">4.2 Token Dropping vs Residual Bypass</h3>
<p>当某 expert 已收满 $C$ 个 token,新来的 token 怎么办?<strong>两条路线</strong></p>
<table><thead><tr><th>方案</th><th>行为</th><th>谁用</th></tr></thead><tbody><tr><td><strong>Token Drop</strong></td><td>直接丢,输出 0</td><td>GShard 早期;通常 ↓ 训练稳定性</td></tr><tr><td><strong>Residual Bypass</strong></td><td>Expert 输出置 0,但 <strong>residual $x$ 自然通过 layer norm + skip connection 透传</strong></td><td>Switch Transformer / Mixtral / DeepSeek 默认</td></tr></tbody></table>
<p>Residual bypass 的好处:被 drop 的 token <strong>不是变 0</strong>——它在残差通路上仍然带着自己的表示,只是这层"没被任何 expert 加工",相当于退化成 identity layer。整个网络仍然能学到东西。</p>
<div class="callout callout-warn"><div class="callout-title">必考易踩坑</div><p>面试常问"MoE 为什么不 catastrophic 地坏掉"。答案:residual bypass + 多层堆叠下,<strong>单层 drop 不致命</strong>——下一层 router 仍然能为该 token 选到合适 expert。</p></div>
<h3 id="43-routing-collapse-经典-bug">4.3 Routing Collapse(💣 经典 bug</h3>
<p>如果 router 没有 balance 机制,训练初期某些 expert 偶然被多选 → 它们参数被更新更多 → 下次更可能被选 → 强者愈强 → <strong>少数 expert 通吃,多数饿死</strong>。这就是 routing collapse。</p>
<p>诊断信号:</p>
<ul><li>监控 expert load(每 step 每 expert 接到的 token 数)→ 极不均</li><li>监控 aux loss → 大或在涨</li><li>Validation loss 平台期早出现</li></ul>
<p>修复手段:</p>
<ol><li><strong>Aux loss</strong>Switch 公式,§2.2</li><li><strong>Expert dropout</strong> / <strong>Z-loss</strong>(对 router logits 加 entropy regularizer</li><li><strong>Expert Choice</strong>(§2.3,硬约束每 expert 选固定数 token</li><li><strong>Aux-loss-free bias</strong>(§2.4DeepSeek-V3</li><li><strong>Capacity factor 适度调大</strong>(让 collapse 触发 drop,间接惩罚集中)</li></ol>
<h3 id="44-top-k-的硬选择如何反传梯度">4.4 Top-k 的"硬"选择如何反传梯度?</h3>
<p>经典问题:$\text{TopK}$ 是不可微的离散操作。</p>
<p>答案:<strong>top-k 只决定走哪条路径(discrete),不参与梯度</strong>softmax 权重 $g_i(x)$ 进入最终输出 $y = \sum_i g_i E_i(x)$,对 $g_i$ 求导是连续的(standard softmax 链式法则)。所以 router 的 $W_g$ 是可微的(注意 softmax / renorm 的耦合带来 <strong>完整 Jacobian</strong>,不只是对角项):</p>
<p>记 $s = W_g x$logits),$p = \text{softmax}(s)$top-k 选 $\mathcal{T}_k(x)$ 后做 renormalize $g_i = p_i / Z$$Z = \sum_{j \in \mathcal{T}_k} p_j$):</p>
<p>$$\frac{\partial g_i}{\partial s_l} = \mathbf{1}[i \in \mathcal{T}_k]\cdot\left(\frac{\partial p_i / \partial s_l}{Z} - \frac{p_i}{Z^2}\cdot \mathbf{1}[l \in \mathcal{T}_k] \cdot \frac{\partial Z}{\partial s_l}\right)$$</p>
<p>其中 $\partial p_i/\partial s_l = p_i(\delta_{il} - p_l)$ 是 softmax Jacobian(含交叉项 $-p_i p_l$,不只是对角 $p_i(1-p_i)$)。汇总:</p>
<p>$$\frac{\partial \mathcal{L}}{\partial W_g} = \sum_{i \in \mathcal{T}_k(x)}\sum_{l} \frac{\partial \mathcal{L}}{\partial g_i} \cdot \frac{\partial g_i}{\partial s_l} \cdot \frac{\partial s_l}{\partial W_g}$$</p>
<p>对 $l \notin \mathcal{T}_k$(被 top-k 排除),$\partial g_i/\partial s_l$ 通过 $p_l$ 仍非零(softmax 的全局耦合),但这一项在 forward 不影响输出、在 backward 的贡献仅经 $\partial p_i / \partial s_l = -p_i p_l$ 间接进入。<strong>实操上</strong>,只有 top-k 内的 expert 才能拿到主导梯度信号;top-k 外的 expert 长期得不到 "我应该被选" 的训练 push——这是 chicken-and-egg:不被选 → 不更新 → 永远不被选。Aux loss / bias 就是为了打破这个循环。</p>
<h2 id="5-经典模型谱系从-shazeer-到-deepseek-v3">§5 经典模型谱系:从 Shazeer 到 DeepSeek-V3</h2>
<p>按时间和"工程门槛"排成 7 行(必背年表 + 关键贡献):</p>
<table><thead><tr><th>年份</th><th>论文 / 模型</th><th>核心贡献</th><th>arXiv</th></tr></thead><tbody><tr><td>2017</td><td><strong>Shazeer et al., Outrageously Large NN</strong></td><td>首个深度 MoE 层(LSTM 旁挂),noisy top-k, load-balance loss 雏形</td><td>1701.06538</td></tr><tr><td>2020</td><td><strong>GShard (Lepikhin et al.)</strong></td><td>Top-2 routing, capacity factor, <strong>automatic sharding / all-to-all</strong>600B 多语言 NMT</td><td>2006.16668</td></tr><tr><td>2021</td><td><strong>Switch Transformer (Fedus, Zoph, Shazeer)</strong></td><td>Top-1 极简,aux loss 标准公式,1.6T 参数 T5-MoE</td><td>2101.03961</td></tr><tr><td>2022</td><td><strong>Expert Choice (Zhou et al., NeurIPS)</strong></td><td>Expert-choice routing,硬平衡,无需 aux loss(但 autoregressive 不友好)</td><td>2202.09368</td></tr><tr><td>2024.1</td><td><strong>DeepSeekMoE (Dai et al.)</strong></td><td><strong>Fine-grained experts + shared experts</strong>16B / 145B 验证</td><td>2401.06066</td></tr><tr><td>2024.1</td><td><strong>Mixtral 8x7B (Jiang et al.)</strong></td><td>首个开源主流 MoE8 experts top-213B active 干过 LLaMA-2 70B</td><td>2401.04088</td></tr><tr><td>2024.4</td><td><strong>Mixtral 8x22B</strong></td><td>141B 总 / 39B 激活,64k context</td><td>(Mistral blog)</td></tr><tr><td>2024.5</td><td><strong>DeepSeek-V2 (DeepSeek-AI)</strong></td><td>236B / 21B<strong>MLA + DeepSeekMoE</strong>KV cache 减 93.3%</td><td>2405.04434</td></tr><tr><td>2024.8</td><td><strong>Loss-Free Balance (Wang et al.)</strong></td><td>Aux-loss-free 偏置更新方案</td><td>2408.15664</td></tr><tr><td>2024.12</td><td><strong>DeepSeek-V3</strong></td><td><strong>671B / 37B</strong>256 routed + 1 shared expertaux-loss-free, MTP, DualPipe</td><td>2412.19437</td></tr><tr><td>2025.4</td><td><strong>Llama 4 (Meta)</strong></td><td>Scout (17B/16E)、Maverick (17B/128E)、Behemoth (288B/16E)</td><td>(Meta blog)</td></tr><tr><td>2025.5</td><td><strong>Qwen3 MoE</strong></td><td>Qwen3-235B-A22B22B 激活)</td><td>2505.09388</td></tr></tbody></table>
<h3 id="51-主流开源-moe-横向对比2026-面试常考">5.1 主流开源 MoE 横向对比(2026 面试常考)</h3>
<table><thead><tr><th>模型</th><th style="text-align:center">总参数</th><th style="text-align:center">激活 / token</th><th style="text-align:center">Routed experts</th><th style="text-align:center">Top-k</th><th style="text-align:center">Shared expert</th><th style="text-align:center">Aux 方案</th><th style="text-align:center">Released</th></tr></thead><tbody><tr><td>Mixtral 8x7B</td><td style="text-align:center">46.7B</td><td style="text-align:center">12.9B</td><td style="text-align:center">8</td><td style="text-align:center">2</td><td style="text-align:center">0</td><td style="text-align:center">aux loss</td><td style="text-align:center">2023.12</td></tr><tr><td>Mixtral 8x22B</td><td style="text-align:center">141B</td><td style="text-align:center">39B</td><td style="text-align:center">8</td><td style="text-align:center">2</td><td style="text-align:center">0</td><td style="text-align:center">aux loss</td><td style="text-align:center">2024.4</td></tr><tr><td>Qwen2-57B-A14B</td><td style="text-align:center">57B</td><td style="text-align:center">14B</td><td style="text-align:center">64</td><td style="text-align:center">8</td><td style="text-align:center">0</td><td style="text-align:center">aux loss</td><td style="text-align:center">2024.6</td></tr><tr><td>DeepSeek-V2</td><td style="text-align:center">236B</td><td style="text-align:center">21B</td><td style="text-align:center">160</td><td style="text-align:center">6</td><td style="text-align:center">2</td><td style="text-align:center">aux loss + device-level</td><td style="text-align:center">2024.5</td></tr><tr><td><strong>DeepSeek-V3</strong></td><td style="text-align:center"><strong>671B</strong></td><td style="text-align:center"><strong>37B</strong></td><td style="text-align:center"><strong>256</strong></td><td style="text-align:center"><strong>8</strong></td><td style="text-align:center"><strong>1</strong></td><td style="text-align:center"><strong>Aux-loss-free + sequence-aux</strong></td><td style="text-align:center">2024.12</td></tr><tr><td>Llama 4 Scout</td><td style="text-align:center">109B</td><td style="text-align:center">17B</td><td style="text-align:center">16</td><td style="text-align:center">1</td><td style="text-align:center">1</td><td style="text-align:center">undisclosed</td><td style="text-align:center">2025.4</td></tr><tr><td>Llama 4 Maverick</td><td style="text-align:center">400B</td><td style="text-align:center">17B</td><td style="text-align:center">128</td><td style="text-align:center">1</td><td style="text-align:center">1</td><td style="text-align:center">undisclosed</td><td style="text-align:center">2025.4</td></tr><tr><td>Qwen3-235B-A22B</td><td style="text-align:center">235B</td><td style="text-align:center">22B</td><td style="text-align:center">128</td><td style="text-align:center">8</td><td style="text-align:center">0</td><td style="text-align:center">(loss-free 变体)</td><td style="text-align:center">2025.5</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">读这张表的姿势</div><p>注意 "总参 / 激活" 比例(Mixtral $\approx 4\times$V3 $\approx 18\times$Maverick $\approx 24\times$)。这个比例越高,"用稀疏换容量"的杠杆越大,但也越依赖 routing 质量。V3 把这个比值推到极致并 still work,靠的就是 fine-grained + shared + loss-free。</p></div>
<h2 id="6-deepseek-v3-全景必背重点">§6 DeepSeek-V3 全景(必背重点)</h2>
<p>DeepSeek-V3DeepSeek-AI, arXiv 2412.19437, 2024.12)是 2026 面试季最高频的 MoE 系统。把它的设计点拆开:</p>
<h3 id="61-架构层面">6.1 架构层面</h3>
<pre class="diagram"><code> DeepSeek-V3 MoE Layer
────────────────────────
x [B, L, D=7168]
├──────► Shared Expert (1 个, d_ff ≈ 2048) → shared_out
│ (永远激活, 不进 router)
└──────► Router (D → 256)
├ score s_i(x) + b_i (bias 仅用于 top-k 选择)
Top-8 (256 选 8)
│ 实际 gating weight: sigmoid(s_i) / Σ sigmoid(s_j)
┌────────┬────────┬────────┬────────┐
↓ ↓ ↓ ↓ ↓
E_a E_b E_c ... E_h (8 个 routed expert)
│ │ │ │ │
└─ × g ─┴─ × g ─┴─ × g ─┴─ × g ─┘
routed_out + shared_out → y</code></pre>
<p>关键数字:</p>
<ul><li><strong>Total</strong> 671B parameters</li><li><strong>Active per token</strong> 37B parameters</li><li><strong>MoE layer</strong>256 routed experts + 1 shared,每 token 选 <strong>9 个 expert</strong>8 routed + 1 shared</li><li><strong>Attention</strong><strong>MLA</strong>Multi-head Latent Attention),把 KV 压缩到低秩 latent → KV cache 降 ~93%</li><li><strong>MTP</strong>Multi-Token Prediction)作为辅助 objectiveinference 时可做 speculative decode</li></ul>
<h3 id="62-auxiliary-loss-free-balance详见-24">6.2 Auxiliary-Loss-Free Balance(详见 §2.4</h3>
<p>V3 论文里写了两件事的组合:</p>
<ol><li><strong>Per-expert bias</strong> $b_i$(核心)—— §2.4 描述</li><li><strong>Sequence-wise auxiliary loss</strong>(兜底)—— 极小权重的辅助 loss,用于防止单条序列内部极端不均(不是跨 batch 的传统 aux loss</li></ol>
<p>第二点经常被忽略:V3 不是"完全无 aux loss",而是<strong>主要靠 bias 做平衡,留一个 sequence-level 的极小 aux 兜底</strong>。面试时如果说"V3 完全没有 aux loss"是不准确的。</p>
<h3 id="63-node-limited-routing">6.3 Node-Limited Routing</h3>
<p>V3 训练时跑 64-way EP 跨 8 个 node。Naïve top-8 routing 可能让一个 token 的 8 个 expert 散布到所有 8 个 node → 严重 all-to-all 通信。V3 加了硬约束:<strong>每个 token 最多路由到 4 个 node</strong>,且每 node 最多选 3 个 expert。这是 algorithm × system 联合优化。</p>
<h3 id="64-systemdualpipe--deepep">6.4 SystemDualPipe + DeepEP</h3>
<ul><li><strong>DualPipe</strong>github: deepseek-ai/DualPipe):双向 pipeline parallelism,把 forward 和 backward 的 compute / comm 重叠到几乎 0 bubble。</li><li><strong>DeepEP</strong>github: deepseek-ai/DeepEP):专门为 MoE all-to-all 优化的通信库,支持 FP8 dispatch + asymmetric inter/intra-node bandwidth。</li></ul>
<p>这两个开源库是 V3 在 2048 张 H800 上把 MoE 训练 cost 压到 $5.6M USD 的关键工程基础。</p>
<h2 id="7-复杂度显存推理算账">§7 复杂度、显存、推理算账</h2>
<h3 id="71-训练时-flops--参数对比">7.1 训练时 FLOPs / 参数对比</h3>
<p>设 dense baseline 用 $D$ hidden, $d_\text{ff} = 4D$, 层数 $L$, token 数 $T$。FFN 部分 FLOPsdense):</p>
<p>$$\text{FLOPs}_\text{FFN}^\text{dense} \approx 2 \cdot T \cdot L \cdot D \cdot d_\text{ff} \cdot 2 = 16 T L D^2$$</p>
<p>MoE:把 FFN 换成 $N$ expert, 每 expert $d_\text{ff}$, 每 token 走 $k$。<strong>计算量只算被激活的 expert</strong></p>
<p>$$\text{FLOPs}_\text{FFN}^\text{MoE} \approx 16 T L D^2 \cdot \frac{k}{N} \cdot \frac{N \cdot d_\text{ff}^\text{expert}}{4D}$$</p>
<p>如果 $d_\text{ff}^\text{expert} = 4D$"每个 expert 等于一个完整 FFN"Mixtral 风格),$\text{FLOPs} \propto k$<strong>与 $N$ 无关</strong>(典型 Mixtral 8x7B $k=2$ → 计算 $\approx$ 2 个 FFN)。</p>
<p>参数量则与 $N$ 线性增长:</p>
<p>$$\text{Params}_\text{FFN}^\text{MoE} = N \cdot 8D^2 + D \cdot N \;(\text{router}) \approx 8 N D^2$$</p>
<p><strong>结论</strong>MoE 的"参数容量 $N$ 倍, 计算量 $k$ 倍"——这正是为什么 Mixtral 8x7B 总参 47B / active 13B 比同 active 13B 的 dense 表现强。</p>
<h3 id="72-推理显存必考">7.2 推理显存(必考!)</h3>
<p><strong>面试常见陷阱问题:MoE 推理时显存是按"active 参数"还是"总参数"算?</strong></p>
<p>答案:<strong>几乎按总参数</strong>——所有 expert 必须<strong>常驻 GPU</strong>,因为下一个 token 可能路由到任何 expert。不能"按需 load 一个 expert",因为 PCIe / NVLink 加载延迟远高于 expert 计算延迟。</p>
<p>具体地,单 GPU 推理 671B modelFP8 / FP16):</p>
<table><thead><tr><th>模型</th><th style="text-align:center">存储 (FP16)</th><th style="text-align:center">存储 (FP8)</th><th style="text-align:center">单卡能塞下吗</th></tr></thead><tbody><tr><td>LLaMA-2 70B (dense)</td><td style="text-align:center">140 GB</td><td style="text-align:center">70 GB</td><td style="text-align:center">H100 80G 单卡 FP8 可</td></tr><tr><td>Mixtral 8x22B (141B)</td><td style="text-align:center">282 GB</td><td style="text-align:center">141 GB</td><td style="text-align:center">单卡不行,2× H100 / 8×A100</td></tr><tr><td>DeepSeek-V3 (671B)</td><td style="text-align:center">1342 GB</td><td style="text-align:center">671 GB</td><td style="text-align:center"><strong>8× H100 80G = 640 GB 还差一点</strong>;实际部署需 <strong>2 节点 16× H100 80G</strong><strong>8× H200 141G</strong>(≈1128 GB)才能塞下 FP8 权重 + KV cache</td></tr></tbody></table>
<p><strong>带宽的好处</strong>:每 token 只取 <code>active_param / total_param</code> 比例的权重做 matmul → <strong>memory bandwidth bottleneck 缓解</strong>(推理多数是 memory-bound)。671B / 37B → 每 token 只读 ~5% 权重——这是 V3 在 H800 上 inference throughput 与 dense 30B 同台的根源。</p>
<h3 id="73-kv-cache">7.3 KV Cache</h3>
<p>MoE 不直接影响 KV cache 大小——KV 与 attention 相关,与 FFN sparsity 无关。但 V2/V3 同时引入 <strong>MLA</strong> 把 KV 压缩到低秩 latent,所以 V3 的 KV cache 是 LLaMA-3 70B 的 ~5%。<strong>面试要区分"MoE 减的是 FFN 显存(FP8 OK),MLA 减的是 KV 显存"</strong>——是两条互相独立的优化线。</p>
<h2 id="8-ep--tp--pp--dpmoe-并行的-4-维交织">§8 EP / TP / PP / DPMoE 并行的 4 维交织</h2>
<p>Dense LLM 训练常用 DP + TP + PP(数据 / 张量 / 流水)三维并行。MoE 必须加第 4 维:<strong>Expert Parallelism (EP)</strong></p>
<h3 id="81-expert-parallelism">8.1 Expert Parallelism</h3>
<p>把 $N$ 个 expert 切到不同 GPU 上(如 64 EP, 每 GPU 4 个 expert)。Forward 流程:</p>
<pre><code>
Step 1. Token x [local batch] -&gt; Router -&gt; top-k expert IDs
Step 2. ★ all-to-all dispatch:
按 expert ID 把 token 发到对应 GPU
(每 GPU 收到的 token 数 ≤ capacity)
Step 3. 本地 expert forward
Step 4. ★ all-to-all combine:
把 expert 输出按原始 token ID 发回起点 GPU
Step 5. Gate 加权求和 -&gt; 下一层</code></pre>
<p>两次 <strong>all-to-all</strong> 是 MoE 的灵魂也是痛点:通信量 $\propto T \cdot k \cdot D$,可以轻易吃掉一半时间。</p>
<h3 id="82-ep--dp--tp--pp">8.2 EP × DP × TP × PP</h3>
<table><thead><tr><th>维度</th><th>拆什么</th><th>通信</th></tr></thead><tbody><tr><td><strong>DP</strong></td><td>Batch</td><td>all-reducegradient</td></tr><tr><td><strong>TP</strong></td><td>Hidden / heads(每层内部)</td><td>all-reducein-layer</td></tr><tr><td><strong>PP</strong></td><td>层(不同 stage</td><td>point-to-pointcross-stage</td></tr><tr><td><strong>EP</strong></td><td>Expert</td><td>all-to-all(每 MoE layer 2 次)</td></tr></tbody></table>
<p>实践组合(DeepSeek-V3 训练):</p>
<ul><li>16-way PP × 64-way EP × ZeRO-1 DP</li><li>单 H800 cluster: 8 GPU per node × 256 nodes = 2048 GPU</li><li>DualPipe 让 PP bubble 接近 0DeepEP 把 EP all-to-all 与 expert compute overlap</li></ul>
<h3 id="83-ep-通信量算账">8.3 EP 通信量算账</h3>
<p>设每 token 平均 $k$ expert, $D$ hidden, $G$ EP group size, 每 step token 数 $T$</p>
<p>$$\text{all-to-all volume per layer} \approx 2 \cdot T \cdot k \cdot D \cdot \frac{(G-1)}{G}$$</p>
<p>$\times 2$ 是 dispatch + combine 各一次。$(G-1)/G$ 是因为 1/G 的 token 本地命中无需通信。)</p>
<p>对 V3$T \sim$ few-M tokens/step, $k=8$, $D=7168$, $G=64$ → 单 layer 通信量 ~ TB 级。这就是为什么 DeepEP / NVLink-aware kernel 这么关键。</p>
<h2 id="9-与相关方法对比">§9 与相关方法对比</h2>
<h3 id="91-moe-vs-dense同-flops--同参数">9.1 MoE vs Dense(同 FLOPs / 同参数)</h3>
<table><thead><tr><th>角度</th><th>Dense</th><th>MoE</th></tr></thead><tbody><tr><td>同 active 参数</td><td>表现更稳</td><td>容量上限低</td></tr><tr><td>同 total 参数</td><td>计算成本高</td><td><strong>MoE 算力高效,容量大</strong></td></tr><tr><td>训练稳定性</td><td></td><td>中(routing collapse / EP comm 不稳)</td></tr><tr><td>推理显存</td><td>按 total ≈ 按 active</td><td><strong>按 total(必须全 expert 常驻)</strong></td></tr><tr><td>推理 latency</td><td>memory-bound</td><td><strong>memory bandwidth 优势</strong>active 部分小)</td></tr><tr><td>部署难度</td><td></td><td>高(需 EP / 多卡)</td></tr><tr><td>微调</td><td>简单</td><td><strong>复杂</strong>(见 §9.3 ESFT</td></tr></tbody></table>
<h3 id="92-moe-vs-mixture-of-depths-mod">9.2 MoE vs Mixture-of-Depths (MoD)</h3>
<p>MoDRaposo et al. 2024)思路不同:<strong>每层选 top-k 个 token 走完整层,其他 token skip 整层</strong>(沿 depth 方向稀疏化)。对比 MoE 沿 width 方向稀疏化(每个 token 选部分 expert)。</p>
<table><thead><tr><th>维度</th><th>MoE</th><th>MoD</th></tr></thead><tbody><tr><td>稀疏方向</td><td>width (FFN expert)</td><td>depth (whole layer)</td></tr><tr><td>每 token 计算</td><td>固定(top-k expert</td><td>动态(每层是否走)</td></tr><tr><td>Router 决策</td><td>per-layer per-token</td><td>per-layer 选 top-k token</td></tr><tr><td>训练稳定性</td><td></td><td>较新,工程化弱</td></tr></tbody></table>
<p>不互斥——理论上可以同时用,2025 后的几个工作(如 Llama 4 的细节、Qwen3 的某些实验)开始混用。</p>
<h3 id="93-moe-微调esft-和它的同类">9.3 MoE 微调:ESFT 和它的同类</h3>
<p>标准 LoRA 在 MoE 上有几个坑:</p>
<ul><li>LoRA 加到 $W_q/W_k/W_v$ 上是 attention 层级,<strong>不动 expert</strong></li><li>加到每个 expert 上 → $N$ 倍 LoRA 参数,部分 expert 训不到(因为不被路由)</li></ul>
<p><strong>ESFT</strong>Expert-Specialized Fine-Tuning, Wang et al. arXiv 2407.01906):</p>
<ul><li>先用任务数据做 forward,统计 router score</li><li>找出<strong>任务最相关的 top-k expert</strong>per-layer),其余冻结</li><li>只训这些"任务专家"<strong>显存 ↓ 90%、时间 ↓ 30%、性能 ≈ 全参 fine-tune</strong></li></ul>
<div class="callout callout-info"><div class="callout-title">面试加分</div><p>比 LoRA 更适合 MoE 的本质:MoE 已经 specialize 了,微调只该 specialize 进一步,不该把通用 expert 也搅动。这是"sparse pretrain → sparse fine-tune"的自然延伸。</p></div>
<h3 id="94-moe-vs-mqagqa">9.4 MoE vs MQA/GQA</h3>
<p>完全正交:</p>
<ul><li><strong>MQA/GQA</strong> 减 KV cacheattention 端)</li><li><strong>MoE</strong> 减 FFN 算力(FFN 端)</li></ul>
<p>可以同时用(V2/V3 = MLA + MoELLaMA-3 = GQA + dense;理论上 GQA + MoE 也合法)。</p>
<h2 id="10-25-高频面试题">§10 25 高频面试题</h2>
<p>按难度分 L1(10 必会)/ L210 进阶)/ L3(5 顶级 lab)。每题点开看答案要点 + 易踩坑。</p>
<h3 id="l1必会题任何-ml-工程岗都会问">L1必会题(任何 ML 工程岗都会问)</h3>
<details>
<summary>Q1.MoE 在做什么?为什么用 sparse activation</summary>
<ul><li>把 dense FFN 换成 $N$ 个独立 expert + router,每 token 只走 $k \ll N$ 个</li><li><strong>总参数 ↑(容量)但激活参数不变(FLOPs)</strong>——用参数换计算效率</li><li>训练存得下,推理算力可控</li><li>关键:MoE 减少 FLOPs,但<strong>不减少推理显存</strong>all experts 必须常驻)</li></ul>
<p>把 MoE 想成 ensemble。MoE 是<strong>单一前向通路上选 expert</strong>,与"训多个模型 vote"完全不同。</p>
</details>
<details>
<summary>Q2.Top-k token-choice MoE 的 routing 公式是什么?</summary>
<ul><li>Router: $s(x) = W_g x \in \mathbb{R}^N$$g(x) = \text{softmax}(s(x))$</li><li>选 $\mathcal{T}_k(x) = \text{TopK}_i\, g_i$,输出 $y = \sum_{i \in \mathcal{T}_k} \tilde{g}_i E_i(x)$</li><li>Mixtral/DeepSeektop-k 上 <strong>renormalize</strong>$\tilde{g}_i = g_i / \sum_{j \in \mathcal{T}_k} g_j$)让权重和 = 1</li><li>Router 没 bias(标准做法)</li></ul>
<p>只说"选 top 几个 expert"不写公式;忘了 renormalize;说 Q/K/V 那套(attention 公式,搞混了)。</p>
</details>
<details>
<summary>Q3.Switch Transformer 的 aux load-balance loss 是什么?</summary>
<ul><li>$\mathcal{L}_\text{aux} = \alpha \cdot N \sum_i f_i P_i$</li><li>$f_i$ = expert $i$ 接到的 token 比例(不可微,但有数值)</li><li>$P_i$ = expert $i$ 的平均 gate 概率(可微)</li><li>乘积越小说明分布越均匀(均匀时 $\sum_i f_i P_i = 1/N$</li></ul>
<p>只说"鼓励均匀"但写不出公式;只写 $\sum P_i^2$(这是 entropy regularizer,不是 Switch 公式)。</p>
</details>
<details>
<summary>Q4.Capacity factor 是什么?为什么需要?</summary>
<ul><li>$C = \lceil \alpha \cdot Tk/N \rceil$,每个 expert 的容量上限</li><li>$\alpha = 1.25$ 是 Switch / GShard 默认(留 25% 缓冲)</li><li>超 capacity 的 token → <strong>residual bypass</strong>expert 输出 0,残差通过)</li><li>没 capacity 限制无法静态 bufferEP 通信无法 schedule</li></ul>
<p>只答"防止 OOM";不知道 residual bypass,以为 drop 就是直接归零。</p>
</details>
<details>
<summary>Q5.MoE 推理时显存按什么算?</summary>
<ul><li><strong>按总参数算,不是激活参数</strong>——所有 expert 必须常驻 GPU</li><li>因为下一个 token 可能路由到任意 expert,按需 load 延迟无法接受</li><li>671B MoE FP8 ≈ 671 GB<strong>8× H100 80G = 640 GB 还差一点</strong>;实际部署常用 16× H100 80G2 节点)或 8× H200 141G</li><li>真正节省的是 memory bandwidth(每 token 只读 active 部分权重)</li></ul>
<p>以为 MoE 显存按 active 算,所以"Mixtral 13B 单卡能跑"——错,Mixtral 47B 总参,单 24G 卡跑不下。</p>
</details>
<details>
<summary>Q6.DeepSeek-V3 总参数 / 激活参数 / expert 数?</summary>
<ul><li><strong>总参数 671B,激活 37B / token</strong></li><li><strong>256 routed experts + 1 shared expert</strong></li><li>Top-8 routed + 1 shared = 9 个 expert per token</li><li>Attention 用 <strong>MLA</strong>FFN 用 DeepSeekMoE</li><li>arXiv: 2412.19437, 2024.12</li></ul>
<p>把 V3 跟 V2 (236B/21B) 数字搞混;说 V3 有"多少 expert"但记错 256。</p>
</details>
<details>
<summary>Q7.Mixtral 8x7B 真的有 7B × 8 = 56B 参数吗?</summary>
<ul><li><strong>不是</strong>。8x7B 的命名只表示"8 expert, 每 expert 7B 量级"</li><li><strong>实际总参 46.7B</strong>——因为 attention / norm / embedding 在所有 expert 间共享,不是每个 expert 复制一份</li><li>激活参数 12.9B(不是 14B,因为 router gate 是 sparse 加权)</li></ul>
<p>被名字误导,直接 $8 \times 7 = 56$。</p>
</details>
<details>
<summary>Q8.MoE 训练为什么常 routing collapse?怎么治?</summary>
<ul><li>没 balance 机制时,强者愈强:被多选 → 训得多 → 更可能被选</li><li>治疗:<strong>aux loss (Switch)</strong> / Expert Choice / Aux-loss-free bias (V3) / Z-loss / expert dropout / 适当增大 capacity factor</li><li>监控:每 expert 的 load + aux loss 数值 + validation loss</li></ul>
<p>只说"加 aux loss"但不给具体公式;不知道还有 expert-choice 这类硬约束方法。</p>
</details>
<details>
<summary>Q9.MoE 训练需要哪些并行?</summary>
<ul><li><strong>DP(数据)+ TP(张量)+ PP(流水)+ EPexpert</strong></li><li>EP 是新增维度:把 $N$ 个 expert 分到不同 GPU</li><li>每个 MoE layer 需要 <strong>2 次 all-to-all</strong>dispatch + combine</li><li>DeepSeek-V316-way PP × 64-way EP × ZeRO-1 DP,跨 2048 卡 H800</li></ul>
<p>忘了 EP;以为 dense 的 DP+TP+PP 就够用。</p>
</details>
<details>
<summary>Q10.MoE 在 inference 端的好处是什么?</summary>
<ul><li>同 active 参数下,<strong>memory bandwidth 优势</strong>:每 token 只读 ~active/total 比例权重</li><li>推理多数 token-by-token 是 memory-boundbandwidth 减小 → throughput ↑</li><li>671B / 37B → 每 token 读 ~5% 权重 → 与 ~30B dense 同 latency</li></ul>
<p>只说"算力少"——但显存上不去(全 expert 加载),所以单卡 deployment 还是难。</p>
</details>
<h3 id="l2进阶题research-oriented-岗位">L2进阶题(research-oriented 岗位)</h3>
<details>
<summary>Q11.Expert Choice routing 是什么?为什么 autoregressive decoder 不能用?</summary>
<ul><li>反向:每个 expert 选 top-$M$ 个 token$M = Tk/N$</li><li><strong>天然 balance</strong>:每 expert 严格选 $M$ 个 token,不需 aux loss</li><li>不 drop tokencapacity 是 hard 给定)</li><li><strong>不能 autoregressive</strong>:第 $t$ 个 token 的路由依赖整个 batch(含未来 token)的 score → 不能 token-by-token 生成</li><li>用在 encoder / vision / BERT-styledecoder 用 token-choice</li><li>Zhou et al. 2022 NeurIPS, arXiv 2202.09368</li></ul>
<p>以为它能直接替代 Mixtral / DeepSeek 的 token-choice;忘了 causal 这一限制。</p>
</details>
<details>
<summary>Q12.Top-k 的离散操作怎么反传梯度?</summary>
<ul><li>Top-k 本身<strong>不可微,不参与梯度</strong></li><li>但被选中的 $k$ 个 expert 的 gate 权重 $g_i$softmax 后)<strong>进入 output 加权求和</strong>,对 $g_i$ 可微</li><li>未被选中的 expert 的 router weight 这次 step 拿不到梯度(chicken-and-egg → 需 aux loss / bias 打破)</li><li>类似 hard attention,但因为加权求和,梯度信号还是能到 router</li></ul>
<p>以为"top-k 必须用 Gumbel-softmax 才能反传"——其实标准做法直接用 softmax + top-k 选择,gate weight 就是梯度通路。</p>
</details>
<details>
<summary>Q13.DeepSeekMoE 的 fine-grained expert 是什么意思?</summary>
<ul><li>把 $N$ 个 baseline expert 拆成 $mN$ 个更小的 expert(每 expert $d_\text{ff}$ 缩到 $d_\text{ff}/m$</li><li>路由数从 $K$ 提到 $mK$</li><li><strong>总计算 / 总参数不变</strong>,但组合数 $\binom{mN}{mK} \gg \binom{N}{K}$</li><li>每 token 的"专家组合身份"指数级丰富 → 专精度提升</li><li>Dai et al. 2024, arXiv 2401.06066</li></ul>
<p>以为 fine-grained = 增加 expert 数量(部分对)但漏掉"每个 expert 缩小、路由数同比例放大"这个保持算力的关键。</p>
</details>
<details>
<summary>Q14.Shared expert 是什么?为什么需要?</summary>
<ul><li>一种<strong>永远激活、不经 router</strong>的 expert(每 token 都走它)</li><li>吸收"通用知识"(语言 / 常识),让 routed expert 专心做 specialization</li><li>DeepSeekMoE / Llama 4 都有;Mixtral / Switch 没有</li><li>DeepSeek-V3: 1 shared expert + 256 routed (8 selected)</li></ul>
<p>误以为 shared expert 是 ensemble;或不知道它不通过 routerrouter 不为 shared expert 投票)。</p>
</details>
<details>
<summary>Q15.DeepSeek-V3 的 aux-loss-free balance 怎么做的?</summary>
<ul><li>每 expert 一个偏置 $b_i$top-k 选择用 <strong>biased score</strong> $s_i + b_i$</li><li><strong>gating weight 用原始 $s_i$</strong>(保持主梯度路径干净)</li><li>每 step 按 sign 更新:$b_i \leftarrow b_i - u \cdot \text{sign}(c_i - \bar{c}_i)$</li><li>$b_i$ 是 buffer 不进梯度图,不被 optimizer 更新</li><li>兜底还有极小 sequence-level aux loss(防单序列内部极端不均)</li><li>Wang et al. arXiv 2408.15664V3 在 2412.19437 中应用</li></ul>
<p>说"V3 完全没有 aux loss"——不准确(还有 sequence-wise 兜底);或把 biased score 用到 gating weight 上(会污染主梯度,违背设计意图)。</p>
</details>
<details>
<summary>Q16.MoE 训练的 all-to-all 是什么?通信量怎么算?</summary>
<ul><li>每 MoE layer 做 2 次 all-to-alldispatch(按 expert 发 token+ combine(按 token 收回)</li><li>通信量 per layer ≈ $2 T k D (G-1)/G$$G$ 是 EP group size</li><li>通常占训练总时间 30-50%DeepEP / DualPipe 是用来 overlap 通信与计算的工程武器</li><li>$G$ 大 → 更稀疏 → 通信比例反而 ↑</li></ul>
<p>只答"用 NCCL 通信";不清楚通信量随 $T \cdot k \cdot D$ 线性增长;不知 DualPipe / DeepEP。</p>
</details>
<details>
<summary>Q17.MoE 上做 LoRA 微调有什么问题?ESFT 怎么解?</summary>
<ul><li>LoRA 加到 attention 层 → 不动 expert;加到所有 expert → $N$ 倍 LoRA 参数,且部分 expert 训不到</li><li><strong>ESFT</strong> (arXiv 2407.01906):先 forward 统计 router score → 选任务最相关的 top-$k$ expert → 只训这些</li><li>性能 ≈ 全参 fine-tune,显存 ↓ 90%、时间 ↓ 30%</li></ul>
<p>不知道 ESFT;以为 "MoE = 大模型,LoRA 一招通吃"。</p>
</details>
<details>
<summary>Q18.MoE 推理时显存全 load,那 sparse activation 的好处去哪了?</summary>
<ul><li>好处转移到 <strong>memory bandwidth</strong>——每 token 只读 active 比例权重</li><li>LLM decode 是 memory-bound(不是 compute-bound),所以 bandwidth 减小直接转化为 throughput</li><li>671B/37B 模型:每 token 读 ~5% 权重 → throughput ≈ 30B dense</li><li>但显存仍按总参数预算:单 H100 80G <strong>跑不动 V3</strong>,需 8 卡 minimum</li></ul>
<p>以为 sparse 推理可以"按需 load 1 个 expert"——硬件延迟禁止;或以为 throughput 收益来自 FLOPs 节省(实际更多来自 bandwidth)。</p>
</details>
<details>
<summary>Q19.MoE 和 MoD (Mixture-of-Depths) 区别?</summary>
<ul><li>MoE 沿 <strong>width</strong> 稀疏:每 token 选部分 expert</li><li>MoD 沿 <strong>depth</strong> 稀疏:每层选 top-$k$ 个 token 走整层,其他 token skip</li><li>每 token 计算量:MoE 固定(top-k expert),MoD 动态(取决于多少层接受它)</li><li>不互斥,可以叠加</li></ul>
<p>以为 MoD = MoE 的别名;或不知道沿 depth 也能稀疏化。</p>
</details>
<details>
<summary>Q20.Mixtral 8x7B 实际总参数为什么不是 56B 而是 46.7B?</summary>
<ul><li>8 个 expert 只在 <strong>FFN 层</strong> —— attention / norm / embedding / unembedding <strong>全部共享,只算一份</strong></li><li>设 Mistral-7B 的 dense 总参 $\approx 7.24\text{B}$FFN 约 $4.8\text{B} \approx 2/3$shared = attention+embed+norm $\approx 2.4\text{B} \approx 1/3$</li><li>Mixtral 8x7B 总参 = <strong>共享部分 + 8 × FFN</strong> = $2.4 + 8 \times 4.8 \approx 2.4 + 38.4 \approx 40.8\text{B}$;再加上 Mistral-7B 比 LLaMA-2-7B 略大、Mixtral 还增加了 router weight,官方 <strong>46.7B</strong></li><li>激活参数(per tokentop-2= <strong>共享部分 + 2 × FFN</strong> = $2.4 + 2 \times 4.8 \approx 12\text{B}$(论文给出 12.9B / 13B active</li></ul>
<p>直接 $8 \times 7 = 56$ 当总参(把 attention/embed 也乘 8);或激活算成 $2 \times 7 = 14$ 忘掉 shared 已经只算一份。</p>
</details>
<h3 id="l3顶级-lab--research-方向">L3顶级 lab / Research 方向</h3>
<details>
<summary>Q21.DeepSeek-V3 aux-loss-free 为何不破坏 sparse routing semantics</summary>
<ul><li><strong>关键设计</strong>bias $b_i$ <strong>只用于 top-k 选择</strong>,不进入 gating weight,更不进入梯度图</li><li>这意味着:router 的梯度信号<strong>只来自语言建模 loss</strong>,与传统 aux loss "把平衡目标作为额外梯度" 完全不同</li><li>类比控制系统:bias 是 outer-loop controller(按 load 反馈调节),主梯度是 inner-loop optimizer(按 task loss 优化表达力)——两个 loop <strong>解耦</strong></li><li>对比 aux lossaux loss 的梯度会拉 router 权重往"均匀分布"方向,可能与"任务最优 routing"方向冲突</li><li>Loss-free 让 router <strong>在主任务下仍然 sparse + specialize</strong>bias 只是后处理纠偏</li><li>一个细节:bias <strong>不进 gating weight</strong> 这一点至关重要——否则相当于通过反向通路偷偷影响梯度,loss-free 名义就名不副实</li></ul>
<p>把 bias 当成 learnable parameter 进 optimizer;以为 loss-free 就是"啥都不加"(实际有 sign 更新和 sequence-aux 兜底)。</p>
</details>
<details>
<summary>Q22.Fine-grained + shared 比同总参 dense 优势在哪?理论 / 实证?</summary>
<ul><li><strong>理论容量论</strong>$mN$ 个小 expert 的 routed combination 数 $\binom{mN}{mK}$ 远超 $\binom{N}{K}$,每 token 可表达的"专家组合身份"指数级丰富,更接近"每 token 独立小子网"的极限</li><li><strong>专精论</strong>shared expert 吸收通用知识,让 routed expert 摆脱"既要又要"的负担,更可能 specialize</li><li><strong>算力论</strong>:同 active FLOPs 下,dense 必须把容量塞进激活参数;MoE 把容量塞进 dormant expert,活时只取一小撮——可以在不增加 FLOPs 的前提下把总参数推到 10x+</li><li><strong>实证</strong>DeepSeekMoE 16B vs LLaMA-2 7BFLOPs 相近,benchmark 上接近或超过(论文 Table 3);DeepSeekMoE 145B vs DeepSeek 67B dense:性能持平,但 145B MoE 训练 FLOPs 只用 ~28%</li><li><strong>失败模式</strong>:若 routing 不够好(collapse),fine-grained 反而退化(少数小 expert 通吃,等效 dense 但效率更低)</li><li><strong>2025-2026 实证趋势</strong>:V3 把这条路线推到极致(256 routed),证明在 1T 级 token 训练下 fine-grained 优势显著</li></ul>
<p>只说"参数多就行"——但没拆开"组合容量 ≠ 单参数容量";忽略 routing 质量是这条路线 work 的前提。</p>
</details>
<details>
<summary>Q23.MoE inference latency 真的能与 dense active 同台吗?瓶颈在哪?</summary>
<ul><li><strong>理论</strong>:每 token 只读 active 比例权重,bandwidth-bound 下 throughput 应正比于 active 参数</li><li><p><strong>实际几个 caveat</strong></p>
<ol><li><strong>Routing overhead</strong>router 计算 + top-k 选择 + scatter/gather 本身有 latency,长 context 下也分摊不少</li><li><strong>Expert load 不均</strong>:单 batch 内 routing 不均 → 部分 expert 排队,wave 浪费</li><li><strong>EP 通信</strong>(多卡推理):dispatch / combine 的 all-to-all 占很大比例</li><li><strong>Memory bandwidth ≠ memory size</strong>:每 token 5% 权重,但 5% 落到哪些 expert 是 token-dependentcache 命中差 → 实际带宽利用率不如理论上限</li></ol></li><li><strong>Mixtral 8x7B 在单卡 H100 80G</strong>bandwidth 利用率 ~70-80%vs dense 70B 利用率类似),latency 与 13B dense 接近</li><li><strong>V3 671B</strong>8 卡部署下 inference 延迟略高于 30B dense(多卡通信不可忽略)</li><li><strong>结论</strong>:理论可以,工程上还有 5-30% gap 取决于 batch size / 上下文长度 / 多卡拓扑</li></ul>
<p>只说"理论上一样",不知道实际有 routing / EP 通信 / cache 命中等几个隐藏 cost;或反过来说"MoE 推理一定慢",也不全对(small batch / single GPU 下 Mixtral 与 dense 13B 接近)。</p>
</details>
<details>
<summary>Q24.MoE 训练的 stability 问题有哪些?怎么 mitigate</summary>
<ul><li><strong>Router logit 爆炸</strong>:训练初期 router 权重大幅波动,fp16 下 logit 可 overflow → <strong>Z-loss</strong> $\mathcal{L}_z = \beta \sum_t (\log \sum_i e^{s_i(x_t)})^2$ 惩罚 logit 量级(ST-MoE 2022 提出)</li><li><strong>Routing collapse</strong>:解 §4.3 提到的方案(aux loss / bias / capacity factor / expert dropout</li><li><strong>Expert representation drift</strong>:训练中 expert 间表示渐变(被 router 协同 shape),可能让 fine-tune 时小数据 overfit 某些 expert</li><li><strong>Aux loss 干扰</strong>$\alpha$ 大 → loss 拉 router 往均匀,损害性能;$\alpha$ 小 → balance 不够。<strong>这是 loss-free 的根本动机</strong></li><li><strong>bf16 vs fp16</strong>:bf16 动态范围与 fp32 接近,更适合 routerfp16 下 router logit 容易 overflow,必须 z-loss + softmax 稳定化</li><li><strong>Small batch 下 router 噪声大</strong>:每 step 的 $f_i$ 估计噪声大,aux loss 不稳。生产里通常用 micro-batch grad accumulation 增大 effective batch</li></ul>
<p>只答"加 aux loss"——但忽略 z-loss、router 数值稳定、bf16 重要性、small-batch 的统计估计问题。</p>
</details>
<details>
<summary>Q25.如果让你设计下一代 MoE,会改什么?</summary>
<p>这是开放题,没标准答案。给一些 2026 前沿 lab 关心的方向 + 你的判断角度:</p>
<ul><li><strong>更细的 expert</strong>DeepSeek 把 expert 推到 256,进一步推到 1024 / 4096?瓶颈在 routing collapse + EP 通信,需要新的 balance 方法</li><li><strong>动态 top-k</strong>:每 token 选不同数量 experteasy token 走 1hard token 走 4)。和 MoD 思想结合,"既稀疏 width 又稀疏 depth"</li><li><strong>Differentiable routing</strong>Sinkhorn / hash-based / learned permutation,让 top-k 部分可微,router 训练更快收敛</li><li><strong>Expert sharing across layers</strong>:跨层共享一部分 expert pool(类似 ALBERT 的 cross-layer sharing),进一步压总参数</li><li><strong>更好的 MoE post-training</strong>ESFT 之后是什么?对 SFT/RLHF 阶段 router 不一定保持 pretrain 时的 specialization,需要专门的 alignment 方案</li><li><strong>MoE × long context</strong>1M context 下每层 routing 决策乘以 1M,router 本身可能变成瓶颈 → routing reuse / hierarchical routing</li><li><strong>MoE 服务化</strong>:单卡跑不动 671B,怎么让中小公司也能部署?expert offload / streaming weights / disaggregated expert servers</li><li><strong>理论分析</strong>fine-grained vs dense 容量的严格量化?routing 的 implicit regularizationaux-loss-free 的收敛保证?目前都还很少</li></ul>
<p>回答时<strong>先说一个具体方向</strong>,给清楚 motivation + technical sketch + 可能的 failure mode,比泛泛而谈"我会让 expert 更多"加分多。</p>
</details>
<h2 id="a-附录参考文献清单按时间">§A 附录:参考文献清单(按时间)</h2>
<p>按出现先后整理。注:<strong>所有 arXiv ID 已交叉验证</strong></p>
<ol><li><strong>Shazeer, N., Mirhoseini, A., Maziarz, K., Davis, A., Le, Q., Hinton, G., Dean, J.</strong> (2017). Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. ICLR. arXiv:1701.06538.</li><li><strong>Lepikhin, D., Lee, H., Xu, Y., Chen, D., Firat, O., Huang, Y., Krikun, M., Shazeer, N., Chen, Z.</strong> (2020). GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding. arXiv:2006.16668.</li><li><strong>Fedus, W., Zoph, B., Shazeer, N.</strong> (2021). Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity. JMLR. arXiv:2101.03961.</li><li><strong>Zhou, Y., Lei, T., Liu, H., Du, N., Huang, Y., Zhao, V., Dai, A., Chen, Z., Le, Q., Laudon, J.</strong> (2022). Mixture-of-Experts with Expert Choice Routing. NeurIPS. arXiv:2202.09368.</li><li><strong>Zoph, B., Bello, I., Kumar, S., Du, N., Huang, Y., Dean, J., Shazeer, N., Fedus, W.</strong> (2022). ST-MoE: Designing Stable and Transferable Sparse Expert Models. arXiv:2202.08906.Z-loss 来源)</li><li><strong>Dai, D., Deng, C., Zhao, C., Xu, R., et al.</strong> (2024). DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models. ACL. arXiv:2401.06066.</li><li><strong>Jiang, A. Q., Sablayrolles, A., et al.</strong> (2024). Mixtral of Experts. arXiv:2401.04088.</li><li><strong>DeepSeek-AI</strong> (2024). DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model. arXiv:2405.04434.</li><li><strong>Wang, Z., Chen, D., Dai, D., Xu, R., Li, Z., et al.</strong> (2024). Let the Expert Stick to His Last: Expert-Specialized Fine-Tuning (ESFT). arXiv:2407.01906.</li><li><strong>Wang, L., Gao, H., Zhao, C., Sun, X., Dai, D.</strong> (2024). Auxiliary-Loss-Free Load Balancing Strategy for Mixture-of-Experts. arXiv:2408.15664.</li><li><strong>DeepSeek-AI</strong> (2024). DeepSeek-V3 Technical Report. arXiv:2412.19437.</li><li><strong>Raposo, D., Ritter, S., Richards, B., Lillicrap, T., Humphreys, P., Santoro, A.</strong> (2024). Mixture-of-Depths: Dynamically Allocating Compute in Transformer-Based Language Models. arXiv:2404.02258.</li><li><strong>Meta AI</strong> (2025). Llama 4: Scout, Maverick, Behemoth (blog announcement, April 5, 2025).</li><li><strong>Qwen Team</strong> (2025). Qwen3 Technical Report. arXiv:2505.09388.</li><li><strong>DeepSeek-AI / DualPipe / DeepEP</strong> (2025). Open-source releases at github.com/deepseek-ai/{DualPipe,DeepEP}.</li></ol>
<div class="callout callout-info"><div class="callout-title">Tip</div><p><strong>2026 秋招高频提问 paper top-5</strong>(按面试出现频率):DeepSeek-V3 (2412.19437) &gt; Mixtral (2401.04088) &gt; Switch Transformer (2101.03961) &gt; DeepSeekMoE (2401.06066) &gt; Loss-Free Balance (2408.15664)。准备这 5 篇基本覆盖 95% MoE 问题。</p></div>
<footer class="aris-footer">
Generated by <a href="https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md">ARIS <code>/render-html</code></a> ·
source path <code>docs/tutorials/moe_tutorial.md</code> ·
SHA256 <code>e860589270ed</code> ·
generated at 2026-05-19 10:10 UTC.
This is a generated view — edit the source Markdown, then re-render.
</footer>
</main>
</div>
</body>
</html>