为什么 DeepSeek 说 800B 训练不起?把大模型的账算一遍

「7B 参数」「671B 总参数、37B 激活」——这类说法你几乎每天都能刷到。但真去算一笔账,会发现对不上:参数到底是什么?参数多为什么就要很多张卡?为什么 DeepSeek 说稠密 800B 他们训练不起,转头却训出了一个 671B?

我最开始把参数理解成「每一层里被模型理解的 token」,结果显存账怎么算都是错的。后来把这条线一段段捋直,发现它其实就是一道小学算术题,只是有几个概念容易串门。

参数是矩阵里的数字,不是 token

这是最容易混的第一步。

token 是数据——文本切成的片段,一句话差不多切成二十来个。它是喂进去的输入。

参数是模型——一堆权重矩阵,训练完就固定了。它是做运算的那个表格。

一个 token 进来,是流过每一层的参数矩阵,被做乘法。参数不「理解」token,参数是用来算 token 的那台机器。分开看:

1
token(数据) → 乘 W(参数矩阵) → 输出(数据)

所以「7B 参数」的意思很朴素:把整个模型里所有矩阵的数字加起来,一共七十亿个。

一层里到底有多少个矩阵

我原来以为一层就一个矩阵,其实远不止。一个 Transformer 隐藏层里至少有六个:

  • Attention 部分W_qW_kW_v(生成 Q/K/V)、W_o(输出投影)
  • FFN 部分W_1(升维)、W_2(降维)

这里有个容易搞反的地方:Q、K、V 不是参数。它们是 x 乘完矩阵之后算出来的数据,是激活值。真正被训练的是 W_qW_kW_v 那三张表格。

再算一笔账就更有意思了。假设隐藏维度是 4096:

矩阵 形状 参数量
W_q / W_k / W_v / W_o 4096×4096 各 16.8M
W_1 4096×16384 67M
W_2 16384×4096 67M

Attention 四个加起来约 67M,FFN 两个就 134M——一层里 FFN 的参数是 attention 的两倍。 所以整个模型里,参数大头在 FFN,不在 attention。

(顺带一说,MoE 拆的正是 FFN:既然这里参数最多,切成「专家」收益才最大。)

唯一不带参数的,是激活函数

讲到这里通常会有个疑问:矩阵之间夹的那个 ReLU 呢?它算参数吗?

不算。ReLU(x) = max(0, x)——负数变 0,正数不变。它是写死的公式,里面没有任何可以训练的数字,像计算器上那个开根号键。

这带来一个干净的结论:

参数量只统计那些训练出来的矩阵。激活函数、公式、超参数,都不算进「7B」里。

所以「参数量 × 某个常数」这个算法能成立——因为只有参数才需要存、需要算梯度、需要优化器状态。 别的什么都没有。

一个参数,训练时要占 18 个字节

接下来是显存账的核心。

参数不是只存一份,训练时要存五份。 拿其中一个参数举例:

存的东西 精度 字节 干什么用
权重 bf16 2 前向传播
master weight fp32 4 高精度副本,承接更新
梯度 fp32 4 反向传播算出的方向
Adam 一阶动量 fp32 4 平滑方向
Adam 二阶动量 fp32 4 自适应步长
18

拆开看,就是「参数本体」加「训练时才有的一堆附件」。

本体是两份:bf16 那份走前向,图快;fp32 那份是「真身」,负责承接更新。为什么要多一份?因为参数更新时会减去一个很小的量,这个小量在 bf16 的粗糙刻度上经常被直接抹掉——参数就冻住了。所以要有一份 fp32 来承接它。

附件是三份:梯度是这一轮算出来的方向;两个动量是 Adam 记的历史,得跨迭代留着。

这五份,每一个参数都要存一份,跟它在哪一层没关系W_1W_vW_2 里的每个数字都是同样的 18 字节,最后乘总参数量。

于是:

  • 训练:参数量 × 18
  • 推理:只留权重那份 = 参数量 × 2

差 9 倍。这就是为什么「本地能跑 671B」和「能训练 671B」隔着一条鸿沟——它们根本不是同一笔账。

显存和算力,是两张分开的账单

账到这里要分岔了。模型有两个成本:

显存账单 算力账单
管什么 装多少参数 每次算多少参数
什么时候付 加载时一次性 每处理一个 token 都付

粗看这两者绑在一起:参数多,两张账单一起涨。但 MoE 出现之后,它们被拆开了——这是后面所有内容的钥匙。

先看算力账单怎么算。训练所需的计算量有个经验公式:

1
FLOPs ≈ 6 × N × D

N 是参数量,D 是训练用了多少 token。

DeepSeek 那道算术题

现在可以回答标题里的问题了。

假设要训一个稠密 800B 模型,用 15T token:

1
6 × 8e11 × 1.5e13 ≈ 7.2e25 FLOPs

一张 H100 的 bf16 理论算力约 1000 TFLOPS,训练时实际能跑到四成,按 400 TFLOPS 算:

1
7.2e25 / 4e14 ≈ 1.8e11 秒 ≈ 5700 GPU-年

2048 张卡也要跑将近三年。 这就是「训练不起」的由来——不是存不下,是算力账单付不起。

那 DeepSeek 的 671B 又是怎么训出来的?因为它走的是 MoE。

MoE:用显存换算力

MoE(混合专家)的做法,是把 FFN 拆成很多个专家,每个 token 只走其中几个。

以 671B / 37B 激活为例:总参数 671B,但每个 token 实际参与计算的只有 37B。算力公式里的 N,从「总参数」换成了「激活参数」:

1
6 × 3.7e10 × 1.48e13 ≈ 3.3e24 FLOPs

比稠密 800B 低了二十倍——2048 张 H800 跑两个月就够。这跟公开的技术报告是对得上的。

「激活」在这里的准确含义是:这个参数这一轮参与计算了。像点名——全班五十个同学都在名单上,但这节课只点到两个回答问题。没点到的不是「没发挥作用」,是这一轮没上场,下个 token 可能就轮到他们。

所以 MoE 的本质是一次交易:

1
2
付出:按 671B 买的显存(专家全都要待命)
换来:按 37B 付的算力

有个地方特别容易想反——MoE 省算力,不省显存。那些没被选中的专家,权重照样得放在显存里,因为下一批 token 可能就选中它们了。所以 MoE 不是省钱的魔法,是拿显存换算力

这笔交易划算吗?看两个事实:显存是一次性买卡,算力是每 token 持续烧;而模型「懂多少」跟总参数量有关,「跑多快」跟激活参数量有关。稠密模型把这两个旋钮焊死了,MoE 把它们拆成了两个。 能分开调,就有优化的空间。

这条线其实只有三个数字

回头看,整篇东西能压成三句:

  1. 参数是矩阵里的数字,参数量 = 所有矩阵数字的总和
  2. 训练时每个参数占 18 字节,乘出来是显存账单
  3. 训练算力约 6 × N × D,其中 N 用激活参数量,乘出来是算力账单

MoE 做的事,就是让第 2 条按大数付、第 3 条按小数付。


如果你也想验证自己有没有真的搞懂,有个很快的自测:说清楚「一个稠密 70B 模型,训练和推理分别要多少显存」。算出来大概分别是 1.2TB 和 140GB,差的那八倍,就是梯度和优化器状态。能把这两个数分开,这条线就通了。