第1章:显存去哪儿了?——模型权重、KV Cache、中间激活值、优化器状态的内存占用拆解与估算公式
做推理部署的朋友,十有八九都被显存问题折磨过。
我记得刚入行那会儿,拿着一个13B的模型往A100上怼,心想“80G显存,总够了吧?”结果一跑起来,OOM了。我当时就懵了——模型权重才26G啊,剩下的54G去哪儿了?
后来我才明白,显存里住着的,远不止模型权重这一位“房客”。今天咱们就来拆一拆,显存到底被谁吃了,以及怎么提前算清楚。
1.1 显存里的四大“住户”
推理时,显存主要被四类数据占据:
- 模型权重:就是模型参数本身,比如W、b这些矩阵。
- KV Cache:自回归推理时,缓存的历史Key和Value。
- 中间激活值:前向传播过程中,每一层的输出(比如LayerNorm后的结果)。
- 优化器状态:训练时才有的东西,比如Adam里的momentum和variance。推理时没有。
嗯,这里要注意:推理和训练,显存占用结构完全不同。推理时没有优化器状态,但KV Cache是主角。训练时KV Cache可以忽略,但优化器状态和中间激活值才是大头。
1.2 模型权重:最直观的“房客”
模型权重的计算最简单。说白了就是:参数量 × 每个参数占的字节数。
举个例子:
- 一个7B模型,用FP16(2字节)存储,权重占用 = 7 × 10⁹ × 2 = 14 GB
- 如果换成INT4(0.5字节),占用 = 7 × 10⁹ × 0.5 = 3.5 GB
我个人习惯用这个公式快速估算:
权重显存(GB) = 参数量(B) × 字节数 × 1.05
多出来的5%是给一些额外开销留的,比如embedding层、bias等。我在项目中遇到过,有些模型实际加载后比理论值多出2-3%,所以加个5%的buffer比较稳妥。
| 精度 | 每参数字节 | 7B模型 | 13B模型 | 70B模型 |
|---|---|---|---|---|
| FP32 | 4 | 28 GB | 52 GB | 280 GB |
| FP16 | 2 | 14 GB | 26 GB | 140 GB |
| INT8 | 1 | 7 GB | 13 GB | 70 GB |
| INT4 | 0.5 | 3.5 GB | 6.5 GB | 35 GB |
1.3 KV Cache:推理时的“隐形巨兽”
KV Cache是自回归推理特有的东西。你想想看,每次生成一个token,都要重新计算所有历史token的注意力吗?当然不是。我们把之前算好的Key和Value缓存起来,这就是KV Cache。
它的计算公式是:
KV Cache大小 = 2 × 层数 × 隐藏维度 × 序列长度 × 字节数
为什么是2?因为Key和Value各一份。
举个例子,一个7B模型(32层,隐藏维度4096),用FP16,生成2048个token:
KV Cache = 2 × 32 × 4096 × 2048 × 2 = 1.07 GB
如果生成8192个token呢?
KV Cache = 2 × 32 × 4096 × 8192 × 2 = 4.29 GB
看到了吗?序列长度翻4倍,KV Cache也翻4倍。这就是为什么长文本推理时显存会暴涨。
1.4 中间激活值:被忽略的“隐形住户”
中间激活值,就是每一层前向传播时产生的临时张量。比如LayerNorm的输出、FFN的中间结果等。
它的估算比前两个复杂一些。一般来说,中间激活值的大小约等于:
中间激活值 ≈ 层数 × 隐藏维度 × 序列长度 × 字节数 × 系数
这个系数取决于模型结构。对于标准的Transformer,系数大约在3-5之间。为什么?因为每层至少需要保存:
- Attention的Q、K、V(3个张量)
- Attention的输出(1个)
- FFN的中间结果(2个,gate和up)
- LayerNorm的输入输出(2个)
加起来差不多8-10个张量,但有些可以复用或释放,所以实际系数在3-5左右。
举个例子,7B模型,序列长度2048,FP16:
中间激活值 ≈ 32 × 4096 × 2048 × 2 × 4 = 2.15 GB
1.5 优化器状态:训练时的“显存黑洞”
训练时,优化器状态才是真正的显存大户。以Adam优化器为例,每个参数需要保存:
- 模型权重本身(1份)
- 动量(momentum,1份)
- 方差(variance,1份)
- 梯度(gradient,1份)
一共4份。如果模型是FP16,那优化器状态就是4 × 2 = 8字节每参数。加上权重本身的2字节,总共10字节每参数。
所以一个7B模型,用Adam训练:
总显存 ≈ 7B × 10 = 70 GB
这就是为什么训练大模型需要那么多卡。我见过有人用单卡A100(80G)训练7B模型,结果连模型都加载不进去。
- 推理时:权重 + KV Cache + 中间激活值
- 训练时:权重 × 4(Adam) + 中间激活值(更大)
- KV Cache随序列长度线性增长,是长文本推理的瓶颈
- 优化器状态是训练时的显存黑洞,占权重的3倍
1.6 一张图看懂显存分配
下面这张图,是我自己画的一个显存分配示意图。你可以看到,不同场景下,各个“住户”的占比完全不同。
1.7 实战估算:一个完整的例子
假设我们要部署一个13B模型,FP16精度,最大序列长度4096。我们来算算需要多少显存。
- 模型权重:13B × 2 = 26 GB
- KV Cache:2 × 40层 × 5120隐藏维度 × 4096序列长度 × 2 = 3.35 GB
- 中间激活值:40 × 5120 × 4096 × 2 × 4 = 6.71 GB
- 其他开销(CUDA context、框架缓存等):约2-3 GB
总计:26 + 3.35 + 6.71 + 2.5 ≈ 38.6 GB
嗯,这个数字意味着什么?一张A100(80G)完全够用,甚至还能跑个batch size 2。但如果换成H100(80G),因为H100的显存带宽更高,实际吞吐会更好。
1.8 避坑指南
最后,分享几个我踩过的坑:
- 别信理论值:理论计算只是起点。实际加载模型时,框架会多分配一些内存。我见过一个13B模型,理论26G,实际加载后吃了31G。
- KV Cache的batch维度:上面算的是单条序列。如果batch size是4,KV Cache要乘以4。很多人忘了这个。
- 中间激活值可以优化:推理时,有些框架(如TensorRT-LLM)会复用中间张量的内存,实际占用比理论值小很多。
- 量化不是免费的:INT4虽然省显存,但推理时可能需要额外的反量化操作,反而增加延迟。我建议先测一下再决定。
好了,显存去哪儿了,现在你应该心里有数了。记住一句话:推理看KV Cache,训练看优化器状态。这两个是显存的大头,其他都是小角色。
公众号:蓝海资料掘金营,微信deep3321