首页 / 本地大模型

MoE 混合专家模型:为什么 35B 等级的大模型也能在小显卡上跑(前提是系统内存够大)

作者 JC · 更新于 · 最后查证

简体版由繁体自动转换,少数用词可能和你习惯的不同。

要点速览

MoE 混合专家模型每个 token 只激活部分专家,所以每个 token 的运算量与要读取的权重量取决于「激活参数」,而不是总参数。但所有专家的权重仍然要放在某处,总大小仍是「总参数 × 比特数 ÷ 8」。因此 35B 等级的 MoE 能在小显卡上跑,前提是把常驻的部分放进显存、大量专家权重放在够大的系统内存,速度则要看内存带宽。

你在模型清单里看到「30B-A3B」「235B-A22B」这类名字,很容易以为「A3B 就只要 3B 的显存」,或是「30B、35B 等级的模型竟然能在小显卡上跑」。这篇要讲的 MoE 混合专家模型,确实让大模型有机会在小显卡上跑,但这句话要有条件才成立:MoE 省的是每个 token 的运算量与读取量,不省「权重放在哪里」的空间。 读完这篇,你会知道 MoE 到底是什么、怎么从模型文件判断它是不是 MoE、怎么用几个算式估出需要多少显存与系统内存,以及为什么速度只能做定性的推论。

MoE 混合专家模型是什么?

MoE 是 Mixture of Experts 的缩写,中文叫「混合专家模型」。先交代两个基本词:参数(parameter),也常叫权重(weight),是模型训练出来、存在文件里的数字,模型的大小就是这些数字的数量。它的做法是:在模型的每一层里,不是只放一个大的前馈网络(feed-forward network,每一层里负责做非线性转换、占掉大部分参数的那一块,Hugging Face 文档称为 MLP),而是放多个结构相同的「专家」(expert)子网络,再加上一个「路由器」(router),由路由器决定每个 token 要交给哪几个专家处理。token 是模型处理文字的最小单位,详见术语表。没被选到的专家,在这个 token 上就不参与运算。

这个说法有官方文档可以对照。Hugging Face Transformers 的 Mixtral 说明文档写到:Mixtral 是一个 MoE 模型,每个 MLP 有 8 个专家,总共 45 billion(450 亿)参数;每个 token 的隐藏状态会被派送两次(文档称 top 2 routing,也就是挑两个专家)。文档并且说明,虽然模型有 45 billion 参数,单次前向运算(forward pass)所需的运算量却与一个 14 billion 参数的模型相当。文档开头引用的发表文字则把这类模型称为稀疏混合专家模型(sparse mixture of experts,SMoE)。路由器在 GGUF 格式规格里也有对应:规格列出一种叫 ffn_gate_inp 的张量,说明是「MoE 模型中前馈网络的专家路由层」。

这里出现了两个一定要分开的数字:

  • 总参数量(total parameters):模型所有专家加上共用部分的参数总数。它决定模型文件有多大、权重总共要占多少空间。
  • 激活参数量(activated parameters):处理一个 token 时实际用到的参数数量。它决定每个 token 的运算量,以及每个 token 需要从内存读多少权重。

相对地,每个 token 都会用到全部参数的一般模型,这篇称为「稠密(dense)模型」,英文常直接写 Dense。Dense 模型的总参数量与激活参数量是同一个数字,所以平常谈模型大小时,没有人需要区分两者;MoE 模型才必须分开看。

模型名称里的 A3B 是什么

Qwen3 的 README 把模型分成密集与 MoE 两类,列出的 MoE 模型是 30B-A3B 与 235B-A22B;后来更新的 2507 版本则提供 235B-A22B、30B-A3B 与 4B 三种大小。README 本身没有逐字解释名称里的 A。不过 Qwen3 技术报告在描述旗舰模型时写得很明确:Qwen3-235B-A22B 是 MoE 模型,总共 235B 参数,每个 token 激活 22B。照这个对应,名称「总参数-A激活参数」的读法就很自然:A 后面的数字是每个 token 激活的参数量。(这是从技术报告的描述对应出来的读法,不是 README 的原文,不同厂商的命名习惯也未必一致,遇到不熟悉的名称请回到模型页面确认。)

