【问题标题】:Tensorflow Lite inference - how do I scale down the convolution layer outputs?Tensorflow Lite 推理 - 如何缩小卷积层输出?
【发布时间】:2020-06-22 10:57:50
【问题描述】:

我用一个卷积层构建了一个简单的 CNN 模型,并使用 Tensorflow Lite 对其进行了转换。 (对于 MNIST !!) 所以现在我的模型得到 8 位整数输入,权重也是 8 位整数。

我想测试从 TFLite 得到的参数,所以我为推理步骤编写了 C 代码。

输入图像像素被赋予 0 到 255 之间的 8 位整数,权重在 -128 到 127 之间。 (偏差是 32 位整数。) 当然,卷积结果由大于 255 的数字组成。

我查看了这篇论文(https://arxiv.org/pdf/1712.05877.pdf,“用于高效整数运算的神经网络的量化和训练”),它提供了一些关于如何处理这个卷积结果的提示。 它说我必须 (1) 缩小规模, (2)向下(到 uint8),和 (3) 应用激活函数生成8位输出。

据我了解,我需要将 2^(-n) 乘以卷积结果。 所以我将卷积输出划分为 256 并将最大数量限制为 255,并使用全连接层的权重进一步计算它们。

它显示了一个很好的结果(准确度0.96+),但它没有TFLite评估所说的那么高。 (精度 0.98+)

我认为我没有以正确的方式做到这一点,因为“256”(我将卷积输出划分为)是一个随机数。实际上,当我将其更改为 340 时,它显示了最好的结果,但仍远低于使用 TFLite Interpreter 进行的 TFLite 评估。

实现推理步骤的正确且复杂的方法是什么?如何缩小规模?

【问题讨论】:

    标签: tensorflow-lite inference quantization inference-engine


    【解决方案1】:

    这是一个关于 TF Lite 中量化基础的好问题。您提到的论文是一个很好的参考和理解基础数学的指南。 TF Lite 现在使用与上述论文略有不同的量化方案,但对于在当前方案实施之前转换的模型仍完全支持该方案。供您参考,您可以在此处查看新量化方案的详细信息:

    https://www.tensorflow.org/lite/performance/quantization_spec

    您的问题的答案同样适用于 TF Lite 中的所有量化方案。对于您的问题的细节,您希望了解如何从 32 位累加器(将所有激活 * 过滤器的乘积相加的结果)下降到量化值(uint8 或 int8)。从论文中,您可以看到除了第 2.2 节中的公式 5 中定义的实值乘数 M 之外,所有整数运算都可以完成矩阵乘法(它与您感兴趣的卷积情况类似)。量化方案的目标是在仅整数算术中执行所有数学运算,因此挑战是“如何仅通过整数运算与实值 M 相乘?”。

    “技巧”是将M 表示为等式 6 中所做的那样,将 2 的乘积提高到某个负指数乘以 M_0,这是一个至少为 0.5 的实数,并且从上方以 1 为界. 乍一看,这似乎并没有使我们的问题变得更容易。但是,首先考虑2^(-n) 部分。这可以在计算机上表示为一个位移(我将在稍后讨论四舍五入)。假设处理了任何舍入问题,那么仅使用整数算术就可以轻松完成该部分。现在是M_0 部分。通过构造,我们将M_0 绑定到一个范围,在该范围内我们可以使用整数类型的定点表示(例如 int32)并将所有位用作小数位(如果您不熟悉定点表示,您可能需要参考外部信息来源)。

    我们将M_0 的 32 位定点表示称为“量化乘数”。您可以在下面的链接中看到该操作的详细信息,但本质上,将累加器乘以量化乘数涉及标准整数乘法,得到一个 64 位数字,然后取该结果的高 32 位。

    实际的代码有点难以理解,因为有各种问题需要通过适当的舍入(如本文中讨论的)、溢出、值饱和、钳位等来处理。不过,您可以通过查看来开始理解它在此处的参考实现中:

    https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/kernels/internal/common.h#L153-L162

    其中SaturatingRoundingDoublingHighMul 用量化乘法器实现定点乘法,RoundingDivideByPOT 用2^(-n) 实现乘法。

    在设备上运行的实际代码中,TF Lite 使用各种优化指令来实现这种算法,但参考代码得到相同的答案,更易于检查和理解。希望对您有所帮助!

    【讨论】:

    • 非常感谢您的详细解释。对此,我真的非常感激。我思考了几个小时你的答案,但我没有完全理解。使用 M_0 的所有位作为小数位意味着您乘以 2^32(在 int32 的情况下),不是吗?假设“激活 * 过滤器”的结果是 F(32 位),缩小后的值应该是 (F * (M_0> 32。但这仍然是 32 位。你能解释一下我如何从这个 32 位结果中得到 8 位整数吗?
    • 我不知道您所说的“使用 M_0 的所有位作为小数位意味着您乘以 2^32 [..] 不是吗?”我会花一些时间练习两个定点数相乘的练习。例如,快速搜索显示这是一个很好的例子:allaboutcircuits.com/technical-articles/…
    • 当您处理一些基本示例时,您可以提出以下问题:“如果一个操作数全是整数位,没有小数位,而另一个操作数全是小数位,没有整数位怎么办?”这基本上就是我们在这种情况下所拥有的。一旦你理解了这部分,那么 RoundingDivideByPOT 就非常简单了,因为它只是处理舍入,然后钳制在允许的 8 位范围内。
    • @T.J.Alumbaugh 我正在做同样的事情,在 FPGA 中实现,我不确定如何处理每轴量化。我的示例是一个执行量化卷积的 convnet,其 12 个通道的每个输出具有不同的量化。然后将结果展平并馈送到完全连接的位置。全连接是否考虑到输入每个通道具有不同的量化这一事实?
    猜你喜欢
    • 1970-01-01
    • 2019-05-03
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 2021-04-09
    • 1970-01-01
    • 2018-02-11
    • 2017-10-21
    相关资源
    最近更新 更多