4.1 数值表示与精度损失


4.1 数值表示与精度损失

本节摘要:SOURCE 4.1:它上承第三章“TensorRT运行时架构”的系统观,下启4.1.1性能对比与4.1.2误差建模等工程实践;它既回答“为什么FP16比FP32快”,更必须阐明“为…

数值表示的边界条件:FP32、FP16、TF32 与 INT8

深度学习模型在训练阶段几乎无条件信任 FP32 的表示能力,但部署阶段的每一毫秒延迟都要求我们把同一组权重与激活压缩进更窄的比特通道。TensorRT 的精度选项并非简单的"降级开关",每一种格式都是对动态范围、量化步长与硬件吞吐的一次独立取舍。理解这层取舍,是判断"该压到多低"的前提。

FP32 采用 1 位符号、8 位指数与 23 位尾数,其核心设计在于尾数被归一化到 [1, 2) 区间,使得数值在指数区间 [2^k, 2^(k+1)) 内以步长 2^(k-23) 均匀分布。这意味着小数值附近精度极高,而大数值附近步长急剧增大——这是"对数尺度上的不均匀采样"。FP16 把指数压到 5 位、尾数压到 10 位,动态范围收窄到约 [6.1e-5, 6.5e4],在 [1,2) 区间的最小步长约 9.77e-4,比 FP32 粗了 1024 倍。TF32 则是一种混合策略:保留 FP32 的 8 位指数以维持大范围,尾数压缩到 10 位,通过硬件在累加阶段补足精度,本质上是"计算时态的精度调度协议"。

格式 符号位 指数位 尾数位 动态范围 [1,2) 步长 相对 FP32 精度
FP32 1 8 23 ~1e-38 ~ 3e38 1.19e-7 基准
FP16 1 5 10 6.1e-5 ~ 6.5e4 9.77e-4 粗 1024 倍
TF32 1 8 10 同 FP32 1.19e-4 粗 1024 倍
INT8 1 7 位有效 由 scale 决定 线性均匀 强依赖校准

整数量化则彻底离开浮点范式。INT8 有符号整数的取值范围是 [-128, 127],要将连续张量映射进去,需要仿射变换 X_int8 = clip(floor((X - alpha) / beta), -128, 127),其中 beta 是缩放因子(scale),alpha 是零点偏移(zero-point)。beta 直接决定量化粒度,alpha 保证数值零能被精确表示,从而避免 ReLU 等算子在零点处产生系统性偏差。权重通常采用对称量化(alpha 固定为 0),激活值由于分布通常不为零对称而倾向非对称量化。这里存在一个反直觉的事实:INT8 只有 256 个离散值,但神经网络权重与激活往往高度聚集在均值附近,FP32 把比特均匀撒在 1e38 的巨大范围上,其中 99% 的比特在"空转";INT8 则把全部 256 个值聚焦在实际出现的动态区间内,表达效率反而更高——代价是放弃了对分布漂移的自适应能力,一旦输入异常冲出校准区间就会触发硬裁剪。

四类误差源:精度损失的完整归因

精度损失不是单一故障,而是四类误差在深度网络中交织、放大、重构的复杂过程。

表示误差是最底层、最纯粹的误差,任何实数量化后与原值之差被界定在 ±beta/2 之内。但 beta 受限于动态范围与 255 个量化台阶的比值,当残差连接导致激活突然激增,动态范围可能瞬间扩大十倍,表示误差随之暴涨。更危险的是其结构性偏置:若零点不严格等于数据均值,误差分布整体偏斜,会系统性地侵蚀分类边界。计算误差来自算术逻辑本身,FP32 的乘加指令虽然符合 IEEE 标准,但长链计算中的舍入累积不可忽视;INT8 的整数矩阵乘(如 DP4A)结果还需反量化回浮点域,中间累加和可达 1e6 量级,一旦溢出 INT32 就发生静默截断,TensorRT 通过自动插入重缩放节点来铺设防溢出缓冲带。传播误差由深度与非线性放大,Softmax 的指数运算会把输入误差放大到改变概率分布的量级,且一个通道的量化误差经卷积核加权后会污染所有输出通道。校准误差则是认知层面的失配——校准集无法充分代表真实推理分布时,scale 与零点成为"过时的地图"。

