Pytorch显存管理

了解显存管理的机制

Posted by jinaoliu on March 28, 2023

Pytorch显存管理

Pytorch显存

显存分配机制

torch.cuda.memory_allocated() # torch.tensor占用的显存
torch.cuda.max_memory_allocated() # 全部占据的缓存有多少,效果与memory_reserved一样
torch.cuda.memory_reserved() # 查看当前进程分配的显存缓冲区是多少
torch.cuda.empty_cache() # 将未占用的缓存收回
torch.cuda.memory_summary() # 显示显存信息

PyTorch context: 第一次执行CUDA操作,也就是使用GPU的时候所需要创建的维护设备间工作的一些相关信息。只要你把任何东西(无论是多小的tensor)放到GPU显存中,那么你至少会占用1000MiB左右的显存(根据cuda版本,会略有不同)。这部分显存是cuda running时固有配件必须要占掉的显存,是无法避免的,这就是pytorch的context开销。

总显存:Reserved_memory + Pytorch context

Reserved_memory:剩余的缓存 + 变量Allocated memory

在 Pytorch 中,显存分配应该是按页分配,即使是很小的tensor,分配的显存也是以页为单位的。CUDA 内存以内存块的形式存在,在你创建一条 4 Bytes 的 Tensor 时,默认情况下,Pytorch 会向 CUDA 申请 2 MB 的内存块,然后再为我们分配 512 Bytes 的显存来储存我们申请的 Tensor。剩余的 1.5 MB 以 reserved memory(以前叫 cache memory)的形式保持占用。假设这时候,你想再申请一条 Tensor,Pytorch 首先会去看之前申请的内存块够不够放。如果我们申请的 Tensor 小于 1.5MB,那就直接放在刚刚申请的内存块中。如果大于 1.5MB,Pytorch 则再向 CUDA 申请新的内存块。假设 CUDA 也没有足够内存的时候,Pytorch 会尝试切分释放掉这些内存块中闲置的部分,再重新向 CUDA 申请。如果还不够,那就会报我们模型训练中常见的 out of memory 错误了。

刚刚讲的是小于 1 MB 的显存申请,大于 1 MB 小于 10MB 的申请,在没有足够空闲内存块的情况下,则会向 CUDA 申请 20 MB 的内存块。

模型显存分配

PyTorch在进行深度学习训练的时候,有4大部分的显存开销,分别是模型参数(parameters),模型参数的梯度(gradients),优化器状态(optimizer states)以及中间激活值(intermediate activations) 或者叫中间结果(intermediate results)。

1.模型定义定义了模型的网络结构产生模型参数 
while(你想训练):    
	2.前向传播执行模型的前向传播产生中间激活值    
	3.后向传播执行模型的后向传播产生梯度    
	4.梯度更新执行模型参数的更新第一次执行的时候产生优化器状态

模型定义:显存占用量约为参数量乘以4

前向传播:显存增加等于每一层模型产生的结果的显存之和,且跟batch_size成正比。

后向传播:后向传播会将模型的中间激活值给消耗并释放掉掉,并为每一个模型中的参数计算其对应的梯度。在第一次执行的时候,会为模型参数分配对应的用来存储梯度的空间。

参数更新:第一次增加,以后不再额外分配显存

其中中间结果显存占50%以上

GPU中显存优化:

  • 数据:采用更小的精度
  • 模型:得到的中间结果、loss 等不在显存中把保存;total_loss += loss.item()
  • 中间结果(forward):训练结束后将cuda上的变量删除
  • 梯度信息:测试时使用 torch.no_grad() 不保留梯度信息