☰
大模型显存估算全指南:从权重到KV Cache的完整计算与避坑
2026/10/12 4:10:22 网站建设 项目流程

我平时被问得最多的一类问题就是:“这个7B模型我手上的卡能跑吗?”或者“微调一下要多大的显存?”说实话,大模型的显存估算这块,看起来就是乘一下参数量,但实际算下来经常翻车。真正把权重、KV Cache、激活、优化器状态这些项分开拉一遍,你才能知道自己到底缺的是显存还是缺的是算法。

这篇就是把我自己常用的估算方法、手算公式和踩过的坑整理一遍。既适合要本地部署推理的人,也适合准备微调的开发者。我会先讲显存到底被什么吃掉,再给出可以手算的公式,最后用几个实例和速查表直接给结论,你可以照着抄。

1. 先搞清楚一个铁律:模型有多大,不完全等于显存要多少

很多人上来就问“7B模型几个G”,然后拿模型文件的体积去比显卡容量,这其实是个误区。模型文件的大小主要等于权重参数的体积,但实际跑起来,显存里装的可不止权重这一样。

我习惯把显存消耗拆成两部分:固定开销和动态开销。固定开销是模型加载进来就必须占住的,比如权重本身、CUDA上下文、计算图、框架缓存;动态开销则是跟着输入走的,比如推理时的KV Cache、前向传播的中间激活值、训练时的梯度和优化器状态。

拿推理举例,一个7B模型,FP16权重文件大概14GB,你觉得一张24GB显存的显卡肯定能装下。但实际跑起来,系统要留驱动上下文,CUDA要预留一些显存,如果你的batch大于1,KV Cache还会随序列长度快速增长。结果就是,明明权重只占14GB,跑4096上下文却可能直接OOM。这不是模型想多占,而是你没把动态开销算进去。

我自己的习惯是,所有估算先分成“推理”和“训练”两套账本,因为两者的开销项差太多了。推理只要管权重、KV Cache、激活,训练还要额外管梯度、优化器状态和反向传播的激活。后面各部分我会分开讲。

1.1 推理和训练账单的关键差异

开销项推理场景训练/微调场景
权重参数高高
梯度无有
优化器状态(动量、方差等)无极高,常超过权重本身
KV Cache随序列长度增长随序列长度增长
中间激活值相对较小反向传播需要保存,内存占用大

表格里最反直觉的是训练那一列:混合精度训练时,优化器状态占的显存经常是权重的好几倍。后面我专门计算一下,你会发现全参数微调7B模型,光优化器状态就可能需要接近权重6倍的显存。

2. 手算的前提:先记住这些基础数字

估算显存之前,有四个概念必须先量化,不然公式都套不进去。

第一,参数精度对应的字节数。FP32是4字节,FP16/BF16是2字节,INT8是1字节,INT4大约0.5字节。绝大多数开源模型默认发布FP16或BF16权重,所以7B模型的裸权重直接就是 7 × 10^9 × 2字节 ,约等于13.04GB。我看到网上很多人用14GB这个说法,那是用2^30换算后加了点格式头的近似值,手算时用1000进制和1024进制会有细微差别,差别不大,但别被精确数值误导。

第二,层数和头数决定KV Cache大小。以Transformer decoder结构为例,每层都会有KV Cache,多头注意力里K和V要各自缓存一份。这里有个最容易被忽略的变量叫“KV头数”,很多新模型用GQA(分组查询注意力),KV头数不等于注意力头数,如果拿注意力头数去算,会高估不少。

第三,batch和序列长度是乘法因子。KV Cache总量直接正比于batch × 序列长度,激活内存也正比于batch。很多人估算时只看了模型参数量,忽略了自己要跑多大的batch,结果算出来总是偏小。

第四,显存的最低基准不是0。哪怕只加载一个模型什么都不做,CUDA上下文也可能吃掉几百MB到1GB。显卡驱动、推理框架的预分配空间,这些都是“保护费”,估算时必须预留。

有了这些基础,下面的公式就能看懂了。我直接给出我手算时用的版本,不搞复杂推导。

2.1 单位换算的坑

