首页 > 教程攻略 > ai资讯 >彻底理解系列之:FP32、FP16、TF32、BF16、混合精度

彻底理解系列之:FP32、FP16、TF32、BF16、混合精度

来源:互联网 时间:2026-08-19 14:44:35

模型训练和推理的速度,随着大模型的到来变得越来越关键。减小计算过程中数据的长度,从而降低存储和带宽消耗,是提速的重要途径。正因如此,深入理解各种浮点精度的细节,避免一知半解,就显得尤为重要。

彻底理解系列之:FP32、FP16、TF32、BF16、混合精度

从FP32说起

计算机处理数字类型包括整数和浮点两大类,而IEEE 754标准规定了浮点数的存储结构。一个浮点数由三部分组成,以最常见的FP32(Float Point 32)为例:

图1. FP32位数分配,来源[2]

  • Sign(符号位)

    :最高位用1位表示,1表示负数,0表示正数,记为S
  • Exponent(指数位)

    :中间8位表示指数,记为E
  • Mantissa(小数位)

    :低23位表示小数部分,记为M

拿十进制数9.625来举个例子,看看十进制与FP32二进制之间如何互转。

十进制 → 二进制


先拆成整数部分9和小数部分0.625。9转二进制是1001,0.625通过“乘2取整、余下小数继续乘2”的方法得到0.101,合起来就是1001.101,写成二进制的指数形式也就是1.001101 × 23

按照IEEE 754规范,FP32的指数部分要加上127的偏移量,于是3+127=130,对应二进制10000010;小数部分001101后面补足23位;符号位为0。拼在一起就是:
0 10000010 00110100000000000000000。

用转换工具验证一下,果然正确。工具的上面把三个部分都标示出来了,复选框打勾表示对应位置的二进制数为1,否则为0。

图2. FP32转换工具,来源[3]

二进制 → 十进制


反过来就是同样的步骤。将二进制按符号、指数、小数分成S、E、M三部分,FP32转十进制的公式为:
(-1)S × (1.M)2 × 2E-127
具体到这个例子,S=0,E=10000010(十进制130),M=00110100000000000000000,1.M去掉末尾的0,实为1.001101(二进制),转成十进制:1×20 + 0×2-1 + 0×2-2 + 1×2-3 + 1×2-4 + 0×2-5 + 1×2-6 = 1 + 0.125 + 0.0625 + 0.015625 = 1.203125,再乘以23 = 9.625,完美还原。

FP32搞清楚了,FP16、FP64也是类似的套路,只是指数位和小数位的长度不同:

类型 符号位长度 指数位长度 小数位长度 偏移量

半精度 FP16

1 5 10 15

单精度 FP32

1 8 23 127

双精度 FP64

1 11 52 1023

模型训练中不同精度的问题

先对比FP32和FP64。一个FP32只占32位,相比FP64有两大优势:

  1. 减少存储使用

    :显存是关键瓶颈,FP32只用一半的空间,同样的GPU能训练更大的模型,或者同模型下可以塞进更大的batch size。
  2. 提高训练速度

    :同样一次计算,FP32位数少、计算量小,时间自然更短。

同样的道理放在FP16和FP32比较上也成立。但这是否意味着直接全部用FP16就好?当然不是。位数少有两个硬伤:

  1. 精度不足

    :位数少,表示的数就不够精确,可能导致准确率下降。
  2. 范围受限

    :位数少,能表示的最大值和最小值范围更窄,数据可能溢出,“装不下了”。

先看精度问题。用FP64、FP32、FP16分别表示1/3,看看差别有多大:

不同精度表示1/3

精度越高,数据表示和计算越准确,模型训练拟合出来的参数也就越精准。具体得看任务对精度的要求。

再看表示范围。通过PyTorch的接口查一下FP32和FP16能表示的数范围:

范围对比

试试看106这个大数能不能表示:

测试10^6

很明显,106已经超过了FP16的上限65504,无法处理。如果模型训练中某些参数超过这个数,FP16就直接“罢工”了。

混合精度

既然FP32和FP16各有长短,那就混合着用吧。在模型训练的不同步骤选用不同的精度:

图3. 混合精度使用流程,来源[1]

  1. 把神经网络权重参数从初始化的FP32转为FP16;
  2. 用FP16进行前向和反向计算,并算出梯度;
  3. 把FP16的梯度转回FP32;
  4. 用FP32的梯度乘以学习率;
  5. 用FP32更新网络权重,得到更新后的FP32权重。

以上步骤不断循环。简单说就是:梯度更新权重这一步必须用FP32,因为梯度乘上学习率后数值往往很小,FP16的精度不够,容易丢成0。

混合精度里还有一项关键技术叫

“损失缩放”

:在反向计算前,先把损失值放大一个倍数,避免数据太小变成0。放大到FP16能表达的范围后,再算反向传播,最后把梯度缩小同样的倍数,确保最终数值正确。

资料[1]用DistilBERT模型做了一个电影情感分类任务的微调,对比了纯FP32、纯FP16和混合精度的性能与准确率:

图4. FP32、FP16和混合精度训练对比,来源[1]

从图4可以看出,混合精度的训练时间接近FP16,只有FP32的三分之一;存储用量介于两者之间;但预测准确率却和FP32差不多,甚至略高。原文作者提到,可能因为用了正则化。而纯FP16的准确率低很多,多半是发生了数据溢出,模型已经不准了。

BF16、TF32

FP16的指数位只有5位,小数位10位,能表示的整数范围太小。于是谷歌为了自家的TPU定义了一种新格式:Brain Floating Point 16,简称BF16。和FP16一样总长16位,但指数位变成了8位(和FP32相同,因此整数范围一样),小数位缩短到7位。

英伟达则为自家GPU定义了TF32:指数位8位(和FP32、BF16一样),小数位10位(和FP16一样,比BF16多3位)。

图5. BF16、TF32位数,来源:英伟达白皮书

资料[1]把所有类型(除TF32外)以及混合精度又做了统一对比,还是同一个任务:

图6. 各种精度综合对比

可以看出,BF16速度最快(和FP16相当,因为都是16位)、存储最少(小数位短)、准确率也最高(其余几个92%在同一水平,微小偏差可能来自其他因素)。

到此为止,我们把各种浮点类型的定义、转换方法、在模型训练中的使用方式以及性能对比都理了一遍。希望这篇梳理能帮你彻底搞懂这些精度——从FP32到FP16、BF16、TF32,再到混合精度的实践。