技术报告还写了这两个 Qwen3 MoE 模型的专家设计:共 128 个专家,每个 token 激活 8 个,并且与前一代 Qwen2.5-MoE 不同,没有共享专家(shared experts,一种每个 token 都会经过的专家)。不同 MoE 模型的专家数与每次激活的数量不同,Mixtral 是 8 选 2,Qwen3 是 128 选 8,所以不能拿一个模型的比例套到另一个模型。

为什么 8x7B 不是 56B

Mixtral 常被称为「8x7B」,容易让人算成 8 × 7 = 56B。但上面引用的 Mixtral 文档写的总参数是 45 billion,并不是 56。原因是专家只取代每一层里的 MLP(文档写法是「8 experts per MLP」),注意力层等其他部分是所有专家共用的,并没有拷贝八份。GGUF 的命名规范也把「专家数」与「每个专家的参数量」放在文件名的尺寸标签里,格式是「专家数 x 参数量」,所以看到 8x7B 这种标示,请把它读成「8 个专家、每个标称 7B」,而不是总量等于 8 乘 7。

先讲清楚:MoE 省了什么,没省什么

这是整篇最重要的一段,请对照 Mixtral 文档的原话来读。文档说:虽然模型有 45 billion 参数,单次前向运算的运算量与 14 billion 参数的模型相同,「因为每个专家都必须加载 RAM」(原文:even though each of the experts have to be loaded in RAM),而每个 token 只被派送到两个专家,所以运算量才会少。换句话说,MoE 做了两件完全不同的事:

面向MoE 有没有帮上忙依据
每个 token 的运算量有,看激活参数而不是总参数Mixtral 文档:45B 的模型,单次运算量相当于 14B 的模型
每个 token 需要读取的权重量有,只需读到被选中的专家,加上共用部分由「激活参数 × 比特数 ÷ 8」推出,见下方估算方法
权重总共要放在哪里、占多少空间没有,所有专家都要存在某处Mixtral 文档:每个专家都必须加载 RAM;该文档在量化一节也以 45 billion 参数来估算 fp16 需要约 90GB
模型文件大小没有,仍由总参数与比特数决定同上,由总参数计算

所以「MoE = 小模型」是错的。它是一个很大、但每次只动用一小部分的模型:大,所以要有地方放;小部分,所以每个 token 算得少、读得少。

「35B 也能在小显卡上跑」到底要满足什么条件

既然所有专家的权重都要放在某处,那小显卡的显存(VRAM,显卡上专用的内存)放不下的时候,唯一的办法就是放到系统内存(RAM,电脑主板上的一般内存)。这正是 MoE 特别适合这种做法的原因:专家权重占了总量的绝大部分,但每个 token 只会用到其中一小部分;而注意力层等「每个 token 都会用到」的共用部分相对很小,可以常驻在显存里。

llama.cpp 是一套常见的本地推论程序(可以运行 GGUF 格式的模型)。它的官方服务器说明里,启动参数表直接有针对 MoE 的选项。该文档的内容整理如下(旗标名称与行为以官方文档为准,版本不同可能改名,这里只为说明原理):

旗标官方文档的描述(中文转述)
--cpu-moe(简写 -cmoe)把所有 MoE 专家的权重留在 CPU 端
--n-cpu-moe N(简写 -ncmoe)只把前 N 层的 MoE 专家权重留在 CPU 端
--n-gpu-layers(简写 -ngl)最多有多少层存放在显存
--override-tensor(简写 -ot)依张量名称的样式,覆盖该张量要放的缓冲区类型
--moe-cache-mib为「留在 CPU 的专家」在 GPU 上创建的缓存大小(MiB),预设为 0(禁用)

这几行文档有三个地方值得注意。第一,MoE 专家被独立成一个设置,不跟「放几层到显存」的一般设置混在一起,这和 GGUF 规格里专家权重本来就是独立张量(ffn_up_exp、ffn_down_exp、ffn_gate_exp,与注意力的 attn_q、attn_output 等分开命名)互相呼应。第二,--n-cpu-moe 是「前 N 层」,也就是你可以只把一部分的专家留在系统内存,其余仍放显存,这是在显存与系统内存之间取舍的旋钮。第三,官方文档对这些旗标只有一句话的描述,没有给出速度数字,也没有说「放哪些设置最好」,所以这篇也不会给。

