本文首发于微信公众号: GTOC
业界经常使用量化的手段来提高大模型的训练和推理效率和节省成本,因此衍生了很多浮点精度和格式,比如 TF32、BF16、FP8、FP4 等。
在 AI 芯片的虚拟原型平台开发中,对于各类 FPU 硬件的 model 建模,一般采用软浮点的方式来模拟,常使用 C/C++ 来实现,在保证准确性的同时,性能也不会太差。
常见的模拟器,比如 QEMU TCG 使用 Berkeley SoftFloat-3 开源库(遵循 IEEE 754)来模拟浮点计算。
经过社区的大量重构后,目前 QEMU SoftFloat 为 16/32/64 位的浮点运算提供了统一的实现路径,并支持了 NaN 传播规则以及舍入溢出模式的可配置化,方便客户机 FPU 的模拟。
但对于 4/8 位的浮点精度还不支持。
GEM5 同样集成 Berkeley SoftFloat-3 开源库实现跨平台的浮点运算,确保结果符合 IEEE 754 标准。
本文优先介绍 LLM 常用的浮点格式。
一、浮点标准介绍。
由于浮点的值可以这样表示:
也就是浮点数的实际值,等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。
因此在计算机中,也可以拆成这三部分来表示浮点。
按照 IEEE 754 标准的定义,浮点格式的二进制形式,由符号位(sign)、指数位(exponent)、小数位(fraction)三部分组成,按照符号数值表示法的格式存储(小端序)。
所谓符号数值表示法,指的是在计算机进行运算时,需要将负数编码至二进制形式中。
如上图所示,符号位,占据最高有效位,0 代表正数,1 代表负数;指数位,占据次高有效位的 e 个 bits ;小数位,占据最低有效位的 f 个 bits。
e 和 f 的具体值,因浮点精度和格式的变化而不同。
值得一提的是指数的计算。
为了方便,IEEE 标准定义了指数的实际值加上固定的偏移值的办法来表示浮点数的指数。
固定偏移值为 2^(e-1) - 1 ,e 表示存储指数的比特的长度。
最后计算的值,称为指数偏移值(exponent bias),代表指数域的编码值。
这样定义的好处是,可以用长度为 e 个比特的无符号整数,来表示所有的指数取值。
这使得两个浮点数的指数大小的比较,更为容易(实际上,可以按照字典次序,比较两个浮点表示的大小)。
这种移码表示的指数部分,中文也称作阶码。
如果指数域的编码值范围在 (0, 2^e - 2) 以内,且在科学计数法的方式下,小数位的最高有效位为 1 ,那么这个浮点数将被称为规约形式的浮点数。“规约”是指用唯一确定的浮点形式去表示一个值。
以单精度浮点的格式表示十进制数 5.0 为例:
其二进制表示为 101.0,科学计数法表示 1.01 * 2^2。此时:
整数部分为 1(符合“最高有效位为 1 ”的条件),被隐含在分数部分;指数实际值为 2 ,分数部位的小数部分为 01(即 0.012)。
由于单精度浮点数的固定偏移值为 127(2^7 - 1),因此指数编码值为 2 + 127 = 129。这个值的范围在 (0, 254) 以内。
现在我们来拼接这个浮点数的完整二进制表示:
这个值,是 5.0 的唯一归约形式。
规约化浮点数适用于大多数普通的浮点运算,提供了广泛的数值范围和较高的精度。例如,在单精度(32位)浮点数中,只有23位用于存储小数部分,但实际上表示的是24位精度。
除了规约化浮点数,还有非规约化浮点数。
非规约化浮点数的定义是:指数部分的编码值是0,分数部分非零。一般是某个数字相当接近零时才会使用非规约型式来表示。
IEEE 754标准规定:非规约形式的浮点数的指数偏移值比规约形式的浮点数的指数偏移值小 1 。
例如,最小的规约形式的单精度浮点数的指数部分编码值为 1 ,指数的实际值为 -126 ;而非规约的单精度浮点数的指数域编码值为 0 ,对应的指数实际值也是 -126 而不是 -127。
实际上非规约形式的浮点数仍然是有效可以使用的,只是它们的绝对值已经小于所有的规约浮点数的绝对值;即所有的非规约浮点数比规约浮点数更接近 0 。
下面给出归约形式和非归约定形式浮点的差异总结:
还有一些浮点的特殊值,需要介绍一下:
-
如果指数是 0 并且尾数的小数部分是 0 ,这个数 ±0(和符号位相关);
-
如果指数 = 2^e − 1,并且尾数的小数部分是 0 ,这个数是 ±∞(同样和符号位相关);
-
如果指数 = 2^e − 1,并且尾数的小数部分非 0,这个数表示为非数(NaN)。
我们再聊聊浮点数的比较和舍入。
浮点数基本上可以按照符号位、指数域、尾数域的顺序作字典比较。显然,所有正数大于负数;正负号相同时,指数的二进制表示法更大的其浮点数绝对值更大。
IEEE 标准列出 4 种不同的浮点舍入方法:
-
舍入到最接近:在一样接近的情况下偶数优先(Ties To Even,这是默认的舍入方式),会将结果舍入为最接近且可以表示的值,但是当存在两个数一样接近的时候,则取其中的偶数(在二进制中以 0 结尾的)。
-
朝 +∞ 方向舍入:会将结果朝正无限大的方向舍入。
-
朝 -∞ 方向舍入:会将结果朝负无限大的方向舍入。
-
朝 0 方向舍入:会将结果朝0的方向舍入。
二、常见浮点格式介绍。
除了 IEEE 754 标准中规定的双精度、单精度、半精度,在 LLM 量化中又扩展了 8 位精度、4 位精度(INT3/INT5/INT6 比较少用),浮点格式也类似。
我们先讨论传统的双精度、单精度和半精度浮点格式。
双精度浮点数(Double-precision floating-point)是用来表示浮点数的一种资料类型,在计算机中占据 64 比特(8字节)大小,因此也称为 float64 或 FP64 。
在 IEEE 754 中这种 64 位二进制格式被正式称为 binary64 格式,而在 IEEE 754-1985 中被称为 double,即双精度。
单精度浮点数(single-precision floating-point)是用来表示浮点数的一种数据类型,在计算机中占据 32 比特(4字节)大小,因此也称为 float32 或 FP32 。
在 IEEE 754 中这种 32 位二进制格式被正式称为 binary32 格式,而在 IEEE 754-1985 中被称为 single ,即单精度。
半精度浮点数(half-precision floating-point)是用来表示浮点数的一种数据类型,在计算机中占据16比特(2字节)大小,因此也称为 float16 或 FP16 。
在 IEEE 754 中这种 16 位二进制格式被正式称为 binary16 格式,因为只有单精度浮点数的一半大小,也简称为 half ,即半精度。这种数据类型只适合存储对精度要求不高的数字,不适合用来计算。
接着我们讨论 LLM 量化中扩展的特殊浮点精度。
三、特殊浮点格式介绍。
下表整理了常见浮点精度的格式。
对应的二进制位数示意图:
我们展开来讲讲。
TF32(Tensor Float 32)是英伟达专为机器学习设计的特殊数值类型,旨在替代 FP32,首次于 A100 GPU 中支持。
其结构由 1 位符号位、8 位指数位(与 FP32 的指数位对齐)和 10 位小数位(与 FP16 的小数位对齐)组成,实际占用 19 位。
这种设计使其在性能、数值表示范围和精度之间取得了平衡 —— 既保留了接近 FP32 的表示范围(得益于 8 位指数位),又通过减少小数位降低了计算和存储成本,同时精度能满足多数机器学习场景需求,适合在训练和推理中提升效率。
BF16(Brain Float 16)是由 Google Brain 专为机器学习设计的浮点格式,其结构由 1 位符号位、8 位指数位和 7 位小数位组成。
与 FP32 的关系:指数位与 FP32 相同(均为 8 位),因此表示范围和 FP32 一致,且两者之间转换便捷;但小数位仅 7 位(FP32 为 23 位),精度远低于 FP32。
与 FP16 的关系:小数位少于 FP16(FP16 为 10 位),因此精度低于 FP16;但指数位多于 FP16(FP16 为 5 位),表示范围大于 FP16。
在硬件支持上,NVIDIA 的 Ampere 架构及后续 GPU 才支持 BF16,适合在平衡存储 / 计算成本与模型性能的机器学习场景中使用。
FP8-E4M3 和 FP8-E5M2 是两种 8 位浮点数据类型,旨在加速深度学习推理,由英伟达、ARM 和英特尔联合开发。
FP8-E4M3:包含 1 位符号位、4 位指数位和 3 位尾数位,可存储的值最大约为 ±448 和 NaN,精度相对较高。
FP8-E4M3 通常适用于神经网络的前向传播过程。因为在前向传播中,激活值和权重需要更高的精度来保证计算结果的准确性,FP8-E4M3 能较好地满足这一需求。
FP8-E5M2:包含 1 位符号位、5 位指数位和 2 位尾数位,可存储的值最大约为 ±57344,还支持 ±inf 和 NaN。其指数位更多,动态范围更大,但由于尾数位较少,精度相对较低。
FP8-E5M2 更适合用于神经网络的反向传播过程。反向传播中,梯度对精度的敏感度相对较低,但需要更大的动态范围来表示,FP8-E5M2 的特性使其能够更好地胜任。
与 FP32 和 FP16 相比,FP8 数据类型占用的存储空间更小,能提高模型的吞吐量,减少延迟。对于计算密集型操作,如矩阵乘法和卷积,使用 FP8 可以在相同的硬件资源下处理更多的数据,从而提升计算效率。
最后再聊聊 FP4 。
FP4 有两种选择,NVFP4 和 MXFP4 。两者均为 4 位浮点数(FP4)格式,核心共性是都采用 E2M1 数据类型,即由 1 位符号位、2 位指数位、1 位尾数位组成,总位数为 4 位,适用于低精度计算场景以节省内存和提升效率。
两者的差异主要体现在两个方面:
量化块大小:NVFP4 的量化块大小为 1×16,意味着量化操作以 1 行 16 列的块为单位进行;MXFP4 的量化块大小为 1×32,量化单位是 1 行 32 列的块,更大的块可能影响量化粒度和精度保留效果。
扩展因子类型:NVFP4 使用 E4M3 数据类型作为扩展因子,而 MXFP4 的扩展因子为 E8M0 类型。扩展因子的差异可能影响数值范围的扩展能力和精度补偿方式,进而影响整体计算的动态范围和准确性(NVFP4 精度优于 MXFP4)。
FP4-E2M1 可以显著减少显存开销,因为每个浮点数仅占用 4 位,相比于 FP16(16位)或 FP32(32位)格式,这大大降低了存储需求。
同时,它与特定硬件加速单元的结合,如 NVIDIA 的 Blackwell 架构中的 Tensor Cores,可以实现更高的计算吞吐量。
但由于动态范围和精度的限制,直接使用 FP4-E2M1 可能会导致模型训练和推理时的精度损失。特别是在需要高精度计算的任务中,如某些复杂的语言模型任务,FP4 -E2M1 可能无法保持足够的精度。
四、小节。
前面我们已经讲解了常见的浮点精度与格式。限于篇幅,在后续的文章,我们将讲解如何扩展 QEMU 的 SoftFloat 库,来支持更多的浮点精度。
图片、资料来源:
[1] 维基百科 - IEEE 754 [2] 知乎 - LLM 大模型之精度问题(FP16,FP32,BF16)详解与实践 [3] 知乎 - 大模型涉及到的精度有多少种?FP32、TF32、FP16、BF16、FP8、FP4、NF4、INT8都有什么关联,一文讲清楚 [4] scaleway Docs - Understanding the NVIDIA FP8 format [5] 论文 - FP8 Formats for Deep Learning [6] 论文 - LLM-FP4: 4-Bit Floating-Point Quantized Transformers