Skip to content

大模型(LLMs)显存问题面

来源:AI大模型面试宝典 作者:宁静致远 日期:2023年09月16日


1. 大模型大概有多大,模型文件有多大?

一般放出来的模型文件都是 FP16 的,假设是一个 $n$ B 的模型,那么模型文件占 $2n$ GB,FP16 加载到显存里做推理也是占 $2n$ GB,对外发布的都是 $10n$ 亿参数的模型。

📝通俗解释:模型文件大小和参数量成正比。一个7B(70亿参数)的模型,FP16精度下文件大小约14GB。这是因为每个参数用16位(2字节)表示。


2. 能否用 4 * V100 32G 训练 Vicuna 65B?

不能。

  • 首先,LLaMA 65B 的权重需要 5 张 V100 32G 才能完整加载到 GPU。
  • 其次,Vicuna 使用 Flash Attention 加速训练,暂不支持 V100,需要 Turing 架构之后的显卡。

📝通俗解释:65B模型太大,需要多张显卡才能装下。Flash Attention是一种更快的注意力计算方法,但需要较新的显卡支持。V100是Volta架构,不够新。


3. 如果就是想要试试 65B 模型,但是显存不多怎么办?

最少大概 50GB 显存,可以在 LLaMA-65B-Int4(GPTQ)模型基础上做 LoRA 微调,当然各种库要安装定制版本的。

📝通俗解释:Int4量化把模型压缩到原来的1/4,LoRA是一种"轻量级微调"方法,只训练少量参数而不是整个模型,这样显存需求大大降低。


4. nB 模型推理需要多少显存?

考虑模型参数都是 FP16,$2n$ GB 的显存能把模型加载。

📝通俗解释:推理就是让模型回答问题,只需要加载模型参数,不需要存储梯度和优化器,所以显存需求相对较低。


5. nB 模型训练需要多少显存?

基础显存:模型参数 + 梯度 + 优化器,总共 $16n$ GB。

📝通俗解释:训练时需要存储三部分内容:

  • 模型参数(FP16,2字节/参数)= $2n$ GB
  • 梯度(FP16,2字节/参数)= $2n$ GB
  • 优化器状态(FP32,4字节/参数 × 2个动量)= $12n$ GB 总计 $16n$ GB

activation 占用显存,和 max length、batch size 有关。

📝通俗解释:Activation(激活值)是模型计算过程中产生的中间结果,就像解题时的草稿纸。max_length 越长、batch_size 越大,草稿纸越多,显存消耗越大。

举例说明: 7B 的 Vicuna 在 FSDP 下总共 160GB 显存勉强可以训练。(按照上面计算 $7 \times 16 = 112$ GB 是基础显存)

所以全量训练准备显存 $20n$ GB 大概是最低要求,除非内存充足,显存不够 offload 到内存补。

📝通俗解释:实际训练还需要额外的激活值显存,所以要准备比基础计算更多的显存。20nGB是经验值,比如7B模型准备140GB显存。


6. 如何估算模型所需的 RAM?

首先,我们需要了解如何根据参数量估计模型大致所需的 RAM,这在实践中有很重要的参考意义。我们需要通过估算设置 batch_size,设置模型精度,选择微调方法和参数分布方法等。

接下来,我们用 LLaMA-6B 模型为例估算其大致需要的内存。

6.1 精度对所需内存的影响

精度格式每个参数占用空间
FP3232 bits, 4 bytes
FP1616 bits, 2 bytes
Int88 bits, 1 byte

📝通俗解释:精度越高,内存占用越大,但计算结果更准确。FP32是标准32位浮点数,FP16是半精度,Int8是整数量化。

6.2 内存占用的三大部分

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

(1)模型参数

  • 对于 FP32,LLaMA-6B 需要 6B × 4 bytes = 24GB
  • 对于 Int8,LLaMA-6B 需要 6B × 1 byte = 6GB

(2)梯度

梯度存储和模型参数大小相同(通常使用与模型参数相同的精度)。

(3)优化器参数

对于常用的 AdamW 来说,需要储存两倍的模型参数(用来储存一阶和二阶 momentum)。

  • FP32 的 LLaMA-6B,AdamW 需要 6B × 8 bytes = 48 GB
  • Int8 的 LLaMA-6B,AdamW 需要 6B × 2 bytes = 12 GB

📝通俗解释:AdamW优化器需要存储两个"动量"变量,就像记录每个参数的历史变化趋势,一个是一阶动量(平均值),一个是二阶动量(方差)。

除此之外,CUDA kernel 也会占据一些 RAM,大概 1.3GB 左右,查看方式如下:

python
import torch
torch.ones((1, 1)).to("cuda")
print_gpu_utilization()
# 输出:GPU memory occupied: 1343 MB

6.3 计算示例

综上,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) \times 2048 \times 32 \times 1\text{ byte} = 990\text{ MB}$$

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

📝通俗解释:中间变量就是模型前向传播时每一层计算产生的临时数据,这些数据在反向传播时还需要用到,所以也要占用显存。


7. 如何评估你的显卡利用率

ZeRO3 如果没有 NVLink,多卡训练下会变慢。但是一直不知道究竟会变得多慢,下面给出几种方法来评估自己在训练时发挥了多少 GPU 性能,以及具体测试方法。

📝通俗解释:NVLink是显卡之间的高速通道,没有它多卡通信会变慢,就像没有高速公路只能走普通马路。

