在计算机中,浮点数的表示基于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。
从上面可以看出,float32
和bfloat16
使用相同的偏置值(127),因为它们的指数部分都是8位。
而float16
的指数部分只有5位,所以它的偏置值是15。
这种设计反映了不同格式在精度和表达范围之间的平衡。
尾数位 (Mantissa or Fraction)
在将数值标准化后,尾数部分就是除去整数部分的1之后的小数部分。对于float32,尾数有23位。
对于10.2345434
,标准化后的尾数是.01000111100001110101
。在存储时,省略开头的1(因为在标准化的二进制形式中1总是存在,除非数值是0),直接存储后面的部分,并根据需要截断或舍入到23位。
组合所有部分
将这些部分组合起来,得到float32格式的二进制表示:
符号位:0
指数:10000010
尾数:01000111100001110101000 (这里是截断后的结果)
这个组合的完整二进制串可以直接用于计算机内的存储和计算。
实际的存储和计算可能会涉及舍入和一些优化,这取决于具体的硬件和编译器实现。浮点数的表示并不总能完全精确,小数部分可能会因为二进制表示的限制而有轻微的误差。这是在使用浮点数时需要注意的精度问题。
LLM 场景的浮点数
在大型语言模型(LLM)中,通常使用不同的数值精度来平衡性能和资源消耗。主要包括以下几种精度格式:bfloat16
、float16
和 float32
。
float32:
精度:32位,其中1位用于符号,8位用于指数,23位用于尾数。
对于 float32
格式,位布局可以这样表示:
- 最高位(第32位)是符号位。
- 接下来的8位(第31至第24位)是指数位。
- 最后的23位(第23至第1位)是尾数位。
S|EEEEEEEE|MMMMMMMMMMMMMMMMMMMMMMM
其中 S
是符号位,E
是指数位,M
是尾数位。
精确度:提供较高的数值精确度,适用于需要高精度计算的场景。
资源消耗:相比于float16
和bfloat16
,使用float32
会消耗更多的内存和计算资源,这可能导致速度较慢。
使用场景:在模型训练的初期或者在对精度要求极高的应用中使用。
float16:
精度:16位,其中1位用于符号,5位用于指数,10位用于尾数。
对于 float16
格式,位布局可以这样表示:
- 最高位(第16位)是符号位。
- 接下来的5位(第15至第11位)是指数位。
- 最后的10位(第10至第1位)是尾数位。
S|EEEEE|MMMMMMMMMM
其中 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
其中 S
是符号位,E
是指数位,M
是尾数位。
精确度:指数的表示范围与float32
相同,但尾数的精度低于float16
。
资源消耗:与float16
相似,但由于指数范围的扩展,它在处理极大或极小的数值时更为稳定。
使用场景:特别适合用于深度学习训练,因为它能够处理广泛的数值范围,同时保持较好的计算性能。
总结
选择哪种格式取决于具体的应用需求、计算资源和对精度的需求。float32
因其高精度而广泛用于需要精确计算的领域,float16
适用于需要较低存储和计算资源的场景,而bfloat16
在深度学习训练中特别有用,尤其是在需要广泛数值范围和数值稳定性的场合。