状态:草稿(2026-10-16),包含部分实测数据。 GPU 测量在家庭实验室的一张 8GB 笔记本级 GPU 上通过单次运行 Pod 完成(预热后取重复测量中位数)。准确度比较基于一个公开数据集 OpenTTGames 测试比赛(带有标记的 60fps 帧 388 张)。INT8 在同一 Pod 的 CPU(限制 4 核)上使用 PyTorch 的后量化进行。格式和校准的原理通过笔记本 CPU 示例(
code/02_quant_error.py,耗时约 1 秒)进行了验证。 「视觉模型阅读 3」系列第 2 篇。延迟预算在 第 1 篇中。
目录
- 1. 为什么减少位数会更快
- 2. 两种 16 位格式:FP16 与 BF16
- 3. INT8:将实数对齐到刻度
- 4. 后量化(PTQ)与校准数据
- 5. 量化感知训练(QAT)
- 6. 我们的实测:球体模型的 FP16
- 7. 我们的实测:球体模型的 INT8 后量化(CPU)
- 8. 局限性
- 9. 与我们项目的联系
- 参考文献
1. 为什么减少位数会更快
神经网络推理的大部分时间花在两件事上:从内存中获取数值的时间,以及乘法和加法的时间。将单个数值从 32 位减少到 16 位,需要获取的字节数就会减半。此外,如今的 GPU 和手机芯片都有专门的硬件单元,可以同时执行大量的 16 位和 8 位乘法。根据 NVIDIA 2018 代 GPU 的规格,INT8 的计算吞吐量是 FP32 的 16 倍,FP16 是 8 倍(Wu 等人 2020,表 1 —— 这是规格比例,而非实际测量值)。
代价是准确度。如果数值记录得不够密集,就会产生舍入误差;如果范围太窄,大数值就会溢出。
2. 两种 16 位格式:FP16 与 BF16
浮点数分为符号位、指数位和尾数位。指数决定“最大或最小到什么程度的数”,尾数决定“精度有多密”。
| 格式 | 符号·指数·尾数 | 最大数 | 与 1 之后的下一个数的间隔(eps) |
|---|---|---|---|
| FP32 | 1 · 8 · 23 | 3.4×10³⁸ | 1.19×10⁻⁷ |
| FP16 | 1 · 5 · 10 | 65,504 | 9.77×10⁻⁴ |
| BF16 | 1 · 8 · 7 | 3.4×10³⁸ | 7.81×10⁻³ |
(code/02_quant_error.out.txt 的 PyTorch 值。)FP16 精度高但范围窄,而 BF16 拥有与 FP32 相同的范围,但精度较为稀疏。亲自输入测试后发现,70,000 在 FP16 中变为 inf,在 BF16 中变为 70,144;1e-8 在 FP16 中变为 0,在 BF16 中变为 1.00117e-8。256×256 矩阵乘法的相对误差在 FP16 中为 3.6×10⁻⁴,在 BF16 中为 2.9×10⁻³。
在训练中,这个范围问题非常严重。Micikevicius 等人的《Mixed Precision Training》(2018)通过将损失乘以一个较大的数然后再除以它,解决了 FP16 中小于 2⁻²⁴ 的梯度变为 0 的问题,即 损失缩放,并且为权重单独保留了 FP32 副本。这样一来,ResNet-50 在 ImageNet 上的 top-1 准确率与 FP32 的 75.92 以及混合精度的 76.04 保持一致(表 1)。BF16 的范围与 FP32 相同,因此无需损失缩放即可使用;Kalamkar 等人(2019)使用 BF16 训练 ResNet-50,取得了与 FP32 相同的 74.7 准确率(在同类评估方法之间进行比较,见第 4.1.2 节)。
在推理中,由于使用的是已经训练好的权重,范围问题相对较小。只要激活值不超过 65,504,在许多情况下 FP16 就足够了。
3. INT8:将实数对齐到刻度
8 位整数只有 256 个格子。Jacob 等人(2018)的仿射量化将实数 r 记为:
r = S · (q − Z)
S: 눈금 한 칸의 크기(실수), Z: 실수 0 에 해당하는 정수(영점), q: 0–255 의 정수
범위 [최솟값, 상한] 을 정하면 S = (상한 − 최솟값) / 255, Z = round(−최솟값 / S)
关键在于设置 Z,使得实数 0 能够准确地对应到一个整数。这样就可以无误差地表示用 0 填充的 padding 以及 ReLU 之后的 0。由于乘法是通过 uint8 × uint8 累加为 int32,最后仅在结尾乘一次缩放因子再缩减回 8 位,因此整个推理过程可以完全只用整数进行。同一篇论文将批归一化提前折叠到卷积中(第 3 篇)的图进行量化。
| 结果(Jacob 等人 2018) | 实数 | INT8 | 条件 |
|---|---|---|---|
| ResNet-50 ImageNet top-1 | 76.4 | 74.9 | 表 4.1 |
| MobileNet SSD(宽度 50%)COCO mAP · 延迟 | 16.7 · 121ms | 16.6 · 61ms | 2017 年手机大核,单线程(表 4.4) |
| 人脸检测(宽度 25%)延迟 | 44ms(约 23fps) | 28ms(约 36fps) | 同一手机的单个大核(表 4.6) |
这篇论文的核心观点是,在相同的 33ms 预算内,INT8 模型的准确度比实数模型高出约 10%(第 4.2.1 节,手机小核)。这是因为可以通过降低精度节省时间来运行更大的模型。
设置多少个刻度:单个张量与按通道对比
如果为整个权重张量使用一个 S,那么只要有一个通道的值变大,刻度就会为了适应那个通道而变宽,导致其余部分被模糊。如果为每个输出通道单独设置 S(per-channel),这个问题就会消失。亲自尝试后发现(256×256 矩阵乘法,仅权重为 INT8):
| 权重 | 单个张量 | 按通道 |
|---|---|---|
| 均衡权重 | 1.06×10⁻² | 7.13×10⁻³ |
| 仅一个通道大 20 倍 | 7.25×10⁻²(其余通道 1.13×10⁻¹) | 6.32×10⁻³ |
。在实际模型中也会发生同样的情况。Krishnamoorthi(2018)的白皮书显示,如果将 MobileNet-v1 在不进行训练的情况下转换为 8 位,单个张量的刻度会导致 top-1 降至 0.001(完全崩溃),而按通道的刻度则为 0.703(实数为 0.709)(表 3)。深度可分离卷积(第 4 篇)各个通道的值的大小差异很大,因此特别容易受到此问题的影响。
4. 后量化(PTQ)与校准数据
后量化是指将训练结束的模型输入几百张代表性样本,测量各层激活值的范围(校准,calibration),并根据该范围确定 S 和 Z。这不需要训练。Nagel 等人(2021)的白皮书指出,500–1,000 张无标签图像通常就足够了,并报告称遵循标准流程后,在 W8A8(权重和激活值均为 8 位)中处理的所有模型都在 FP32 的 0.7% 误差范围内。
困难之处在于 范围应该抓取到哪里。如果直接使用最小值和最大值,由于偶尔出现的一个大值,刻度会变得非常稀疏。如果稍微截断顶部(例如 99.99 百分位)以缩小范围,刻度会变密,但被截断的值就会出错。我们使用两种类型的激活值进行了实测(校准样本和测试样本是分开抽取的)。
| 激活值 | 校准批次 | 最小值–最大值 | 99.99 百分位 | 99.9 百分位 |
|---|---|---|---|---|
| (甲)长尾分布 | 1 | 上限 23.1,误差 0.057 | 21.0, 0.067 | 11.0, 0.158 |
| (甲) | 64 | 上限 215.1,误差 0.144 | 22.7, 0.059 | 10.3, 0.170 |
| (乙)罕见的大值是信号 | 64 | 上限 109.4,误差 0.076 | 51.0, 0.180 | 4.1, 0.707 |
- 在(甲)中,最小值–最大值方法 随着校准数据的增加而变差。 这是因为样本越多,越容易遇到极端值,导致范围从 23 扩大到 215。99.99 百分位数与样本数量无关,表现得很稳定。
- 在(乙)中情况则相反。如果罕见的大值用于产生结果,将其截断会导致严重错误。语言模型的“异常值特征”就是这种情况,Dettmers 等人(2022)展示了如果擦除这 0.1% 的值,模型就会崩溃。
因此,校准方法必须针对每个模型进行测试。在 Wu 等人(2020)的表中,EfficientNet-B0 经过 PTQ 后,最小值–最大值法为 22.3%,熵法为 72.1%,99.9 百分位法为 70.9%(FP32 为 76.9%);而 Inception v4 中,最小值–最大值法为 0.1%,99.99 百分位法为 79.6%(FP32 为 79.7%)(ImageNet top-1,校准图像 1,024 张,表 5)。
5. 量化感知训练(QAT)
如果 PTQ 不够用,可以在训练时就开始模拟量化。在前向传播中,使用“量化后再还原”的数值(伪量化)来计算权重和激活值,而反向传播则像没有舍入一样直接传递梯度。这相当于让模型学着将权重调整到契合刻度上。在 Krishnamoorthi 的白皮书中,因单个张量刻度而崩溃的 MobileNet-v1(0.001)通过 QAT 恢复到了 0.70(表 4);在 Wu 等人的研究中,EfficientNet-B0 通过 QAT 达到了 76.95%,超过了 PTQ 的最佳水平(72.1%)(微调幅度约为原始训练周期的 10%,表 7)。代价是需要训练流水线和数据。
6. 我们的实测:球体模型的 FP16
速度
我们在实验 GPU 上对 TT Score 的球体模型(BlurBall,3 帧窗口,512×288)进行了测量(批大小为 1,预热 20 次后取 250 次的中位数)。
| 精度 | 单个窗口 |
|---|---|
| FP32(运行方式) | 15.09ms |
| FP16, autocast(每个运算自动使用 16 位) | 9.77ms(1.54 倍) |
| FP16, 整个模型设为 half | 9.49ms(1.59 倍) |
在同一个 GPU 上,Transformer 编码器(EfficientTAM-S,第 4 篇)的速度提升了 3.1 倍。球体模型是一个保持分辨率到最后的卷积网络,因此 16 位专用硬件的发挥空间相对较小,且其中还残留了批归一化、ReLU 等逐元素运算。在第 3 篇中,如果用编译器将这些融合,时间下降到了 4.3ms。
准确度:球体位置会改变吗
在 OpenTTGames 的一场测试比赛中,针对 388 张带有球体位置标记的 60fps 帧(全部包含球),使用与线上相同的方式(最后一帧的热力图,阈值 0.5 的连通域查找),在相同的输入上运行了 FP32 和 FP16。如果距离正确答案在 5px(基于 512×288)以内,则计为正确。同时,将同一输入经 JPEG 质量 70 压缩再解压后(与浏览器发送的条件相同),也用 FP32 进行了运行。
| 输入与精度 | 命中 / 误报 / 漏报 | 精确率 · 召回率 | 与 FP32 检测结果相同的帧 | 热力图最大值位置发生移动的帧 |
|---|---|---|---|---|
| 原始 · FP32 | 384 / 2 / 4 | 0.995 · 0.990 | — | — |
| 原始 · FP16(autocast) | 384 / 2 / 4 | 0.995 · 0.990 | 100% | 4.6%(最大 2px) |
| 原始 · FP16(half) | 384 / 2 / 4 | 0.995 · 0.990 | 100% | 0.8%(最大 1px) |
| JPEG 70 · FP32 | 377 / 1 / 11 | 0.997 · 0.972 | 97.9% | 34.3% |
在这 388 帧中,FP16 没有改变任何一个判定结果。 热力图数值的差值在中央 0.0001 量级。相比之下,JPEG 压缩将漏球数从 4 个增加到 11 个,并将最大值位置在帧的三分之一处移动。比起将精确率减半, 压缩输入带来的误差要大得多。在实际运营中我们已经接收的是 JPEG,因此我们认为即使开启 FP16,结果也不会比现在有太大差异。不过这只是一场比赛、一个条件下的结果,我们并未在劣化条件(昏暗、模糊)以及生产 GPU 上进行测量(未实测)。
7. 我们的实测:球模型的 INT8 后量化(CPU)
由于 GPU 的 INT8 需要 TensorRT 等运行时,本次未能测量(第 3 篇)。取而代之的是,我们使用 PyTorch 的后量化(FX 图模式,x86 整数引擎)在 CPU 上将相同模型转换为 INT8。校准帧选自比赛前半部分,评估帧选自后半部分,两者互不重叠。
评估帧 220 张(有球 215 张),校准候选 146 张,限制 CPU 4 核,每 4 张一组运行的每窗口时间中位数。
| 方式 | 校准帧 | 命中 / 误报 / 漏报 | 召回率 | 热力图最大差值(相比 FP32,中位数) | CPU 每窗口 |
|---|---|---|---|---|---|
| FP32 | — | 208 / 3 / 7 | 0.967 | — | 519.9ms |
| INT8,默认(激活值直方图,每个权重通道) | 1 | 206 / 3 / 9 | 0.958 | 0.064 | 72.2ms |
| 相同 | 8 | 207 / 3 / 8 | 0.963 | 0.056 | 71.3ms |
| 相同 | 64 | 208 / 3 / 7 | 0.967 | 0.057 | 63.1ms |
| INT8,最小值–最大值,单个权重张量 | 8 | 207 / 3 / 8 | 0.963 | 0.086 | 71.4ms |
| INT8,最小值–最大值,每个权重通道 | 8 | 208 / 2 / 7 | 0.967 | 0.084 | 72.5ms |
- 当校准帧为 64 张时,INT8 给出了与 FP32 相同的判定数(命中 208);当为 1 张时,多漏掉了两个。该模型非常稳健,单张校准帧也不会崩溃。
- 热力图数值的差值大约是 FP16(0.001 量级)的 50 倍,但阈值 0.5 的判定几乎没有改变。这是因为热力图中球位置的数值通常远离阈值。
- CPU 时间约为 FP32 的七分之一。不过,很难说这倍数完全归功于 8 位。在该模型(全程保持分辨率的卷积)中,FP32 路径在同款 CPU 上可能异常缓慢,其原因并未单独确认(未实测)。即便排除这个差异,GPU FP16 上的同款模型也只需 10ms,因此 CPU INT8 的 72ms 无法成为实时服务器路径的替代方案。手机 CPU 上的数值未进行测量。
8. 局限性
- 准确率对比基于一场公开比赛、侧面摄像头以及清晰的条件。我们并未测量在球模糊或昏暗的帧中 16 位和 8 位误差是否会进一步扩大。
- 所谓“判定相同”,是以阈值 0.5 的区域查找标准为准。阈值附近的球(置信度在 0.5 左右)在微小的误差下也可能会翻转判定结果。
- 论文中的数据是各自的数据及硬件产生的结果,其倍数无法直接套用到我们的模型中。
9. 与我们项目的联系
- 生产环境操作人员目前使用的是 FP32。从第 6 节的结果来看,FP16 在不改变判定的同时,将单个窗口耗时减少了约 35%。在正式开启之前,我们计划在多场比赛、劣化条件以及生产 GPU 上再进行一次相同的对比。
- 迁移至手机端的模型(第 7 篇)将把 8 位作为默认选项。此时,校准数据必须挑选实际的手机屏幕画面(JPEG,不同的光照)。正如第 4 节所示,校准数据的性质决定了其适用范围。
下一篇文章将介绍在保持精确率的同时更改 执行方式的编译器与运行时。
TT 的一言。 数字减半了,但球的位置保持不变。反而在压缩照片时晃动得更厉害。在实际测量误差产生的位置之前,根本无从知晓!
参考资料
- Micikevicius, P. et al. (2018). Mixed Precision Training. ICLR 2018. https://arxiv.org/abs/1710.03740
- Kalamkar, D. et al. (2019). A Study of BFLOAT16 for Deep Learning Training. arXiv. https://arxiv.org/abs/1905.12322
- Jacob, B. et al. (2018). Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference. CVPR 2018. https://arxiv.org/abs/1712.05877
- Krishnamoorthi, R. (2018). Quantizing deep convolutional networks for efficient inference: A whitepaper. arXiv. https://arxiv.org/abs/1806.08342
- Nagel, M. et al. (2021). A White Paper on Neural Network Quantization. arXiv. https://arxiv.org/abs/2106.08295
- Wu, H. et al. (2020). Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation. arXiv. https://arxiv.org/abs/2004.09602
- Dettmers, T. et al. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv. https://arxiv.org/abs/2208.07339
- 公开数据:OpenTTGames (CC BY-NC-SA 4.0)
- 示例代码与输出:此文件夹中的
code/02_quant_error.py,code/02_quant_error.out.txt