百科问答小站 logo
百科问答小站 font logo



Pytorch有什么节省显存的小技巧? 第1页

  

user avatar   zhengzhedong 网友的相关建议: 
      

在不修改网络结构的情况下, 有如下操作:

  1. 同意 @Jiaming , 尽可能使用inplace操作, 比如relu 可以使用 inplace=True 。一个简单的使用方法,如下:
       def inplace_relu(m):     classname = m.__class__.__name__     if classname.find('ReLU') != -1:         m.inplace=True  model.apply(inplace_relu)     

2.进一步,比如ResNet 和 DenseNet 可以将 batchnorm 和relu打包成inplace,在bp时再重新计算。使用到了pytorch新的checkpoint特性,有以下两个代码。由于需要重新计算bn后的结果,所以会慢一些。

3. 每次循环结束时 删除 loss,可以节约很少显存,但聊胜于无。可见如下issue

Tensor to Variable and memory freeing best practices


4. 使用float16精度混合计算。我用过 @NVIDIA英伟达 apex,很好用,可以节约将近50%的显存,但是要小心一些不安全的操作如 mean和sum,溢出fp16。

NVIDIA/apex

补充:最近我也尝试在我CVPR19的GAN模型中加入fp16的训练,可以从15G的显存需求降到约10G,这样大多数1080Ti等较为常见的显卡就可以训练了。欢迎大家star一波 github.com/NVlabs/DG-Ne

5. 对于不需要bp的forward,如validation 请使用 torch.no_grad , 注意model.eval() 不等于 torch.no_grad() 请看如下讨论。

'model.eval()' vs 'with torch.no_grad()'


6. torch.cuda.empty_cache() 这是del的进阶版,使用nvidia-smi 会发现显存有明显的变化。但是训练时最大的显存占用似乎没变。大家可以试试。

How can we release GPU memory cache?


另外,会影响精度的骚操作还有:

把一个batchsize=64分为两个32的batch,两次forward以后,backward一次。但会影响 batchnorm等和batchsize相关的层。


相关链接:老外写的提高pytorch效率的方法,包含data prefetch等

Optimizing PyTorch training code


最后感谢大家看完~欢迎关注分享点赞~也可以check我的一些其他文章

郑哲东:【新无人机数据集】从 行人重识别 到 无人机目标定位

郑哲东:利用Uncertainty修正Domain Adaptation中的伪标签

郑哲东:用CNN分100,000类图像

郑哲东:NVIDIA/悉尼科技大学/澳洲国立大学新作解读:用GAN生成高质量行人图像,辅助行人重识别


user avatar   meng-xin-43-24 网友的相关建议: 
      

来说说实际点的吧。

首先你要放弃Python的思维

用C语言的思路去写pytorch代码。


思路一:显存释放

制作一个显存管理类,可以根据需要自行定制。

我贴一份我的解决思路,想要的可以直接抄。

       class MemCache:      @staticmethod     def byte2MB(bt):         return round(bt / (1024 ** 2), 3)      def __init__(self):         self.dctn = {}         self.max_reserved = 0         self.max_allocate = 0      def mclean(self):         r0 = torch.cuda.memory_reserved(0)         a0 = torch.cuda.memory_allocated(0)         f0 = r0 - a0          for key in list(self.dctn.keys()):             del self.dctn[key]         gc.collect()         torch.cuda.empty_cache()          r1 = torch.cuda.memory_reserved(0)         a1 = torch.cuda.memory_allocated(0)         f1 = r1 - a1          print('Mem Free')         print(f'Reserved  	 {MemCache.byte2MB(r1 - r0)}MB')         print(f'Allocated 	 {MemCache.byte2MB(a1 - a0)}MB')         print(f'Free      	 {MemCache.byte2MB(f1 - f0)}MB')      def __setitem__(self, key, value):         self.dctn[key] = value         self.max_reserved = max(self.max_reserved, torch.cuda.memory_reserved(0))         self.max_allocate = max(self.max_allocate, torch.cuda.memory_allocated(0))      def __getitem__(self, item):         return self.dctn[item]      def __delitem__(self, *keys):         r0 = torch.cuda.memory_reserved(0)         a0 = torch.cuda.memory_allocated(0)         f0 = r0 - a0          for key in keys:             del self.dctn[key]          r1 = torch.cuda.memory_reserved(0)         a1 = torch.cuda.memory_allocated(0)         f1 = r1 - a1          print('Cuda Free')         print(f'Reserved  	 {MemCache.byte2MB(r1 - r0)}MB')         print(f'Allocated 	 {MemCache.byte2MB(a1 - a0)}MB')         print(f'Free      	 {MemCache.byte2MB(f1 - f0)}MB')      def show_cuda_info(self):         t = torch.cuda.get_device_properties(0).total_memory         r = torch.cuda.memory_reserved(0)         a = torch.cuda.memory_allocated(0)         f = r - a          print('Cuda Info')         print(f'Total     	{MemCache.byte2MB(t)} MB')         print(f'Reserved  	{MemCache.byte2MB(r)} [{MemCache.byte2MB(self.max_reserved)}] MB')         print(f'Allocated 	{MemCache.byte2MB(a)} [{MemCache.byte2MB(self.max_allocate)}] MB')         print(f'Free      	{MemCache.byte2MB(f)} MB')     