# 用 NumPy 模拟 FP16 与 INT8 量化对同一张量的影响 import numpy as np np.random.seed(0) x = np.random.randn(1, 64, 64).astype(np.float32) * 0.8 # 模拟某层激活 # FP16 量化:直接降精度 x_fp16 = x.astype(np.float16).astype(np.float32) # INT8 仿射量化 scale = np.max(np.abs(x)) / 127.0 # 对称量化求 scale x_int8 = np.clip(np.round(x / scale), -127, 127).astype(np.int8) x_deq = x_int8.astype(np.float32) * scale # 反量化回浮点域 print("FP16 相对误差:", np.mean(np.abs(x - x_fp16))) print("INT8 相对误差:", np.mean(np.abs(x - x_deq))) # 结论:FP16 误差集中在尾数舍入,INT8 误差集中在离散化与裁剪

精度与性能的帕累托权衡

不同精度的性能差异远超直觉。在 A100 上,FP32 峰值约 19.5 TFLOPS,FP16 借助 Tensor Core 可达约 312 TFLOPS,而 INT8 吞吐约 624 TOPS——但 INT8 每次操作包含更多的逻辑开销,不能直接与 FLOPS 画等号。内存带宽维度同样关键:加载 1MB 权重,FP32 要搬 1MB,FP16 只要 0.5MB,INT8 仅 0.125MB,在 PCIe 带宽受限的场景下,低位宽直接抬升有效计算占比。端到端延迟才是用户感知的指标:在 T4 上运行 BERT-Base,FP32 平均约 12.4ms,FP16 降到约 7.8ms,INT8 进一步降到约 4.2ms。但这一收益依赖理想校准与良好的算子融合,若模型含大量非融合算子,INT8 的延迟优势可能坍缩到仅 20% 左右。

# trtexec 中切换精度模式,观察 FP16/INT8 的延迟与精度对比 trtexec --onnx=model.onnx --fp16 --workspace=2048 --verbose trtexec --onnx=model.onnx --int8 --calib=calib.bin --verbose
# 用 trtexec 导出各精度下的平均延迟(单位 ms) trtexec --onnx=model.onnx --fp16 | grep -i "Average" trtexec --onnx=model.onnx --int8 --calib=calib.bin | grep -i "Average"

因此,选择何种精度本质是回答三个问题:任务对精度的敏感度是多少(医学分割的 Dice 系数下降 2% 即不可接受,而分类可容忍 0.5% 的 Top-1 下降);硬件是否提供对应加速单元(Jetson 的 NVDLA 对 INT8 原生支持,对 TF32 仅靠 CUDA core 模拟);模型结构是否适配(卷积密集型模型对 INT8 鲁棒性强,Transformer 的 Softmax 与 LayerNorm 数值敏感区常需 FP16 保底)。缓解策略不是消除误差,而是在误差源、传播路径与任务容忍度之间编织弹性缓冲网:逐通道权重量化让每个卷积核拥有独立 scale,关键层插入 FP16 残差连接形成"精度锚点",校准环节用 KL 散度而非简单极值使量化后分布与原始分布的相对熵最小化——这些策略全部源于对数值表示结构的第一性理解。

框架锚点

故障场景切入 4.1 数值表示与精度损失:先固定输入与硬件环境,再定位瓶颈属于图优化、量化还是 I/O。

  • 它上承第三章“TensorRT运行时架构”的系统观,下启4.1.1性能对比与4.1.2误差建模等工程实践;它既回答“为什么FP16比FP32快”,更必须阐明“为什么INT8在ResNet50上可容忍,却可能让ViTL的注意力头彻底失效”。
  • FP32浮点数不是“真实数字”的复刻,INT8整数亦非粗暴的截断;它们是一套套精心设计的有限符号系统,在硅基世界里以离散比特模拟连续数学。
  • 当我们谈论TensorRT加速推理时,常有人将它想象成一台高速运转的精密引擎——CUDA核心是活塞,张量核是曲轴,内存带宽是供油管路。
维度 SOURCE 事实 检验方式
要点 1 它上承第三章“TensorRT运行时架构”的系统观,下启… SOURCE 可验证
要点 2 FP32浮点数不是“真实数字”的复刻,INT8整数亦非粗… SOURCE 可验证
要点 3 当我们谈论TensorRT加速推理时,常有人将它想象成一… SOURCE 可验证

04-04-fig01-4

⚠️ 常见坑:只记结论不记适用边界——超出 SOURCE 所述浓度、尺度或版本范围,规律可能失效。

💡 关键直觉:4.1 数值表示与精度损失 应能对应至少一项可复现实验或算例。

重点提炼

  • 主干:4.1 数值表示与精度损失 连接「输入—过程—输出」
  • 边界:对照 SOURCE 中的参数与版本条件
  • 方法:用表格对齐假设与观测

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U