这个问题我一个月能被问翻几十遍一张显卡到底能跑多大的模型很多人的第一反应是看模型文件占多大文件40GB就以为需要40GB显存结果真跑起来要么OOM要么加载完显卡占用高得不正常。显存的账从来不是“文件多大就占多大”这么简单尤其把推理和微调混在一起算的时候差距能拉开好几倍。这篇内容我不打算说虚空理论直接用数字拆给你看。从推理到微调从FP16到4bit量化把“显卡能跑多大模型”这件事变成一张可以自己套用的表格。适合刚接触大模型部署的开发者也适合准备买卡或者租卡前先做功课的人。读完你会得到两个东西一套能落地的显存估算方法以及不同显存档位下的模型选型清单。1. 推理时真正占显存的四笔钱权重、KV Cache、激活、框架开销很多人只知道“模型参数占显存”但这只是其中一笔账。一次正常的大模型推理显存开销其实是四部分叠加模型权重、KV Cache、中间激活值、CUDA上下文与框架缓冲区。不把这四笔分开算你很难解释为什么同一个模型有时候能跑有时候却莫名其妙OOM。1.1 权重开销是最直观的但精度一换数字就变了权重这一笔最好算模型参数量乘以每个参数占用的字节数。FP32是4字节FP16/BF16是2字节INT8是1字节4bit量化大约是0.5字节不过实际还要加上量化 scale 等额外数据所以真实文件通常会比理论值高一点点。以常见的7B开源模型为例BF16/FP16 推理7 × 2 14GBINT8 量化7 × 1 7GB但实际文件大约 7.5GB4bit 量化GPTQ/AWQ 等7 × 0.5 3.5GB但很多模型文件实际是 4.2GB 到 4.8GB因为 embedding、lm_head 这些层往往保留更高精度所以“模型文件多大”这个直觉只有在已经确定量化方案的情况下才勉强能用。同一个模型下载的文件格式不同显存需求能差出三倍以上。这也是为什么经常有人买了一张24GB显存的卡下了一个7B模型的高精度版本结果连加载都费劲。1.2 KV Cache 会随上下文长度和并发数膨胀KV Cache 是自回归生成过程中缓存的 Key 和 Value 张量作用是避免每生成一个 token 都重新计算之前的注意力。它的大小和模型架构强相关公式可以写成KV Cache 字节数 2 × 层数 × KV头数 × 每个头的维度 × 上下文长度 × batch size × 每个元素字节数为什么是 2因为 Key 和 Value 各一份。乘出来的结果很多人第一次算会吓一跳它并不大但它会随上下文长度线性增长。拿一个典型 7B 模型举例假设 32 层、KV头数 4、头维度 128、上下文 4096、batch 为 1、FP162 × 32 × 4 × 128 × 4096 × 1 × 2 268MB单条请求下约 268MB听起来不多对吧但如果把上下文拉到 32K这个数变成约 2GB如果 batch 拉到 8又翻 8 倍到 16GB。很多人在短文本测试时感觉一切正常一上长文档分析或者高并发服务就 OOM根子就在 KV Cache 这里。另外注意KV头数这个参数很容易被忽略。现在的模型很多用 GQA 或 MQA 来减少 KV 头的数量同样 7B 参数如果 KV头数从 4 变成 32旧的 MHA 架构KV Cache 直接翻 8 倍。所以不要拿着别人的公式硬套一定要看自己这个模型的具体 config。1.3 中间激活值与 CUDA 上下文就是那“看不见的20%”中间激活值是指前向传播过程中每一层算出来的中间张量。推理模式下不需要保存梯度这些激活值用完即弃但峰值依旧存在。粗略估算可以按 batch × 序列长度 × 隐藏层维度 × 层数 × 2 字节来算。还是拿 7B 举例如果隐藏层维度 4096、32 层、上下文 4096峰值大约在 1GB 上下。CUDA 上下文则是一笔固定开销进程启动时就会占用几百MB例如 500MB 左右不同的推理框架还会预留自己的工作区。这就是为什么很多人习惯在估算结果后面再乘一个 1.1 到 1.3 的系数。综合起来一个 7B 模型的 BF16 推理权重 14GBKV Cache 约 0.3GB激活峰值约 1GB上下文和框架开销约 2.8GB按权重的 20% 估合计约 18GB。所以 16GB 显卡跑 7B 的 BF16 版本理论上勉强实际基本会碰壁。换成 4bit 量化后权重降到 4GB 上下总占用五六GB情况就完全不一样了。这就是为什么很多老手会默认“先量化再上卡”。2. 从FP16到4bit量化到底能省多少显存怎么选位宽量化不是简单地把精度砍掉它本质上是把一个 2 字节的浮点数压缩成 1 字节或者 0.5 字节的整数用更少的 bit 去近似原来的数值。省显存是立竿见影的但代价是精度损失和可能的推理速度变化。这一节把几种常见量化方案的显存账算清楚再聊聊怎么选。2.1 一张按参数规模整理的显存需求表下表是不同规模模型在不同精度下的“权重”显存估算注意这里只算权重运行时的 KV Cache 和框架开销还需要额外留出 10% 到 20% 的余量。模型规模BF16/FP16 权重INT8 权重4bit 量化权重GPTQ/AWQGGUF Q4_K_M 文件常见大小1B2GB1GB0.7GB0.8GB3B6GB3GB1.9GB2.2GB7B14GB7GB4.2GB4.4GB13B26GB13GB7.2GB8.1GB33B 左右66GB33GB18GB19GB70B140GB70GB35GB40GB看到 70B 那一行就知道为什么市面上大部分人跑 70B 都要用 4bit 量化或多卡。单张 80GB 显存的卡虽然看起来很大但跑 70B 的 BF16 版需要 140GB根本装不下换成 4bit 之后35GB 权重加其他开销单卡才有戏。2.2 GPTQ、AWQ、GGUF三种格式不是随便选的很多人看到模型页面上一堆量化格式就晕其实核心差异就三条目标设备、推理引擎、质量取向。GPTQ面向 GPU 推理的经典方案transformers 和 vLLM 支持都很成熟。如果要用现成的高并发推理框架优先选 GPTQ。AWQ同样面向 GPU但按激活值分布保留更重要的权重通道为高精度同 bit 下质量通常略好一点适合对精度更敏感的任务。GGUFllama.cpp 生态的格式最大特点是支持 GPU 和 CPU 混跑。显存不够时不会直接崩而是把一部分层丢到内存里慢是慢但能跑。这个特性决定了它是“没有足够显存但必须把模型跑起来”时的救兵。挑选原则也简单固定 GPU 部署、要求吞吐上 GPTQ 或 AWQ跨设备、内存与显存混用优先 GGUF如果只是临时测试bitsandbytes 的 4bit 动态量化也行但速度和生态不如前两者。2.3 别把省显存当成唯一目标量化后的质量损失要心里有数我自己的实测体感是普通聊天、摘要、改写这类任务4bit 和 FP16 的差距很小普通用户基本感知不到。但到了数学推理、代码生成、严格 JSON 输出这些场景低 bit 量化之后模型“突然变笨”的情况会明显增加比如指令理解偏差、格式偶尔塌陷。所以这里给一条我的选型顺序先看任务对精度的容忍度再决定位宽。如果你要跑的是结构化抽取或代码补全哪怕显存紧张也建议优先 INT8 而不是硬上 4bit如果只是做对话机器人4bit 完全够用。另外还要注意4bit 量化推理不一定比 8bit 快某些算子反量化开销会吃掉省下来的带宽收益。3. 一训练显存就翻倍的原因优化器状态与激活项才是真正的“隐藏吃显存大户”推理算的是“一次前向传播要多少显存”训练和微调算的是另一套完全不同的账。很多人在本地刚把 7B 模型加载起来感觉挺轻松一跑微调脚本立刻 OOM原因就是把推理的显存模型套到了训练上。这里面的差距最高能达到四五倍。3.1 Adam 优化器状态每多一个参数就要多背12到16字节训练和推理最大的区别在于反向传播时要保存梯度优化器要维护动量等状态。以最常用的 AdamW 为例模型权重 FP16每参数 2 字节梯度 FP16每参数 2 字节Adam 的动量 m 和方差 v各一份 FP32合计每参数 8 字节某些实现还会保留一份 FP32 的 master weight每参数 4 字节这意味着一个 7B 模型全参微调仅权重、梯度、优化器状态加起来每参数要背 16 字节左右也就是 7 × 16 112GB。哪怕去掉 master weight也要 84GB。这就是为什么单卡 24GB 跑 7B 全参微调基本是天方夜谭跟显存怎么调参数没关系是数学上就不够。3.2 激活值在训练时会保存batch size 一放大就爆炸训练模式下反传需要每一层的中间激活值所以显存占用会随 batch size 和序列长度线性上涨。即使有 gradient checkpointing也只是用“重新计算”换“不保存”把激活峰值砍到原来的几分之一但计算量会增加。推理时那个 1GB 的激活值到了训练模式可能直接变成 10GB 以上如果不开梯度重计算24GB 显存光激活可能就不够用。所以在微调前先开 gradient checkpointing再调 batch size基本是节省显存的第一课。3.3 LoRA 和 QLoRA 的本质不是把模型变小而是让更新变少LoRA 的思路是冻结原模型权重只训练一小部分额外的低秩矩阵。比如只训练 0.1% 的参数优化器状态也就只负担这 0.1%显存大头变成“基座权重 激活值”。所以 7B 基座用 BF16 跑 LoRA大约是 14GB 权重加几GB激活24GB 卡能勉强跑。QLoRA 更进一步把基座权重先量化到 4bit加载后大约 4GB再把 LoRA 参数保持高精度去训练。这样就给激活值留下了大量空间。我用 24GB 显存跑过 13B 模型的 QLoRA也用 48GB 跑过 33B 左右量级模型的 QLoRA只要不开太大的 batch基本都稳。如果你打算在本地微调QLoRA 是性价比最高的路线没有之一。4. nvidia-smi 看到的数据可能是“假象”实测显存必须知道的几件事算完理论值之后很多人会打开显卡监控软件发现显存占用率居高不下于是开始怀疑是不是自己算错了。其实监控软件看到的数据和“当前实际张量占用的显存”不是一回事这里面的坑我在早期踩过很多次。4.1 reserved 和 allocated 的区别决定你看的是“真实占用”还是“缓存池”PyTorch 在显存管理上有一套缓存分配机制进程向 CUDA 申请显存时往往一次性申请一大块然后自己按需分配给你。所以哪怕你只加载了一个很小的模型卡上的监控也可能显示占用了好几个GB。关键要看两个数import torch print(torch.cuda.memory_allocated()) # 当前真正被张量占用的字节数 print(torch.cuda.memory_reserved()) # 当前进程向 CUDA 预留的字节数allocated 是你实际用的reserved 是 PyTorch 提前占着备用的。后者大于前者非常正常如果你看到两者差得离谱那多半是显存碎片化或者之前的缓存没释放。可以使用torch.cuda.empty_cache()清理 PyTorch 的缓存释放掉一部分 reserved 显存但注意它只清空闲缓存不会影响正在使用的张量。4.2 三种典型的“假 OOM”背后都不是显存不够第一种同一个模型短文本跑得好好的一换长文本就 OOM。这就是 KV Cache 随上下文长度线性增长的直观体现跟上文公式完全对应。第二种上一个任务结束后立刻跑新任务OOM 了但重启进程又恢复正常。这通常是 PyTorch 的缓存池还在占着显存或者显存碎片化导致新的大块申请失败。先试torch.cuda.empty_cache()再考虑重启进程。第三种用 vLLM 这类推理框架时启动阶段设置了一个很大的 max-model-len比如 32K框架会提前预留对应的 KV Cache 空间即使你当前输入只有几十个 token显存也已经按 32K 预留了。这时候 nvidia-smi 看到的高占用不是模型本身大而是预留策略。这三种情况如果你只看表面现象会以为是卡不行或者模型有问题但实际上调一下配置都能解决。4.3 用一个二分测试脚本找到你的真实临界点与其反复猜不如直接写一个小脚本去压边界。思路很简单加载模型后从短输入开始逐步拉长上下文同时记录memory_allocated的峰值直到 OOM 为止。import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_name 你的模型路径 tok AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name, torch_dtypetorch.float16).cuda() for length in [512, 1024, 2048, 4096, 8192]: try: ids torch.randint(0, 30000, (1, length)).cuda() with torch.no_grad(): model.generate(ids, max_new_tokens1) allocated torch.cuda.memory_allocated() / 1024 ** 3 print(flength{length}, allocated{allocated:.2f}GB) except torch.cuda.OutOfMemoryError: print(flength{length}, OOM) break如果你有多个候选模型要测这种“掐头去尾”的探测方式比把模型完整跑一遍快得多省下的时间也值得。5. 按显存档位选模型8G、16G、24G、48G、80G分别适合什么算清楚单模型需求之后接下来的问题就是我现在手里就是这张卡到底选什么模型、什么量化档位最合适下面这组建议是我自己长期部署下来的经验值注意不是只看能不能装下还要看余量够不够应付实际任务。5.1 不同显存档位的模型边界参考表显存推荐选择勉强能跑但不推荐适合的微调方式6GB7B 4bit 短上下文3B 高精度13B 4bit 极勉强1B 级别小模型 LoRA8GB7B 4bit 宽裕3B BF1613B 4bit 可能 OOM3B 模型 QLoRA12GB7B INT813B 4bit7B BF16 紧13B BF16 基本不行7B QLoRA16GB7B BF16 长上下文仍紧13B 4bit 宽松7B BF16 不量化有点悬7B QLoRA24GB13B BF1633B 左右 4bit7B BF16 富余33B BF16 不行13B QLoRA48GB33B 左右 BF1670B 4bit70B INT8 很紧33B QLoRA80GB70B 4bit 宽裕70B INT8 紧33B BF16 宽敞70B BF16 单卡仍不够70B QLoRA举个例子16GB 显存跑 7B 的 BF16理论估算 18GB实际用更短上下文、不开太大 batch 可能能贴着边跑但稍微来一个长文档或者并发请求就崩。所以我通常建议 16GB 显存优先上 13B 的 4bit而不是硬跑 7B 全精度前者的回答质量通常更好后者只是“能加载”而已。5.2 跑不动的四条出路量化、限上下文、换MoE、offload第一是降量化位宽这个前面已经讲透。第二是限制上下文长度很多任务根本不需要 32K把 KV Cache 预期砍下来显存立刻就松了。第三是换 MoE 结构模型MoE 总参数看起来很大但实际激活参数少生成速度不一定差不过要注意权重还是要全部加载进显存所以显存账依旧按总参数算。第四是 CPUGPU 混合 offload这个只能作为兜底方案速度会掉到每秒几个 token交互类应用基本不可用。5.3 我的选型顺序先定任务再定量化最后才定卡每次别人问我怎么选我都先反问一句你要拿模型干什么如果只是聊天4bit 大模型完全够用如果要代码补全或数学推理我会优先保住精度而不是模型规模如果要长时间跑自动化任务我还会考虑推理框架的吞吐不只是单个模型能不能装下。核心原则是不要为了“装下一个更大的数字”而牺牲质量和速度卡是工具不是尺子。6. 一个可以直接跑的显存估算脚本算完再下单理论公式看了很多最后还是要落在一个能跑的工具上。下面这个函数是我平时拿来快速估算的输入模型的关键参数输出推理场景下的显存水位。先说明这不是精确值但量级和相对关系非常靠谱。def estimate_inference_memory( num_params_billion, bits_per_param, num_layers, num_kv_heads, head_dim, seq_len, batch_size1, bytes_per_elem2, overhead_factor1.2 ): weight_gb num_params_billion * 1e9 * bits_per_param / 8 / 1024**3 kv_cache_gb ( 2 * num_layers * num_kv_heads * head_dim * seq_len * batch_size * bytes_per_elem / 1024**3 ) total_gb (weight_gb kv_cache_gb) * overhead_factor return weight_gb, kv_cache_gb, total_gb # 示例7B 模型4bit32层4个KV头head_dim1288K上下文 w, k, t estimate_inference_memory( num_params_billion7, bits_per_param4.5, num_layers32, num_kv_heads4, head_dim128, seq_len8192, ) print(fweight: {w:.2f}GB, kv cache: {k:.2f}GB, total: {t:.2f}GB)用这个脚本跑了几个常见组合7B 模型、BF16、8K 上下文权重约 14GBKV 约 0.5GB总约 17.4GB16GB 卡没有余量。13B 模型、4bit、8K 上下文权重约 7.3GBKV 约 0.5GB总约 9.4GB12GB 卡可以16GB 卡宽裕。70B 模型、4bit、4K 上下文权重约 35GBKV 约 1.3GB总约 43.6GB48GB 卡可以40GB 卡就会很悬。使用这个脚本之前先到模型目录里打开 config.json把 num_hidden_layers、num_key_value_heads、head_dim 这几个字段抄出来。如果你用的模型没有 head_dim 字段就用 hidden_size 除以 num_attention_heads 来算。有些老模型没有 GQA 参数直接用 num_attention_heads 当 KV头数也可以但这种模型 KV Cache 会偏大估算时自动把它视为更耗显存的一类。最后再分享一个我最近养成的习惯新模型发布后不等下载完先把 config.json 拉下来用脚本粗算一遍再决定用哪张卡、哪个量化档位。这比等到下载完再试错省时间也比盯着监控软件反复重启省心。显存这东西算清楚了再动手整个部署流程会顺一大截。