本页目录

模型架构:Dense 与 MoE

几乎所有现代 LLM 都是 Transformer decoder。MoE 在过去几年成为最重要的架构折中——让模型"参数很大、但每个 token 只用一小部分",把容量和每 token 算力解耦。

概述

几乎所有现代 LLM 都是 Transformer decoder。差异主要在规模、以及怎么花这些规模⁠:参数越多通常越强,但每个 token 的推理成本、显存占用、延迟也随之涨。MoE(Mixture of Experts) 是过去几年最重要的架构折中——让模型"⁠参数很大、但每个 token 只用一小部分⁠",把"容量"和"每 token 算力"解耦。读懂 dense 与 MoE 的机制,直接关系到三件实事:本地部署选型与显存预算(见 本地 LLM 部署)、推理吞吐、以及看懂像 Qwen-AgentWorld 那样的前沿模型卡。

先划清边界:⁠闭源模型(Claude、GPT 等)不公开架构⁠——是不是 MoE、多少层、多少参数,官方都没说。本文讲通用机制 + 开源模型的可核实事实⁠;对闭源模型,能依赖的是 Models API 报告的能力,而不是臆测的内部结构。下文凡涉及 Claude,只谈能力查询,不谈结构。

Transformer decoder 怎么工作

flowchart TD
    E["token embedding + 位置编码"] --> R["残差流 residual stream"]
    R --> L1["Layer 1"]
    subgraph L1["每一层 (×N)"]
        direction TB
        A["self-attention<br/>(Q·K → 权重, 加权 V)"] --> F["FFN<br/>(升维→激活→降维)"]
    end
    L1 --> LN["... Layer N"]
    LN --> O["输出 logits<br/>(下一个 token 分布)"]

要点(decoder-only,即 GPT 谱系):

  • 残差流(residual stream):每层的输出 = 输入 + 该层增量。信息沿这条"主干"流动,每层往上读写。
  • self-attention:每个 token 投影出 Q/K/V 三组向量;用 Q·K 算出"该关注谁"的权重,再对 V 加权求和。multi-head 是并行跑多组 Q/K/V 看不同关系。⁠因果掩码(causal mask)⁠保证一个 token 只能看它之前的——这才能自回归生成。注意力是 ~O(n²) 成本与 KV cache 的来源(见 上下文工程)。
  • FFN(前馈网络):对每个位置独立地"升维→非线性→降维"(典型升 4×)。⁠模型的大部分参数在 FFN 里⁠——这正是 MoE 动刀的地方。

Dense vs MoE:机制

  • Dense(稠密):每个 token 都流经全部 FFN 参数。70B dense 模型,每个 token 都动用 70B。
  • MoE:把每层的 FFN 换成 N 个专家(expert)+ 一个路由器(router/gating)。router 给每个 token 算一个"该用哪些专家"的分布,⁠只激活 top-k 个专家,其余不参与计算。
Dense vs MoE:token 如何流经 FFN Dense 全部参数参与 token 整块 FFN · 全部参数 输出 总参 = 激活参 MoE 只让 top-k 参与 token router softmax 打分 → top-k expert 7 expert 23 加权求和 输出 其余 N−k 个专家本步不参与 —— 总参 ≫ 激活参 Dense 每 token 用全部参数;MoE 每 token 只让 router 选中的 top-k 专家参与,其余专家不计算—— 这就是"总参 = 容量"与"激活参 = 每 token 算力"解耦的来源。

router 的机制(以现代 MoE 为例):对每个 token,gating 网络输出对 N 个专家的打分,softmax 后取 top-k,按门控权重加权合并被选专家的输出。两个工程关键:

  • load balancing(负载均衡):不加约束的话 router 会偏爱少数专家("赢家通吃"),其余专家训练不足。训练时加负载均衡辅助损失鼓励均匀路由。
  • shared expert(共享专家):部分设计(DeepSeekMoE、Qwen)留一个始终激活的共享专家承接通用能力,其余路由专家负责专精——这就是 "8 routed + 1 shared" 这类描述的由来。

