机器学习和高性能计算中常用的几种浮点数精度
浮点数 (Floating-Point Number) 是一种在计算机中表示带有小数部分的数字的方式。它通过科学记数法类似的方式(尾数 × 基数 ^ 指数)来近似表示实数。浮点数的精度决定了它可以表示的数值范围以及数值之间的精细程度。
常见的浮点数精度包括:
-
FP32 (Single-Precision Floating-Point)
- 也称为单精度浮点数。
- 使用 32 位(4 字节)来存储数字。
- 这 32 位通常分配给:1 位符号位,8 位指数位,23 位尾数/有效数字位。
- 特点:具有相对较好的数值范围和精度。在传统的科学计算、图形渲染以及较早期的深度学习模型中广泛使用。
- 缺点:相对于低精度格式占用更多内存,计算速度也可能较慢(因为硬件通常针对更小的数据类型进行优化)。
-
FP16 (Half-Precision Floating-Point)
- 也称为半精度浮点数。
- 使用 16 位(2 字节)来存储数字。
- 这 16 位通常分配给:1 位符号位,5 位指数位,10 位尾数/有效数字位。
- 特点:
- 优势: 相比 FP32 占用内存减半,可以存储更大的模型或处理更大的批量数据。在支持 FP16 计算的硬件上(如 NVIDIA 的 Tensor Cores),计算速度可以显著加快(理论上可达 FP32 的两倍甚至更高)。这对于深度学习的训练和推理非常重要。
- 劣势: 由于指数位和尾数位都减少了,FP16 的数值范围更小,精度也较低。这可能导致在训练过程中出现数值下溢(Underflow,数字太小无法表示)或上溢(Overflow,数字太大无法表示),以及梯度消失等问题,使得模型训练不稳定或难以收敛。
-
BF16 (Bfloat16 - Brain Floating Point)
- 由 Google Brain 提出的一种 16 位浮点格式。
- 使用 16 位(2 字节)来存储数字。
- 这 16 位通常分配给:1 位符号位,8 位指数位,7 位尾数/有效数字位。
- 特点:
- 优势: 与 FP32 具有相同的指数位数量(8 位),因此拥有与 FP32 几乎相同的数值范围。这大大减少了训练过程中出现上溢或下溢的风险,使得从 FP32 切换到 BF16 训练时通常比切换到 FP16 更稳定、更容易收敛。同样拥有 16 位格式带来的内存和计算效率优势。
- 劣势: 尾数位只有 7 位,比 FP16 的 10 位少,这意味着 BF16 的表示精度比 FP16 要低。
FP16 和 BF16 的对比总结:
- 位数相同: 都是 16 位。
- 内存/速度: 都比 FP32 节省内存并可能在支持硬件上加速计算。
- 主要区别:
- FP16: 范围小,但精度相对 BF16 高。
- BF16: 范围大(与 FP32 相似),但精度相对 FP16 低。
- 应用: FP16 广泛应用于各种支持的硬件,尤其在对精度要求不极致或通过混合精度训练可以缓解精度问题的场景。BF16 在 TPU 和较新的 GPU 上得到很好的支持,因其大范围带来的训练稳定性而在很多大型模型训练中受到青睐。
在现代深度学习中,FP16 和 BF16 通常被称为“标准精度”,相对于传统的 FP32(全精度)以及更低的 INT8、INT4 等量化(整数)精度而言。许多现代模型和训练技术都默认或推荐使用 FP16 或 BF16 来提高训练和推理效率。
当 llama.cpp
转换脚本要求输入标准精度模型时,它就是指期望模型的权重是 FP16 或 BF16 格式,而不是经过 4 位或 8 位等其他库的量化处理后的格式(这些格式通常会添加一些 llama.cpp
不认识的元数据张量)。
除了我们之前讨论的 FP32 (单精度)、FP16 (半精度) 和 BF16 (Bfloat16) 这些浮点数精度之外,在 AI 特别是深度学习领域,还有一些非常主流的精度格式,主要用于追求更高的效率和更低的内存占用:
-
INT8 (8-bit Integer)
- 描述: 使用 8 位整数来表示数值。由于整数没有小数部分,为了表示浮点数,通常需要结合一个缩放因子(scalar)和零点(zero point)来实现(这叫做量化)。
- 特点: 相比 FP32/FP16/BF16 占用内存显著减少(权重大小变为原来的 1/4 到 1/2),计算量也大大降低。现代 AI 硬件(GPU, TPU, 专用推理芯片)通常都有专门的 INT8 计算单元,可以提供极高的吞吐量。
- 主要用途: 推理 (Inference)。INT8 是目前最普遍的模型量化格式,广泛应用于各种边缘设备和数据中心推理场景,以加速计算和降低部署成本。
- 挑战: 将模型从浮点数转换为 INT8 需要进行量化过程,可能需要校准数据集(Calibration)或进行量化感知训练(Quantization-Aware Training, QAT)来最小化精度损失。
-
INT4 (4-bit Integer)
- 描述: 使用 4 位整数来表示数值。这是比 INT8 更低的精度。
- 特点: 相比 INT8 进一步减少模型大小(再次减半)和计算量。对于在资源极度受限的设备上部署超大型模型,或者在有限的显存中运行更大模型非常有用(比如您之前遇到的 L4 + 22GB 显存,INT4 是进一步压缩模型的手段)。
- 主要用途: 极限推理场景,尤其是在内存或带宽是主要瓶颈时。
- 挑战: 精度极低,量化到 INT4 对模型的精度影响更大,更容易导致性能下降。需要更先进的量化技术和算法来保持可用精度。您之前尝试的 4 位加载 (
load_in_4bit=True
) 就属于这一范畴。
-
FP8 (8-bit Floating-Point)
- 描述: 一种新兴的 8 位浮点格式。与 INT8 不同,它保留了浮点数的指数和尾数结构,因此具有一定的数值范围。目前主要有两个主流变体:E5M2 (5 位指数,2 位尾数) 和 E4M3 (4 位指数,3 位尾数)。
- 特点: 旨在结合 INT8 的效率和 FP16/BF16 的数值鲁棒性。E5M2 强调范围(与 FP16 范围相似),E4M3 强调精度。在支持的硬件上提供 8 位计算的高吞吐量。
- 主要用途: 正在成为大型模型训练的一种重要精度格式(尤其在最新的硬件如 NVIDIA Hopper/Blackwell 和 Google Trillium 上),以及高性能推理。它可以减少训练时的内存和计算需求,同时提供比 INT8 更好的训练稳定性。
此外,还有一个重要的概念是:
- 混合精度 (Mixed Precision)
- 描述: 这不是一种单一的精度格式,而是一种训练技术。它在训练过程中同时使用多种精度,通常是将模型权重和激活值存储在低精度(如 FP16 或 BF16)以节省内存和加速计算,但在执行对数值精度要求较高的操作(如梯度累加、更新模型权重)时使用高精度(如 FP32)来保持数值稳定性。
- 特点: 是目前训练大型深度学习模型的主流方法,可以在不牺牲太多精度的情况下大幅提高训练速度和显存利用率。需要硬件支持低精度浮点计算以及在不同精度之间高效转换的能力。
除了 FP32, FP16, BF16 之外,目前深度学习领域主流的精度还包括用于量化推理的 INT8 和 INT4,以及用于高性能训练和推理的新兴 FP8。同时,混合精度是结合不同精度进行训练的普遍技术。选择哪种精度取决于具体的任务(训练还是推理)、硬件平台的能力、对性能和内存的严格要求,以及对模型精度损失的容忍度。