当现代神经网络正在开发中,最大的挑战是让他们上班!这意味着培训中的准确性和速度是最重要的。使用浮点运算是保持精度的最简单的方法,并且GPU具有完善的加速计算能力,所以很自然,对其他数值格式没有太多的关注。

这些天,我们实际上已经有很多模型被部署在商业应用中。培训的计算需求随着研究人员数量的增长而增加,但是推理所需的周期与用户的比例扩大。这意味着纯粹的推理效率已经成为许多团队的一个亟待解决的问题。

这就是量化的原理。它是一个总括术语,涵盖了许多不同的技术来存储数字,并以比32位浮点更紧凑的格式对它们执行计算。我将重点关注八位固定点,因为稍后会详细介绍。

为什么量化工作?

训练神经网络通过对权重应用许多微小的推动来完成,而这些小增量通常需要浮点精度才能工作(尽管有研究工作也在此使用量化表示)。

采用预先训练的模型和运行推理是非常不同的。深层网络的神奇素质之一是,他们倾向于很好地应付输入中高水平的噪音。如果您考虑识别刚刚拍摄的照片中的对象,则网络必须忽略所有CCD噪声,照明变化以及其之前与之前的培训示例之间的非本质区别,并将重点放在重要的位置相反。这种能力意味着他们似乎将低精度计算作为另一个噪声源,即使使用较少信息的数字格式仍能产生准确的结果。

 

为什么量化?

神经网络模型可以占用磁盘上的大量空间,例如,原始AlexNet的浮动格式超过200 MB。几乎所有这些尺寸都被用于神经连接的权重,因为在单个模型中通常有数百万个。因为它们的浮点数都是稍微不同的,像ZIP这样的简单压缩格式不能很好地压缩它们。它们虽然被排列成很大的层次,但是在每个层内,权重通常在一定范围内正常分布,例如-3.0至6.0。

量化的最简单的动机是通过存储每个层的最小和最大值来收缩文件大小,然后将每个浮点值压缩为表示该范围内256个线性集合中最接近实数的8位整数。例如,在-3.0到6.0范围内,0个字节将代表-3.0,255个代表6.0,128个将代表约1.5。稍后我会进行精确的计算,因为有一些细微之处,但这意味着你可以获得一个缩小75%的磁盘上的文件的好处,然后在加载之后转换为float,以便现有的浮点代码可以工作没有任何变化。

量化的另一个原因是通过使用8位输入和输出完全运行它们来减少进行推理计算所需的计算资源。这是非常困难的,因为它需要改变你到处做计算,但提供了很多潜在的奖励。获取8位值只需要浮点数的25%的内存带宽,因此您可以更好地使用高速缓存,并避免对RAM访问造成瓶颈。您也可以通常使用每个时钟周期执行更多操作的SIMD操作。在某些情况下,您将拥有可以加速八位计算的DSP芯片,这样可以提供很多优点。

将计算转移到8位将有助于您更快地运行模型,并减少功耗(这在移动设备上尤其重要)。它也为许多无法高效运行浮点代码的嵌入式系统打开了大门,因此可以在IoT世界中实现许多应用。

 

为什么不直接在低精度训练?

在较低位深处进行了一些实验训练,但结果似乎表明您需要高于8位来处理反向传播和梯度。这使得实施训练更加复杂,所以开始推理是有道理的。我们也已经有很多我们使用和知道的浮标模型,所以直接转换是非常方便的。

 

你如何量化你的模型?

TensorFlow具有内置8位计算的生产级支持。它还具有使用量化计算进行推理的将浮点训练的许多模型转换为等效图形的过程。例如,以下是如何将最新的GoogLeNet模型转换为使用八位计算的版本:

curl http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz -o /tmp/inceptionv3.tgz
tar xzf /tmp/inceptionv3.tgz -C /tmp/
bazel build tensorflow/tools/quantization/tools:quantize_graph
bazel-bin/tensorflow/tools/quantization/tools/quantize_graph \
  --input=/tmp/classify_image_graph_def.pb \
  --output_node_names="softmax" --output=/tmp/quantized_graph.pb \
  --mode=eightbit
这将产生一个新的模型,它运行与原始操作相同的操作,但内部有八位计算,并且所有权重都被量化。如果你看文件大小,你会看到它大约是原来的四分之一(23MB到91MB)。您仍然可以使用完全相同的输入和输出运行此模型,您应该获得相同的结果。以下是一个例子:
# Note: You need to add the dependencies of the quantization operation to the
#       cc_binary in the BUILD file of the label_image program:
#
#     //tensorflow/contrib/quantization:cc_ops
#     //tensorflow/contrib/quantization/kernels:quantized_ops