我建议所有计算统一用字节再做除法。比如7B参数,FP16:7_000_000_000 × 2 = 14_000_000_000字节。然后除以1024三次得到GB:14_000_000_000 / 1024^3 ≈ 13.04GB。如果你用1000进制去除,会得到14GB,虽然结果相差不大,但如果后面要精确设计batch size,差1GB可能会影响OOM判断。我自己会在心里用“参数量 × 字节数 × 0.931 = GB”的口诀,0.931是1/1024^3约等于的值,估算时直接乘。

3. 推理场景估算:权重、KV Cache和激活三件套

推理显存的完整公式可以写成:

总显存 ≈ 权重显存 + KV Cache显存 + 激活/临时显存 + 框架/上下文预留

这里我把权重显存单独讲,因为它最好算。公式是:

权重显存 = 参数量 × 精度字节数

一个常见7B模型,FP16权重约13.04GB;如果加载成INT8,则约6.51GB;INT4约3.26GB。这也是为什么量化之后模型“缩水”这么明显——权重是模型唯一文件级别的显存开销,量化直接砍这部分。

3.1 KV Cache公式与推导

KV Cache是推理里最容易被低估的一块。公式:

KV Cache显存 = batch × 序列长度 × 层数 × KV头数 × 头维度 × 2(K和V各一份) × 精度字节数

这里的精度字节数和权重一样,FP16就是2字节。为什么每层都要缓存KV?因为生成新token时要计算注意力,而前面所有token的K和V会被重复用到,为了不重算历史计算结果,就把它们存下来。

举个例子:假设某7B模型有32层,每个head的维度是128,KV头数是8(GQA结构,与注意力头数不同),FP16,batch为1,序列长度4096。

KV Cache = 1 × 4096 × 32 × 8 × 128 × 2 × 2

先算每层每头KV:8 × 128 × 2 × 2 = 4096字节;乘以32层得到每token约131072字节,也就是128KB/token。再乘以4096长度,得到512MB。嗯,512MB看起来不多,但如果batch变成8,就是4GB;如果模型是70B级别,层数和头数都变大,4096上下文可能直接吃满20GB以上。

我之前调一个本地部署时遇到一个问题:一张24GB的卡,加载13B模型,FP16权重约26GB,显然不行;换成4bit量化后权重只有约7GB,我以为很稳,结果batch设8、序列长度一拉长,KV Cache直接超过10GB,最后还是OOM。原因就是我只关注了权重,没有关注KV Cache随batch和长度线性放大。

3.2 激活值与临时显存

除了KV Cache,前向计算中间也会产生各种中间张量,比如注意力分数、LayerNorm输出等。推理时一般不需要保存完整的反向图,所以激活显存相对训练要小很多,但并不为零。粗略估算时,我们可以把它当成“最后10%到20%的余量”,或者用更朴素的方法:把权重、KV Cache算完后,再看剩余显存是否够激活和框架开销。

3.3 我一个可以直接抄的Python估算脚本

很多人喜欢用库里的方法,但我手算时习惯写一个几十行的小脚本,把模型参数敲进去,立刻能看到各分项。脚本逻辑非常简单,就是上面几个公式的代码化。我在本地常用下面这个原型逻辑:

def estimate_inference(model_params=7_000_000_000, layers=32, kv_heads=8, head_dim=128, seq_len=4096, batch_size=1, bytes_per_param=2, context_reserve_gb=1.5, activation_reserve_gb=1.0): # 权重显存,单位 GB weight_gb = model_params * bytes_per_param / 1024**3 # KV Cache 显存 kv_bytes_per_token = layers * kv_heads * head_dim * 2 * bytes_per_param kv_cache_gb = batch_size * seq_len * kv_bytes_per_token / 1024**3 total_gb = weight_gb + kv_cache_gb + context_reserve_gb + activation_reserve_gb print(f"权重: {weight_gb:.2f} GB") print(f"KV Cache: {kv_cache_gb:.2f} GB") print(f"框架/上下文预留: {context_reserve_gb:.2f} GB") print(f"激活预留: {activation_reserve_gb:.2f} GB") print(f"合计: {total_gb:.2f} GB") estimate_inference()

跑出来的结果:

权重: 13.04 GB KV Cache: 0.50 GB 框架/上下文预留: 1.50 GB 激活预留: 1.00 GB 合计: 16.04 GB