把上面整理成条件,「35B 等级的 MoE 能在小显卡上跑」大致要同时满足:

  1. 共用部分放得进显存:注意力层等每个 token 都会用到的权重,加上 KV 缓存(KV cache,模型为了不重复计算前面内容而存下的中间结果,会随上下文变长而变大),要放得进你的显存。怎么估 KV 缓存与显存,请看依显存选模型与显存估算方法。
  2. 系统内存够大:专家权重总量要放得进系统内存,还要留空间给操作系统与其他程序。llama.cpp 说明里的加载模式也提到,可以要求系统把模型锁在内存里(mlock),避免被换出到硬盘(swapping);一旦内存不够,这件事就没有办法成立。显存、内存、硬盘各自的角色,见显存、内存、硬盘各扮演什么角色。
  3. 接受速度取决于内存带宽:带宽(bandwidth)是内存每秒能发送多少数据。专家在系统内存时,每个 token 要读专家权重,就会受那一块内存的带宽限制。这点在后面的算式里会看得更清楚。

少了任何一条,「也能在小显卡上跑」这句话就不成立。

怎么看一个模型是不是 MoE

不必靠名称猜。GGUF 是 llama.cpp 使用的模型文件格式,文件里除了权重,还带有 metadata(描述模型的字段)。GGUF 规格在 LLM 的字段里定义了两个与 MoE 有关的字段,其中 [llm] 要替换成架构名称:

metadata 字段规格上的意思
[llm].expert_countMoE 模型的专家总数(非 MoE 架构可以没有,规格标示为 optional)
[llm].expert_used_count每个 token 评估时使用的专家数(同样是 optional)

架构名称是什么,要看实际的文件。llama.cpp 的量化工具说明里就有一个例子,使用 qwen3moe.expert_used_count 这个名称来覆写该字段,可以看出字段前面确实是「架构名称加句点」的形式。你拿到一个 GGUF 文件时,能列出 metadata 的工具都可以确认这两个字段;如果完全没有专家相关字段,多半就是稠密模型。

另一个线索是张量名称。GGUF 规格列出的 MoE 专用张量有 ffn_gate_inp(专家路由层)以及逐专家的 ffn_gate_exp、ffn_up_exp、ffn_down_exp。看得到这些名称,就表示该层有路由器与多个专家。

至于文件名,GGUF 的命名规范把尺寸标签写成「专家数 x 参数量」,像规范范例里的 Mixtral-8x7B-v0.1,专家数为 8、参数量为 7B。不过并不是所有模型文件名都照这个规范,像 Qwen3 的 30B-A3B 就是另一种写法,所以文件名只能当第一个线索,最后仍以 metadata 与模型说明为准。

另外,专家数与每次激活的数量有一个直接的用途:它们告诉你「激活比例」,也就是每一层里只有 expert_used_count ÷ expert_count 的专家权重会被用到。后面的估算方法会用到这个比例。

估算方法:总权重、每 token 读取量、显存与系统内存

下面所有算式都是估算,用来判断「放不放得下」与「大致谁比谁重」,不是精确的量测结果。单位上,算式里的 1 GB 取 10⁹ 字节;llama.cpp 量化工具说明的表格使用的是 GiB(约 1.074 × 10⁹ 字节),两者有一点差距。

先备数据

符号意思从哪里取得
N_total总参数量模型页面、模型名称(如 30B)
N_active每个 token 激活的参数量模型页面、名称里 A 后面的数字
E专家总数GGUF 的 expert_count
k每个 token 激活的专家数GGUF 的 expert_used_count
b平均每个参数用几比特存放由量化格式决定(量化是把权重改用更少的比特保存),见量化是什么

关于 b,要注意标称和实际不同。llama.cpp 量化工具说明针对 Llama-3.1-8B 列出各种量化格式的每权重比特数(bits/weight):Q4_K_M 是 4.8944,Q8_0 是 8.5008,F16 是 16.0005。也就是说,标称「4 比特」的格式,实际平均会略高于 4 比特,因为还要存缩放系数等信息。这是特定模型的量测值,换成别的模型会略有出入,只当量级参考。

公式

总权重大小 W_total = N_total × b ÷ 8
每个 token 需读取的权重量 W_token = N_active × b ÷ 8