命名里的 35B-A3B 就是这个意思:35B 总参,每 token 只激活约 3B

为什么 MoE:容量/算力解耦(及其代价)

DenseMoE
每 token 计算全部参数只激活的 top-k 专家 → 由激活参数决定
推理速度/吞吐随总参数变慢⁠(算力 ∝ 激活参)
显存放下总参数仍需放下全部专家⁠(任一都可能被选)
扩容方式加参数=加算力加专家≈加容量但几乎不加每 token 算力
代价简单路由不均、训练更复杂、显存吃紧、batch 内专家命中分散

一句话:MoE 用"全专家常驻显存"换"每 token 只算一小部分"——大而省(算力/吞吐),但不省显存⁠。

一道显存/算力的账(本地视角)

以本机常驻的 Qwen 35B-A3B 为例(本地 LLM 部署):

  • 显存⁠:35B 全部专家都要常驻。FP16 要 ~70GB,塞不进 24GB 卡 → 必须量化:Q3_K_XL 把权重压到 ~17GB,加 64k 的 Q8_0 KV cache ~2.85GB,再加 ~1.7GB 运行时计算缓冲,合计 ~21.5GB,给桌面留 ~3GB 余量。⁠显存看的是"总参 + KV cache",MoE 一点没省这块。
  • 算力⁠:每 token 只激活 ~3B,所以吞吐接近一个 3B dense 模型——配上 MTP 草稿头实测 ~111 t/s。⁠速度看的是"激活参",这才是 MoE 的红利。

这就是为什么本地选 MoE:用得起的显存里塞下高容量模型,还跑得快——前提是用量化把"总参"压进卡里。⁠但代价是每 token 推理深度由激活参数决定⁠(见下节),agent/工具调用等需要多步推理链的任务,选型时把这个代价算进去。

MoE 的隐性代价:每 token 推理深度

上面只算了显存和吞吐,但还有第三个维度:⁠每 token 的推理深度由激活参数决定,不由总参数决定。

回想 Transformer 的结构:

每层 = self-attention + FFN 注意力(看哪里) 参数固定,完整 前馈网络(加工信息) MoE 只激活 top-k 专家

attention 层是完整的——模型知道该关注输入的哪个 token。但 FFN 负责对关注到的信息做加工推理,而 MoE 每 token 只激活一小部分 FFN 参数。35B 总参 / 3B 激活的模型,每一步推理的"思考深度"约等于一个 3B dense 模型。 换言之:⁠注意力决定"看哪里",激活 FFN 决定"看懂了什么、接下来做什么"——MoE 的眼睛不瞎,但每步干活的脑细胞只有激活参数那么多。

这对单步任务(分类、摘要、简单问答)影响不大——3B 的加工能力够用。但对多步串行推理⁠——典型如 agent 工具调用链:

理解任务 选工具 填参数 读结果 决定下一步 每一步都在消费 FFN 的推理预算 —— 循环直到任务完成或链路断裂

每一步都在消费 FFN 的推理预算。3B 激活参数走到第二步就开始"忘"了第一步的意图,表现就是:输出了 <think> 文本,但在应该产出 tool call token 的时候注意力散了,继续当作文本往下编——思考了,但没行动。

经验阈值⁠:稳定单步工具调用需要 ~7–10B 激活参数;稳定多步 agent 循环需要 ~20B+ 激活参数。这也是为什么 DeepSeek V3(MoE,671B 总参但激活 37B)的工具调用很稳——它的激活参数够大。在 24GB 显存约束下,做 agent 任务优先选 dense 模型(如 Qwen 2.5 14B/32B、Qwen 3 32B),它们的激活参数 = 总参数,多步推理链比同体量的低激活 MoE 可靠得多。

