在计算机中,浮点数的表示基于IEEE 754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如 10.2345434,它会被分解为符号位、指数位和尾数位。

这里以最常见的 float32(单精度浮点数)为例来解释这个过程:

符号位

如果数值是正的,符号位为0;如果数值是负的,符号位为1。

对于10.2345434,因为它是正数,所以符号位为0

指数位 (Exponent)

IEEE 754标准采用偏置(或称为偏移)指数来表示。对于float32,指数宽度为8位,偏置值为127。指数的计算方法是将实际的指数值加上偏置值。实际的指数值是将数值标准化为1.xxxxx的形式后的指数。

例如,将10.2345434转化为二进制,约等于1010.00111100001110101(省略无限重复的部分),标准化为1.01000111100001110101 * 2^3。因此,实际的指数是3,偏置后的指数为3 + 127 = 130,二进制表示为10000010

为啥要有偏置值?

支持负指数:浮点数需要能表示小于1的数,这就要求指数可以是负的。通过引入偏置值,实际的指数(可能为负)被转化为一个非负整数,这样就可以用标准的无符号二进制形式存储。

简化硬件设计:使用偏置指数允许浮点数的比较和排序可以像整数一样处理,不需要特别处理指数的正负,这简化了硬件的实现。

偏置值是否一样?

不同的浮点数格式根据其指数的位数有不同的偏置值:

  • float32(单精度):指数位为8位,偏置值为127。
  • float16:指数位为5位,偏置值为15。
  • bfloat16:指数位为8位,偏置值为127。

从上面可以看出,float32bfloat16使用相同的偏置值(127),因为它们的指数部分都是8位。

float16的指数部分只有5位,所以它的偏置值是15。

这种设计反映了不同格式在精度和表达范围之间的平衡。

尾数位 (Mantissa or Fraction)

在将数值标准化后,尾数部分就是除去整数部分的1之后的小数部分。对于float32,尾数有23位。

对于10.2345434,标准化后的尾数是.01000111100001110101。在存储时,省略开头的1(因为在标准化的二进制形式中1总是存在,除非数值是0),直接存储后面的部分,并根据需要截断或舍入到23位。

组合所有部分

将这些部分组合起来,得到float32格式的二进制表示:

符号位:0
指数:10000010
尾数:01000111100001110101000 (这里是截断后的结果)

这个组合的完整二进制串可以直接用于计算机内的存储和计算。

实际的存储和计算可能会涉及舍入和一些优化,这取决于具体的硬件和编译器实现。浮点数的表示并不总能完全精确,小数部分可能会因为二进制表示的限制而有轻微的误差。这是在使用浮点数时需要注意的精度问题。

LLM 场景的浮点数

在大型语言模型(LLM)中,通常使用不同的数值精度来平衡性能和资源消耗。主要包括以下几种精度格式:bfloat16float16float32

float32:

精度:32位,其中1位用于符号,8位用于指数,23位用于尾数。

对于 float32 格式,位布局可以这样表示:

  • 最高位(第32位)是符号位。
  • 接下来的8位(第31至第24位)是指数位。
  • 最后的23位(第23至第1位)是尾数位。
S|EEEEEEEE|MMMMMMMMMMMMMMMMMMMMMMM
  • 1.

其中 S 是符号位,E 是指数位,M 是尾数位。

精确度:提供较高的数值精确度,适用于需要高精度计算的场景。

资源消耗:相比于float16bfloat16,使用float32会消耗更多的内存和计算资源,这可能导致速度较慢。

使用场景:在模型训练的初期或者在对精度要求极高的应用中使用。

float16:

精度:16位,其中1位用于符号,5位用于指数,10位用于尾数。

对于 float16 格式,位布局可以这样表示:

  • 最高位(第16位)是符号位。
  • 接下来的5位(第15至第11位)是指数位。
  • 最后的10位(第10至第1位)是尾数位。
S|EEEEE|MMMMMMMMMM
  • 1.

其中 S 是符号位,E 是指数位,M 是尾数位。

精确度:低于float32,但通常足够用于许多深度学习任务。

资源消耗:内存和计算资源的消耗低于float32,使得模型可以更快地运行。

使用场景:在对计算速度有较高要求的场景中使用,例如在模型推理阶段或者资源有限的设备上。

bfloat16:

bfloat16 中的 “b” 代表 “brain”,这是因为这种数据格式最初是由谷歌大脑(Google Brain)团队为 TensorFlow 和后来的 TPU(Tensor Processing Unit)开发的。

bfloat16 是为了优化深度学习训练和推理在硬件上的执行效率而设计的,特别是在处理需要大量浮点运算的应用时,它通过提供与 float32 相同宽度的指数部分来增强数值的稳定性,同时减少尾数位以节省内存和加速计算。

这种格式现在广泛用于多种深度学习硬件和软件框架中。

精度:16位,其中1位用于符号,8位用于指数,7位用于尾数。

对于 bfloat16 格式,位布局可以这样表示:

  • 最高位(第16位)是符号位。
  • 接下来的8位(第15至第8位)是指数位。
  • 最后的7位(第7至第1位)是尾数位。
S|EEEEEEEE|MMMMMMM
  • 1.

其中 S 是符号位,E 是指数位,M 是尾数位。

精确度:指数的表示范围与float32相同,但尾数的精度低于float16

资源消耗:与float16相似,但由于指数范围的扩展,它在处理极大或极小的数值时更为稳定。

使用场景:特别适合用于深度学习训练,因为它能够处理广泛的数值范围,同时保持较好的计算性能。

总结

选择哪种格式取决于具体的应用需求、计算资源和对精度的需求。float32因其高精度而广泛用于需要精确计算的领域,float16适用于需要较低存储和计算资源的场景,而bfloat16在深度学习训练中特别有用,尤其是在需要广泛数值范围和数值稳定性的场合。