÷ 8 是把比特换算成字节。两个公式的差别只有一个:前者用总参数,后者用激活参数。稠密模型两者相同;MoE 模型 W_token 远小于 W_total。

如果你想进一步知道「共用部分」与「专家部分」各占多少(决定显存放什么、系统内存放什么),可以用专家数与激活数倒推。假设所有专家一样大、没有其他会随选择改变的部分,则:

N_active = N_shared + (N_total − N_shared) × (k ÷ E)

移项得:N_shared = (N_active − N_total × k ÷ E) ÷ (1 − k ÷ E)
专家部分 N_experts = N_total − N_shared

N_shared 是每个 token 一定会用到的参数(注意力层、把文字转成矢量的嵌入层、路由器等),N_experts 则是所有专家的参数总和。显存的需求大致是:

显存 ≈ N_shared × b ÷ 8 + KV 缓存 + 其他开销
系统内存 ≈ N_experts × b ÷ 8 + 操作系统与其他程序

这两个式子假设专家全部放在系统内存。如果你只把前 N 层的专家放到系统内存,就按比例扣掉放在显存的那部分即可。

示范计算 A:一个假设的「35B、激活 3B」模型

以下的 35B 与 3B 是为了算式而假设的数字,不是任何真实模型的规格。

面向以标称 4 比特(b = 4)估算以 Q4_K_M 的 4.8944 比特估算
总权重 W_total35 × 10⁹ × 4 ÷ 8 = 17.5 GB35 × 10⁹ × 4.8944 ÷ 8 ≈ 21.4 GB
每 token 读取量 W_token3 × 10⁹ × 4 ÷ 8 = 1.5 GB3 × 10⁹ × 4.8944 ÷ 8 ≈ 1.8 GB
同总量的稠密 35B,每 token 读取量17.5 GB≈ 21.4 GB
稠密与 MoE 每 token 读取量比35 ÷ 3 ≈ 11.7 倍35 ÷ 3 ≈ 11.7 倍

从这张表可以直接读出两个重点。第一,总权重不会因为是 MoE 而变小:17.5 GB 到 21.4 GB 的权重,还是得放得进显存加系统内存的总和,所以「35B 只要 3B 的内存」是误解。第二,每个 token 要读的量从 17.5 GB(稠密)降到 1.5 GB,这才是 MoE 的好处所在。

示范计算 B:用 Qwen3-30B-A3B 的公开数据倒推共用部分

已知(Qwen3 技术报告):128 个专家、每次激活 8 个,所以 k ÷ E = 8 ÷ 128 = 1/16。名称给出 N_total ≈ 30B、N_active ≈ 3B。这两个名称数字是四舍五入的,所以结果只能看量级。

N_total × k ÷ E = 30 × 1/16 = 1.875 (B)
N_shared = (3 − 1.875) ÷ (1 − 1/16) = 1.125 ÷ 0.9375 = 1.2 (B)
N_experts = 30 − 1.2 = 28.8 (B)

以 b = 4.8944 估算:

共用部分 ≈ 1.2 × 10⁹ × 4.8944 ÷ 8 ≈ 0.73 GB
专家部分 ≈ 28.8 × 10⁹ × 4.8944 ÷ 8 ≈ 17.6 GB
合计     ≈ 30 × 10⁹ × 4.8944 ÷ 8 ≈ 18.3 GB

意思是:假如这个粗估大致成立,那么专家约占总权重的 28.8 ÷ 30 = 96%(每种参数用同样的比特数时,字节比例等于参数比例),每次只用其中 1/16;每个 token 都会用到的共用部分只占约 4%。这正是「把共用部分放显存、专家放系统内存」这个做法可行的原因。再次提醒,1.2B 是由四舍五入的名称数字倒推的,实际的共用参数量要看模型官方数据。

示范计算 C:用 Mixtral 说明文档的数字

文档给的是:总参数 45 billion,单次运算量相当于 14 billion 的模型,且文档在量化一节写到 45 billion 参数在半精度(float16,每参数 2 字节)需要约 90GB。

fp16 总权重:45 × 10⁹ × 16 ÷ 8 = 90 GB(与文档一致)
fp16 每 token 读取量(以「相当于 14B」近似激活参数):14 × 10⁹ × 16 ÷ 8 = 28 GB
同样 45B 的稠密模型,每 token 读取量:90 GB

