了解 tf.contrib.lite.TFLiteConverter 量化参数

Posted

技术标签:

【中文标题】了解 tf.contrib.lite.TFLiteConverter 量化参数【英文标题】:Understanding tf.contrib.lite.TFLiteConverter quantization parameters 【发布时间】:2019-07-16 18:30:40 【问题描述】:

我在将 tensorflow 模型转换为 tflite 模型时尝试使用 UINT8 量化:

如果使用post_training_quantize = True,模型大小比原始 fp32 模型小 x4,所以我假设模型权重是 uint8,但是当我加载模型并通过 interpreter_aligner.get_input_details()[0]['dtype'] 获取输入类型时,它是 float32。量化模型的输出与原始模型大致相同。

converter = tf.contrib.lite.TFLiteConverter.from_frozen_graph(
        graph_def_file='tflite-models/tf_model.pb',
        input_arrays=input_node_names,
        output_arrays=output_node_names)
converter.post_training_quantize = True
tflite_model = converter.convert()

转换模型的输入/输出:

print(interpreter_aligner.get_input_details())
print(interpreter_aligner.get_output_details())
['name': 'input_1_1', 'index': 47, 'shape': array([  1, 128, 128,   3], dtype=int32), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0)]
['name': 'global_average_pooling2d_1_1/Mean', 'index': 45, 'shape': array([  1, 156], dtype=int32), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0)]

另一种选择是明确指定更多参数: 模型大小比原始 fp32 模型小 x4,模型输入类型为 uint8,但模型输出更像垃圾。

converter = tf.contrib.lite.TFLiteConverter.from_frozen_graph(
        graph_def_file='tflite-models/tf_model.pb',
        input_arrays=input_node_names,
        output_arrays=output_node_names)
converter.post_training_quantize = True
converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8
converter.quantized_input_stats = input_node_names[0]: (0.0, 255.0)  # (mean, stddev)
converter.default_ranges_stats = (-100, +100)
tflite_model = converter.convert()

转换模型的输入/输出:

['name': 'input_1_1', 'index': 47, 'shape': array([  1, 128, 128,   3], dtype=int32), 'dtype': <class 'numpy.uint8'>, 'quantization': (0.003921568859368563, 0)]
['name': 'global_average_pooling2d_1_1/Mean', 'index': 45, 'shape': array([  1, 156], dtype=int32), 'dtype': <class 'numpy.uint8'>, 'quantization': (0.7843137383460999, 128)]

所以我的问题是:

    仅设置post_training_quantize = True 时会发生什么?即为什么第一种情况可以正常工作,但第二种情况不行。 如何估计第二种情况的均值、标准差和范围参数? 看起来在第二种情况下模型推理更快,这取决于模型输入是 uint8 的事实吗? 第一种情况下的'quantization': (0.0, 0) 和第二种情况下的'quantization': (0.003921568859368563, 0),'quantization': (0.7843137383460999, 128) 是什么意思? converter.default_ranges_stats 是什么?

更新:

问题4的答案找到What does 'quantization' mean in interpreter.get_input_details()?

【问题讨论】:

@suharshs 看起来你与 tensorflow 的这一部分有关,你能详细说明一下吗? 4a。对于 float32dtype量化 被忽略 【参考方案1】:

仅设置 post_training_quantize = True 时会发生什么?即为什么第一种情况可以正常工作,但第二种情况不行。

在 TF 1.14 中,这似乎只是量化了存储在磁盘上的 .tflite 文件中的权重。这本身不会将推理模式设置为量化推理。

即,您可以拥有一个推理类型为 float32 的 tflite 模型,但模型权重被量化(使用 post_training_quantize=True)以减小磁盘大小,并在运行时更快地加载模型。

如何估计第二种情况的均值、标准差和范围参数?

文档让很多人感到困惑。让我解释一下我在研究后得出的结论:

    不幸的是,量化参数/统计在 TF 库和文档中具有 3 个等效形式/表示: 答)(mean, std_dev) B) (zero_point, scale) C)(min,max) 从 B) 和 A) 转换: std_dev = 1.0 / scale mean = zero_point 从 C) 到 A) 的转换: mean = 255.0*min / (min - max) std_dev = 255.0 / (max - min) 说明:量化统计是用于将范围(0,255)映射到任意范围的参数,可以从min / std_dev + mean = 0max / std_dev + mean = 255这2个方程开始,然后按照数学得出上述转换公式李> 从 A) 到 C) 的转换: min = - mean * std_dev max = (255 - mean) * std_dev 命名“mean”和“std_dev”令人困惑,在很大程度上被视为用词不当。

回答您的问题:,如果您的输入图像有:

范围 (0,255) 然后mean = 0, std_dev = 1 范围 (-1,1) 然后mean = 127.5, std_dev = 127.5 范围 (0,1) 然后mean = 0, std_dev = 255

看起来在第二种情况下模型推理更快,是否取决于模型输入是 uint8 的事实?