然后这么使用

       mc = MemCache() mc.show_cuda_info()   mc['X_train'], mc['Y_pred'] = NNMaker.load_nnBuffer_mono(Fpath, mode='train') mc['X_test'], mc['Y_pred'] = NNMaker.load_nnBuffer_mono(Fpath, mode='test') mc['X_pred'], mc['Y_pred]'] = NNMaker.load_nnBuffer_mono(Fpath, mode='pred')  mc['X_train_tensor'] = mm.predict(ModelManager.aray2torch(mc['X_train'])) mc['X_test_tensor'] = mm.predict(ModelManager.aray2torch(mc['X_test'])) mc['X_pred_tensor'] = mm.predict(ModelManager.aray2torch(mc['X_pred']))  mc['pred_Y_train'] = mc['X_train_tensor'][:, 0].tolist() mc['pred_Y_test'] = torch.cat([mc['X_train_tensor'][-1:, 0], mc['X_test_tensor'][:, 0]], dim=0).tolist() mc['pred_Y_pred'] = torch.cat([mc['X_test_tensor'][-1:, 0], mc['X_pred_tensor'][:, 0]], dim=0).tolist()  # 训练  mc.mclean() mc.show_cuda_info()     


记得每一个epoch,在结束的时候都清空内存。

mclean是清空所有,如果不想全部清空,你还可以用del方法手动清空。


总结一句话,用完的东西立刻free memory。

所有的torch.tensor 都不要直接裸在外面,放到MemoryCache的字典里,用完之后,立刻清除。


PS:本来显存爆炸,做不起来的,这么一通操作之后完美运行,萌新大佬均可使用,没有太多技巧。


思路二:古典方法

一整块tensor如果放不进GPU,就切小块分批训练。当然这会造成运行IO时间稍长的问题。

能用卷积的一定要用卷积,卷积可以快速缩小input tensor的大小。避免在大tensor之间使用fully connected。

总之就是input拿到,尽量快速的把tensor大小收敛,然后输出的时候尽量快速放大。

很多图片缩小个1-2倍没啥区别的,直接缩小切块。时间序列使用slide window,moving average(本质还是缩小切块)

激活函数relu解释了,永远滴神。除非你能证明这个东西一定要用其他激活函数,否则就用relu。


思路三:神经网络分析师

仔细查看,神经网络试在哪个步骤的时候显存激增,到底有没有必要使用这么多显存。没必要的就砍掉。查看有没有leek memory 有的话就清空。把每个在一些关键的步骤打印log,然后查看通过分析log的异常数字,来反推哪个结构出了问题


每个层的参数和数据大小全部列出来,然后对着每层的tensor大小思考,哪个可以小一点。


思路四:硬盘换显存

基本上用不到,但如果你实在穷得买不起GPU,又想训练大模型,这是最后的方法。

也就是你训练一小块,存进硬盘,然后再读另一小块训练,结果存硬盘。然后再把之前存进硬盘的结果读起来训练下一层,然后存硬盘。最终这个方法能解决一切不能训练的问题。

PS:这是玩黄油的时候给我的启发,当时玩CM3D2,电脑跑不起来,于是使用了virtual Memory的思路。

只要到这一步,你就可以暴力破解一切显存问题。硬盘和显存可以互换。

你还可以在硬盘和GPU之间建立RAM cache。

当然缺点也是有的,IO会变得特别长,甚至远超模型真正训练的时间。


user avatar   zhu-xiao-lin-22-96 网友的相关建议: 
      

节省显存方面,欢迎关注我们团队最近开源的工作:

个人认为这个工作把单卡训练,或者是数据并行下的显存节省做到极致了~