文档接着说,量化成 4 比特后大约需要 27 GB。如果直接套公式,45 × 10⁹ × 4 ÷ 8 = 22.5 GB,比文档写的 27 GB 小。这个差距正好示范了一件事:公式算出的是下限,实际格式还有额外开销,所以估算要留余裕。另外文档的 14 billion 是「运算量相当于」的说法,不是逐字的「激活参数量」,所以上面第二行只是近似。

速度的上限分析(只做算式,不给数字)

在逐个 token 生成文字的时候,每生成一个 token,就要把这个 token 用到的权重从内存读进处理器。如果瓶颈在「读权重」(这是一个简化假设,实际还有计算、其他数据搬移等因素),那么:

每秒能生成的 token 数 ≤ 内存带宽 ÷ 每个 token 需读取的权重量

这是一个「上限」的算式,不是预测值。套用到 MoE 上,有三点可以做定性的推论:

  1. 其他条件相同,W_token 越小,上限越高。MoE 的 W_token 比同总量的稠密模型小,所以上限的算式比较有利。

  2. 若专家全在显存,分母用显存带宽;若专家在系统内存,专家那部分的读取量要除以系统内存带宽。时间大致是两段相加:

    每个 token 的时间 ≳ 读取自显存的量 ÷ 显存带宽 + 读取自系统内存的量 ÷ 系统内存带宽

    哪一项大,就是瓶颈。如果你的系统内存带宽比显存带宽低很多(请查自己电脑的规格,这篇不列任何型号的数字),专家那一项就会主导。

  3. 上限算式忽略了很多实务因素,所以它只能用来比较方向,例如「专家全放显存」与「专家放系统内存」哪个上限比较高,而不是算出你的机器会跑几 tokens/s。要知道实际速度,请自己测,方法见 Token/s 怎么看与正确的速度测试方法。

llama.cpp 的 llama-bench 说明里,--n-cpu-moe 与 -ot 也列在可以调整并比较的参数里,这代表你可以用同一套测试方法,比较不同「放哪里」的设置。但具体结果因硬件而异,本文不引用任何数字。

Dense 与 MoE 比较表

下面的每一格,都来自前面读到的文档,或是由前面的算式推出来的说法。「Dense」指稠密模型。

比较项目Dense 模型MoE 模型
总权重放在哪里全部放在显存,或其中一部分放在系统内存所有专家都要存在某处(Mixtral 文档:每个专家都必须加载 RAM);可选择把专家留在系统内存(llama.cpp 有相关旗标,以官方文档为准)
每个 token 的运算量与总参数量对应与激活参数量对应(Mixtral:45B 的模型,单次运算量相当于 14B)
每个 token 需读取的权重量N_total × b ÷ 8N_active × b ÷ 8,比同总量的稠密模型小
总权重大小N_total × b ÷ 8同样由 N_total × b ÷ 8 决定,不因 MoE 而缩小
对显存的要求全放显存时,约为总权重+KV 缓存+额外开销专家全放显存时与稠密模型同量级;专家放系统内存时,约降为共用部分+KV 缓存+额外开销
对系统内存的要求全放显存时要求较低专家放系统内存时,约为专家部分的权重大小,再加上操作系统与其他程序
速度主要受什么限制读取全部权重的内存带宽(由上限算式推得)读取激活权重的内存带宽;专家在系统内存时,受系统内存带宽影响(由上限算式推得)
适合的情况显存足够放下整个模型、想要行为单纯、好估算显存不足但系统内存很大,愿意用较复杂的设置,换取能使用更大的总参数量

最后一列是依前面推导的条件所下的判断:MoE 的好处需要「有一块够大的内存放专家」,而且你得接受设置较复杂、速度与带宽有关。如果你的显存本来就够放下整个模型,用 Dense 或 MoE 的取舍就会回到模型品质与用途,而不是内存。

常见误解

误解 1:「MoE 就是小模型。」 MoE 的总参数量是大的,文件也是大的。它的特点是「每个 token 只用一部分」,不是「整个模型比较小」。

