张量量化:未被讲述的故事

Tensor Quantization Untold Story

机器学习框架中量化实现细节的深入研究

与Naresh Singh共同撰写。

目录

  • 引言
  • 量化中的缩放和零点是什么意思?
  • 量化方案的类型
  • 量化缩放和零点示例
  • 量化和激活归一化
  • 结论
  • 参考文献

引言

在本文的其余部分,我们将尝试通过具体的例子回答以下问题。

  1. 量化中的缩放和零点是什么意思?
  2. 不同类型的量化方案有哪些?
  3. 如何计算不同量化方案的缩放和零点?
  4. 为什么零点对于量化很重要?
  5. 归一化技术如何有助于量化?

量化中的缩放和零点是什么意思?

缩放:在量化浮点范围时,通常会将浮点范围[Fmin..Fmax]表示为量化范围[Qmin..Qmax]。在这种情况下,缩放是浮点范围和量化范围的比值。

稍后我们将看到如何计算它的示例。

零点:量化中的零点是浮点0.0在量化范围中的表示。具体来说,零点是一个量化值,它代表了实际目的上的浮点值0.0。稍后我们将看到如何通过示例计算它,以及为什么这样的表示对我们来说是有实际意义的。

接下来,让我们看一下实践中使用的主要量化方案,并熟悉它们的相似之处和不同之处。

量化方案的类型

在考虑模型压缩期间可用于使用的量化类型时,可以选择2种主要类型

  1. 对称量化:在这种情况下,零点是零 – 即浮点范围的0.0与量化范围中的0相同。通常情况下,这在运行时计算更有效,但如果浮点范围在浮点0.0周围不均匀分布,则可能导致较低的精度。
  2. 仿射(或非对称)量化:这是具有非零值的零点的量化方案。

但在我们深入了解细节之前,让我们试着定义一下零点是什么。

量化缩放和零点示例

让我们从一个非常简单的例子开始,并逐步构建它。

示例1:对称uint8量化

假设我们希望将浮点范围[0.0 .. 1000.0]映射到量化范围[0 .. 255]。范围[0 .. 255]是可以适应无符号8位整数的值集合。

为了进行这种转换,我们希望重新缩放浮点范围,使以下条件成立:

浮点0.0 = 量化0

浮点1000.0 = 量化255

这称为对称量化,因为浮点0.0被量化为0。

因此,我们定义一个尺度,它等于

其中,

在这种情况下,尺度 = 3.9215

要将浮点值转换为量化值,我们可以简单地将浮点值除以尺度。例如,浮点值500.0对应于量化值

在这个简单的例子中,浮点范围的0.0与量化范围的0完全对应。这被称为对称量化。让我们看看当情况不是这样时会发生什么。

示例2:仿射uint8量化

假设我们希望将浮点范围[-20.0 .. 1000.0]映射到量化范围[0 .. 255]。

在这种情况下,我们有一个不同的缩放因子,因为我们的xmin是不同的。

让我们看看如果将缩放因子应用于0.0时,浮点数0.0在量化范围中表示的是什么

嗯,这似乎不太正确,因为根据上面的图表,我们希望浮点值-20.0映射到量化值0。

这就是零点的概念。 零点作为偏差,用于移动缩放的浮点值,并对应于表示浮点值0.0的量化范围中的值。 在我们的例子中,零点是-20.0的缩放浮点表示的负值,即-(-5) = 5。零点始终是最小浮点值的负值,因为最小值始终为负值或零。我们将在解释示例4的部分更详细地了解为什么这是这种情况。

每当我们量化一个值时,我们总是将零点添加到这个缩放值上,以得到有效量化范围内的实际量化值。如果我们希望量化值为-20.0,我们计算它为-20.0的缩放值加上零点,即-5 + 5 = 0。因此,quantized(-20.0, scale=4, zp=5) = 0。

示例3:仿射int8量化

如果我们的量化范围是有符号的8位整数而不是无符号的8位整数,会发生什么?嗯,范围现在是[-128 .. 127]。

在这种情况下,浮点范围中的-20.0映射到量化范围中的-128,浮点范围中的1000.0映射到量化范围中的127。

我们计算零点的方式是,假设量化范围是[0 .. 255],然后将其偏移-128,所以新范围中的零点是

因此,新范围的零点为-123。

到目前为止,我们已经看过了浮点范围包括值0.0的例子。在接下来的一组例子中,我们将看一下当浮点范围不包括值0.0时会发生什么。

0.0的重要性

为什么浮点值0.0在浮点范围中的表示很重要?

当使用填充卷积时,我们期望边界像素在最常见的情况下以值0.0进行填充。因此,0.0在浮点范围中的表示很重要。同样,如果值X将用于网络中的填充,您需要确保值X在浮点范围中表示,并且量化可以意识到这一点。

示例4:不为零的故事-偏斜的浮点范围

现在,让我们看看如果0.0不是浮点范围的一部分会发生什么。

在这个示例中,我们试图将浮点范围[40.0 .. 1000.0]量化为量化范围[0 .. 255]。

由于我们无法在浮点范围中表示值0.0,我们需要将范围的下限扩展到0.0。

我们可以看到量化范围的一部分被浪费。为了确定浪费了多少,让我们计算浮点值40.0映射到的量化值。

因此,我们浪费了量化范围中的[0 .. 9],约占范围的3.92%。这可能会严重影响模型在量化后的准确性。

如果我们希望确保浮点范围中的值0.0能够在量化范围中表示,这种偏斜是必要的。

将值0.0包括在浮点范围中的另一个原因是,高效比较量化值以检查其是否为浮点范围中的0.0非常有价值。考虑到诸如ReLU的操作符,它将所有小于浮点范围中的0.0的值剪切为0.0。

对于我们能够使用相同的数据类型(有符号或无符号int8)来表示零点,这非常重要,以便我们能够快速高效地执行这些比较。

接下来,让我们看看激活标准化如何帮助模型量化。我们将特别关注激活值的标准化如何有效地使用整个量化范围。

量化和激活标准化

批/层标准化将激活张量更改为每个通道或每个层具有零均值和单位方差。

假设我们有一个浮点范围为[2000.0 .. 4000.0]的输入张量。这是量化范围的样子。

我们观察到量化范围的一半[-127 .. -1]未使用。这是有问题的,因为我们仅使用了其中的7个可用8位来量化整个浮点范围。这无疑会导致更高的量化误差和降低的模型准确性。为了解决这个问题,让我们对激活张量应用层标准化。

在对激活张量应用层归一化之后,激活张量的浮点范围将为[-2.0 .. 2.0]。这可以表示为有符号int8范围[-128 .. 127]。为了确保分布的对称性,我们将量化范围限制为[-127 .. 127]。

因此,归一化可以避免量化范围中的空洞或未使用部分。

结论

我们了解了仿射(非对称)量化和对称量化的区别。我们还学习了尺度和零点的含义以及如何计算它们适用于这两种量化方案。

接下来,我们看到需要在浮点范围中包含0.0,并且为什么以及如何在实践中完成这一点。这会导致一个缺点,即量化范围中的空间浪费。

最后,我们看到归一化如何通过将激活值带入一个固定范围并避免量化范围中的空间浪费来帮助量化。实际上,基于0均值的归一化可以帮助将仿射量化转换为对称量化,并且可以在推断过程中加快速度。

本文中的所有图片均由作者创建。

参考资料

  1. 高效深度学习书籍,第2章:压缩技术介绍
  2. Hugging Face:量化
  3. TensorRT:量化
  4. 神经网络精简器:量化
  5. Lei Mao:量化
  6. 浮点数量化