存储在 8 位中的浮点数¶
论文¶
2022 年发表了两篇论文,介绍了存储在一个字节中的浮点数,而不是存储在 4 个字节中的 float 32。浮点精度大大降低,但训练精度受影响不大。
NVIDIA、Intel 和 ARM 的一篇论文介绍了遵循 IEEE 规范的两种类型。第一种是 E4M3,1 位用于符号,4 位用于指数,3 位用于尾数。第二种是 E5M2,1 位用于符号,5 位用于指数,2 位用于尾数。第一种类型主要用于权重,第二种用于梯度。
第二篇论文 用于深度神经网络的 8 位数值格式 介绍了类似的类型。IEEE 标准对 +0
(或整数 0)和 -0
(或整数 128)赋予相同的值。他们选择为这两个数字赋予不同的浮点值。该论文实验了指数和尾数之间的不同划分,并表明 E4M3 和 E5M2 是最佳的。
因此,onnx==1.15.0
中引入了四种新类型,以支持有限的算子集,从而实现 8 位浮点计算。
E4M3FN
:1 位用于符号,4 位用于指数,3 位用于尾数,只有 NaN 值,没有无穷大值 (FN),E4M3FNUZ
:1 位用于符号,4 位用于指数,3 位用于尾数,只有 NaN 值,没有无穷大值 (FN),没有负零 (UZ)E5M2
:1 位用于符号,5 位用于指数,2 位用于尾数,E5M2FNUZ
:1 位用于符号,5 位用于指数,2 位用于尾数,只有 NaN 值,没有无穷大值 (FN),没有负零 (UZ)
实现通常依赖于硬件。NVIDIA、Intel 和 Arm 在其最新的图形处理器中实现了 E4M3FN
和 E5M2
。GraphCore 只实现了 E4M3FNUZ
和 E5M2FNUZ
。
E4M3FN 和 E5M2¶
\(S\) 代表符号。\(10_2\) 表示以 2 为底的数字。
E4M3FN |
E5M2 |
|
---|---|---|
指数偏差 |
7 |
15 |
无穷大 |
\(S.11111.00_2\) |
|
NaN |
\(S.1111.111_2\) |
\(S.11111.\{01, 10, 11\}_2\) |
零 |
\(S.0000.000_2\) |
\(S.00000.00_2\) |
Max |
\(S.1111.110_2\) |
\(1.75 \times 2^{15}= 57344\) |
Min |
\(S.0000.001_2 = 2^{-9}\) |
\(S.00000.01_2 = 2^{-16}\) |
我们将位表示记为 \(S.b_6 b_5 b_4 b_3 b_2 b_1 b_0\)。浮点值由以下表达式定义
E4M3FN |
E5M2 |
|
---|---|---|
指数 \(\neq\) 0 |
\((-1)^S 2^{\sum_{i=3}^6 b_i 2^{i-3} - 7} \left( 1 + \sum_{i=0}^2 b_i 2^{i-3} \right)\) |
\((-1)^S 2^{\sum_{i=2}^6 b_i 2^{i-2} - 15} \left( 1 + \sum_{i=0}^1 b_i 2^{i-2} \right)\) |
指数 \(=\) 0 |
\((-1)^S 2^{-6} \sum_{i=0}^2 b_i 2^{i-3}\) |
\((-1)^S 2^{-14} \sum_{i=0}^1 b_i 2^{i-2}\) |
E4M3FNUZ 和 E5M2FNUZ¶
之前的类型支持正零和负零、正 NaN 和负 NaN。GraphCore 引入了另一种类型定义,以更好地利用这四个值。名称中包含 UZ 的每种类型只有一个零和一个 NaN(= 负零)。另一个区别来自指数偏差。因此,一个非空、非 NaN 的 float 8 FLOAT8E4M3FN 不能仅仅因为指数偏差的差异就简单地转换为 FLOAT8E4M3FNUZ。即使尾数相同,指数也不同。
E4M3FNUZ |
E5M2FNUZ |
|
---|---|---|
指数偏差 |
8 |
16 |
无穷大 |
||
NaN |
\(1.0000.000_2\) |
\(1.00000.00_2\) |
零 |
\(0.0000.000_2\) |
\(0.00000.00_2\) |
Max |
\(S.1111.111_2\) |
\(S.11111.11_2\) |
Min |
\(S.0000.001_2 = 2^{-10}\) |
\(S.00000.01_2 = 2^{-17}\) |
浮点值由以下表达式定义
E4M3FNUZ |
E5M2FNUZ |
|
---|---|---|
指数 \(\neq\) 0 |
\((-1)^S 2^{\sum_{i=3}^6 b_i 2^{i-3} - 8} \left( 1 + \sum_{i=0}^2 b_i 2^{i-3} \right)\) |
\((-1)^S 2^{\sum_{i=2}^6 b_i 2^{i-2} - 16} \left( 1 + \sum_{i=0}^1 b_i 2^{i-2} \right)\) |
指数 \(=\) 0 |
\((-1)^S 2^{-7} \sum_{i=0}^2 b_i 2^{i-3}\) |
\((-1)^S 2^{-15} \sum_{i=0}^1 b_i 2^{i-2}\) |
Cast¶
从 float 8 Cast 到 float 16(或 E5M10)、bfloat16(或 E8M7)、float32(或 E8M23)更容易。Cast 是精确的。对于某些特定值(例如 -0
或 -NaN
),转换不一定保留符号。
Cast 到 float 8 包括找到最接近原始 float 32 值的 float 8。这通常通过移位和截断完成。
转换可能带饱和(saturation),每个超出范围的值都会变成可用的最大值。下表总结了所有情况。[x]
表示四舍五入到目标尾数宽度的值。
x |
E4M3FN |
E4M3FNUZ |
E5M2 |
E5M2FNUZ |
---|---|---|---|---|
0 |
0 |
0 |
0 |
0 |
-0 |
-0 |
0 |
-0 |
0 |
NaN |
NaN |
NaN |
NaN |
NaN |
Inf |
FLT_MAX |
NaN |
FLT_MAX |
NaN |
-Inf |
-FLT_MAX |
NaN |
-FLT_MAX |
NaN |
[x] > FLT_MAX |
FLT_MAX |
FLT_MAX |
FLT_MAX |
FLT_MAX |
[x] < -FLT_MAX |
-FLT_MAX |
-FLT_MAX |
-FLT_MAX |
-FLT_MAX |
否则 |
RNE |
RNE |
RNE |
RNE |
转换也可以不带任何饱和进行定义。
x |
E4M3FN |
E4M3FNUZ |
E5M2 |
E5M2FNUZ |
---|---|---|---|---|
0 |
0 |
0 |
0 |
0 |
-0 |
-0 |
0 |
-0 |
0 |
NaN |
NaN |
NaN |
NaN |
NaN |
-NaN |
-NaN |
NaN |
-NaN |
NaN |
Inf |
NaN |
NaN |
Inf |
NaN |
-Inf |
-NaN |
NaN |
-Inf |
NaN |
[x] > FLT_MAX |
NaN |
NaN |
Inf |
NaN |
[x] < -FLT_MAX |
NaN |
NaN |
-Inf |
NaN |
否则 |
RNE |
RNE |
RNE |
RNE |