在大模型推理和训练过程中,显存(GPU内存)资源往往是瓶颈。显存优化技术能够让我们在有限的硬件资源下运行更大的模型、使用更大的批量数据。本文总结了常用的显存优化策略。
大模型的显存占用主要包括几个部分:
不同精度格式每个参数占用的字节数不同:
对于nB参数的模型,使用fp16精度推理:
2n GB 显存举例:
16n GB:max_length、batch_size 成正比。举例:
20n GB 显存是最低要求,显存不够可以用Offloading将部分数据卸载到CPU内存。梯度累积是一种在显存有限情况下模拟大批量训练的技术。
传统的梯度更新方式对每个batch都进行一次损失计算和梯度更新:
1for (inputs, labels) in data_loader:
2 inputs = inputs.to(device)
3 labels = labels.to(device)
4 with torch.set_grad_enabled(True):
5 # 前向传播
6 preds = model(inputs)
7 loss = criterion(preds, labels)
8 # 反向传播
9 loss.backward()
10 # 更新权重
11 optimizer.step()
12 optimizer.zero_grad()梯度累积累积多个小batch的梯度,累积到指定步数后再一次性更新参数:
1gradient_accumulation_steps = 4
2for batch_idx, (inputs, labels) in enumerate(data_loader):
3 inputs = inputs.to(device)
4 labels = labels.to(device)
5 with torch.set_grad_enabled(True):
6 preds = model(inputs)
7 loss = criterion(preds, labels)
8 loss /= gradient_accumulation_steps # 平均损失
9 loss.backward() # 反向传播累积梯度
10 # 达到累积步数或最后一个batch才更新参数
11 if ((batch_idx + 1) % gradient_accumulation_steps == 0) or ((batch_idx+1) == len(data_loader)):
12 optimizer.step()
13 optimizer.zero_grad()| 优点 | 缺点 |
|---|---|
| 在有限显存下实现更大有效batch | 降低训练速度 |
| 梯度更稳定,收敛更好 | 累积步数过大可能影响优化器性能 |
| 几乎不引入额外计算开销 |
梯度检查点是一种以计算换显存的经典技术。
传统反向传播需要保存所有前向传播的中间激活值,占用大量显存。梯度检查点通过选择性地只保存一部分"检查点"节点的激活值,其他节点不保存,反向传播时重新计算未保存的激活值。
反向传播时:
| 优点 | 缺点 |
|---|---|
| 显著减少激活值显存占用(可减少约30%-50%) | 增加前向传播计算量 |
| 支持更大模型、更大batch训练 | 训练速度变慢 |
| 实现简单,易用 |
在PyTorch中使用:
1from torch.utils.checkpoint import checkpoint
2
3class MyModel(nn.Module):
4 def forward(self, x):
5 # 将部分层用checkpoint包装
6 x = checkpoint(self.block1, x)
7 x = self.block2(x)
8 return xOffloading(卸载)是将参数、梯度或优化器状态从GPU显存卸载到CPU内存,以空间换时间解决显存不足问题。
DeepSpeed ZeRO (Zero Redundancy Optimizer) 支持多级卸载:
使用ZeRO-3 Offloading后,单个GPU可以训练远大于自身显存的模型。
推理阶段的目标是在有限显存下运行更大模型,常用优化:
利用DeepSpeed的flops_profiler实测FLOPs,除以GPU理论峰值FLOPs得到利用率:
1{
2 "flops_profiler": {
3 "enabled": true,
4 "profile_step": 1,
5 "module_depth": -1,
6 "top_modules": 1,
7 "detailed": true,
8 "output_file": null
9 }
10}计算公式:
GPU利用率 = 实测FLOPs / GPU理论峰值FLOPs举例:A100理论峰值312 TFLOPS,实测100 TFLOPS → 利用率32%。
根据实际训练速度估算利用率:
吞吐量 = 样本数 / 秒 / GPU × max_length
GPU利用率 = 实际吞吐量 / 论文吞吐量(假设100%利用率)torch.profiler记录各函数执行时间,在TensorBoard中查看tensor core利用率,可以精确分析瓶颈。查看多卡NVLINK拓扑:
1nvidia-smi topo -m查看显卡型号:
1cd /usr/local/cuda/samples/1_Utilities/deviceQuery
2make
3./deviceQuery检查DeepSpeed环境:
1ds_report查看多机训练网速:
1iftop**如何估算一个nB大模型推理和训练分别需要多少显存?
**Gradient Checkpointing 的原理是什么?它是如何节省显存的?
**梯度累积 (Gradient Accumulation) 的作用是什么?
**什么是Offloading?有什么优缺点?
**为什么量化能节省显存?INT4量化能节省多少显存?
**LoRA为什么能大幅减少显存占用?