大模型(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 精度对所需内存的影响
| 精度格式 | 每个参数占用空间 |
|---|---|
| FP32 | 32 bits, 4 bytes |
| FP16 | 16 bits, 2 bytes |
| Int8 | 8 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 左右,查看方式如下:
import torch
torch.ones((1, 1)).to("cuda")
print_gpu_utilization()
# 输出:GPU memory occupied: 1343 MB6.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 命令,看网速很方便。
📝通俗解释:多机训练需要通过网络传输数据,网速太慢会拖慢整体训练速度。
8.2 如何查看服务器上的多卡之间的 NVLink topo?
nvidia-smi topo -m8.3 如何查看服务器上显卡的具体型号?
cd /usr/local/cuda/samples/1_Utilities/deviceQuery
make
./deviceQuery8.4 如何查看训练时的 FLOPs?(也就是每秒的计算量)
理论上,如果 FLOPs 比较低,说明没有发挥出显卡的性能。
如果基于 DeepSpeed 训练,可以通过配置文件很方便的测试:
{
"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 的环境配置是否正确?
ds_report8.6 TF32 格式有多长?
19 位(8位指数 + 10位尾数)
📝通俗解释:TF32是NVIDIA推出的特殊格式,用19位存储,兼容FP32的范围但用FP16的精度,专门用于加速Tensor Core计算。
图表描述: 图片展示了不同浮点数格式的位宽分配结构(Sign, Range, Precision):
| 格式 | 符号位 | 指数位(范围) | 尾数位(精度) |
|---|---|---|---|
| FP32 | 1 bit | 8 bits | 23 bits |
| TF32 | 1 bit | 8 bits | 10 bits |
| FP16 | 1 bit | 5 bits | 10 bits |
| BF16 | 1 bit | 8 bits | 7 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