1 常用GPU显存不足时的各种Trick

1)监控GPU

2)估计模型显存

3)显存不足时的Trick

4)提高GPU内存利用率

2 数据处理及算法角度提高利用率

1 常用GPU显存不足时的各种Trick

1)监控GPU

监控GPU最常用的当然是nvidia-smi,但有一个工具能够更好的展示信息:gpustat

nvidia-smi
watch --color -n1 gpustat -cpu  #动态事实监控GPU

2)估计模型显存

GPU的内存占用率主要由两部分组成。

一是优化器参数模型自身的参数模型中间每一层的缓存,都会在内存中开辟空间来进行保存,所以模型本身会占用很大一部分内存。模型自身的参数指的就是各个网络层的 Weight 和Bias,这部分显存在模型加载完成之后就会被占用, 注意到的是,有些层是有参数的,如CNN, RNN;而有些层是无参数的, 如激活层, 池化层等。从Pytorch 的角度来说,当你执行    model.to(device) 时, 你的模型就加载完毕,此时你的模型就已经加载完成了。

二是batch size的大小,在模型结构固定的情况下,尽量将batch size设置大,充分利用GPU的内存。

计算模型参数量:torchsummary

import torch as t
from torchsummary import summaryrgb = t.randn(1,3,352,480).cuda()
net = FCN(12).cuda()
out = net(rgb)
summary(net,input_size=(3,352,480),batch_size=1)

3)显存不足时的Trick

此处不讨论多GPU,分布式计算等情况,只讨论一些常规的Trick。

降低batch size

适当降低batch size,则模型每层的输入输出就会成线性减少, 效果相当明显。batch_size是训练神经网络中的一个重要的超参数,该值决定了一次将多少数据送入神经网络参与训练。在显存允许的前提下, batch_size应该越大越好,可以修改图像输入尺寸达到平衡。即,在合理范围内调整图像尺寸,使显存尽可能占满, batch_size尽可能大。

选择更小的数据类型

一般默认情况下,整个网络中采用的是32位的浮点数,如果切换到 16位的浮点数,其显存占用量将接近呈倍数递减。

精简模型

在设计模型时,适当的精简模型,如原来两层的LSTM转为一层;原来使用LSTM, 现在使用GRU;减少卷积核数量;尽量少的使用 Linear ,全连接层参数较多,较少参数或则不用全连接层。使用全局平均池化进行替代等。

数据角度

对于文本数据来说,长序列所带来的参数量是呈线性增加的, 适当的缩小序列长度可以极大的降低参数量。

total_loss

考虑到loss本身是一个包含梯度信息的tensor,因此,正确的求损失和的方式为:

total_loss += loss.item()

释放不需要的张量和变量

采用del释放你不再需要的张量和变量,要求我们在写模型的时候注意变量的使用,不要随心所欲,漫天飞舞。

Relu 的 inplace 参数

激活函数 Relu() 有一个默认参数 inplace ,默认为Flase, 当设置为True的时候,我们在通过relu() 计算得到的新值不会占用新的空间而是直接覆盖原来的值,这表示设为True, 可以节省一部分显存。

梯度累积

首先了解一些Pytorch的基本知识:

  • 在Pytorch 中,当我们执行 loss.backward() 时, 会为每个参数计算梯度,并将其存储在 paramter.grad 中, 注意到, paramter.grad 是一个张量, 其会累加每次计算得到的梯度。

  • 在 Pytorch 中, 只有调用 optimizer.step()时才会进行梯度下降更新网络参数。

我们知道, batch size 与占用显存息息相关,但有时候我们的batch size 又不能设置的太小,这咋办呢?答案就是梯度累加。

传统的训练:

for i,(feature,target) in enumerate(train_loader):outputs = model(feature)  #前向传播loss = criterion(outputs,target)  #计算损失optimizer.zero_grad()   #清空梯度loss.backward()      #计算梯度optimizer.step()     #反向传播,更新网络参数

加入梯度累加之后的代码如下:

for i,(features,target) in enumerate(train_loader):outputs = model(images)    #前向传播loss = criterion(outputs,target)   #计算损失loss = loss/accumulation_steps    #可选,如果损失要在训练样本上取平均loss.backward()    #计算梯度if ((i+1)%accumulation_steps) == 0:optimizer.step()   #反向传播,更新网络参数optimizer.zero_grad()    #清空梯度

       我们发现,梯度累加本质上就是累加  accumulation_steps 个 batchsize 或accumulationsteps 的梯度, 再根据累加的梯度来更新网络参数,以达到真实梯度类似batch_size 的效果。在使用时,需要注意适当的扩大学习率。

更详细来说, 我们假设 batch size = 4 , accumulation steps = 8 , 梯度积累首先在前向传播的时候以 batch_size=4 来计算梯度,但是不更新参数,将梯度积累下来,直到我们计算了 accumulation steps 个 batch, 我们再更新参数。其实本质上就等价于:

真正的 batch_size = batch_size * accumulation_steps

梯度积累能很大程度上缓解GPU显存不足的问题,推荐使用。

梯度检查点

梯度检查点是一种以时间换空间的方法,通过减少保存的激活值压缩模型占用空间,但是在计算梯度时必须重新计算没有存储的激活值。详情参考:陈天奇的 Training Deep Nets with Sublinear Memory Cost

混合精度训练

混合精度训练在单卡和多卡情况下都可以使用,通过cuda计算中的half2类型提升运算效率。一个half2类型中会存储两个FP16的浮点数,在进行基本运算时可以同时进行,因此FP16的期望速度是FP32的两倍。

分布式训练Distribution Training

数据并行 Data Parallelism

模型并行 Model Parallelism

4)提高GPU内存利用率

当没有设置好CPU的线程数时,Volatile GPU-Util是在反复跳动的,0% → 95% → 0%。这其实是GPU在等待数据从CPU传输过来,当从总线传输到GPU之后,GPU逐渐开始计算,利用率会突然升高,但是GPU的算力很强大,0.5秒就基本能处理完数据,所以利用率接下来又会降下去,等待下一个batch的传入。

GPU会很快算完给进去的数据,利用率的主要瓶颈在CPU的数据吞吐量上面,解决方法:

配置更强大的内存条,配合更好的CPU;

在PyTorch的Dataloader上做更改和优化,包括num_workers,pin_memory,会提升速度

num_workers

为了提高利用率,首先要将num_workers设置得体,4、8、16是几个常选的参数。经过测试,将num_workers设置的非常大,如24、32等,其效率反而降低,因为模型需要将数据平均分配到几个子线程去进行预处理,分发等数据操作,设高了反而影响效率。当然,线程数设置为1,是单个CPU来进行数据的预处理和传输给GPU,效率也会低。

pin_memory

当服务器或者电脑的内存较大,性能较好的时候,建议打开pin_memory。该参数为True时可以直接映射到GPU的相关内存块上,省掉了一点数据传输时间。

2 数据处理及算法角度提高利用率

以医学图像分割算法为例从算法工程化的角度出发,探讨算法流程的设计、模型的训练和部署,使设计的分割算法能够落地,可以考虑多阶段分割合理的图像分块模型优化模型训练技巧提高GPU显存的利用率

医学影像数据是多样性的,如何进行预处理,然后送入网络进行训练,可选的方案比较灵活,我们就来探讨一下处理医学影像数据的常用方式。

不同设备厂商、医院、医生的差异,导致扫描的胸部CT影像存在较大的差异。扫描的身体范围差异较大,包括胸部、腹部和骨盆。不同设备扫描的图像分辨率差异也较大。个体差异导致影像中器官的形状和大小差异较大。综合以上因素,最终扫描重建的CT影像的图像矩阵差异大,尤其Z轴图像大小从515到2024变化。针对CT影像特点,可采用以下的数据处理方法:固定大小/固定分辨率整体输入/分块输入,各个方法的优缺点如下:

固定大小

优点:不同case的显存占用一致,可多batch的模型训练和推理;

缺点:图像缩放到固定大小会导致目标的变形

固定分辨率

优点:能够保留人体器官的尺度信息;

缺点:不同case的显存占用不一致,存在out of memory(OM)的风险。同时单卡只能采用one batch的方式进行训练和推理

输入整图

优点:能够保留图像的全局上下文信息;算法的预处理和后处理逻辑相对简单

缺点:对GPU显存的依赖性比较高

分块输入

