1. 减少Batch Size
Batch Size是影响GPU显存占用的核心因素之一。减小训练或推理时的batch size可直接降低单次迭代的内存需求,例如将batch size从256降至128,显存占用通常会减半。但需注意,过小的batch size可能导致训练速度下降或模型收敛不稳定,需通过实验找到平衡点。
2. 使用梯度累积(Gradient Accumulation)
若无法进一步减小batch size,梯度累积是模拟大批次训练的有效方法。通过在多个小batch上计算梯度并累加,最后再进行一次参数更新,可将有效batch size扩大至原来的N倍(N为累积步数),同时保持显存占用不变。例如:
accum_steps = 4
for i, (inputs, labels) in enumerate(dataloader):
outputs = model(inputs)
loss = criterion(outputs, labels) / accum_steps # 平均损失
loss.backward() # 累积梯度
if (i + 1) % accum_steps == 0:
optimizer.step() # 更新参数
optimizer.zero_grad() # 清空梯度
该方法适用于大batch训练场景,可将显存需求降低至原来的1/N。
3. 释放不必要的缓存
PyTorch会缓存计算结果以加速后续操作,但长期运行可能导致缓存占用过多显存。可通过torch.cuda.empty_cache()手动释放未使用的缓存,尤其适用于迭代训练中不再需要的中间张量。建议在每个epoch结束后或在内存紧张时调用。
4. 使用混合精度训练(Automatic Mixed Precision, AMP)
混合精度训练结合了float16(半精度)和float32(单精度)计算,在保持模型精度的前提下,将显存占用减少约50%。PyTorch的torch.cuda.amp模块可自动管理精度转换,无需修改模型代码:
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler() # 用于梯度缩放,防止数值溢出
for inputs, labels in dataloader:
optimizer.zero_grad()
with autocast(): # 自动选择float16/float32
outputs = model(inputs)
loss = criterion(outputs, labels)
scaler.scale(loss).backward() # 缩放梯度
scaler.step(optimizer) # 更新参数
scaler.update() # 调整缩放因子
该方法尤其适用于Transformer、CNN等大型模型。
5. 优化数据加载
数据加载过程中的预处理(如图像缩放、归一化)可能占用大量CPU或GPU内存。通过以下方式优化:
num_workers参数(如num_workers=4),启用多进程数据加载,避免主线程阻塞;pin_memory=True,将数据预加载到固定内存(Pinned Memory),加速GPU数据传输;ToTensor()、Normalize())高效,避免不必要的复制。6. 使用梯度检查点(Gradient Checkpointing)
对于超大型模型(如GPT-3、BERT-large),梯度检查点通过在前向传播时不保存所有中间激活值,而在反向传播时重新计算,可减少显存占用约75%。PyTorch的torch.utils.checkpoint模块提供了实现:
from torch.utils.checkpoint import checkpoint
def forward_with_checkpoint(segment):
return segment(inputs) # segment为模型的一部分
outputs = checkpoint(forward_with_checkpoint, model.segment1) # 仅保存输入,不保存中间激活
该方法适用于无法通过梯度累积扩展batch size的场景。
7. 优化模型结构
选择或设计内存高效的模型结构可显著降低显存占用:
8. 监控与分析显存使用
使用工具定位显存瓶颈是解决问题的关键:
nvidia-smi:实时监控GPU显存占用,查看哪个进程占用了最多显存(如nvidia-smi -l 1每秒刷新一次);torch.cuda.memory_summary():打印当前显存分配详情,包括已用显存、空闲显存、缓存显存等;with torch.profiler.profile(activities=[torch.profiler.ProfilerActivity.CUDA], profile_memory=True) as prof:)。9. 分布式训练
若单个GPU显存不足,可使用多GPU或多机分布式训练分散内存负载。推荐使用DistributedDataParallel(DDP)替代DataParallel(DP),因为DDP每个GPU独立计算梯度,减少了主GPU的负担,且支持多机训练:
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
dist.init_process_group(backend='nccl') # 初始化进程组
model = DDP(model.to(device)) # 包装模型
DDP可通过torchrun或python -m torch.distributed.launch启动,支持多GPU/多机训练。
10. 清理系统与升级硬件
sudo echo 3 | sudo tee /proc/sys/vm/drop_caches清理系统缓存(需root权限);sudo dd if=/dev/zero of=/swapfile bs=64M count=16创建16GB Swap文件,sudo mkswap /swapfile格式化,sudo swapon /swapfile启用),但Swap的性能远低于物理内存,仅作为临时解决方案;