Half-Precision
半精度(FP16)用 16 位而非 FP32 的 32 位存储数字,可将内存占用减半并加快 GPU 计算。内容涵盖其限制、损失缩放和 FP16 推理。
半精度也称为 FP16、float16 或 IEEE 754 binary16,是一种 16 位浮点格式,包含 1 个符号位、5 个指数位和 10 个尾数位。与标准的 32 位单精度 (FP32) 相比,每个数值所需的内存和带宽减半;带有 Tensor Cores 的 GPU 执行 FP16 矩阵运算的速度远快于 FP32。代价是数值范围较窄,最大值为 65,504,且只有约 3 位有效十进制数字。神经网络能够很好地适应这种舍入,因此 FP16 成为 GPU 推理的标准精度;与 FP32 结合用于混合精度时,也是加速训练的常见方式。
FP16 与 FP32 的比较#
FP32 将 8 位用于指数、23 位用于尾数,因此范围约为 10⁻³⁸ 到 10³⁸,约有 7 位有效数字。FP16 的 5 个指数位将正规数限制在 6.1 × 10⁻⁵ 到 65,504 之间;精度较低的非正规数则可以小至约 6 × 10⁻⁸。更大的数会溢出为无穷大,更小的数会舍入为零。转换模型可以展现其优势和局限:
import torch
import torchvision
# ResNet-18 stores its weights in FP32 by default
model = torchvision.models.resnet18()
fp32_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
# Casting to FP16 stores every weight in 2 bytes instead of 4
model.half()
fp16_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
print(f"Weights: {fp32_mb:.1f} MB in FP32, {fp16_mb:.1f} MB in FP16")
# FP16 overflows above 65,504 and rounds to about 3 significant digits
x = torch.tensor([65504.0, 70000.0, 0.1])
print("FP16 values:", x.half().tolist())权重从 46.8 MB 缩小到 23.4 MB。第二行中,65,504 仍是 FP16 可表示的最大值,70,000 变成 inf,而 0.1 存储为 0.0999755859375,这是 FP16 能表示的最接近值。
训练中的半精度#
完全使用 FP16 训练会导致许多模型的精度下降或训练发散,因为较小的梯度会下溢为零,而相对于权重而言极小的权重更新会被舍入掉。混合精度训练可以同时解决这两个问题:它会保留一份 FP32 主权重副本用于更新,并以 FP16 执行前向和反向传播,同时将损失乘以缩放因子,使梯度保持在 FP16 的表示范围内,然后再以 FP32 取消缩放。框架会自动调整该因子,例如 PyTorch 的 GradScaler:
FP16 激活值也会使训练时批次所需的内存减半,因此同一块 GPU 可以容纳更大的批次或更高分辨率的输入。在支持该格式的硬件上,训练通常更倾向于使用 bfloat16 (BF16),因为它的指数位数与 FP32 相同,无需进行损失缩放。
推理中的半精度#
推理无需梯度,而且大多数视觉模型的激活值都在 FP16 的表示范围内,因此将已训练完成的模型转换为 FP16,通常可以将精度保持在与 FP32 相差很小的范围内,且无需像 INT8 模型量化那样提供校准数据。TensorRT 等 GPU 引擎会构建可在 Tensor Cores 上运行的 FP16 引擎,而 NVIDIA Jetson 等嵌入式 GPU 则依靠 FP16 实现实时推理。CPU 的收益较小,因为许多 CPU 缺乏快速 FP16 运算能力。默认情况下,Ultralytics YOLO 会在 GPU 上以 FP16 混合精度训练(amp=True),并使用 quantize=16 执行 FP16 预测和导出,例如使用 YOLO26:
from ultralytics import YOLO
# 加载最小的 YOLO26 检测模型
model = YOLO("yolo26n.pt")
# 在 GPU 上使用 FP16 进行预测;在 CPU 上,PyTorch 模型以 FP32 运行
results = model.predict("https://ultralytics.com/images/bus.jpg", quantize=16)
# 导出一个权重以 FP16 存储的 ONNX 模型
path = model.export(format="onnx", quantize=16)导出的文件将每个权重存储为 2 字节,而不是 4 字节;同一个 quantize=16 参数可以为 TensorRT、OpenVINO、CoreML及其他导出格式生成 FP16 模型。
半精度与相关格式的比较#
- BFloat16 (BF16):另一种 16 位浮点格式,包含 8 个指数位和 7 个尾数位;它牺牲精度来保留 FP32 的数值范围,适合训练大型模型。
- FP8:新款 NVIDIA GPU 支持的 8 位浮点格式,可进一步将内存占用减半,主要用于训练和部署大型语言模型。
- INT8 量化:通过逐张量或逐通道缩放,将数值转换为 8 位整数;其速度比 FP16 更快、占用更小,但需要校准数据或量化感知训练来维持精度。