这里主要介绍一下单机训练上的思路。

随着模型越来越大,GPU 逐渐从一个计算单元变成一个存储单元了,显存的大小限制了能够训练的模型大小。微软的 DeepSpeed 团队提出我们其实可以把优化器状态(Adam 的 momentum 和 variance)放在 CPU 上,用一个实现的比较快的 CPU Adam 来做更新,这样既不会变慢很多,也可以明显省出来很多空间。我们把这个思想再往前推一步,我们是不是可以只把需要计算的模型参数放在 GPU 上,其余的模型参数,优化器状态都放在 CPU 上,这样就可以尽最大能力降低对显存的需求,让 GPU 回归它计算单元的本色。

为了达成这样的效果,我们就需要一个动态的显存调度——相对于 DeepSpeed 在训练前就规定好哪些放在 CPU 上,哪些放在 GPU 上,我们需要在训练过程中实时把下一步需要的模型参数拿到 GPU 上来。利用 pytorch 的 module hook 可以让我们在每个 nn.Module 前后调用回调函数,从而动态把参数从 CPU 拿到 GPU,或者放回去。

但是,相信大家能够想象到,如果每次都运行到一个 submodule 前,再现把参数传上来,肯定就很慢,因为计算得等着 CPU-GPU 的传输。为了解决计算效率的问题,我们提出了 chunk-based management。这是什么意思呢?就是我们把参数按照调用的顺序存储在了固定大小的 chunk 里(一般是 64M 左右),让内存/显存的调度以 chunk 为单位,第一次想把某个 chunk 中的参数放到 GPU 来的时候,就会直接把整个 chunk 搬到 GPU,这意味着虽然这一次的传输可能需要等待,但是在计算下一个 submodule 的时候,因为连着的 module 的参数都是存在一个 chunk 里的,这些参数已经被传到 GPU 上来了,从而实现了 prefetch,明显提升了计算效率。同时,因为 torch 的 allocator 会缓存之前分配的显存,固定大小的 chunk 可以更高效利用这一机制,提升显存利用效率。

在 chunk 的帮助下,我们的模型可以做到,CPU 内存加 GPU 显存有多大,模型就能训多大。和 DeepSpeed 相比,在同等环境下模型规模可以提升 50%,计算效率(Tflops)也更高。

对于多卡的数据并行场景,我们扩展了上述方法,可以做到多卡中只有 1 整份模型,1 整份优化器状态,同时具备了数据并行的易用性和模型并行的显存使用效率。如果想了解多卡训练的方案,以及更详细的一些优化,也欢迎来看看我们的论文:

以上。


user avatar   shi-hou-11 网友的相关建议: 
      

女王:求求题主放过我,我可不敢有什么政绩。。。


user avatar   marisa.moe 网友的相关建议: 
      

这个问题问得很好啊,我的建议是看今年年会的摘要集:

中国化学会第32届学术年会 - 论文检索系统 - 中国化学会

可以看到有很多分会,不过计算化学分布得比较散,夹杂在各个分会中。各分会的主题可以从这里找到,可能相关的包括:

有一些主题是理论计算夹杂着实验的,还需要仔细辨别。回到摘要集,以第一分会为例:

中国化学会第32届学术年会摘要集-第一分会:物理化学前沿 - 论文检索系统 - 中国化学会

可以看到题目和单位全都标出来了,而且还可以下载。

显然,能找到相关方向的摘要的单位,就是开设了相关方向的院校,甚至还能精确到具体的某个课题组。




  

相关话题

  基于深度学习的自然语言处理在 2016 年有哪些值得期待的发展? 
  如何评价 Big Navi 显卡被爆将标配 16GB 显存,对标 3080 但售价低 150 美元? 
  为什么多通道内存可以显著提升性能,而双显卡却没有那么大? 
  一个完整的Pytorch深度学习项目代码,项目结构是怎样的? 
  显卡等等党应该向我们商家赔偿损失吗? 
  如何看待2021年秋招算法岗灰飞烟灭? 
  视频剪辑显卡该如何选择? 
  作为AMD的粉丝,你对AMD这几年的发挥满意吗? 
  如何看待「秋名山显卡团」遭大量硬件厂商抵制和封杀? 
  pytorch 的高层库ignite怎么样? 

前一个讨论
想成为一位诗人 要具备什么条件?
下一个讨论
如何评价张枣的诗?





© 2025-02-20 - tinynew.org. All Rights Reserved.
© 2025-02-20 - tinynew.org. 保留所有权利