7.1 FLOPs 比值法

  • 测试工具:DeepSpeed
  • 参考数据:NVIDIA 公布的显卡 FP16 峰值计算速度(Tensor Core)

$$\text{GPU 利用率} = \frac{\text{实测的 FLOPs}}{\text{显卡理论上的峰值 FLOPs}}$$

举例:DeepSpeed 实测 FLOPs 100 TFLOPs,而用的是 A100 卡理论峰值 312 TFLOPs,可以得到 GPU 利用率只有 32.05%

📝通俗解释:FLOPs就是"每秒计算次数",就像测量工厂的实际产能和理论产能的比值。

7.2 Throughput 估计法

  • 测试工具:手动估算或者 DeepSpeed
  • 参考数据:论文中的训练速度或者吞吐量

$$\text{吞吐量} = \text{example 数量/秒/GPU} \times \text{max_length}$$

$$\text{GPU 利用率} = \frac{\text{实际吞吐量}}{\text{论文中的吞吐量(假设利用率 100%)}}$$

举例: 实测训练时处理样本速度为 3 example/s,一共有 4 卡,max length 2048,则吞吐量为 1536 token/s/GPU 根据 LLaMA 论文知道,他们训练 7B 模型的吞吐量约为 3300 token/s/GPU,那么 GPU 利用率只有 46.54%

📝通俗解释:Throughput就是"吞吐量",衡量单位时间能处理多少数据。和论文对比就能知道自己的训练效率是否正常。

7.3 Torch Profiler 分析法

  • 测试工具:torch profiler 及 tensorboard
  • 参考数据:无

利用 torch profiler 记录各个函数的时间,将结果在 tensorboard 上展示,在 GPU kernel 视图下,可以看到 Tensor Core 的利用率,比如 30%

📝通俗解释:Profiler就像给训练过程装一个"监控摄像头",能看到每个步骤花了多长时间,精确找出性能瓶颈。

7.4 总结

以上三种方法,在笔者的实验中能得到差不多的利用率指标。

评估方法准确性易用性
方案三(Profiler)★★★★★★★☆☆☆
方案一(FLOPs比值)★★★★☆★★★☆☆
方案二(Throughput)★★★☆☆★★★★★

如果不想改代码就用方案二估算自己的训练速度是不是合理的,如果想精确分析训练速度的瓶颈还是建议使用方案三。


8. 测试显卡利用率 - 实现细节篇

8.1 如何查看多机训练时的网速?

使用 iftop 命令,看网速很方便。

📝通俗解释:多机训练需要通过网络传输数据,网速太慢会拖慢整体训练速度。

bash
nvidia-smi topo -m

8.3 如何查看服务器上显卡的具体型号?

bash
cd /usr/local/cuda/samples/1_Utilities/deviceQuery
make
./deviceQuery

8.4 如何查看训练时的 FLOPs?(也就是每秒的计算量)

理论上,如果 FLOPs 比较低,说明没有发挥出显卡的性能。

如果基于 DeepSpeed 训练,可以通过配置文件很方便的测试:

json
{
  "flops_profiler": {
    "enabled": true,
    "profile_step": 1,
    "module_depth": -1,
    "top_modules": 1,
    "detailed": true,
    "output_file": null
  }
}

📝通俗解释:开启 FLOPs profiler 后可以看到每个模块的计算量,方便定位哪些层是计算瓶颈。

参考:https://www.deepspeed.ai/tutorials/flops-profiler/

8.5 如何查看 DeepSpeed 的环境配置是否正确?

bash
ds_report

8.6 TF32 格式有多长?

19 位(8位指数 + 10位尾数)

📝通俗解释:TF32是NVIDIA推出的特殊格式,用19位存储,兼容FP32的范围但用FP16的精度,专门用于加速Tensor Core计算。

图表描述: 图片展示了不同浮点数格式的位宽分配结构(Sign, Range, Precision):

格式符号位指数位(范围)尾数位(精度)
FP321 bit8 bits23 bits
TF321 bit8 bits10 bits
FP161 bit5 bits10 bits
BF161 bit8 bits7 bits

8.7 哪里看各类显卡算力比较?

https://lambdalabs.com/gpu-benchmarks

8.8 如何查看训练中的通信开销?

用 PyTorch Profiler 查看,下面给出基于 Transformers 的一种快捷的修改方式: https://github.com/yqhu/profiler-workshop/blob/c8d4a7c30a61cc7b909d89f88f5fd36b70c55769/hf_training_trainer_prof.py

用记录的 pt_trace.json 文件放到 tensorboard 上,可以看出 Tensor Core 的利用率。

根据实践经验,使用 DeepSpeed ZeRO3 时,PCIe 版本的卡很大部分时间都在通信上,AllGather 和 ReduceScatter 的时间超过 Tensor Core 计算的时间,所以 FLOPs 上不去。

📝通俗解释:AllGather和ReduceScatter是多卡通信的操作,就像一个团队需要经常开会同步进度。如果通信时间比计算时间还长,说明显卡在"等待",利用率自然低。


附录:消费级显卡性能对比

查看消费级显卡的内存和算力: https://www.gpucheck.com/gpu-benchmark-graphics-card-comparison-chart

或访问:https://lambdalabs.com/gpu-benchmarks

基于 MIT 许可发布