这时候用24GB的卡就比较从容。如果改成batch=8,KV Cache变成4GB,合计接近20GB,24GB仍能扛住;如果batch=16,KV Cache变成8GB,合计约24GB,这就非常危险了,因为驱动、库缓存等可能还额外占几百MB,极其容易OOM。

3.4 量化后怎么改数字

量化推理时,权重显存变少,但KV Cache依然可以用高精度,比如有些框架会默认KV Cache保持FP16。4bit权重加上FP16的KV Cache,很多时候瓶颈就不是权重而是KV Cache了。我自己碰到过一个情况:模型只有6GB,但长文本下KV Cache占到8GB,再加激活,整卡快满了。

这引出一个操作建议:如果长上下文场景多,优先保证KV Cache准确性,因为KV Cache是逐token复用的,误差会在长序列里累积。不要为了省显存把KV Cache也压到低精度,除非框架在注意力层面做了补偿。

4. 训练和微调场景:为什么显存需求瞬间翻倍

训练比推理多出来的是梯度和优化器状态,特别是优化器状态,这个是最大的坑。很多人第一次跑微调时看到loss正常降,突然反馈显存不足,还以为代码有问题,其实是因为没有算优化器状态。

4.1 AdamW优化器的显存账单

以混合精度训练为例,比如FP16权重前向、FP32主权重更新这种常见策略。普遍规则是:

  • 模型参数FP16:2字节/参数
  • 梯度FP16:2字节/参数
  • FP32主权重副本:4字节/参数
  • AdamW的一阶动量m:4字节/参数
  • AdamW的二阶动量v:4字节/参数

合在一起是16字节/参数。这比单纯推理的2字节/参数,直接翻了8倍。假设同样是7B模型,满参数训练仅参数、梯度和优化器状态就需要:

7_000_000_000 × 16 / 1024^3 ≈ 104.3GB

一张24GB的卡连边都摸不到。这也是为什么全参微调大模型都得靠多卡或者80GB级别的高显存卡。算法上很大程度就是被这个账逼出来的。

4.2 中间激活的“隐藏账单”

训练时反向传播需要用到前向传播的中间结果,这些结果被缓存下来,这部分叫中间激活(activation memory)。它的量级和层数、batch、序列长度、隐藏层维度直接相关。粗略估算可以用:

单层单样本激活字节数 ≈ hidden_size × seq_len × 精度字节数 × 若干倍

这个“若干倍”不同结构差别很大,注意力分数、残差连接、LayerNorm的各种中间变量都会占。比如hidden_size=4096,seq_len=4096,FP16,单样本单层激活可能就有3200万字节,约32MB,7B模型30多层,再乘batch,很快就会到十几GB。我之前用不精确的经验值估算时常常算不准,后来会直接把nvidia-smi里的实时显存曲线导出来看,发现激活峰值常常出现在反向传播的开始阶段,而不是前向结束的时候。

4.3 LoRA和梯度检查点为什么能省这么多

LoRA省显存的本质,是冻结原权重、不计算冻结部分的梯度,也不存储冻结部分的优化器状态。假设训练7B模型时只训练几千个参数的LoRA适配器,参数项变成:

  • 原权重FP16:2字节/参数
  • LoRA权重和梯度:可忽略
  • 不再有FP32主权重和Adam状态覆盖全部参数

这样优化器状态和梯度就从7B缩小到几十万甚至几百万参数级别。整体显存大头回到原权重和激活,自然一张24GB的卡就能跑了。

梯度检查点则是用“时间换空间”。它不保存每一层的激活,而是在反向传播时重新计算前向。这会把激活内存降到一个极低的水平,代价是训练时间明显增加。我在实际对比中,梯度检查点往往能让总显存减少40%到50%,但step time可能变慢30%以上。如果batch本来就大,这点时间成本还是划算的。

5. 算完还对不上?我踩过的4个典型偏差

公式算出来16GB,实际跑起来显存却飙到20GB,这种“正常”可能的原因我心里已经有清单了。

5.1 CUDA上下文和框架预分配

每个GPU进程只要初始化CUDA,就可能固定吃到几百MB甚至1GB的显存。这块在你输入任何数据之前就已经占住了。如果显卡容量本身就很小,比如只有8GB,那么一轮加载下来,可能模型还没开始跑就只剩7GB了。我在小卡设备上做推理时,发现这就是模型加载直接OOM的元凶。

