pytorch中的混合精度训练

pytorch中的混合精度训练,可以帮助我们更加快速地、使用更大的batch_size去训练模型,这其中涉及到的是不同精度的浮点数类型(单精度FP32以及半精度FP16)之间的切换和混合使用,所以叫做混合精度训练。

字节

二进制数系统中,每个0或1就是一个位(bit),位是数据存储的最小单位。1个字节是8个比特,即:1byte = 8bit。而半精度浮点数FP16(FP,Floating Point浮点运算)使用2字节(16位)存储,单精度FP32使用4字节。

半精度Fp16的优势

深度学习系统大都采用的都是Fp32来进行权重参数的存储,随着模型越来越大,加速训练模型的需求就产生了。使用Fp32主要存在2个问题:第一是模型大,训练的时候对显卡的显存要求高;第二是模型训练速度慢。

与FP32相比,FP16仅有16bit,2个字节。使用Fp16可以解决或者缓解使用FP32的两个问题,FP16的优势就是:

(1)显存占用更少:通用的模型 FP16 占用的显存只需原来的一半,训练的时候可以使用更大的batchsize。

(2)计算速度更快:有论文指出半精度的计算吞吐量可以是单精度的 2-8 倍。

但是无脑全部使用FP16也是存在风险的,因为相比FP32,FP16也有自身的一些缺陷。

半精度Fp16存在的问题

(1) 溢出错误

Fp16表示的范围是(6x108~65504),Fp32表示的范围是(1.4x10-45~1.7x1038),前者的范围比后者的小很多。当由Fp32计算转化为Fp16的时候,有很大的概率会出现溢出错误。当一个数比6x108还小的时候,Fp16就表示不了了会出现下溢出Underflow;当一个数比65504还要大的时候,就会出现上溢出Overflow。

一般而言,深度学习模型训练过程中,神经网络的激活函数的梯度比权重的梯度要小,也更容易出现下溢出错误。当出现上下溢出错误的时候,反向传播中误差累积会把梯度变成0或者 nans; 这会导致不准确的梯度更新,影响网络收敛。

(2) 舍入误差

FP16的最大舍入误差约为 (~2 ^-10),比FP32的最大舍入误差(~2 ^-23) 要大不少。在FP16下,对很小的浮点数执行的任何操作都会将该值四舍五入到零,而事实上在深度学习中很小的数也是有意义的,不能直接舍入为0。

那么如何解决FP16的这些问题,或者说如何综合利用FP32与FP16各自的优点呢?那就是混合精度训练,混合着用嘛!

如何解决

混合精度训练+动态损失放大+梯度缩放

(1)混合精度训练

在内存中用FP16做储存和乘法从而加速计算,用FP32做累加避免舍入误差。这样在权重更新的时候就不会出现舍入误差导致更新失败。

(2)损失放大+梯度缩放

使用了混合精度训练,还是会存在无法收敛的情况。这是因为当梯度值非常小,小到FP16不能表示就会出现下溢出错误。因此需要对损失loss进行放大,然后再对梯度进行缩放。

代码实战

在torch中实现混合精度训练,有两种方式。

一是采用NVIDIA的apex包,但是apex 库安装比较麻烦,不太能顺利安装成功。

二是Torch自带的amp,这是着重介绍和推荐的方式。

torch自带的amp实现混合精度主要包括两个部分:autocast 和 GradScaler。

· autocast主要用作上下文管理器或者装饰器,来确定使用混合精度的范围。只包含网络的前向过程(包括loss的计算),而不要包含反向传播。

·GradScaler主要用来完成损失放大和梯度缩放。在反向传播时,通过放大loss的值来防止梯度消失underflow,在更新权重的时候把放大的梯度再unscale回去。
pytorch中的混合精度训练_第1张图片

效果亲测

不使用混合精度在10000条文本数据上一个epoch耗时8分钟,使用后耗时6分钟。

你可能感兴趣的:(混合精度训练,1024程序员节,pytorch,自然语言处理)