是的,可能。但是,除非您使用特定硬件的矢量化指令,否则量化模型通常较慢。 TFLite 已针对运行 ARM 处理器的专用指令进行了优化。从 TF 1.14 或 1.15 开始,如果您在本地机器 x86 Intel 或 AMD 上运行它,那么如果量化模型运行得更快,我会感到惊讶。 [更新:在 TFLite 的路线图中添加对 x86 矢量化指令的一流支持,以使量化推理比浮点更快]

第一种情况下的“量化”:(0.0, 0) 和第二种情况下的“量化”:(0.003921568859368563, 0),“量化”:(0.7843137383460999, 128) 是什么意思?

这里的格式是quantization: (scale, zero_point)

在第一种情况下,您只激活了post_training_quantize=True,这不会使模型运行量化推理,因此无需将输入或输出从浮点数转换为 uint8。因此这里的量化统计本质上是null,表示为(0,0)

在第二种情况下,您通过提供 inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8 激活了量化推理。所以你有输入和输出的量化参数,在进入模型的过程中将你的浮点输入转换为 uint8,在输出的过程中将 uint8 输出转换为浮点输出。

在输入时,进行转换:uint8_array = (float_array / std_dev) + mean 在输出时,进行转换:float_array = (uint8_array.astype(np.float32) - mean) * std_dev 注意 .astype(float32) 这是在 python 中获得正确计算所必需的 请注意,其他文本可能使用scale 而不是std_dev,因此除法将变为乘法,反之亦然。

这里的另一个令人困惑的事情是,即使在转换过程中您指定了quantization_stats = (mean, std_dev)get_output_details 也会返回quantization: (scale, zero_point),不仅形式不同(比例与 std_dev)而且顺序也不同!

现在要了解您为输入和输出获得的这些量化参数值,让我们使用上面的公式来推断输入和输出的实际值 ((min,max)) 的范围。使用上面的公式我们得到:

输入范围:min = 0, max=1(是你通过提供quantized_input_stats = input_node_names[0]: (0.0, 255.0) # (mean, stddev)指定的) 输出范围:min = -100.39, max=99.6

【讨论】:

你的答案中converter.default_ranges_stats是否对应minmax 同样遵循你的方程式if input image range [0,1] then mean = 0, std_dev = 255,然后min = - mean * std_devmax = (255 - mean) * std_dev -> min=0,max=255*255,那么min = -100.39, max=99.6 来自哪里? default_ranges_stats 指定(min,max) 对于你的输出你有get_output_details()返回'quantization': (0.7843137383460999, 128)所以min = -128 * 0.7434313 = -100.39max = (255-128)*0.7434313 = 99.6。如果您发现错误,请告诉我 是的,default_range_stats 不仅会影响输出的量化参数,还会影响图中的每个张量。请注意,您永远不应该自己定义default_range_stats。此选项仅可用于调试。正确的方法是对模型进行量化感知训练,以便每个张量根据训练数据都有自己的最小值/最大值:github.com/tensorflow/tensorflow/tree/r1.13/tensorflow/contrib/…【参考方案2】:

1) 见documantation。简而言之,这种技术可以让您获得一个量化的 uint8 图形,其工作精度接近原始图形,并且不需要进一步训练量化模型。但是,速度明显低于使用conventional 量化的情况。

2) 如果您的模型已经使用标准化的 [-1.0, 1.0] 输入进行训练,您应该设置converter.quantized_input_stats = input_node_names[0]: (128, 127),之后输入张量的量化将接近(0.003921568859368563, 0)mean 是从 0 到 255 的整数值,映射到浮点 0.0f。 std_dev 是 255 / (float_max - float_min)。这将解决一个可能的问题

3) Uint8 神经网络推理大约快 2 倍(基于设备),然后是 float32 推理

【讨论】:

对于2) ,您的意思是图像预处理吗?我使用 bgr 图像作为输入除以 255.0,所以我的输入在 [0,1] 范围内,所以据我了解,在我的情况下,这将是 mean=0.0 和 std_dev=255.0 converter.default_ranges_stats 呢? 是的,这取决于图像预处理。关于 default_ranges_stats。通常,要创建量化的 tflite 图,所有张量都必须具有可能值的最小/最大信息。此信息将用于创建量化参数:scale 和 zero_point。如果缺少此 minmax 值,则将使用 default_ranges_stats 中的 minmax,在这种情况下,这意味着量化图推理将像垃圾一样 有什么方法可以看出 TFLite 模型确实通过了post_training_quantize?在我的测试中,在两个模型上运行 //tensorflow/lite/tools:visualize 会得到相同的结果(不完全相同,缓冲区索引不同)。此外,两个模型(在 CPU 中)运行推理的时间成本在统计上没有变化。

以上是关于了解 tf.contrib.lite.TFLiteConverter 量化参数的主要内容,如果未能解决你的问题,请参考以下文章

了解了解grpc

数据库 了解知识

深入了解ASP.NET运行内幕

了解GStreamer

深入了解Java的String

Vue UI 了解一下