bazel build tensorflow/examples/label_image:label_image
bazel-bin/tensorflow/examples/label_image/label_image \
--image=<input-image> \
--graph=/tmp/quantized_graph.pb \
--labels=/tmp/imagenet_synset_to_human_label_map.txt \
--input_width=299 \
--input_height=299 \
--input_mean=128 \
--input_std=128 \
--input_layer="Mul:0" \
--output_layer="softmax:0"
您会看到它运行新量化的图形,并输出与原始答案非常相似的答案。

您可以在自己的模型上运行相同的过程,保存为GraphDefs,其输入和输出名称适合您网络所需的名称。我建议您先通过freeze_graph脚本运行它们,将检查点转换为存储在文件中的常量。

 

量化过程如何工作?

我们通过编写在推理中常用的等效的8位版本的操作来实现量化。这些包括卷积,矩阵乘法,激活函数,池操作和级联。转换脚本首先用量化的等价物替换它所知道的所有个别操作。这些是在float和8位之间移动数据之前和之后的转换函数的小子图。下面是他们的样子。首先是原始的Relu操作,带有浮动输入和输出:

然后,这是等效的转换子图,仍然有浮动输入和输出,但内部转换,所以计算是在8位完成。

最小和最大运算实际上会查看输入浮点张量中的值,然后将它们提供给Dequantize操作,将该张量转换为8位。有关量化表示如何工作的更多细节。

一旦单个操作被转换,下一个阶段就是去除和从浮动中移除不必要的转换。如果有连续的操作序列都有浮动等价物,那么将有很多相邻的Dequantize / Quantize操作。这个阶段发现这种模式,认识到它们彼此抵消,并将它们删除,如下所示:

大规模应用于所有操作已经量化了等价物的模型,这给出了所有张量计算以8位完成的图形,而无需转换为浮动。

 

什么代表用于量化传感器?

我们将数字的浮点数转换为8位表示作为压缩问题。我们知道,训练有素的神经网络模型中的权重和激活张量倾向于具有分布在较小范围内的值(例如,对于权重,您可能具有-15至+15,而对于图像模型的激活则为-500至1000)确切的数字会有所不同)。我们也从实验中知道,神经网络在面对噪声时往往是非常强大的,因此通过量化到一小段值产生的类噪声误差不会非常危害整体结果的精度。我们还想选择一个易于执行计算的表示,特别是形成运行模型所需的大部分工作的大矩阵乘法。

这些导致我们选择一个具有两个浮点数的表示,以存储以最低和最高量化值表示的总体最小值和最大值。量化数组中的每个条目表示该范围内的浮点值,在最小值和最大值之间呈线性分布。例如,如果我们有最小值= -10.0,最大值= 30.0f,还有一个八位数组,这里是量化值所代表的:

Quantized | Float
--------- | -----
0         | -10.0
255       | 30.0
128       | 10.0
这种格式的优点是它可以表示任意范围的幅度,它们不必是对称的,它可以表示有符号和无符号的值,而线性扩展使得可以直接进行乘法。有诸如宋汉的代码本 可以通过在表示中非线性分配浮点值来使用较低位深度的替代方案,但这些代码书往往要花费更多的代价。

对量化格式有一个强有力的清晰定义的优点在于,对于没有量化就绪的操作来说,总是可以从浮点数来回转换,或者为了进行调试而检查张量。我们希望在将来可以改进的TensorFlow中的一个实现细节是,最小和最大浮点值需要作为单独的张量传递给持有量化值的一个,所以图形可以有点密集!

关于最小和最大范围的好处是它们通常可以预先计算。重量参数是加载时已知的常数,因此它们的范围也可以存储为常数。我们经常知道输入的范围(例如,图像通常是范围0.0到255.0的RGB值),许多激活函数也有已知的范围。这可以避免必须分析操作的输出来确定范围,我们需要对数学运算进行操作,例如卷积或矩阵乘法,从而产生8位输入的32位累积结果。

 

下一步是什么?

我们发现,通过使用8位算术而不是浮点,我们可以在移动和嵌入式设备上获得非常好的性能。您可以看到我们用来优化gemmlowp的矩阵乘法的 框架。我们仍然需要将所学到的所有经验教训应用于TensorFlow操作系统,以便在移动设备上获得最大的性能,但是我们正在积极地开展工作。现在,这个量化的实现是一个相当快速和准确的参考实现,我们希望能够在更广泛的设备上更广泛地支持我们的八位模型。我们也希望这个示范会鼓励社区用低精度的神经网络探讨可能的。