优点:增加了数据的多样性;能够灵活的利用GPU显存

缺点:丢失了目标的上下文信息;不合理的图像分块会导致图像块边缘处的目标分割效果欠佳。采用overlap的图像分块也会增加运算量;算法预处理和后处理逻辑更加复杂。

采用多阶段算法(定位+分割)解决扫描范围的差异,例如肺分割

解决方案如下:

采用二阶段分割算法,第一阶段采用低分辨率的整图作为输入,实现肺区域的定位;第二阶段采用高分辨率的分块图像(分左肺和右肺)作为输入,实现肺分割。算法流程如下:

1)二阶段级联3D UNet,第一阶段粗分割模型(比如spacing=2mm)实现肺区域定位,第二阶段细分割模型(比如 spacing=1mm)实现肺分割。粗分割过程处理速度较快,增加的时间在可接受的范围;粗分割的准确率对细分割的影响较低,由此可以尽可能降低粗分割模型的复杂度。

2)根据人体解剖结构对图像进行切块处理,最后对分块分割结果进行合并。根据第一阶段的肺定位结果,裁剪背景区域,按照人体解剖结构特点对CT影像进行切块,切分为左肺和右肺。同时保证肺处于图像块的中心,消除目标处于图像块的边缘导致的分割效果变差。这样保留了单肺的完整性,降低了上下文信息的丢失,增加了数据的多样性。当然除了图像切块,也可以对网络切块,此方案并未采用。

3)降低显存占用的网络结构设计,采用bottleneck block、降低Unet深度(三次下采样)、降低初始卷积的宽度;为模型或模型的一部分设置checkpoint,检查点用计算换内存。检查点部分并不保存中间激活值,而是在反向传播时重新计算它们;混合精度训练减少显存占用,实现半精度推理的加速;分布式训练解决各个GPU显卡负载不均衡问题,提高显存的利用率。

4)模型并行。模型并行的高级思想是将模型的不同子网放置到不同的设备上,并相应地实现该forward方法以在设备之间移动中间输出。由于模型的一部分只能在任何单个设备上运行,因此一组设备可以共同为更大的模型服务。这里仅展示模型并行的思想,具体可参考模型并行最佳实践

5)spacing处理:不同CT扫描的spacing存在差异;CT扫描的x,y,z轴spacing不一致。

针对所有CT图像的spacing存在差异,可以将其归一化到同一分布(比如平均spacing)。但是对于长尾数据,经过归一化,数据原始特征将发生较大变化,需要注意resample方法。如果spacing满足均匀分布,可采用多尺度spacing训练,或设计多尺度spacing模型。在推理时,增加条件判断,不同spacing的数据采用不同的模型。除此之外,可以采用从粗到细、固定大小的分割pipeline。虽然采用固定大小会导致图像形变,但通过粗分割定位能够消除不同的CT扫描差异(扫描范围、图像spacing、个体差异),降低图像形变。细分割采用固定尺度,实现目标的分割。

针对x,y,z轴spacing不一致的问题,可以resample到各项同性或者各项异性。基于最长轴的spacing和size,设置目标图像的spacing和size,进行归一化,对其中不足目标大小的图像轴,可以扩大crop的范围,或者采用零值padding。

参考:

GPU 显存不足怎么办

显存不够,如何训练大型神经网络

PyTorch中在反向传播前为什么要手动将梯度清零?

如何破解医学影像分析算法显存不足的困境

