大模型训练的成本焦虑已经从「能不能训」变成「能不能训得起」。同样规模的模型,不同团队烧的钱可能差一个数量级,差距不在显卡型号,而在算法与系统是不是一起设计的。DeepSeek-V3 用 1024 张 H800、约 278.8 万 GPU 小时、折合约 558 万美元跑完 14.8 万亿 token,这个数字本身就说明了一件事:省钱不是抠出来的,是设计出来的。
常规思路很难接近这个效率。只优化算法,KV Cache 和激活值照样把显存吃满;只优化系统,通信气泡和专家负载不均照样让 GPU 空转;FP8 敢不敢用、辅助损失加不加,每一个决定都会牵动另一端。单点优化解决不了协同问题。
本文分享一套可复现的拆解视角:MLA + 细粒度 MoE + 无辅助损失均衡作为算法侧,DualPipe + 跨节点重叠通信 + FP8 混合精度作为系统侧,再用真实账单反推成本结构,可直接对照到自己的训练任务上。
一、成本漏点:GPU 时间究竟空转在哪
先看 1024 卡集群的钱到底漏在哪几处:
- 通信空转:张量并行每层 all-reduce、流水线并行的阶段等待,都会让 GPU 停在 barrier 上不产生有效 FLOPs。
- 显存挤压:优化器状态、梯度、激活值三座大山决定了 micro-batch 大小,batch 上不去,算力利用率跟着上不去。
- 负载失衡:MoE 里少数热门专家被打满,其余专家闲着,等效算力远低于峰值。
- 精度保守:全程 BF16 意味着同样的显存放更少的参数、跑更小的 batch,隐性成本极高。
问题:算法和系统各改各的,单边收益被另一端吃掉。治理:从「每个 token 的显存」和「每次通信的等待」两条线同时下手。
**核心结论:**训练成本的三大漏点是通信空转、显存挤压、负载失衡,DeepSeek-V3 的每个设计都精准对着这三点。

