非常教程

TensorFlow Guide参考手册

表现 | Performance

How to Quantize Neural Networks with TensorFlow(如何量化神经网络的张力?)

当现代神经网络得到开发时,最大的挑战是让他们工作!这意味着培训期间的准确性和速度是首要任务。使用浮点算法是保持准确性的最简单方法,并且GPU可以加速这些计算,所以很自然的是没有太多关注其他数字格式。

现在,我们实际上有很多模型被部署在商业应用程序中。培训的计算需求随着研究人员数量的增加而增加,但推断所需的周期与用户成比例地增加。这意味着纯粹的推理效率已经成为许多团队的热门话题。

这就是量化出现的地方。它是一个涵盖很多不同技术的术语,用于存储数字并以比32位浮点更紧凑的格式对它们进行计算。我将关注八位固定点,因为稍后我会详细讨论。

为什么量子化工作?

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

采用预先训练的模型并运行推理是非常不同的。深度网络的神奇特性之一是它们倾向于很好地处理输入中的高噪声。如果您考虑识别刚刚拍摄的照片中的物体,网络必须忽略所有CCD噪音,光线变化以及它与之前看到的训练样例之间的其他非本质区别,并将重点放在重要相反。这种能力意味着他们似乎将低精度计算看作是噪声的另一个来源,即使数字格式保存的信息较少,仍可产生准确的结果。

为什么量化?

例如,神经网络模型可占用大量磁盘空间,原始AlexNet的浮点格式超过200 MB。几乎所有的大小都被神经连接的权重所占据,因为在单个模型中经常有数百万个。因为它们都是略有不同的浮点数,所以像zip这样的简单压缩格式并不能很好地压缩它们。尽管它们排列得很大,并且在每一层内,权重倾向于正常分布在一定范围内,例如-3.0至6.0。

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

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

将计算结果移至8位可以帮助您更快地运行模型,并降低功耗(这对于移动设备尤为重要)。它也为许多无法高效运行浮点代码的嵌入式系统打开了大门,因此它可以在物联网世界中启用大量应用程序。

为什么不直接以较低的精度训练?

在比特深度较低的地方进行了一些实验性的训练,但结果似乎表明你需要高于8比特来处理反向传播和梯度。这使得实施培训变得更加复杂,因此从推理入手是有道理的。我们已经有很多已经使用和熟悉的浮点模型,所以能够直接转换它们非常方便。

你如何量化你的模型?

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

curl -L "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz" |
  tar -C tensorflow/examples/label_image/data -xz
bazel build tensorflow/tools/graph_transforms:transform_graph
bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
  --in_graph=tensorflow/examples/label_image/data/inception_v3_2016_08_28_frozen.pb \
  --out_graph=/tmp/quantized_graph.pb \
  --inputs=input \
  --outputs=InceptionV3/Predictions/Reshape_1 \
  --transforms='add_default_attributes strip_unused_nodes(type=float, shape="1,299,299,3")
    remove_nodes(op=Identity, op=CheckNumerics) fold_constants(ignore_errors=true)
    fold_batch_norms fold_old_batch_norms quantize_weights quantize_nodes
    strip_unused_nodes sort_by_execution_order'

这将产生一个新的模型,其运行与原始操作相同的操作,但内部具有8位计算,并且所有权重也被量化。如果你看一下文件大小,你会发现它大约是原来的四分之一(23MB与91MB)。尽管如此,您仍然可以使用完全相同的输入和输出来运行此模型,并且您应该可以得到相同的结果。这是一个例子:

bazel build tensorflow/examples/label_image:label_image
bazel-bin/tensorflow/examples/label_image/label_image \
--graph=/tmp/quantized_graph.pb \

你会看到它运行新量化的图,并输出与原始图非常相似的答案。

您可以在GraphDefs保存的模型上运行相同的流程,输入和输出名称将根据您的网络要求进行调整。我建议您先通过freeze_graph脚本运行它们,将检查点转换为存储在文件中的常量。

量化过程如何工作?

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

How to Quantize Neural Networks with TensorFlow(如何量化神经网络的张力?)

然后,这是等价的转换子图,仍然具有浮点输入和输出,但具有内部转换,因此计算以八位完成。

How to Quantize Neural Networks with TensorFlow(如何量化神经网络的张力?)

min和max操作实际上查看输入浮点张量中的值,然后将它们馈送到Dequantize操作中,该操作将张量转换为8位。关于量化表示后来如何工作的更多细节。

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

How to Quantize Neural Networks with TensorFlow(如何量化神经网络的张力?)

大规模地应用于所有操作都具有量化等效的模型,这给出了所有张量计算均以8位完成而无需转换为浮点的图。

量化张量使用什么表示?

我们将浮点数字数组转换为8位表示形式作为压缩问题。我们知道,经过训练的神经网络模型中的权重和激活张量倾向于具有分布在相对较小范围内的值(例如,对于权重可能有-15到+15,对于图像模型上的激活可能有-500到1000)确切的数字会有所不同)。我们从实验中也知道,神经网络在噪声的情况下往往是非常稳健的,因此量化到一小组值的噪声类错误不会严重影响整体结果的精确度。我们也希望选择一个易于执行计算的表示,特别是构成运行模型所需的大部分工作的大型矩阵乘法。

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

Quantized | Float
--------- | -----
0         | -10.0
255       | 30.0
128       | 10.0

这种格式的优点是它可以表示任意的范围幅度,它们不必是对称的,它可以表示有符号和无符号的值,并且线性扩展可以直接进行乘法运算。还有其他的选择,比如song han的代码,可以通过非线性地分布整个表示中的浮点值来使用较低的位深度,但这些计算的代价往往比较昂贵。

对量化格式有一个强而清晰的定义的好处是, 始终可以从浮点转换为不量化准备的操作, 或者检查张量以进行调试。在TensorFlow中,我们希望在未来改进的一个实现细节是,最小和最大浮点值需要作为单独的张量传递给持有量化值的那个,所以图形可以变得有点稠密!

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

下一步是什么?

我们发现,通过使用8位算法而不是浮点运算,我们可以在移动设备和嵌入式设备上获得非常好的性能。你可以看到我们用来优化gemmlowp矩阵乘法的框架。我们仍然需要将我们学到的所有经验应用到TensorFlow操作系统,才能在移动设备上获得最佳性能,但我们正在积极努力。目前,这种量化实现是一个相当快速和准确的参考实现,我们希望能够在更广泛的设备上为我们的8位模型提供更广泛的支持。我们也希望这个演示能够鼓励社区探索低精度神经网络的可能性。

TensorFlow Guide

TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理。Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算,TensorFlow为张量从流图的一端流动到另一端计算过程。TensorFlow是将复杂的数据结构传输至人工智能神经网中进行分析和处理过程的系统。

主页 https://www.tensorflow.org/
源码 https://github.com/tensorflow/tensorflow
版本 Guide
发布版本 1.4

TensorFlow Guide目录

1.指南 | Guide
2.教程 | Tutorials
3.配置 | Deploy
4.扩展 | Extend
5.开始 | Get Started
6.表现 | Performance