存储在 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 在其最新的图形处理器中实现了 E4M3FNE5M2。GraphCore 只实现了 E4M3FNUZE5M2FNUZ

E4M3FN 和 E5M2

\(S\) 代表符号。\(10_2\) 表示以 2 为底的数字。

Float8 类型

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\)。浮点值由以下表达式定义

Float8 类型值

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。即使尾数相同,指数也不同。

Float8 类型

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}\)

浮点值由以下表达式定义

Float8 类型值

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