fp32/bfp16数据类型

本文介绍了深度学习中使用半精度(FP16)浮点数进行混合精度训练的原因,包括减少显存占用和加速计算。FP16虽能节省一半的存储空间,但也引入了溢出错误和舍入误差。为解决这些问题,提出了FP32权重备份、Loss Scale等方法,以及利用高精度计算来减少精度损失。文章还提及Nvidia Tensor Core在混合精度训练中的作用。
部署运行你感兴趣的模型镜像

1.基础理论

在日常中深度学习的系统,一般使用的是单精度 float(Single-Precision)浮点表示。在了解混合精度训练之前,我们需要先对其中的主角半精度『float16』进行一定的理论知识学习。

在上图可以看到,与单精度float(32bit,4个字节)相比,半进度float16仅有16bit,2个字节组成。天然的存储空间是float的一半。 其中,float16的组成分为了三个部分:

  1. 最高位表示符号位;
  2. 有5位表示exponent位;
  3. 有10位表示fraction位;

当5位exponent为0时,则指数部分为n-15+1,当5位exponent为1时,则指数部分为n-15。

preview

2.为什么需要FP16?

在使用FP16之前,我想再赘述一下为什么我们使用FP16。

  1. 减少显存占用 现在模型越来越大,当你使用Bert这一类的预训练模型时,往往显存就被模型及模型计算占去大半,当想要使用更大的Batch Size的时候会显得捉襟见肘。由于FP16的内存占用只有FP32的一半,自然地就可以帮助训练过程节省一半的显存空间。
  2. 加快训练和推断的计算 与普通的空间时间Trade-off的加速方法不同,FP16除了能节约内存,还能同时节省模型的训练时间。在大部分的测试中,基于FP16的加速方法能够给模型训练带来多一倍的加速体验(爽感类似于两倍速看肥皂剧)。
  3. 张量核心的普及 硬件的发展同样也推动着模型计算的加速,随着Nvidia张量核心(Tensor Core)的普及,16bit计算也一步步走向成熟,低精度计算也是未来深度学习的一个重要趋势,再不学习就out啦。

3. FP16带来的问题:量化误差

这个部分是整个博客最重要的理论核心。 讲了这么多FP16的好处,那么使用FP16的时候有没有什么问题呢?当然有。FP16带来的问题主要有两个:1. 溢出错误;2. 舍入误差。

preview

2. 舍入误差(Rounding Error) 

舍入误差指的是当梯度过小,小于当前区间内的最小间隔时,该次梯度更新可能会失败,用一张图清晰地表示:

这个例子非常直观的阐述了『舍入误差』这个说法。而至于上面提到的,FP16的最小间隔是一个比较玄乎的事,在wikipedia的引用上有这么一张图: 描述了 fp16 各个区间的最小gap。

3. 解决办法

3.1 FP32 权重备份

这种方法主要是用于解决舍入误差的问题。其主要思路,可以概括为:weights, activations, gradients 等数据在训练中都利用FP16来存储,同时拷贝一份FP32的weights,用于更新。 在这里,我直接贴一张论文[1]的图片来阐述:

可以看到,其他所有值(weights,activations, gradients)均使用 fp16 来存储,而唯独权重weights需要用 fp32 的格式额外备份一次。 这主要是因为,在更新权重的时候,往往公式: 权重 = 旧权重 + lr * 梯度,而在深度模型中,lr * 梯度 这个值往往是非常小的,如果利用 fp16 来进行相加的话, 则很可能会出现上面所说的『舍入误差』的这个问题,导致更新无效。因此上图中,通过将weights拷贝成 fp32 格式,并且确保整个更新(update)过程是在 fp32 格式下进行的。

看到这里,可能有人提出这种 fp32 拷贝weight的方式,那岂不是使得内存占用反而更高了呢?是的, fp32 额外拷贝一份 weight 的确新增加了训练时候存储的占用。 但是实际上,在训练过程中,内存中占据大部分的基本都是 activations 的值。特别是在batchsize 很大的情况下, activations 更是特别占据空间。 保存 activiations 主要是为了在 back-propogation 的时候进行计算。因此,只要 activation 的值基本都是使用 fp16 来进行存储的话,则最终模型与 fp32 相比起来, 内存占用也基本能够减半。

3.2 Loss Scale

为了解决梯度过小的问题,论文中对计算出来的loss值进行scale,由于链式法则的存在,loss上的scale会作用也会作用在梯度上。这样比起对每个梯度进行scale更加划算。 scaled 过后的梯度,就会平移到 fp16 有效的展示范围内。

这样,scaled-gradient 就可以一直使用 fp16 进行存储了。只有在进行更新的时候,才会将 scaled-gradient 转化为 fp32,同时将scale抹去。论文指出, scale 并非对于所有网络而言都是必须的。而scale的取值为也会特别大,论文给出在 8 - 32k 之间皆可。

