TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

缩减数值:FP16、BF16、INT8 量化,后量化与量化感知训练,校准数据

状态:草稿(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. 为什么减少位数会更快

神经网络推理的大部分时间花在两件事上:从内存中获取数值的时间,以及乘法和加法的时间。将单个数值从 32 位减少到 16 位,需要获取的字节数就会减半。此外,如今的 GPU 和手机芯片都有专门的硬件单元,可以同时执行大量的 16 位和 8 位乘法。根据 NVIDIA 2018 代 GPU 的规格,INT8 的计算吞吐量是 FP32 的 16 倍,FP16 是 8 倍(Wu 等人 2020,表 1 —— 这是规格比例,而非实际测量值)。

代价是准确度。如果数值记录得不够密集,就会产生舍入误差;如果范围太窄,大数值就会溢出。

숫자 형식과 INT8 눈금 비트 나눔(가로 길이 = 비트 수) FP32 지수 8 가수 23 FP16 지수 5 가수 10 최대 65,504 BF16 지수 8 가수 7 범위 = FP32 INT8 정수 8 −128 … 127 s = 부호. 지수는 범위를, 가수는 촘촘함을 정한다 INT8 양자화: 실수 r 을 0–255 눈금 q 에 맞춘다 q=0 q=255 잘림 최솟값 상한 가장 가까운 눈금으로 반올림 → 255 r = S · (q − Z), S = (상한 − 최솟값) ÷ 255 범위를 넓게 잡으면 잘림은 줄지만 눈금 간격 S 가 커진다 범위를 좁게 잡으면 눈금은 촘촘하지만 큰 값이 잘린다
图:FP32、FP16、BF16、INT8 的位数分配,以及 INT8 量化将实数对齐到 0–255 刻度并截断超出上限的值的形态。如果范围设得较宽,截断会减少,但刻度会变得稀疏

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

因此,校准方法必须针对每个模型进行测试。在 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

8. 局限性

9. 与我们项目的联系

下一篇文章将介绍在保持精确率的同时更改 执行方式的编译器与运行时。

TT 的一言。 数字减半了,但球的位置保持不变。反而在压缩照片时晃动得更厉害。在实际测量误差产生的位置之前,根本无从知晓!

参考资料

登录后即可点赞

评论

还没有评论。

登录后即可发表评论