「7B 参数」「671B 总参数、37B 激活」——这类说法你几乎每天都能刷到。但真去算一笔账,会发现对不上:参数到底是什么?参数多为什么就要很多张卡?为什么 DeepSeek 说稠密 800B 他们训练不起,转头却训出了一个 671B?
我最开始把参数理解成「每一层里被模型理解的 token」,结果显存账怎么算都是错的。后来把这条线一段段捋直,发现它其实就是一道小学算术题,只是有几个概念容易串门。
参数是矩阵里的数字,不是 token
这是最容易混的第一步。
token 是数据——文本切成的片段,一句话差不多切成二十来个。它是喂进去的输入。
参数是模型——一堆权重矩阵,训练完就固定了。它是做运算的那个表格。
一个 token 进来,是流过每一层的参数矩阵,被做乘法。参数不「理解」token,参数是用来算 token 的那台机器。分开看:
1 | token(数据) → 乘 W(参数矩阵) → 输出(数据) |
所以「7B 参数」的意思很朴素:把整个模型里所有矩阵的数字加起来,一共七十亿个。
一层里到底有多少个矩阵
我原来以为一层就一个矩阵,其实远不止。一个 Transformer 隐藏层里至少有六个:
- Attention 部分:
W_q、W_k、W_v(生成 Q/K/V)、W_o(输出投影) - FFN 部分:
W_1(升维)、W_2(降维)
这里有个容易搞反的地方:Q、K、V 不是参数。它们是 x 乘完矩阵之后算出来的数据,是激活值。真正被训练的是 W_q、W_k、W_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_1、W_v、W_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 | 付出:按 671B 买的显存(专家全都要待命) |
有个地方特别容易想反——MoE 省算力,不省显存。那些没被选中的专家,权重照样得放在显存里,因为下一批 token 可能就选中它们了。所以 MoE 不是省钱的魔法,是拿显存换算力。
这笔交易划算吗?看两个事实:显存是一次性买卡,算力是每 token 持续烧;而模型「懂多少」跟总参数量有关,「跑多快」跟激活参数量有关。稠密模型把这两个旋钮焊死了,MoE 把它们拆成了两个。 能分开调,就有优化的空间。
这条线其实只有三个数字
回头看,整篇东西能压成三句:
- 参数是矩阵里的数字,参数量 = 所有矩阵数字的总和
- 训练时每个参数占 18 字节,乘出来是显存账单
- 训练算力约
6 × N × D,其中N用激活参数量,乘出来是算力账单
MoE 做的事,就是让第 2 条按大数付、第 3 条按小数付。
如果你也想验证自己有没有真的搞懂,有个很快的自测:说清楚「一个稠密 70B 模型,训练和推理分别要多少显存」。算出来大概分别是 1.2TB 和 140GB,差的那八倍,就是梯度和优化器状态。能把这两个数分开,这条线就通了。