5.2 动态shape导致的碎片化

如果序列长度是动态变化的,框架可能按最大shape预分配缓存。比如你用paged KV cache或动态batching时,它会把显存切块预留,看起来used很多,实际有效利用率并不高。我见过一种情况:声卡显存只占17GB,但再加载一个2GB的中间张量直接OOM,原因是显存碎片化严重。这个很难用公式修,只能靠预留更多余量。

5.3 PyTorch系框架的缓存机制

有些框架为了算子提速,会把释放的张量缓存起来,不立刻还给系统。也就是说你手动删除张量后,nvidia-smi看到的显存占用可能不降。这时候你去看进程占用的显存会迷惑自己。正确姿势是通过框架内部API去查它的缓存区实际占用,而不是只看驱动层面的使用量。

我一般这样检查:

# 查看当前已分配缓存 print(torch.cuda.memory_reserved()) # 查看实际使用量 print(torch.cuda.memory_allocated()) # 清空缓存 torch.cuda.empty_cache()

重点是先看 allocated 和 reserved 的差值,差值越大说明缓存池预留越多。如果差值大到几GB,反而说明你模型实际没那么吃显存,是框架帮你在“囤货”。

5.4 多卡通信缓冲

分布式训练时,通信库会为allreduce等操作提前创建通信缓冲区。这些缓冲区大小不定,有时每个rank会占额外几百MB到1GB。微调7B模型如果用多卡,每张卡还必须预留这部分。所以分布式训练里,单卡可用显存应该再扣掉约5%到10%的通信缓冲。

6. 我常用的快速决策表和最终余量建议

为了避免每次都重写公式,我给自己整理了一张速查表。以7B、13B、34B、70B四个常见量级为例,统一假设为FP16权重、KV头使用8头、head_dim=128、层数按照常见趋势近似处理,推理batch=1、seq_len=4096,context和激活预留约2.5GB。不同模型会有差别,但量级足够参考。

模型量级近似层数权重FP16KV Cache(seq4096,batch1)推理总估(预留2.5GB)
7B3213.0GB0.5GB约16GB
13B4024.2GB0.6GB约27.5GB
34B4863.3GB1.6GB约67.5GB
70B80130.4GB8.4GB约141GB

表中的KV Cache差距很大,因为70B模型为了保性能往往KV头更多,层数也更多。如果换成GQA策略更激进的模型,同样70B可能KV Cache只有一半,这就是为什么估算前要确认KV头数,不能只认参数量。

6.1 我的最终决策流程

我先说结论:估算后我会在总需求上至少留10%到15%的余量,小显存卡留20%。因为上面的公式没有完整覆盖碎片、通信缓冲、框架动态分配这些隐性项。

然后按照这个流程来:

  1. 先确定场景是纯推理还是训练。训练直接进入第3步。
  2. 推理先用公式算出权重 + KV Cache + 激活预留,看是否小于总显存的80%。
  3. 训练则先看是否需要全参微调,如果不需要就换LoRA,再按“16字节/参数”规则检查参数和优化器部分。
  4. 加载前用一个小张量测试“安全水位”,例如在一张卡上先分配一个略小于剩余显存的假张量,观察是否报OOM,再反推真实阈值。这样可以快速验证估算结果。

6.2 实测时的一个土办法

如果你不信任公式,还有一个土办法:把batch size设成1,seq_length设为1,先在卡上跑一次,记录峰值显存,再把seq_length增加几百,再次记录,两次的差值除以长度增量就是KV Cache的每token成本。训练场景则固定batch,观察显存随batch增长的曲线斜率,能反推出激活的batch依赖程度。这个方法比任何公式都贴近你自己的实际环境。

回到开头的问题:“7B模型我手上的卡能跑吗?”现在你可以回答了:先确认权重精度、序列长度、batch、KV头数,再查一下卡的实际空闲显存并留余量,一切都清楚。任何离开这些参数的“能跑”都是拍脑袋。写这稿到最后,我还是想提醒大家把“显存估算”当成一个动态预算来管理,而不是一次性结论,模型的输入可以变,显存占用就会变,学会看每一项的驱动因素,才是这套方法真正值钱的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询