神经网络量化白皮书(PTQ)#
虽然神经网络在许多应用中取得了重大进展,但它们通常需要高昂的计算成本。如果我们想要将现代网络集成到具有严格功率和计算要求的边缘设备中,降低神经网络推理的功耗和延迟(power and latency)是关键。神经网络量化是最有效实现这些节省的方法之一,但它引入的额外噪声可能导致准确性下降。
在本白皮书中,我们介绍了最先进的算法,用于减轻量化噪声对网络性能的影响,同时保持低位权重和激活值。我们从硬件驱动的量化介绍开始,然后考虑了两类主要的算法:后训练量化(Post-Training Quantization,简称 PTQ)和量化感知训练(Quantization-Aware-Training,简称 QAT)。PTQ 不需要重新训练或标记数据,因此是一种轻量级的一键式(lightweight push-button)量化方法。在大多数情况下,PTQ 足以实现接近浮点精度的 8 位量化。QAT 需要微调和访问标记的训练数据,但可以实现更低位的量化并产生有竞争力的结果。对于这两种解决方案,我们提供了基于现有文献和大量实验的经过测试的流程,可为常见的深度学习模型和任务提供最先进的性能。
1 简介#
在神经网络量化中,权重和激活张量以比通常训练中使用的 16 或 32 位精度更低的位精度存储。当从 32 位转换为 8 位时,张量存储的内存开销减少了 4 倍,而矩阵乘法的计算成本则减少了 16 倍。神经网络已被证明对量化具有鲁棒性,这意味着它们可以被量化为更低的位宽,而对网络的准确性影响相对较小。此外,神经网络量化通常可以与其他常见的神经网络优化方法一起应用,例如神经网络架构搜索、压缩和剪枝。它是深度学习任何实际用例中模型效率流程的关键步骤。然而,神经网络量化并不是没有代价的。低比特宽度量化会引入噪声到网络中,可能导致准确性下降。虽然一些网络对这种噪声具有鲁棒性,但其他网络需要额外的工作才能利用量化的好处。
在本白皮书中,介绍了神经网络量化的最新技术。从量化的介绍开始,讨论了硬件和实际考虑因素。然后,考虑了两种不同的神经网络量化方案:后训练量化(PTQ)和量化感知训练(QAT)。第 3 节讨论的 PTQ 方法采用经过训练的网络并在很少或没有数据的情况下进行量化,需要最少的超参数调整和端到端的训练。这使得它们成为量化神经网络的简单方法,具有低工程成本和计算成本。相比之下,第 4 节讨论的 QAT 依赖于在训练过程中使用模拟量化来重新训练神经网络。虽然这需要更多的训练和潜在的超参数调整,但它通常比 PTQ 进一步缩小了与全精度准确性的差距,适用于低位量化。对于这两种方案,我们基于现有文献和大量实验引入了标准流程,可为常见的计算机视觉和自然语言处理模型实现最先进的性能。我们还提出了一种调试工作流程,用于识别和解决量化新模型时常见的问题。
2 量化基础#
在本节中,我们介绍了神经网络量化和量化网络运行的定点加速器(fixed-point accelerators)的基本原理。我们从硬件动机开始,然后介绍标准量化方案及其属性。随后,我们讨论了与现代神经网络中常见层相关的实际考虑因素以及它们对定点加速器的影响。
2.1 硬件背景#
在深入技术细节之前,我们首先探讨量化的硬件背景以及它如何在设备上实现高效的推理。图 1 提供了神经网络(NN)加速器中矩阵向量乘法 \(\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b}\) 的计算原理示意图。这是神经网络中更大型的矩阵矩阵乘法和卷积的基础构建块。这种硬件模块旨在通过尽可能多地并行执行计算来提高神经网络推理的效率。这个 NN 加速器的两个基本组件是处理元素(processing elements)\(C_{n,m}\) 和累加器(accumulators)\(A_n\)。我们在 图 1 中的示例具有 16 个排列成正方形网格的处理元素和 4 个累加器。计算从将累加器加载偏置值 \(\mathbf{b}_n\) 开始。然后我们将权重值 \(\mathbf{W}_{n,m}\) 和输入值 \(\mathbf{x_m}\) 加载到数组中,并在相应的处理元素 \(C_{n,m}\) 中计算它们的乘积:\(C_{n,m} = \mathbf{W}_{n,m} \mathbf{x}_m\),在一个周期内完成。然后将它们的结果添加到累加器中:
以上运算也称为乘积累加(Multiply-Accumulate,简称 MAC)。对于更大的矩阵向量乘法,此步骤会重复多次。一旦所有周期完成,累加器中的值将移回内存以供下一层神经网络使用。神经网络通常使用FP32权重和激活进行训练。如果我们要在 FP32 中执行推理,处理元素和累加器必须支持浮点逻辑,并且我们需要将 32 位数据从内存传输到处理单元。MAC 运算和数据传输消耗了神经网络推理期间消耗的大部分能量。因此,通过使用较低位的固定点或量化表示来表示这些数量,可以获得显著的好处。低比特定点表示,如 INT8,不仅可以减少数据传输量,还可以减小 MAC 运算的大小和能耗。这是因为数字算术的成本通常与使用的位数呈线性或二次方比例关系,并且因为定点加法比浮点加法更有效。
要从浮点数转换为高效的定点运算,需要一种将浮点向量转换为整数的方案。浮点向量 \(\mathsf{x}\) 可以近似表示为标量乘以整数值向量:
其中 \(s_{\mathsf{x}}\) 是浮点缩放因子,\(\mathsf{x}_{\operatorname{int}}\) 是整数向量,例如 INT8。我们用 \(\widehat{\mathsf{x}}\) 表示这个向量的量化版本。通过量化权重和激活,我们可以写出累加方程的量化版本:
注意,我们为权重和激活使用了单独的缩放因子 \(s_{\mathbf{W}}\) 和 \(s_{\mathbf{x}}\)。这提供了灵活性并减少了量化误差(详见第2.2节)。由于每个缩放因子都应用于整个张量,因此该方案允许我们将缩放因子从 (15) 中的求和中提取出来,并以定点格式执行 MAC 运算。我们故意忽略偏置量化,因为偏置通常以更高的位宽(32 位)存储,并且其缩放因子取决于权重和激活的缩放因子。
图 2 显示了当我们引入量化时神经网络加速器如何改变。在我们的例子中,我们使用 INT8 算术,但为了讨论起见,这可以是任何量化格式。保持累加器更高的位宽(通常是 32 位)是很重要的。否则,随着计算期间更多的乘积被累加,我们可能会因溢出而产生损失。
存储在 32 位累加器中的激活值需要在下一层使用之前写入内存。为了减少数据传输和下一层操作的复杂性,这些激活值被量化回 INT8。这需要一个反量化步骤,如 图 2 所示。
2.2 均匀仿射量化#
在本节中,我们定义了本文将使用的量化方案。该方案称为均匀量化(uniform quantization),它是最常用的量化方案,因为它允许有效地实现定点算法。
均匀仿射量化(uniform affine quantization),也称为非对称量化(asymmetric quantization),由三个量化参数定义:缩放因子(scale factor) \(s\)、零点(zero-point) \(z\) 和位宽(bit-width)\(b\)。缩放因子和零点用于将浮点值映射到整数网格上,其大小取决于位宽。缩放因子通常表示为浮点数,并指定量化器的步长大小。零点是整数,确保实数零量化时没有误差。这对于确保常见的运算如零填充或 ReLU 不引入量化误差非常重要。
一旦定义了三个量化参数,我们就可以进行量化运算。从实值向量 \(\mathbf{x}\) 开始,我们首先将其映射到无符号整数网格 \(\{{0, \cdots, 2^b -1}\}\):
其中 \(\lfloor \cdot \rceil\) 是取整运算,而 clamping 被定义为:
为了近似实值输入 \(\mathbf{x}\),我们执行反量化(de-quantization)步骤:
结合上述两个步骤,我们可以为量化函数 \(q(·)\) 提供通用定义:
通过去量化步骤,我们还定义了量化网格限制 \((q_{\min}, q_{\max})\),其中 \(q_{\min} = −sz\) 和 \(q_{\max} = s(2^b − 1 − z)\)。任何超出此范围的 \(\mathbf{x}\) 值都将被裁剪到其限制范围内,导致裁剪误差。如果我们想减少裁剪误差,可以通过增加缩放因子来扩展量化范围。然而,增加缩放因子会导致舍入误差增加,因为舍入误差位于范围 \([- \cfrac{1}{2} s, \cfrac{1}{2} s]\) 内。在 3.1 节中,我们更详细地探讨了如何选择量化参数以实现裁剪误差和舍入误差之间的正确权衡。
2.2.1 对称均匀量化#
对称量化是一般非对称情况的简化版本。对称量化器将零点限制为 \(0\),从而减少了在方程 (15) 中的累加运算期间处理零点偏移的计算开销。但是缺少偏移限制了整数和浮点数域之间的映射。因此,选择有符号或无符号整数网格很重要:
无符号对称量化非常适合单尾分布(one-tailed distributions),例如 ReLU 激活函数(见图 3)。另一方面,可以选择有符号对称量化来处理大致关于零对称的分布。
2.2.2 二的幂次量化器#
二的幂次量化是对称量化的一种特殊情况,其中缩放因子被限制为 \(2\) 的幂次方,即 \(s = 2^{−k}\)。这种选择可以带来硬件效率,因为使用 \(s\) 进行缩放相当于简单的位移操作。然而,缩放因子的限制可能会使舍入误差和裁剪误差之间的权衡变得更加复杂。
2.2.3 量化粒度#
到目前为止,我们已经为每个张量定义了一组量化参数(量化器),一个用于权重,一个用于激活函数,如方程 (15) 所示。这被称为逐张量量化。我们还可以为张量的单个段(例如权重张量的输出通道)定义单独的量化器,从而增加量化粒度。在神经网络量化中,由于其更简单的硬件实现,逐张量量化是最常见的粒度选择:方程 (15) 中的所有累加器都使用相同的缩放因子 \(s_{\mathbf{W}} s_{\mathbf{x}}\)。但是,我们可以使用更细粒度的量化来进一步提高性能。例如,对于权重张量,我们可以为每个输出通道指定不同的量化器。这被称为逐通道量化,其影响将在第 2.4.2 节中更详细地讨论。
其他工作超出了逐通道量化参数的范围,并为每组权重或激活函数应用单独的量化器。增加组的粒度通常会提高准确性,但需要付出一些额外的开销。这种开销与处理具有不同缩放因子的值的总和的累加器有关。目前大多数现有的定点加速器不支持这种逻辑,因此我们不会在本工作中考虑它们。然而,随着这个领域的研究增长,预计未来会有更多的硬件支持这些方法。
2.3 量化模拟#
为了测试神经网络在量化设备上运行得有多好,我们经常在用于训练神经网络的通用硬件上模拟量化行为。这被称之为量化模拟(quantization simulation)。我们的目标是使用浮点硬件来近似定点运算。与在实际量化硬件上运行实验或使用量化内核相比,这种模拟实现起来要容易得多。它们允许用户有效地测试各种量化选项,并实现了第4节中描述的针对量化感知训练的GPU加速。在本节中,我们首先解释了这个模拟过程的基本知识,然后讨论了帮助减少模拟和实际设备性能之间差异的技术。
之前,我们看到了如何在专用的定点硬件上计算矩阵向量乘法。在图 4a 中,我们将此过程推广到卷积层,但还包括激活函数以使其更真实。在进行设备上的推理时,硬件的所有输入(偏置、权重和输入激活)都是定点格式。然而,当我们使用常见的深度学习框架和通用硬件模拟量化时,这些量是浮点数。这就是为什么我们在计算图中引入量化器块以引入量化效果的原因。
图4b显示了如何在深度学习框架中对相同的卷积层进行建模。量化器块被添加到权重和卷积之间以模拟权重量化,并在激活函数之后以模拟激活量化。偏置通常不会被量化,因为它存储在更高的精度中。在第2.3.2节中,我们更详细地讨论了将量化器放置在非线性之后是否合适。量化器块实现了方程 (19) 的量化函数,每个量化器都由一组量化参数(缩放因子、零点、位宽)定义。量化器的输入和输出都是浮点格式,但输出位于量化网格上。
2.3.1 批量归一化折叠#
批量归一化是现代卷积网络的标准组成部分。批量归一化在缩放和添加偏移之前对线性层的输出进行归一化(见方程 (23))。对于设备上的推理,这些操作被折叠到前一个或后一个线性层中,这个过程称为批量归一化折叠(batch normalization folding)。这完全从网络中移除了批量归一化操作,因为计算被吸收到了相邻的线性层中。除了减少额外的缩放和偏移的计算开销之外,这还防止了额外的数据移动和该层的输出量化。更正式地,在推理期间,批量归一化被定义为输出 \(\mathbf{x}\) 的仿射映射:
其中,\(\mu\) 和 \(\sigma\) 是在训练期间通过对批量统计数据进行指数移动平均计算得到的平均数和方差,\(\gamma\) 和 \(\beta\) 是每通道学习的仿射超参数。如果批量归一化正好应用于线性层之后,即 \(\mathbf{y} = \operatorname{BatchNorm}(\mathbf{Wx})\),我们可以重新编写这些项,以将批量归一化操作与线性层本身融合在一起。假设权重矩阵 \(\mathbf{W \in \mathbb{R}^{n \times m}}\),我们对每个输出 \(\mathbf{y}_k\) 应用批量归一化,其中 \(k=\{1, \cdots, n\}\):
其中 \(\widehat{\mathbf{W}}_{k,:} = \cfrac{\gamma_{k} \mathbf{W}_{k,:} }{\sqrt{\sigma^2_k + \epsilon}}\) 和 \(\widehat{\mathbf{b}}_k = \beta_{k} - \cfrac{\gamma_{k} \mu_{k} }{\sqrt{\sigma^2_k + \epsilon}}\)。
2.3.2 激活函数融合#
在我们在第2.1节中介绍的朴素量化加速器中,我们看到了在计算矩阵乘法或卷积输出值之后进行激活重量化。然而,在实践中,我们经常直接在线性操作之后使用非线性函数。将线性层的激活写入内存并重新加载到计算核心以应用非线性函数是浪费的。出于这个原因,许多硬件解决方案都配备了一个硬件单元,该单元在重量化步骤之前应用非线性函数。如果是这种情况,我们只需要模拟在非线性函数之后的重量化。例如,ReLU 非线性可以通过重量化块轻松建模,因为您只需将该激活量化的最小可表示值设置为 \(0\)。
其他更复杂的激活函数,如 Sigmoid 或 Swish,需要更多专门的支持。如果这种支持不可用,我们需要在计算图中的非线性之前和之后添加量化步骤。这可能会对量化模型的准确性产生重大影响。尽管像 Swish 这样的新激活函数在浮点数中提供了准确性改进,但这些函数可能在量化后消失或在定点硬件上部署效率较低。
2.3.3 其他层的量化#
神经网络中使用了许多其他类型的层。如何对这些层进行建模在很大程度上取决于具体的硬件实现。有时,模拟量化与目标性能之间的不匹配是由于层未正确量化所致。在这里,我们提供了一些关于如何模拟常见层的量化的指导:
最大池化:激活量化不需要,因为输入和输出值在同一量化网格上。
平均池化:整数的平均值不一定是整数。因此,在平均池化之后需要量化步骤。但是,由于量化范围没有显著变化,我们使用相同的量化器对输入和输出进行量化。
逐元素相加:尽管运算简单,但难以准确模拟。在相加过程中,两个输入的量化范围必须完全匹配。如果这些范围不匹配,需要格外小心以确保相加按预期工作。虽然没有单一的接受解决方案,但添加重量化步骤可以粗略地模拟加法的噪声。另一种方法是通过将输入的量化网格绑定在一起来优化网络。这将防止重量化(requantization)步骤,但可能需要微调。
concat:被连接的两个分支通常不共享相同的量化参数。这意味着它们的量化网格可能不会重叠,因此需要重量化步骤。与逐元素相加一样,可以优化网络以使被连接的分支具有共享的量化参数。
2.4 实际考虑因素#
当量化具有多个层的神经网络时,我们面临着大量的量化选择,包括量化方案、粒度和位宽。在本节中,我们将探讨一些有助于缩小搜索空间的实际考虑因素。
请注意,在本白皮书中,我们仅考虑均匀位宽。这意味着为权重或激活选择的位宽在所有层之间保持不变。均匀位宽更广泛地受到硬件的支持,但最近的一些工作也探索了异构位宽或混合精度的实现。
2.4.1 对称与非对称量化#
对于每个权重和激活的量化,我们必须选择一个量化方案。一方面,非对称量化更具表现力,因为存在额外的偏移参数,但另一方面可能存在计算开销。为了了解为什么会这样,考虑当非对称权重 \(\widehat{\mathbf{W}} = s_{\mathbf{W}} (\mathbf{W}_{\operatorname{int}} - z_{\mathbf{W}})\) 与非对称激活 \(\widehat{\mathbf{x}} = s_{\mathbf{x}} (\mathbf{x}_{\operatorname{int}} - z_{\mathbf{x}})\) 相乘时会发生什么:
第一项是如果两个运算都采用对称格式时的情况。第三和第四项仅取决于已知的缩放、偏移和权重值。因此,这两个项可以预先计算并添加到层的偏置项中,几乎没有成本。然而,第二项取决于输入数据x。这意味着对于每批数据,我们需要在推理过程中计算一个额外的项。这可能导致显著的延迟和功耗开销,因为它相当于添加了一个额外的通道。
备注
一种常见的方法是使用 非对称激活量化 和 对称权重量化 来避免额外的数据相关项。
2.4.2 逐张量和逐通道的量化#
在2.2.3节中,我们讨论了不同级别的量化粒度。权重和激活的每个张量的量化已经是一种标准方法,因为它得到了所有定点加速器的支持。然而,权重的逐通道的量化可以提高准确性,特别是当权重的分布在通道之间存在显著差异时。回顾方程 (15) 中的量化 MAC 运算,我们可以看到,通过应用单独的每个通道权重缩放因子,可以在加速器中实现每个通道的权重量化,而无需重新缩放。激活的每个通道的量化很难实现,因为我们无法将缩放因子从求和中提取出来,因此需要在每个输入通道上重新缩放累加器。虽然每个通道的权重量化越来越成为一种常见的实践,但并非所有的商业硬件都支持它。因此,在目标设备上是否可行非常重要。
3 训练后量化#
训练后量化(PTQ)算法采用预先训练好的 FP32 网络,并将其直接转换为定点网络,而无需使用原始的训练管道。这些方法可以是 data-free 的,或者可能需要一个小的校准集,这通常是现成的。此外,由于几乎没有超参数调整,它们可以通过单个 API 调用作为黑盒方法使用,以计算高效的方式量化预训练的神经网络。这使得神经网络设计人员不必成为量化方面的专家,从而允许更广泛的应用神经网络量化。
在 PTQ 过程中,找到每个量化器的良好量化范围是基本步骤。我们在第 2.2 节简要讨论了量化范围的选择如何影响量化误差。在本节中,我们首先讨论实践中常用的各种方法来找到良好的量化参数。然后,我们探讨了在 PTQ 过程中观察到的常见问题,并介绍了最成功的方法来克服它们。使用这些技术,我们提出了一种标准的后训练量化流程,我们发现它在大多数常见场景中效果最佳,最后,我们引入了一系列调试步骤来提高量化模型的性能。
3.1 量化范围设置#
量化范围设置是指确定量化网格的剪切阈值 \(q_{\min}\) 和 \(q_{\max}\)(见方程 (19))的方法。范围设置中的关键权衡是在剪切误差和舍入误差之间,如第 2.2 节所述,以及它们对每个配置的量化器最终任务损失的影响。这里描述的每种方法提供了两种数量之间的不同权衡。这些方法通常优化局部成本函数而不是任务损失。这是因为在 PTQ 中,我们旨在实现计算快速的方法,而无需端到端训练。权重通常可以在没有任何校准数据的情况下进行量化。然而,确定激活量化的参数通常需要几个批次的校准数据。
Min-max
为了覆盖张量的整个动态范围,我们可以定义量化参数如下:
其中 \(\mathsf{V}\) 表示要量化的张量。这种方法不会导致剪切误差。然而,这种方法对异常值敏感,因为强异常值可能导致过度舍入误差(excessive rounding errors)。
MSE
均方误差(Mean squared error,简称 MSE)缓解异常值问题的一种方法是使用基于 MSE 的范围设置。在此范围设置方法中,我们找到最小化原始张量和量化张量之间的 MSE 的 \(q_{\min}\) 和 \(q_{\max}\):
其中 \(\widehat{\mathsf{V}}(q_{\min}, q_{\max})\) 表示量化版本的 \(\mathsf{V}\),\(||\cdot||_{F}^2\) 是 Frobenius 范数。优化问题通常使用网格搜索、黄金分割法或具有闭式解的分析近似方法来解决。这种方法的范围设置在文献中存在几种变体,但它们在目标函数和优化方面都非常相似。
交叉熵
对于某些层,量化张量中的所有值可能并不都同等重要。一种这样的情况是对分类网络最后一层中的 logits 进行量化,其中重要的是保留量化后最大值的顺序。MSE 可能不适合这种情况,因为它不考虑张量中值的顺序,将所有值等同对待。对于更多的类别,我们通常有很多小的或负的对预测准确性不重要的 logits,以及少数几个更大的重要的 logits。在这种情况下,MSE 会在尝试减少更常见的较小的 logits 的量化误差的同时,对少数几个较大的重要的 logits 产生较大的量化误差。在这种情况下,最小化以下交叉熵损失函数是有益的:
这里 \(H(\cdot, \cdot)\) 表示交叉熵,\(\psi\) 是 softmax 函数,\(\mathsf{v}\) 是 logits 张量。
基于 BN 的范围设置
激活量化器的范围设置通常需要一些校准数据。如果一层具有批量归一化的激活,则激活的每通道均值和标准差分别等于学习的批量归一化平移和缩放参数。然后可以使用这些参数来找到适合激活量化器的参数,如下所示:
其中 \(\beta\) 和 \(\gamma\) 是逐通道学习的平移和缩放参数的向量,\(\alpha > 0\),使用 \(\alpha=6\),以便仅剪切大异常值。
在表 1 中,我们对权重量化的范围设置方法进行了比较。对于高比特宽度,MSE 和 min-max 方法大致相当。然而,在较低的比特宽度下,MSE 方法明显优于 min-max 方法。在表 2 中,我们呈现了类似的激活量化比较。我们注意到,结合最后一层使用 MSE 和交叉熵的方法(表示为 MSE + Xent)优于其他方法,尤其是在较低的比特宽度下。该表还清楚地展示了使用交叉熵代替 MSE 目标对最后一层的好处。
3.2 跨层均衡化量化#
误差的一个常见问题是同一张量中的元素可能具有显著不同的大小。如前一节所述,量化网格的范围设置试图在剪切和舍入误差之间找到一个好的折衷方案。不幸的是,在某些情况下,它们之间的大小差异如此之大,即使对于适度的量化(例如 INT8),我们也无法找到一个合适的折衷方案。Nagel等人(2019)指出,这在深度可分离的层中尤为普遍,因为每个输出特征只负责少数几个权重,这可能导致权重的更高变异性。此外,他们还指出,批量归一化折叠会加剧这种影响,并可能导致连接到各种输出通道的权重之间的强烈不平衡(见 图 5)。虽然后者对于更细粒度的量化粒度(例如每通道量化)来说问题较小,但对于更广泛使用的每张量量化来说,这仍然是一个重大问题。
具有深度可分离卷积的有效模型,如 MobileNetV1 和 MobileNetV2,在 PTQ 时会出现明显的下降,甚至导致随机性能。
Nagel等人(2019)介绍了一种解决这种不平衡问题的方法,而无需使用每通道量化。Meller等人(2019)的相关工作中也提出了类似的方法。在这两份论文中,作者观察到对于许多常见的激活函数(例如 ReLU、PreLU),存在正比例缩放等变(positive scaling equivariance)特性:
对于任何非负实数 \(s\),该等变特性适用于任意一次齐次函数,并且可以通过缩放其参数化(例如 ReLU6)来扩展到任意分段线性函数。我们可以在神经网络中的连续层中利用这种正比例缩放等变特性。给定两个层,\(\mathbf{h} = f(\mathbf{W}^{(1)} \mathbf{x} + \mathbf{b}^{(1)})\) 和 \(\mathbf{y} = f(\mathbf{W}^{(2)}\mathbf{h} + \mathbf{b}^{(2)})\),通过缩放等变特性,我们有:
其中 \(\mathbf{S}=\operatorname{diag}(s)\) 是对角矩阵,其值 \(\mathbf{S}_{ii}\) 表示神经元 \(i\) 的缩放因子 \(s_i\)。这允许我们使用 \(\widehat{\mathbf{W}}^{(2)} = \mathbf{W}^{(2)} \mathbf{S}\),\(\widehat{\mathbf{W}}^{(1)} = \mathbf{S}^{-1}\mathbf{W}^{(1)}\) 和 \(\widehat{\mathbf{b}}^{(1)} = \mathbf{S}^{-1}\mathbf{b}^{(1)}\) 重新参数化我们的模型。对于 CNN,缩放将是逐通道的,并在空间维度上相应地广播。我们在 图 6 中说明了这种重标定(rescaling procedure)过程。为了使模型对量化更加鲁棒,我们可以找到缩放因子 \(s_i\),使得重新缩放后的层的量化噪声最小化。跨层均衡(cross-layer equalization,简称 CLE)过程通过在连续的层之间进行动态范围均衡来实现这一点。他们证明,通过设置 \(\mathbf{S}\) 使得:
可以实现最优权重均衡。
其中 \(\mathbf{r}_i^{(j)}\) 是权重张量 \(j\) 的通道 \(i\) 的动态范围。Meller等人(2019)的算法引入了一个类似的缩放因子,该因子还考虑了中间激活张量。然而,他们没有证明这种方法的最优性。
吸收高偏差(Absorbing high biases):Nagel等人(2019)进一步注意到,在某些情况下,特别是在 CLE 之后,高偏差可能会导致激活的动态范围差异。因此,他们提出了一种过程,如果可能的话,将高偏差吸收到下一层中。为了将 \(\mathbf{c}\) 从第一层(接着是 ReLU 激活函数 \(f\))吸收到第二层,我们可以进行以下重新参数化(reparameterization):
其中 \(\widehat{\mathbf{b}}^{(2)}=\mathbf{W}^{(2)} \mathbf{c} + \mathbf{b}^{(2)}\),\(\widehat{\mathbf{h}} = \mathbf{h-c}\) 和 \(\widehat{\mathbf{b}}^{(1)} = \mathbf{b}^{(1)} - \mathbf{c}\)。在第二步中,我们使用以下事实:对于带有 ReLU 函数 \(f\) 的层,存在非负向量 \(\mathbf{c}\) 使得 \(r(\mathbf{Wx + b -c})=r(\mathbf{Wx + b}) - \mathbf{c}\)。平凡解 \(\mathbf{c}=0\) 适用于所有 \(\mathbf{x}\)。然而,根据 \(\mathbf{x}\) 的分布以及 \(\mathbf{W}\) 和 \(\mathbf{b}\) 的值,可能存在一些 \(\mathbf{c}_i \gt 0\) 的值,对于经验分布中的(几乎)所有 \(\mathbf{x}\),该等式都成立。
其中 \(\min_{\mathbf{x}}\) 是在一个小的校准数据集中求值的。为了消除对数据的依赖,作者建议通过批量归一化层的平移和缩放参数来估计式 (34) 的右侧,从而得到 \(\mathbf{c}_i = \max(0, \beta_i - 3 \gamma_i)\)。
在表3中,我们展示了 CLE 和偏差吸收对将 MobileNetV2 量化为 8 位的效果。由于跳跃连接打破了层之间的等变特性,我们仅将跨层均衡应用于每个残差块内的层。类似于 Krishnamoorthi(2018),我们发现当将 MobileNetV2 量化为 INT8 时,模型性能接近随机。应用 CLE 使我们回到了 FP32 性能的 \(2\%\) 以内,接近每通道量化的性能。我们注意到,吸收高偏差会导致 FP32 性能略有下降,因为它是一种近似方法,但由于更精确的激活量化,它在量化性能上提高了 \(1\%\) 。综上所述,CLE 和偏差吸收后进行张量级量化比逐通道量化产生更好的结果。
3.3 偏差修正#
另一个常见问题是量化误差通常是有偏的。这意味着原始层或网络和量化层或网络的预期输出发生了偏移(\(\mathbb{E}[\mathbf{Wx}] \neq \mathbb{E}[\widehat{\mathbf{W}} \mathbf{x}]\))。这种误差在每个输出通道只有少量元素(通常为 \(3 \times 3\) 卷积核的 \(9\))的深度可分离层中更为明显。这种误差的主要贡献者通常是裁剪误差,因为少数被强烈裁剪的异常值可能会导致预期分布的偏移。
几篇论文指出了这个问题,并介绍了用于修正分布预期偏移的方法。对于一个量化层 \(\widehat{\mathbf{W}}\),其量化误差为 \(\varDelta \mathbf{W} = \widehat{\mathbf{W}} - \mathbf{W}\),则预期输出分布为
因此,有偏误差由 \(\mathbb{E}[\varDelta \mathbf{W} \mathbf{x}]\) 给出。由于 \(\varDelta \mathbf{W}\) 是常数,我们有 \(\mathbb{E}[\varDelta \mathbf{W} \mathbf{x}] = \varDelta \mathbf{W} \mathbb{E}[\mathbf{x}]\)。如果 \(\varDelta \mathbf{W} \mathbb{E}[\mathbf{x}]\) 不为零,输出分布将发生偏移。为了抵消这种偏移,我们可以从输出中减去它:
请注意,此校正项是一个与偏差具有相同形状的向量,因此可以在推理时没有任何额外开销地吸收到偏差中。计算偏差校正项有几种方法,其中最常见的是经验偏差校正(empirical bias correction)和分析偏差校正(analytic bias correction)。
经验偏差校正:如果我们有访问校准数据集的权限,则可以通过比较量化模型和全精度模型的激活值来计算偏差校正项。在实践中,这可以通过逐层计算来实施。
分析偏差校正:Nagel等人(2019)介绍了一种无需数据即可分析计算有偏误差的方法。对于具有批量归一化和 ReLU 函数的常见网络,他们使用前一层的 BN 统计数据来计算预期输入分布 \(\mathbb{E}[\mathbf{x}]\)。BN 参数 \(\gamma\) 和 \(\beta\) 对应于 BN 层输出的均值和标准差。假设输入值服从正态分布,可以使用裁剪的正态分布来模拟 ReLU 对分布的影响。他们表明
其中 \(\mathbf{x}^{\operatorname{pre}}\) 是预激活(pre-activation)输出,假设其服从具有逐通道均值 \(\beta\) 和逐通道标准差 \(\gamma\) 的正态分布,\(\varPhi(·)\) 是标准正态 CDF,符号 \(\mathcal{N}(x)\) 用于表示标准正态 PDF。请注意,所有向量运算都是逐元素(逐通道)运算。在计算输入分布 \(\mathbb{E}[\mathsf{x}]\) 之后,可以通过将其与权重量化误差 \(\varDelta \mathbf{W}\) 相乘来简单地推导出校正项。
在表4中,我们展示了将 MobileNetV2 量化为 8 位时偏差校正的效果。应用分析偏差校正将量化模型性能从随机提高到超过 \(50\%\),表明量化引入的有偏误差对模型性能造成了显著的伤害。当将偏差校正与 CLE 结合使用时,我们发现这两种技术是互补的。它们一起实现了接近 FP32 的性能,而无需使用任何数据。
3.4 AdaRound#
神经网络权重通常是通过将每个 FP32 值投影到最近的量化网格点来进行量化的,如 (16) 中均匀量化网格的 \(\lfloor \cdot \rceil\) 所示。我们将这种量化策略称为四舍五入(rounding-to-nearest)最近邻策略。四舍五入最近邻策略的动机是,对于固定的量化网格,它在浮点数和量化权重之间产生最低的 MSE。然而,Nagel等人(2020)表明,在后训练阶段量化权重时,四舍五入最近邻策略在任务损失方面并不是最优的。为了说明这一点,作者使用 100 个不同的随机舍入样本(Gupta等人,2015)将 ResNet18 的第一层权重量化为 4 位,并评估了网络对每个舍入选择的性能。这些舍入选择中的最佳选择比四舍五入最近邻策略高出超过 \(10\%\)。图 7 通过在 \(y\) 轴上绘制这些舍入选择的性能来说明这一点。在本节中,我们描述了 AdaRound(Nagel 等人,2020),一种用于 PTQ 的找到良好权重舍入选择的系统方法。 AdaRound 是一种理论上有充分依据且计算效率高的方法,在实践中显示出显著的性能改进。
由于主要目标是最小化量化对最终任务损失的影响,我们首先从这个损失的角度来制定优化问题
其中,\(\Delta w\) 表示由于量化引起的扰动,并且每个权重可能有两个可能的值,一个是通过将权重四舍五入而得到的,另一个是通过将权重舍入而得到的。我们希望有效地解决这个二进制优化问题。作为第一步,我们使用二阶泰勒级数展开来近似成本函数。这减轻了在优化期间对每个新舍入选择进行性能评估的需求。我们还假设模型已经收敛,这意味着可以忽略近似中的梯度项的贡献,并且 Hessian 是块对角的,忽略了跨层相关性。这导致了以下基于 Hessian 的二次无约束二进制优化(quadratic unconstrained binary optimization,简称 QUBO)问题:
图 7 中验证准确性和等式 (40) 目标之间的明显相关性表明,后者可以作为任务损失(等式 (39))的良好代理,即使对于 4 位权重量化也是如此。尽管有性能增益(见表 5),但等式 (40) 不能广泛用于权重舍入,主要有两个原因:
计算 Hessian 的内存和计算复杂度对于一般用例来说是不切实际的。
等式 (40) 的 QUBO 问题是 NP 难的。
为了解决第一个问题,作者引入了额外的适当假设,允许将等式 (40) 的目标简化为以下局部优化问题,该问题最小化一层的输出激活的 MSE。
等式 (41) 不需要计算 Hessian,也不需要来自后续层的任何其他向后或向前传播信息。请注意,用于将等式 (40) 的 QUBO 问题与等式 (41) 的局部优化问题联系起来的近似和分析与舍入问题无关。因此,这种分析也有益于其他问题的算法设计,包括模型压缩和 NAS(Moons等人,2020)。(41) 的优化仍然是 NP 难的优化问题。为了找到具有合理计算复杂度的良好近似解决方案,作者将优化问题放松为以下连续优化问题:
其中 \(||\cdot||_F^2\) 是 Frobenius 范数,\(\widehat{\mathsf{W}}\) 是 soft-quantized 权重:
我们使用 \(n\) 和 \(p\) 表示整数网格限制,\(n = q_{\min}/s\) 和 \(p = q_{\max}/s\)。\(\mathsf{V}_{i,j}\) 是我们优化的连续变量,\(h\) 可以是任何具有 \(0\) 到 \(1\) 之间值的单调函数,即 \(h(\mathsf{V}_{i,j}) \in [0, 1]\)。在Nagel等人(2020)中,作者使用修正的 sigmoid 作为 \(h\)。等式 (42) 的目标还引入了一个正则化项,鼓励连续优化变量 \(h(\mathsf{V}_{i,j})\) 收敛到 \(0\) 或 \(1\),以便它们是 (41) 中离散优化的有效解决方案。Nagel 等人(2020)使用的正则化项是
其中 \(\beta\) 在优化过程中进行退火(annealed),最初允许 \(h(\mathsf{V}_{i,j})\) 自由移动,后来迫使它们收敛到 \(0\) 或 \(1\)。为了避免神经网络层之间的误差累积并考虑非线性,作者提出了以下最终优化问题:
其中 \(\mathsf{\widehat{x}}\) 是所有先前层量化后的层的输入,\(f_a\) 是激活函数。使用随机梯度下降可以有效地优化等式 (45) 的目标。这种优化权重舍入的方法称为 AdaRound。
总结一下,我们在量化操作期间舍入权重的方式对网络性能有显著影响。AdaRound 提供了一种理论上可靠、计算速度快的权重舍入方法。它只需要少量未标记的数据样本,无需超参数调整或端到端微调,并且可以应用于任何神经网络的全连接层和卷积层。
3.5 标准 PTQ 流程#
在本节中,我们根据相关文献和大量实验提出了一种基于 PTQ 的最佳实践流程。我们在 图 8 中说明了推荐的流程。该流程在许多计算机视觉以及自然语言处理模型和任务上实现了有竞争力的 PTQ 结果。根据模型的不同,有些步骤可能不是必需的,或者选择其他步骤可能会带来相同或更好的性能。
跨层均衡
首先,应用跨层均衡(Cross-layer equalization,简称 CLE),这是一种预处理步骤,用于全精度模型,使其更易于量化。对于具有深度可分离层的模型和每个张量量化来说,CLE 尤为重要,但它通常也会在其他层和量化选择上显示出改进。
添加量化器
接下来,我们选择量化器并在网络中添加量化运算,如第2.3节所述。量化器的选择可能取决于特定的目标硬件;对于常见的 AI 加速器,我们建议对权重使用对称量化器,对激活使用非对称量化器。如果硬件/软件堆栈支持,则对权重使用逐通道量化是有利的。
权重范围设置
为了设置所有权重张量的量化参数,我们建议使用基于层均方误差(MSE)的标准。在特定情况下,对于逐通道量化,使用最小最大值方法可能会有所帮助。
AdaRound
如果我们有可用的小校准数据集,我们接下来应用 AdaRound 以优化权重的舍入。这一步对于在 PTQ 中实现低比特权重量化(例如 4比特)至关重要。
偏差校正
如果我们没有这样的校准数据集并且网络使用批量归一化,我们可以使用分析性偏差校正来代替。
激活范围设置
作为最后一步,我们确定网络中所有数据相关张量的量化范围(即激活值)。对于大多数层,我们使用基于 MSE 的标准,这需要一个小的校准集来找到最小的 MSE 损失。或者,我们可以使用基于 BN 的范围设置来实现完全无数据流程。
3.6 实验#
我们现在评估上述 PTQ 流程在常见的计算机视觉和自然语言理解应用上的性能。我们的结果总结在表 6 中。对于语义分割任务,我们在 Pascal VOC 上评估了具有 MobileNetV2 主干网络的 DeepLabV3(Chen等人,2017),对于对象检测,我们在 COCO 2017 上评估了 EfficientDet(Tan等人,2020)。其余的计算机视觉模型在 ImageNet 分类基准上进行评估。对于自然语言理解,我们在 GLUE 基准上评估了 BERT-base(Wang等人,2018)。
在所有情况下,我们观察到与浮点数相比,权重和激活的 8 位量化(W8A8)仅导致微小的精度损失(所有模型均在 \(0.7\%\) 以内)。对于 W8A8 量化,我们也未看到使用逐通道量化的重大收益。然而,当权重量化为 4 位时(W4A8),情况会发生变化。对于 ResNet18/50 和 InceptionV3,无论是逐张量还是逐通道量化,精度下降仍在浮点数的 \(1\%\) 以内。然而,对于更高效的网络,如 MobileNetV2 和 EfficientNet lite,逐张量量化的精度下降分别增加到 \(2.5\%\) 和 \(4.2\%\)。这可能是由于深度可分离卷积的量化造成的。在这里,逐通道量化可以显示出显著的好处,例如,在 EfficientNet lite 中,与逐张量量化相比,逐通道量化将精度提高了 \(2.8\%\),使其接近全精度精度的 \(1.4\%\)。我们在 EfficientDet-D1 和 DeeplabV3 中看到了类似的效果,它们都在其主干网络中使用了深度可分离卷积。
对于 BERT-base,我们观察到一些激活张量在其动态范围中存在极端差异。为了使 PTQ 仍然有效,我们使用第3.7节概述的调试程序识别了这些层,并将它们保留在16位。否则,BERT-base 遵循与其他大多数模型类似的趋势,并且我们的 PTQ 流程允许 4 位权重量化,GLUE 得分下降不到 \(1.5\%\)。
3.7 调试#
我们展示了标准的 PTQ 流程可以在广泛的模型和网络上实现有竞争力的结果。然而,如果在遵循我们的流程步骤之后,模型的性能仍然不满意,我们建议进行一系列诊断步骤来识别瓶颈并提高性能。虽然这不严格是一个算法,但这些调试步骤可以提供有关量化模型表现不佳的原因的见解,并帮助解决潜在的问题。这些步骤如图9中的流程图所示,并在下面更详细地描述:
FP32 sanity check(FP32 合理性检查):一个重要的初始调试步骤是确保浮点模型和量化模型在前向传播中表现相似,特别是在使用自定义量化流程时。将量化模型的位宽设置为32位(对于权重和激活),或者如果可能的话,绕过量化操作,并检查精度是否与FP32模型相匹配。
Weights or activations quantization(权重或激活量化):下一个调试步骤是独立地确定激活或权重量化对性能的影响。如果所有权重都量化为更高的位宽,而激活保持较低的位宽,或者相反,如果所有激活使用高位数宽而权重使用低位数宽,性能是否会恢复?这个步骤可以显示激活和权重量化对整体性能下降的相对贡献,并指导我们找到适当的解决方案。
修复权重量化:如果上一步显示权重量化确实导致显著的精度下降,那么可以尝试以下几种解决方案:
如果尚未实现,请应用 CLE,特别是对于具有深度可分离卷积的模型。
尝试逐通道量化。这将解决每个通道权重分布不均匀的问题。
如果可用校准数据,请应用偏差校正或 AdaRound。
修复激活量化:为了减少激活量化的量化误差,我们还可以尝试使用不同的范围设置方法或调整修复激活量化。为了减少激活量化的量化误差,我们还可以尝试使用不同的范围设置方法或调整 CLE 以考虑激活量化范围,因为普通的 CLE 可能导致激活分布不均匀。
逐层分析:如果全局解决方案未能将精度恢复到可接受的水平,我们考虑每个量化器单独处理。我们将每个量化器依次设置为目标位宽,同时将网络的其余部分保持为 32 位(参见 图 9 中的内部循环)。
可视化层:如果单个张量的量化导致显著的精度下降,我们建议在不同的粒度上可视化张量的分布,例如按通道(如图5所示),并按维度,例如BERT中的激活按令牌或嵌入。
修复个别量化器:可视化步骤可以揭示张量对量化敏感的来源。一些常见的解决方案包括为此量化器自定义范围设置或允许有问题的量化器使用更高的位宽,例如表 6 中的 BERT-base。如果问题得到解决并且精度恢复,我们继续进行下一个量化器。如果没有,我们可能不得不采用其他方法,例如量化感知训练(QAT),在 4 节中讨论。
完成上述步骤后,最后一步是将整个模型量化到所需的位宽。如果精度可接受,则我们已经准备好最终的量化模型以供使用。否则,我们可以考虑更高的位宽和更小的粒度,或者还原为更强大的量化方法,例如量化感知训练。