3.3 提高算数精度

在论文中还提到一个『计算精度』的问题:在某些模型中,fp16矩阵乘法的过程中,需要利用 fp32 来进行矩阵乘法中间的累加(accumulated),然后再将 fp32 的值转化为 fp16 进行存储。 换句不太严谨的话来说,也就是利用 利用fp16进行乘法和存储,利用fp32来进行加法计算。 这么做的原因主要是为了减少加法过程中的舍入误差,保证精度不损失。

在这里也就引出了,为什么网上大家都说,只有 Nvidia Volta 结构的 拥有 TensorCore 的CPU(例如V100),才能利用 fp16 混合精度来进行加速。 那是因为 TensorCore 能够保证 fp16 的矩阵相乘,利用 fp16 or fp32 来进行累加。在累加阶段能够使用 FP32 大幅减少混合精度训练的精度损失。而其他的GPU 只能支持 fp16 的 multiply-add operation。这里直接贴出原文句子:

Whereas previous GPUs supported only FP16 multiply-add operation, NVIDIA Volta GPUs introduce Tensor Cores that multiply FP16 input matrices andaccumulate products into either FP16 or FP32 outputs

 

参考链接:

https://zhuanlan.zhihu.com/p/103685761

https://zhuanlan.zhihu.com/p/79887894

https://blog.csdn.net/qq_36533552/article/details/105885714

 

 

 

 

您可能感兴趣的与本文相关的镜像

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

07-08
### fp32 数据类型在嵌入式系统中的应用:陀螺仪与加速度计 在嵌入式系统中,`fp32`(即 32 位浮点数)数据类型广泛用于处理传感器数据,尤其是在涉及高精度计算的场景中。例如,在惯性测量单元(IMU)中,陀螺仪和加速度计输出的数据通常需要以较高的精度进行存储和运算,而 `fp32` 能够提供足够的动态范围和精度来满足这一需求。 陀螺仪和加速度计是嵌入式系统中常见的传感器,它们分别用于测量角速度和线性加速度。这些原始数据往往需要经过滤波、积分或融合算法(如卡尔曼滤波或互补滤波)来获得更准确的姿态估计。由于这些运算涉及复杂的数学操作(如三角函数、矩阵运算等),使用 `fp32` 可以显著减少因精度不足而导致的数值误差,从而提升系统的整体性能。 在实际应用中,MPU6050 等惯性测量单元通过 IIC 接口与主控芯片通信,并输出原始的角速度和加速度数据。尽管 MPU6050 的原始数据为整型,但在后续处理中,通常会将其转换为 `fp32` 类型以便于进行角度计算和姿态估计。例如,将原始角速度值乘以采样时间并累加,即可得到当前的角度值;这种积分运算对精度要求较高,因此采用 `fp32` 是一种常见做法[^1]。 此外,现代嵌入式处理器(如 ARM Cortex-M7)已经具备了硬件浮点运算单元(FPU),能够高效地执行 `fp32` 类型的运算,进一步提升了系统的实时性和能效表现。这使得 `fp32` 在高性能嵌入式系统中成为主流的数据表示方式之一[^2]。 以下是一个典型的代码示例,展示了如何将加速度计和陀螺仪的原始数据转换为 `fp32` 并进行简单的角度计算: ```cpp // 假设已获取到陀螺仪的原始数据 int16_t gyro_raw[3]; // X, Y, Z 轴的原始角速度值 float gyro_fp32[3]; // 存储转换后的 fp32 数据 // 将原始数据转换为 fp32 类型(假设灵敏度因子为 131.0f) gyro_fp32[0] = (float)gyro_raw[0] / 131.0f; // X 轴角速度 gyro_fp32[1] = (float)gyro_raw[1] / 131.0f; // Y 轴角速度 gyro_fp32[2] = (float)gyro_raw[2] / 131.0f; // Z 轴角速度 // 假设 dt 为两次采样之间的时间间隔(单位:秒) float dt = 0.01f; // 对角速度进行积分,得到当前角度 float angle_x = 0.0f; angle_x += gyro_fp32[0] * dt; ``` 在某些应用场景中,如智能健身设备或音频空间渲染系统,`fp32` 数据类型还被用于实现高精度的运动轨迹还原或个性化声场调整。这些系统通常依赖多源传感器数据的融合,而 `fp32` 提供了必要的数值精度以确保融合算法的稳定性与准确性[^3]。 综上所述,`fp32` 数据类型在嵌入式系统中具有广泛的应用价值,特别是在处理陀螺仪和加速度计等传感器数据时,其高精度特性可以显著提升系统的性能和可靠性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值