二、算法侧:MLA 压 KV、细粒度 MoE 砍激活量
先在「每个 token 的显存与计算」上做减法:
- MLA 低秩压缩:把多头 KV 投影成一个低维 latent 向量缓存,推理时每 token 的缓存体积远小于 MHA,也显著小于同头数的 GQA。
- 共享专家兜底:保留 1 个共享专家承载通用知识,路由专家只学增量,减少冗余参数被反复激活。
- 细粒度路由专家:把专家切细到 256 个,每 token 只激活 8 个,等效激活参数量约 37 亿,总参数却有 671 亿。
| 方案 | 每 token 缓存 | 计算特性 | 备注 |
|---|---|---|---|
| MHA | 高(头数 × 维度) | 独立投影 | 缓存瓶颈最大 |
| GQA | 中(分组共享) | 分组投影 | 工程折中 |
| MLA | 低(低维 latent) | 压缩后投影 + 恢复 | 缓存最小、矩阵乘更规整 |
下面用最短代码演示细粒度 MoE 的 top-k 路由前向,适配 PyTorch 单卡环境:
python
import torch, torch.nn as nn
class FineGrainedMoE(nn.Module):
def __init__(self, d=512, n_expert=256, k=8):
super().__init__()
self.k = k
self.gate = nn.Linear(d, n_expert, bias=False)
# 细粒度专家:每个专家只有 d/2 维中间层
self.experts = nn.ModuleList(
nn.Sequential(nn.Linear(d, d // 2), nn.GELU(), nn.Linear(d // 2, d))
for _ in range(n_expert))
self.shared = nn.Sequential(nn.Linear(d, d), nn.GELU(), nn.Linear(d, d))
def forward(self, x): # x: [B, T, d]
score = self.gate(x).softmax(-1)
topv, topi = score.topk(self.k, dim=-1) # 每 token 只走 k 个专家
out = self.shared(x) # 共享专家始终参与
for j in range(self.k):
idx = topi[..., j]; w = topv[..., j: j + 1]
picked = torch.stack([self.experts[i](x[t])
for i, t in zip(idx.flatten().tolist(),
range(x.shape[0] * x.shape[1]))])
out = out + w * picked.reshape_as(x)
return out / self.k
总参数撑规模、激活参数控成本,MLA 再把推理缓存一并压下去。
三、路由均衡:不加辅助损失,也能防止专家塌缩
MoE 最怕路由塌缩,DeepSeek-V3 的解法是把「选专家」和「限容量」拆成两个信号:
- 辅助损失的老问题:为了均衡强行加正则项,会直接污染主任务梯度,模型越训越别扭。
- 偏置项调节:路由打分用 logits,容量判定再加上一个可调 bias,bias 只影响是否被选,不参与梯度反传。
- 动态升降:每个专家记录近期负载,过载就抬 bias、欠载就降 bias,均值保持中性,总量不变。
- 配合设备级均衡:把专家按节点切分后先做设备级负载约束,再做设备内路由,避免某个节点整体被打满。
python
# 无辅助损失的偏置调节(每个专家每步更新一次)
# load: 本 step 各专家实际接收的 token 数占比
# target = 1 / n_expert
bias = torch.zeros(n_expert)
bias += learning_rate * (load - target) # 过载抬高会被继续压,欠载反向
# 两套信号分离:routing_score 用原始 logits,capacity 判断用 logits + bias
selected = (logits + bias * is_capacity_check).topk(k, dim=-1)
| 均衡手段 | 是否污染主 loss | token 是否被丢弃 | 工程复杂度 |
|---|---|---|---|
| 辅助损失 | 是 | 可能 | 低 |
| 随机分发 | 否 | 是 | 中 |
| 偏置项动态调节 | 否 | 否 | 中偏高 |
**核心结论:**把均衡压力从梯度转移到一个不可导的偏置上,是「不伤主 loss、不丢 token」的关键一手。
四、系统侧:DualPipe 与通信重叠,把等待藏起来
算法省下的算力,还得靠调度别再空转:
- DualPipe 正反向对插:把前向和反向两个流水线阶段彼此嵌入对方的等待窗口,气泡被压缩到远小于传统 1F1B。
- 分层并行:节点内 NVLink 走 TP 与 DP 的 all-reduce,跨节点 all-to-all 走 InfiniBand,避免不同链路互相挤占。
- EP 通信全重叠:专家分发(dispatch)、结果合并(combine)与每层的前反向计算重叠执行,通信时间基本被算力吃掉。
- 跨节点 RDMA 直连:路由侧开启 IBGDA 类直通路径,减少 CPU 参与转发带来的尾延迟。
| 策略 | 作用域 | 主要流量 | 重叠手段 |
|---|---|---|---|
| TP | 节点内 8 卡 | all-reduce | 层内切分 |
| PP | 跨节点 | 点对点激活 | DualPipe 对插 |
| EP | 跨节点 | all-to-all | dispatch/combine 与计算重叠 |
| DP | 跨节点 | 梯度 all-reduce | 与反向计算重叠 |
A(分层并行)→ B(DualPipe 对插)→ C(EP 通信重叠)→ D(气泡趋近于零)
**核心结论:**并行方式本身不省钱,让每种并行只走自己的链路、并把通信藏进计算,才省钱。

五、精度与显存:FP8 混合精度加按需重计算
显存和算力的最后一刀砍在精度上:
- 双精度格式分工:矩阵乘的前向用 FP8 E4M3 保精度,梯度放大用 E5M2 保动态范围。
- tile/block 级缩放:不整块除以一个 scale,而是按小 tile 求最大绝对值再缩放,避免个别离群值把整块精度拉平。
- 关键路径保底:注意力、归一化、残差等敏感算子回退 BF16,主权重与优化器状态保持更高精度维护。
- 重计算换显存:只存部分层的激活值,反向时按需重算,用约 1/3 的额外计算换近半的激活显存。
下面这段代码演示 FP8 量化的最小实现,适配任意张量环境:
python
import torch
def fp8_quantize(x, block=128):
# tile 级缩放:每个 block 一个 scale,避免离群值毁掉整块精度
scale = x.abs().amax(dim=-1, keepdim=True).clamp(min=1e-12) / 448.0 # E4M3 max
q = (x / scale).to(torch.float8_e4m3fn)
return q, scale
def fp8_matmul(a, b, block=128):
qa, sa = fp8_quantize(a, block)
qb, sb = fp8_quantize(b, block)
return (qa.float() @ qb.float()) * sa * sb.transpose(-1, -2)
强格式分工 → 细粒度缩放 → 敏感算子回退,FP8 才从「冒险」变成「默认选项」。
六、账单复盘:558 万美元的成本结构拆开看
把账算清楚,才能知道省的钱从哪来:
| 项目 | 数值 | 说明 |
|---|---|---|
| GPU 数量 | 1024 张 H800 | 未使用万卡级更大集群 |
| 总 GPU 小时 | 约 2,788,000 | 训练全程累计 |
| 折算价格 | 约 2 美元 / GPU 小时 | 取市场租用中位价 |
| 总成本 | 约 557.6 万美元 | 仅计租金,不含研发 |
| 训练数据 | 14.8 万亿 token | 两阶段多轮退火 |
| 训练时长 | 约 37 天 | 单次成功率高,重跑极少 |
- 省在利用率而非单价:同样 2 美元一小时的卡,气泡少、专家均衡,每小时产出的等效 token 数更高。
- 省在返工率:训练全程稳定、掉卡可恢复,一次跑通比任何折扣都值钱。
- 省在激活参数:671 亿总参数带来模型能力,37 亿激活参数决定训练开销,二者解耦是账单能压住的根本。
- 隐性成本别漏算:算法试验、数据清洗、人力都不在 558 万内,真实投入远高于此,别拿账单当全部成本。
**核心结论:**558 万美元买的不是便宜算力,而是「高利用率 + 高成功率 + 低激活量」三者叠加的每一美元产出。

结语
把 DeepSeek-V3 的技术路线摊开看,它没有一个「神奇算法」,而是一条完整的因果链:MLA 和细粒度 MoE 压住每 token 的显存与计算,无辅助损失均衡保住路由不塌缩,DualPipe 与通信重叠把等待时间藏进计算,FP8 与重计算再从精度和显存上收尾。每一环都在为下一环创造空间。
对工程团队的启示很直接:下一次评估训练方案时,别只看模型结构图,把 GPU 利用率、通信重叠比例、专家负载方差、激活显存占用一起摆上桌面,账单会比任何宣传更诚实。
省钱从来不是省钱本身,而是算法与系统一起被算清楚的结果。