优化GPU显存不足,提高GPU利用率相关推荐

  1. GPU显存占满但利用率却很低

    来帕多瓦联培已经一个多月了,最近调代码跑实验又发现了这个问题.这里提供另外一个解决思路.一个原因还是 cpu没跟上gpu节奏. 通常我们都会用尽可能大的 batch 来占满显存.但是如果cpu的读取和 ...

  2. GPU显存满了, 但是GPU利用率很低

    训练效率低?GPU利用率上不去?快来看看别人家的tricks吧- batch size太小,导致模型的并行化程度太低.还有你的cpu.内存/显存带宽性能不足.

  3. 矩池云中Tensorflow指定GPU及GPU显存设置

    矩池云中Tensorflow指定GPU及GPU显存设置 指定GPU 查看机器上GPU情况 命令: nvidia-smi 功能:显示机器上gpu的情况 命令: nvidia-smi -l 功能:定时更新 ...

  4. 深度学习之GPU显存与利用率 浅析小结

    相信很多人,包括我在内,都对GPU的显存抱有不小的怨念,CUDA out of memory之类的问题一直困扰着咱等,今天这篇文章就是来浅析一下,说不定会有所帮助 首先就一笔带过说一下GPU的重要性吧 ...

  5. pytorch 优化GPU显存占用,避免out of memory

    pytorch 优化GPU显存占用,避免out of memory 分享一个最实用的招: 用完把tensor删掉,pytorch不会自动清理显存! 代码举例,最后多删除一个,gpu显存占用就会下降,训 ...

  6. 阿里 NIPS 2017 Workshop 论文:基于 TensorFlow 的深度模型训练 GPU 显存优化

    NIPS 2017 在美国长滩举办,场面非常热烈.阿里巴巴一篇介绍深度模型训练 GPU 显存优化的论文<Training Deeper Models by GPU Memory Optimiza ...

  7. 开源GPU显存虚拟化项目,你的2080Ti还能救一下

    #开源GPU显存虚拟化项目,你的2080Ti还能救一下 vGPU device plugin 基于NVIDIA官方插件(NVIDIA/k8s-device-plugin),在保留官方功能的基础上,实现 ...

  8. GPU显存 - 深度学习中 GPU 和显存分析

    深度学习中 GPU 和显存分析 原文作者陈云. 本文原载于知乎专栏--人工智障的深度瞎学之路 深度学习最吃机器,耗资源,在本文,我将来科普一下在深度学习中: 何为"资源" 不同操作 ...

  9. 解决矩池云GPU显存未释放问题

    很多用户反馈说终止程序之后,显存依然被占用,这里我们提供了两种解决方案,帮助用户解决这个问题. nvidia-smi查看 我们可以先用如下命令 nvidia-smi 查看一下当前GPU进程情况. _ ...

  10. keras系列︱keras是如何指定显卡且限制显存用量(GPU/CPU使用)

    keras在使用GPU的时候有个特点,就是默认全部占满显存. 若单核GPU也无所谓,若是服务器GPU较多,性能较好,全部占满就太浪费了. 于是乎有以下五种情况: 1.指定GPU 2.使用固定显存的GP ...

最新文章

  1. 特征选择和特征生成问题初探
  2. jpa 动态查询条件 数组_Spring data jpa 复杂动态查询方式总结
  3. 解决go项目报错:fatal: could not read Username for ‘https://gitee.com‘: terminal prompts disabled
  4. Counterfeit Dollar
  5. 分表分库时机选择及策略
  6. docker-compose安装问题
  7. CCIE理论-第九篇-IPV6详细介绍
  8. 入夏多吃这些“杀菌菜”
  9. git clone 遇到的坑
  10. word自带公式编辑_如何在word自带的公式编辑器中设置字体格式
  11. ROS联合Webots之实现趣味机器人巡线刷圈
  12. python谷歌翻译 频率限制_使用Python实现谷歌批量翻译
  13. 3G与2G网络融合中组网策略(转)
  14. 阴阳师维护服务器,阴阳师3月3日服务器维护更新内容公告
  15. 基于模型的新能源汽车整车热管理系统设计方案
  16. 苹果授权登录绑定手机号被拒绝
  17. GEE学习:按照行列号筛选鄱阳湖湖区影像数据并查询相关信息
  18. oracle 10g xe 12505,ORACLE10g的ORA-12505问题解决方法
  19. AssemblyInfo.cs文件中的程序集属性
  20. XMind: ZEN 快捷键超强盘点

热门文章

  1. linux挂马检测,检测网站挂马程序(Python)
  2. php 防挂马,织梦dedecms安全设置防挂马教程
  3. You Only Watch Once(YOWO)
  4. 十个你必须知道的ps技巧
  5. 锐取电视墙服务器型号,锐取录播系统 CL4000系列多媒体录播一体机--投影时代产品专区...
  6. idea保存快捷键_windows10下idea快捷键文件
  7. 算法训练 调和数列问题
  8. BNUOJ 1043 旋转方块
  9. 指示约束条件 (indicator constraint) 与大M (big M) 条件
  10. 微信小程序申请发布流程