百科问答小站 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届学术年会摘要集-第一分会:物理化学前沿 - 论文检索系统 - 中国化学会

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

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




  

相关话题

  adversarial training为什么会起作用? 
  超威半导体(AMD)那么好为什么还要选择英特尔和英伟达? 
  显卡哪个更好? 
  如何把 2080ti 换新成30系列? 
  为什么Transformer要用LayerNorm? 
  30系显卡是否性能过剩? 
  机器学习中如何识别图片中的手是手背还是手心? 
  为啥750ti现在这么贵? 
  如何看待3060显卡被破解,全网3060显卡瞬间被抢空? 
  2021年了,用个人电脑挖矿是否赚钱? 

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





© 2024-05-19 - tinynew.org. All Rights Reserved.
© 2024-05-19 - tinynew.org. 保留所有权利