鹤啸九天 自律更自由,平凡不平庸 Less is More

GPU显存使用专题

2024-03-05
鹤啸九天
阅读量

Notes(温馨提示):

  1. ★ 首次阅读建议浏览:导航指南, 或划到本页末尾, 或直接点击跳转, 查看全站导航图
  2. 右上角工具条搜索文章,右下角二维码关注微信公众号(鹤啸九天),底栏分享、赞赏、评论
  3. ★ 转载请注明文章来源,知识点积累起来不容易,水滴石穿,绳锯木断,谢谢理解
  4. ★ 如有疑问,邮件讨论,欢迎贡献优质资料


GPU 显存

LLM 显存分析

【2023-8-30】大模型要占你多少内存?这个神器一键测量,误差低至0.5MB,免费可用

大模型训练推理要用多少内存?

  • HuggingFace Space 最新火起来工具——Model Memory Calculator,模型内存测量器,在网页端人人可体验。
  • 比如模型bert-base-case Int8估计占用413.18 MB内存,实际占用为413.68MB,相差0.5MB,误差仅有0.1%。

实际推理过程,EleutherAI 发现需要在预测数据基础上,预留 20% 内存

经验

显存不够

【2023-8-30】baichuan-7b (14G) 部署失败,空间不够

  • GPU: A30, 24G 显存

错误信息:

torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 86.00 MiB (GPU 0; 22.20 GiB total capacity; 7.47 GiB already allocated; 51.12 MiB free; 7.48 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation.  See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF

显存工具

Model Memory Calculator

Model Memory Calculator计算的开销

Memory Usage for ‘baichuan-inc/Baichuan-7B’

dtype Largest Layer or Residual Group Total Size Training using Adam
float32 1000.0 MB 26.2 GB 104.82 GB
float16/bfloat16 500.0 MB 13.1 GB 52.41 GB
int8 250.0 MB 6.55 GB 26.2 GB
int4 125.0 MB 3.28 GB  

LLM Check

显存估算开源项目:

LLM 参数分析

只有参数层占用显存。

这部份显存占用和输入无关,模型加载完成之后就会占用。

有参数层

有参数层主要包括:

  • 卷积
  • 全连接
  • BatchNorm
  • Embedding层
  • … …

无参数层:

  • 多数的激活层(Sigmoid/ReLU)
  • 池化层
  • Dropout
  • … …

模型参数数目(不考虑偏置项b)为:

  • Linear(M->N): 参数数目:M×N
  • Conv2d(Cin, Cout, K): 参数数目:Cin × Cout × K × K
  • BatchNorm(N): 参数数目: 2N
  • Embedding(N,W): 参数数目: N × W

参数占用显存 = 参数数目 × n

  • n = 4 :float32
  • n = 2 : float16
  • n = 8 : double64

PyTorch中,当执行完 model=MyGreatModel().cuda() 后就会占用相应显存,占用显存大小基本与上述分析的显存差不多(会稍大一些,因为其它开销)。

GPU 显存占用

GPU 要存哪些参数

【2023-6-28】参考

模型训练中,GPU 要存储的参数

  • 模型本身的参数、优化器状态、激活函数的输出值、梯度、一些零时的Buffer
  • img

微调时,显存占用组成:

  • 1、参数占用: 微调时一般采用bf16/fp16格式来训练,1个参数需要2字节显存,那10亿参数所需的显存就大概为2G左右。
    • 13B 模型,bf16参数占用的显存就至少为2*13=26GB
  • 2、梯度占用: 每个参数对应一个梯度,所以梯度显存占用等于模型参数的占用:26GB。
  • 3、优化器: 采用AdamW优化器(bitsandbytes版本), 为每个参数计算两个状态变量v和r,显存占用就为26*2=52GB,同时优化器还会为模型参数维护一个float32的副本,占用26 * 2=52G。
    • 更新:如果是deepspeed版本的优化器,还会保存一份float32的梯度,额外占用26 * 2=52G的显存。实现代码在这里
  • 4、激活值: 不同 batch size和sequence length会影响显存占用
    • 以Llama-13B为例,batch-size=1,sequence length=2048条件下,激活值得占用大概为:17gb
  • Batch Size 和 Sequence Length:直接影响激活值的显存占用。
  • 框架和库的开销:如 PyTorch, TensorFlow, CUDA kernels 等会占用一部分固定或可变的显存。

13B 模型, 混合精度,全参数微调, 所需显存至少为

  • 26+26+52+52+17=173GB

这只是 bs=1 时的粗略估计,不是很准确,还有一些额外开销没有包括在内。

通常训练时,batch不能取太小,常见的模型训练batch size都是成百上千的,这显存占用可就是天文数字了。

模型参数仅占所有数据的小部分

  • 当进行混合精度运算时,模型状态参数(优化器状态 + 梯度+ 模型参数)占大半以上。

因此,要想办法去除模型训练过程中的冗余数据。

梯度与动量

优化器

  • SGD:W_t+1 = W_t - α * ▽ F(W_t)
    • 除了保存权重W, 还要保存对应的梯度 ▽ F(W_t) ,因此, 显存占用等于参数占用显存 x2
  • 带Momentum-SGD:
    • v_t+1 = ρv_t + ▽ F(W_t)
    • W_t+1 = W_t - α * v_t+1
    • 还需要保存动量, 因此显存 x3
  • Adam优化器
    • 动量占用的显存更多,显存x4

总结,模型中与输入无关的显存占用包括:

  • 参数 W
  • 梯度 dW(一般与参数一样)
  • 优化器的动量
    • 普通SGD没有动量,momentum-SGD动量与梯度一样,Adam优化器动量的数量是梯度的两倍

输入输出

以CNN为例,模型输出的显存占用,总结如下:

  • 需要计算每一层的feature map的形状(多维数组的形状)
  • 需要保存输出对应的梯度用以反向传播(链式法则)
  • 显存占用与 batch size 成正比
  • 模型输出不需要存储相应的动量信息。

深度学习中神经网络的显存占用,可以得到如下公式:

显存占用 = 模型显存占用 + batch_size × 每个样本的显存占用

显存不是和batch-size简单的成正比,尤其是模型自身比较复杂的情况下:比如全连接很大,Embedding层很大

另外需要注意:

  • 输入(数据,图片)一般不需要计算梯度
  • 神经网络每层输入/输出都需要保存下来,用来反向传播,但是在某些特殊的情况下,不要保存输入。
    • 比如 ReLU,PyTorch中,使用nn.ReLU(inplace = True) 能将激活函数ReLU的输出直接覆盖保存于模型的输入之中,节省不少显存。
    • 这时候是如何反向传播? (提示:y=relu(x) -> dx = dy.copy();dx[ y<=0 ] =0)

显存预估

总结

模型规模 参数 梯度 激活值 总数 训练方案
7B 12 * 7 = 84 GB     >100 GB 2*A100 (80GB),并配合 DeepSpeed ZeRO Stage 2/3
70B 12 * 70 = 840 GB     远超 1 TB 大规模的 GPU 集群
           

微调方式

方式

  • 全参微调: (Full Fine-tuning) 模型所有参数都需要计算梯度并由优化器更新
    • 总计 (核心部分): (2 参数 + 2 梯度 + 8 优化器状态) * X GB = 12X GB
    • 粗略预估下限: 14~18X GB
  • 局部参数:
    • LoRA 只训练一小部分注入到模型中的“适配器”参数,而原始模型的绝大部分参数保持冻结。
    • 总显存 ≈ 2X GB + 激活值显存 + 几 GB 开销

估算:

  • 全参数微调 (FP16, AdamW): 考虑 ~20X GB 或更多。
  • LoRA 微调 (FP16): 考虑 ~(2.5 - 4)X GB,主要看基础模型 2X GB + 激活。
  • QLoRA 微调 (4-bit base, LoRA): 考虑 ~(0.7 - 1.5)X GB,主要看基础模型 ~0.5X GB + 激活。

分析

  • 关键变量: batch_size 和 sequence_length 对激活值影响巨大。如果显存不足,优先减小这两个值,或者加强梯度检查点的使用。
  • 梯度检查点: 对于大模型微调(无论是全参数还是 LoRA),几乎是必需的技术,用计算换显存。
  • 优化器: 如果显存极其紧张,可以考虑显存优化型的优化器(如 Adafactor, 8-bit Adam),但这可能会影响收敛效果。
  • 分布式训练 (DeepSpeed ZeRO): 对于全参数微调或者超大模型的 LoRA 微调,单卡显存往往不够。DeepSpeed ZeRO (特别是 Stage 2 和 3) 可以将优化器状态和梯度分片到多张 GPU 上,极大降低单卡显存压力。
  • 实际监控: 最好的方法是在目标硬件上用小 batch_size 跑一个测试批次,并使用 nvidia-smi 或 PyTorch 的 torch.cuda.memory_summary() / torch.cuda.max_memory_allocated() 来监控实际峰值显存占用,然后根据需要调整参数。

计算方法

LLM GPU显存计算公式为:

Memory (GB) = P x (Q/8) × (1 + Overhead)

其中

  • P: 参数数量(十亿)
  • Q: 位精度(如FP16=16位),Q/8将位转换为字节
  • Overhead: 额外开销(通常20%,包括KV缓存、激活缓冲区等)。

例如, 70B 模型使用FP16精度:

  • 70×(16/8)×1.2 = 168GB。

这个公式可快速估算自托管LLM所需GPU显存,帮助选择合适的硬件配置。

Model Size(模型大小) Precision(精度) Overhead(开销) Memory Required(所需内存)
7B FP16 20% 7×(16/8)×1.2 = 16.8 GB
13B FP16 20% 13×(16/8)×1.2 = 31.2 GB
70B FP16 20% 70×(16/8)×1.2 = 168 GB
70B INT8 20% 70×(8/8)×1.2 = 84 GB
175B FP16 20% 175×(16/8)×1.2 = 420 GB

GPU 内存分配由多部分共同构成,核心组件及分配逻辑如下:

核心组成模块

  • 模型权重(Model Weights):存储模型参数,对应占用 基础内存(Base Memory)
  • KV缓存(KV Cache Buffer):保存推理过程中的键值对中间数据,辅助序列生成等任务,产生 内存开销(Overhead)
  • 激活缓冲区(Activation Buffer):暂存计算过程中的激活值(如神经网络层输出),同样带来 内存开销(Overhead)

总显存计算逻辑

总显存(Total VRAM)需整合各模块占用内存,公式可简化为:

  • Total VRAM = 模型权重内存 + KV缓存开销 + 激活缓冲区开销 + 其他潜在开销

GPU需为模型基础参数、推理中间数据、计算临时结果等分配内存,共同决定最终显存占用,理解此流程有助于优化AI任务的显存使用效率 。

推理显存

【2024-8-24】为大型语言模型 (LLM) 提供服务需要多少 GPU 内存?

运行一个大型语言模型,需要多大GPU内存?

GPU 内存估算公式

  • $ M=(P4B)/(32/Q)1.2 $

解释

  • M 代表 GPU 内存的大小,单位是吉字节。
  • P 指的是模型中包含的参数总数。
  • 4B 指的是每个参数平均占用的存储空间,为 4 个字节。
  • Q 表示加载模型时使用的位数,可以是 16 位或者 32 位。
  • 1.2 表示在计算中加入了 20% 的额外空间以应对可能的需求。

分解公式

  • 模型参数量 (P):这个指标反映了你的模型规模。比如,如果你使用的是 LLaMA 模型,它包含 700 亿个参数,那么这个参数量就是 700 亿。
  • 参数内存需求 (4B):通常情况下,每个模型参数需要 4 个字节的存储空间,这是因为浮点数通常需要 4 个字节(即 32 位)来表示。如果你采用的是半精度(16 位)格式,那么所需的内存量会相应减少。
  • 参数位宽 (Q):这个值取决于你是以 16 位还是 32 位的精度来加载模型。16 位精度在许多大型语言模型的应用中较为普遍,因为它在保证足够精度的同时,能够降低内存的消耗。
  • 额外开销 (1.2):乘以 1.2 的系数是为了增加 20% 的额外空间,以应对在模型推理过程中可能需要的额外内存。这不仅仅是为了安全起见,更是为了确保在模型执行过程中,激活操作和其他中间结果的内存需求得到满足。
    • 其他难以估算和未知的显存消耗项在峰值时可能占总显存的30%以上

700亿个参数(以 16位精度加载)的 LLaMA 模型提供服务所需的内存:

  • M = (P * 4B)/(32/Q) * 1.2 = (70 * 4 bytes)/(32/16) * 1.2 = 168 GB
  • 单块 NVIDIA A100 GPU,尽管配备了 80 GB 显存,但仍然不足以支撑该模型的运行。为了高效地处理内存需求,至少需要两块 A100 GPU,每块都具备 80 GB 的显存容量。

知乎帖子

关掉了梯度检查点,用 torch.bfloat16 类型的 Qwen-2.5-7B-instruction 在单卡H20上实验

  • 当输入序列长度约400,batch size=4时,理论显存需要约110G,实际显存用了大概90G
  • 进一步开启梯度检查点,下降到75G

不同 zero策略下,分别需要几卡

  • zero1: 优化器切分
    • 当模型为7B时, k>2.29,约3卡;
    • 当模型为14B时,任何非3D并行的zero1都无法有效训练,无论多少张A100。
  • zero2: 进一步梯度切分
    • 当模型为7B时,k>1.88,约需要2卡;
    • 当模型为14B时, k>8.17 ,约需要9卡。
    • 当模型超过20B,再多的A100也无法训练了。
  • zero3: 进一步模型切分
    • 当模型为7B时, k>1.7,约需要2卡;
    • 当模型为14B时, k>4.31,约需要5卡。
    • 训练上限为40B。

Deepspeed 不仅支持zero策略代表的dp,还支持tp和pp,因此,卡管够,理论上可以训无限大的模型,大力出奇迹。

LLaMA-6B 占用多大内存

【2023-7-13】LLaMA-6B 占用多大内存?计算过程

精度对所需内存的影响:

  • fp32精度,一个参数需要 32 bits, 4 bytes.
  • fp16精度,一个参数需要 16 bits, 2 bytes.
  • int8精度,一个参数需要 8 bits, 1 byte.

模型需要的 RAM 大致分三个部分:

  • 模型参数: 参数量*每个参数所需内存
    • 对于fp32,LLaMA-6B需要 6B*4 bytes = 24GB 内存
    • 对于int8,LLaMA-6B需要 6B*1 byte = 6GB 内存
  • 梯度: 参数量*每个梯度参数所需内存
  • 优化器参数: 不同优化器所储存的参数量不同。
    • 对于常用的AdamW,需要两倍模型参数(用来储存一阶和二阶momentum)。
    • fp32 的 LLaMA-6B,AdamW 需要 6B*8 bytes = 48 GB
    • int8 的 LLaMA-6B,AdamW 需要 6B*2 bytes = 12 GB
  • 其它
    • CUDA kernel 也会占据一些RAM,大概 1.3GB 左右

综上,int8 精度的 LLaMA-6B 模型部分大致需要 6GB + 6GB + 12GB + 1.3GB = 25.3GB 左右。

再根据LLaMA的架构(hidden_size= 4096, intermediate_size= 11008, num_hidden_layers= 32, context_length = 2048)计算中间变量内存。每个instance需要: ( 4096+11008 ) * 2048 * 32 * 1 byte = 990 MB

所以,一张 A100(80GB RAM)大概可以在int8精度,batch_size = 50 的设定下进行全参数训练。

7B 占用多大内存

一个7B规模大模型(如LLaMA-2 7B),基于16-bit混合精度训练时

  • 仅考虑模型参数、梯度、优化器情况下,显存占用就有112GB
  • 参数占 GPU 显存近 14GB(每个参数2字节)。
  • 训练时梯度存储占14GB(每个参数对应1个梯度,也是2字节)
  • 优化器Optimizer(假设是主流的AdamW)则是84GB(每个参数对应1个参数copy、一个momentum和一个variance,这三个都是float32)
    • 2byte 模型静态参数权重(以16bit存储) = 14G
    • 2byte 模型更新参数权重 (以16bit存储)= 14G
    • 2byte 梯度(以16bit存储)= 14G
    • 2byte 梯度更新(以16bit存储)= 14G
    • 4byte 一阶动量优化器更新(以32bit存储)= 28G
    • 4byte 二阶方差优化器更新(以32bit存储)= 28G
  • 目前,合计 112GB
  • 还有:前向传播时激活值,各种临时变量
  • 还与sequence length, hidden size、batch size都有关系。

目前A100、H100这样主流显卡单张是放不下,更别提国内中小厂喜欢用的A6000/5000、甚至消费级显卡。

PPO 显存预估

【2025-8-1】用 verl 进行 PPO 训练,4张80G显存的A800能调起来14B模型?

rollout用vllm,训练用FSDP, 需要同时在显存中维护 3个14B规模的模型(Actor, Critic, Reference)以及 2份优化器状态

  • 模型权重 (全部经过分片)
    • Actor (14B, TP=4): ~7 G
    • Critic (14B, FSDP): ~7 G
    • Reference (14B, TP=4): ~7 G
  • 小计: 21 G

优化器状态 (Actor 和 Critic, 经过分片)

  • Actor 优化器: ~28 G
  • Critic 优化器: ~28 G
  • 小计: 56 G

梯度 (Actor 和 Critic, 经过分片)

  • Actor 梯度: ~7 G
  • Critic 梯度: ~7 G
  • 小计: 14 G

静态占用总计: 21 GB (权重) + 56 GB (优化器) + 14 GB (梯度) = 91 G

这个估算出的 91 GB 静态占用已经超过了单张 80GB 卡的物理上限。

甚至还没有计算几十GB的激活值和 vLLM KV Cache 的空间!

显存不足怎么办

训练时显存不足怎么办?

常见的节省显存操作,优先级从高到低排列。

  1. 去掉 compute_metrics:
    • 有些代码会在输出层后计算rouge分等,这个会输出一个 batch_sizevocab_sizeseq_len 的一个大向量,非常占显存。
  2. 采用bf16/fp16进行混合精度训练:
    • 现在大模型基本上都采用 bf16 来进行训练
    • 但是 v100 不支持 bf16,可以采用fp16进行训练。显存占用能够降低1倍。
  3. Flash attention:不仅能够降低显存,更能提高训练速度。
  4. batch_size 调小:
    • batch size 与模型每层激活状态所占显存呈正相关
    • 降低 batch size 能够很大程度上降低这部分显存占用。
  5. 采用梯度累积
    • global_batch_size = batch_size * 梯度累积
    • 如果降低 batch_size 后想保持 global_batch_size 不变,可适当提高梯度累积值。
  6. 选择合适的上下文长度
    • 上下文长度与激活状态所占显存呈正相关
    • 因此可适当降低上下文长度来降低显存占用。
  7. DeepSpeed Zero
    • 显存占用从高到低为:Zero 1 > Zero 2 > Zero 2 + offload > zero 3 > zero 3 + offload
    • 推荐最多试到 Zero2 + offload
  8. 选择更小的基座模型:在满足需求的情况下,尽量选择更小的基座模型。

慎重选择:

  1. Lora: 能跑全参就别跑 LoraQlora,一方面是麻烦,另一方面的确是效果差点。
  2. Qlora: Qlora 速度比lora慢,但所需显存更少,实在没资源可以试试。
  3. Megatron-LM: 可采用流水线并行和张量并行,使用比较麻烦,适合喜欢折腾的同学。
  4. Pai-Megatron-LM: Megatron-LM 的衍生,支持 Qwen 的sft和pt,坑比较多,爱折腾可以试试。
  5. 激活检查点:不推荐,非常耗时。在反向传播时重新计算深度神经网络的中间值。用时间(重新计算这些值两次的时间成本)来换空间(提前存储这些值的内存成本)。

Adam + fp16 混合精度预估

【2023-6-29】LLM Training GPU显存耗用量估计,以Adam + fp16混合精度训练为例,分析其显存占用有以下四个部分

  • (1) 模型权重 Model
    • Prameters (FP16) 2 bytes
    • Gradients (FP16) 2 bytes
  • (2) 前向激活值 Activations
    • 前向过程中存储, y = w1 * x, 存储x用于计算w1梯度
    • 整体显存占用与batch有关
  • (3) 优化器 Optimizer:梯度、动量等
    • Master Weight (FP32) 4 bytes
    • Adam m (FP32) 4 bytes
    • Adam v (FP32) 4 bytes
  • (4) 临时混存 Buffer & Fragmentation

(1) 和 (3) 可以精确估计

  • 显存占用大头是 Adam 优化器,占可计算部分的 12/16=75%
  • 其次是模型参数+梯度,显存容量至少是参数量的16倍

Adam + fp16混合精度训练

结论:

  • 不考虑Activation,3090 模型容量上限是 24/16=1.5B,A100 模型容量上限是 80/16=5B
    • 假设训练过程中batchsize恒定为1,也即尽最大可能减少Activation在显存中的占用比例,使得理论计算值16Φ更接近真实的显存占用,那么24G的3090的模型容量上限是1.5B(差不多是GPT-2的水平),80G的A100的模型容量上限是5B
  • 考虑Activation,3090的模型容量上限是 0.75B,A100的容量上限是 2.5B
    • batchsize为1的训练效率非常低,batchsize大于1才能充分发挥GPU的效率,此时Activation变得不可忽略。经验之谈,一般需要给Activation预留一半的显存空间(比如3090预留12G,A100预留40G),此时3090的模型容量上限是0.75B,A100的容量上限是2.5B,我们实际测试结果接近这个值
  • [1B, 5B] 是目前市面上大多数GPU卡的分水岭区间
    • [0, 1B) 市面上绝大多数卡都可以直接硬train一发
    • [1B, 5B] 大多数卡在这个区间的某个值上触发模型容量上限,具体触发值和显存大小有关
    • (5B, ~) 目前没有卡能裸训

节省显存

深度学习中,一般占用显存最多的是卷积等层的输出,模型参数占用的显存相对较少,而且不太好优化。

节省显存方法:

  • 降低 batch-size
  • 下采样 (NCHW -> (1/4)*NCHW)
  • 减少全连接层(一般只留最后一层分类用的全连接层

更多信息见原文

DeepSpeed ZeRO 操作就优化梯度优化器里面的参数,达到缩减显存占用空间的操作。

结束


支付宝打赏 微信打赏

~ 海内存知已,天涯若比邻 ~

Share

Related Posts

下一篇 分布式训练

标题:DeepSpeed 学习笔记

摘要:DeepSpeed 知识点、训练技巧总结

标题:分布式训练

摘要:分布式训练知识点

站内可视化导航

文章可视化导读:鼠标划过图形块时,如果出现蓝色光环, 点击即可跳转到对应主题

Comments

--disqus--

    Content
    My Moment ( 微信公众号 )
    欢迎关注鹤啸九天