误解 2:「A3B 就只要 3B 的显存。」 A 后面是激活参数,决定每个 token 的运算与读取量,不决定权重要放多少空间。总权重仍由总参数决定,见示范计算 A 与 B。

误解 3:「8x7B 就是 56B。」 Mixtral 文档写的是 45 billion。专家只取代每层的 MLP,其余共用,所以总量不是 8 乘 7。

误解 4:「把专家放到系统内存,速度就跟全放显存差不多。」 本文引用的文档没有这样说。从算式看,专家那一段的读取会受系统内存带宽限制。实际差多少,要用自己的机器测。

误解 5:「既然激活参数少,量化可以省略或随便选。」 量化决定的是 b,影响 W_total 与 W_token 两个算式,与是否为 MoE 无关。标称的比特数与实际每权重比特数也不同,见前面 Q4_K_M 的例子。

误解 6:「公式算出放得下,就一定载得进去。」 公式给的是权重本身的下限。Mixtral 文档的 4 比特估算(约 27 GB)就比直接套公式(22.5 GB)大。还要再加上 KV 缓存、运行时的额外开销与操作系统占用,估算时请留余裕。

这套估算什么时候不适用

  • 只适用于权重大小与逐个 token 生成的粗估。 上下文很长、一次处理很多 token 的情况,哪些专家会被用到、读取量怎么变,这篇没有查到文档说明,所以不做推论。
  • 名称里的数字是四舍五入的。 30B、3B 这种数字用来算量级可以,用来算精确的字节数不行。
  • 不同模型的专家设计不同。 专家数、每次激活数、有没有共享专家都不一样(Qwen3 技术报告提到它没有共享专家),套公式前先查 expert_count 与 expert_used_count。
  • 旗标与默认值会变。 这篇引用的是 llama.cpp 说明文档的内容,实际使用时请以你手上版本的官方文档为准。
  • 这篇不比较模型的好坏。 MoE 与 Dense 的回答品质如何,不在这篇范围,也没有从文档中查到可以引用的结论。

如果你不想自己下启动参数,可以先看Strata 与 PolyStrata 的比较,了解现成的本地大模型工具各自的定位。这些工具是否提供「把专家放到系统内存」的选项,要看它们自己的说明。

延伸阅读

常见问题

MoE 模型会比同样参数量的一般模型更省显存吗?

不会自动省。MoE 省的是每个 token 的运算量与要读取的权重量,所有专家的权重仍然要存在显存或系统内存。只有在你把专家权重放到系统内存时,显存的需求才会下降,而系统内存的需求会对应上升。

Qwen3-30B-A3B 的「A3B」是什么意思?

Qwen3 的 README 把 30B-A3B 与 235B-A22B 列为 MoE 模型,但没有逐字解释 A。Qwen3 技术报告把 Qwen3-235B-A22B 描述为总共 235B 参数、每个 token 激活 22B,照这个对应,A 后面的数字就是每个 token 激活的参数量。

怎么知道一个 GGUF 模型是不是 MoE?

看 GGUF 的 metadata:MoE 模型会有「专家总数」与「每个 token 使用的专家数」两个字段(expert_count 与 expert_used_count,前面会加上架构名称)。张量名称里出现 ffn_gate_inp、ffn_up_exp 这类名称,也代表有路由器与逐专家的权重。

35B 的 MoE 模型大概需要多少内存才载得进去?

先用「总参数 × 比特数 ÷ 8」算权重大小,例如 35B 以标称 4 比特存放约 17.5 GB。这只是下限,还要再加上 KV 缓存、运行程序的额外开销,以及操作系统与其他程序要用的内存。

MoE 模型一定比同大小的一般模型跑得快吗?

不一定。从算式看,每个 token 要读的权重变少,速度上限可以提高;但如果专家放在系统内存,上限会被那一块内存的带宽压住,实际快慢要用自己的机器测。

Mixtral 8x7B 的总参数是不是 8 乘 7 等于 56B?

不是。Hugging Face 的 Mixtral 说明文档写的是总共 45 billion 参数。因为专家只取代每一层里的 MLP,注意力等其他部分是共用的,所以不是把 7B 的模型拷贝八份。

留言

用 GitHub 账号登录就能留言,留言会公开显示在 GitHub Discussions。留言前请先看社区规则。

本地大模型