实例:从本机到前沿

  • 本地常驻⁠:Qwen 35B-A3B(35B 总 / 3B 活)——标准 MoE,见上账。
  • 前沿变体⁠:Qwen-AgentWorld-35B-A3B 同为 35B-A3B,但更激进:256 个专家,每 token 激活 8 routed + 1 shared;且是混合架构⁠——40 层里大量用 Gated DeltaNet(一种线性注意力变体,把长序列注意力的 ~O(n²) 降到近线性,省长上下文开销),仅每个重复块的最后一层用 Gated Attention(40 层中只 10 层),从而支撑 256K 上下文⁠。它把"MoE 容量解耦"和"线性注意力长上下文"两条线叠在一起,服务于它的 world-model 目标(模拟 agent 环境需要长上下文 + 强容量,见 Agent 循环与工具使用 的前沿小节)。

这些数字来自可核实的开源模型卡。对 Claude 不要套用——官方未公开其是否 MoE、参数规模或注意力变体。

选型:看能力,别猜结构

对应用方,真正该问的不是"它是不是 MoE",而是能力与成本⁠。闭源模型用 Models API 实时查,别猜:

m = client.models.retrieve("claude-opus-4-8")
m.max_input_tokens   # 上下文窗口
m.max_tokens         # 最大输出
m.capabilities       # image_input / thinking / effort / structured_outputs ...
# 也可 client.models.list() 按能力过滤

开源/本地模型则读模型卡的总参 / 激活参 / 层数 / 上下文 / 注意力类型⁠,结合显存预算(本地 LLM 部署 的量化与显存博弈)做选型。

最佳实践

  • 闭源模型查能力,别猜结构。 用 Models API 实时取 max_input_tokens/max_tokens/capabilities,别臆测它是不是 MoE、多少参数。
  • MoE 显存按"总参 + KV cache"估。 激活参小不等于显存小——全部专家都要常驻,用量化把总参压进卡。
  • MoE 吞吐看激活参,不看总参。 35B-A3B 跑得像 3B,这才是 MoE 的红利。
  • 按激活参评 agent 适配性,不看总参。 稳定单步工具调用需 ~7–10B 激活,稳定多步 agent 需 ~20B+;24GB 下做 agent 优先 dense 或高激活 MoE。
  • 开源选型读模型卡的四件套。 总参 / 激活参 / 层数 / 注意力类型,结合显存预算(本地部署)定档。

权衡与失败模式

  • 把"激活参小"当"显存小":MoE 显存看总参,会塞爆卡 → 按总参 + KV cache 估显存,用量化压。
  • 拿 dense 直觉估 MoE 吞吐⁠:MoE 快是因为激活参小,不是因为它"小" → 看激活参。
  • batch 内专家分散⁠:并发请求路由到不同专家,实际算力利用率可能不如理论 → 服务端调度/专家并行的工程问题。
  • 用 MoE 跑 agent 工具调用却频繁断链⁠:激活参数太小(如 3B),多步推理中意图漂移,产出 think 文本但不产出真正的 tool call → agent 场景选 dense 或高激活 MoE,按激活参数而非总参数评估。
  • 臆测闭源架构⁠:无依据 → 只用 Models API 的能力字段。

参考

  • 论文⁠: "Attention Is All You Need"(Vaswani 2017)、"Switch Transformers"(Fedus 2021)、"DeepSeekMoE"(2024, shared expert)、"GShard"(load balancing)
  • 模型卡⁠: Qwen-AgentWorld-35B-A3B
  • 闭源能力查询⁠: Anthropic Models API(platform.claude.com)

Keywords: Transformer, decoder-only, residual stream, self-attention, QKV, multi-head, causal mask, FFN, KV cache, dense, MoE, Mixture of Experts, router, gating, top-k, shared expert, load balancing, 总参数, 激活参数, active parameters, 35B-A3B, throughput, Gated DeltaNet, linear attention, Qwen-AgentWorld, Models API, 量化, 闭源架构