—— 关注获取更多科技前沿知识 ——
在算法工程师平常实现自己idea的过程中,经常会在自己很满意的写完整套模型代码,准备跃跃欲试运行自己的模型代码的时候,Cuda out of Memory!!!. 因此如何解决这样的问题呢,本文总结了一些常用的实战技巧。
作者 | AutoML前沿 全文共1030字,阅读可能需要5分钟 梯度累积(gradient accumulation) 当 GPU的显存不足无法采用稍大的batchSize 的时候,可以采用梯度累积的方法。即前向反向传播 N次后,再进行参数的更新。 如下所示是一个pytorch 的实现版本: 梯度检查点(gradient Checkpointing)在缺乏足够的GPU资源的时候,又想训练大模型,一个直观的思路是用时间换取空间:在前向训练的过程中,会保存下来模型每一个网络层的激活值,这会占用GPU很大一部分的空间,所以一个直观的想法的是能否在用到这些激活值的时候再去得到它们,而不是一直存储它们。机器学习大佬、xgboost和TVM的作者陈天奇提出了一个方法,即在反向传播需要得到梯度的时候再重新计算一遍。更多的细节可以参考Training Deep Nets with Sublinear Memory Cost这篇文章。
混合精度训练(mixed precision trainning)混合精度训练指的是在模型的训练过程中存在两种参数的存储方式,分别是全精度和半精度。由于深度学习模型包含大量的浮点数,常见的存储方式是float32、float16。但是在很多情况下并不需要float32就能解决很多的问题。并且float16相比较float32有两个优势:分别是内存占用更少,计算更快。不仅仅可以减少单机单卡的计算时间(内存占用更少,可以使用更大的batchSize),由此也能大幅的降低多机多卡的时间延迟。
内存占用更少 :这是显然易见的,float16只需要2个字节,但是float32需要4个字节。 计算更快 :由于深度学习中存在大量的矩阵乘法半精度的计算吞吐量可以是单精度的2-8倍 。在整个训练过程中的梯度变化如下图所示,其中保存的内容包括(weights,gradient, activation)。在softmax等操作的时候存在累加, float16会出现舍入误差[1]。所以在整个运算的过程中,采用float16进行乘法等运算操作,float32进行加法的运算。此外在权重进行更新的时候也会存在由于梯度值过小,也会出现舍入误差。为此需要在整个训练的过程中需要保存一份权重的副本,得到的FP32的损失需要转换为float16的梯度,在更新的时候scale为float32的Gradient,确保整个更新的过程都是在float32程中进行。
如何应用Mix Precition对于pytorch框架可以使用nvidia开源的加速框架APEX,支持混合训练和分布式训练:
对于TensorFlow框架,也有很方便的方法支持: 当然,如果你有多个机器,多个GPU的机器,那就可以把以上的技术先抛在一边,下回将介绍如何利用分布式的技术加速深度学习模型的训练。