当前位置: 首页 > 面试题库 >

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

笪欣嘉
2023-03-14
问题内容

我试图在将张量流模型转换为tflite模型时使用UINT8量化:

如果使用use post_training_quantize = True,则模型大小比原始fp32模型小4倍,因此我假定模型权重为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)}]

所以我的问题是:

  1. post_training_quantize = True设置时会发生什么?即为什么第一种情况可以正常工作,而第二种情况却没有。
  2. 如何估计第二种情况的均值,标准差和范围参数?
  3. 看起来在第二种情况下模型推断更快,这取决于模型输入是uint8的事实吗?
  4. 什么手段'quantization': (0.0, 0)在第一种情况下和'quantization': (0.003921568859368563, 0)'quantization': (0.7843137383460999, 128)在第二种情况?
  5. 什么converter.default_ranges_stats

更新:

找到问题4的答案,在解释器.get_input_details()中“量化”是什么意思?


问题答案:

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

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

即,您可以拥有一个tflite模型,float32该模型具有推断类型,但是post_training_quantize=True为了减小磁盘大小和在运行时更快地加载模型,对模型权重进行了量化(使用)。

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

该文档使许多人困惑。让我解释一下研究后得出的结论:

  1. 不幸的是,在TF库和文档中,量化参数/统计具有3种 等效 形式/表示:
    • 一种) (mean, std_dev)
    • B) (zero_point, scale)
    • C) (min,max)
  2. 从B)和A)转换:
    • std_dev = 1.0 / scale
    • mean = zero_point
  3. 从C)转换为A):
    • mean = 255.0*min / (min - max)
    • std_dev = 255.0 / (max - min)
    • 说明:量化统计信息是用于将范围(0,255)映射到任意范围的参数,您可以从2个等式开始:min / std_dev + mean = 0max / std_dev + mean = 255,然后按照数学公式来获得上述转换公式
  4. 从A)转换为C):
    • min = - mean * std_dev
    • max = (255 - mean) * std_dev
  5. 命名“ 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,这不会使模型运行量化推理,因此无需将输入或输出从float转换为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_detailswill也会返回quantization: (scale, zero_point),不仅形式不同(scale vs 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


 类似资料:
  • 问题内容: 我无法解决问题 我看不到为什么被指定?是名称,但我不确定。 的片段 二手教程在这里。 问题答案: 该方法的文档如下: 从意图中检索扩展数据。 参量 所需项目的名称。 如果没有使用给定名称存储所需类型的值,则返回该值。 退货 以前使用putExtra()添加的项目的值;如果未找到,则为默认值。 因此,在您的示例中,如果该键存在于中,则将被分配与该键关联的整数值;如果不存在,则将被分配该整

  • 问题内容: 我试图切换一些硬编码的查询以使用参数化输入,但是遇到一个问题:如何格式化参数化批量插入的输入? 当前,代码如下所示: 一个可能的解决方案(从如何将数组插入到一个带有PHP和PDO的单个MySQL Prepared语句中 修改而来)似乎是: 有没有更好的方法来完成带有参数化查询的批量插入? 问题答案: 好吧,您有三个选择。 一次构建-执行多次。基本上,您只需为一行准备一次插入,然后循环执

  • 本文向大家介绍深入了解MyBatis参数,包括了深入了解MyBatis参数的使用技巧和注意事项,需要的朋友参考一下 深入了解MyBatis参数 相信很多人可能都遇到过下面这些异常: "Parameter 'xxx' not found. Available parameters are [...]" "Could not get property 'xxx' from xxxClass. Caus

  • 问题内容: 我刚刚开始学习Angular.js,并且一直在Angular主页上的“连接后端”示例中查看project.js。 我对控制器功能中的参数感到困惑: 这些控制器函数在routeProvider中调用,但未给出任何参数。 我能找到到目前为止,这可能解释发生了什么事情的唯一的事情是“注入服务整合到控制器”,这说明,而不是参数的方法和。 我的具体问题是: 控制器参数是什么? 带有参数的控制器功

  • 问题内容: 给定下面的python代码,请帮助我了解那里发生的事情。 所以我得到了和之间的区别,在第5行中,我通过强制转换将持续时间取整,现在又有什么进一步的解释? 我知道delta是什么意思(平均值或差值),但是为什么我必须传递给delta以及为什么字符串转换效果如此之好以至于我得到了? 问题答案: 因为timedelta的定义如下: 所有参数都是可选的,默认为0。 您可以轻松地通过可选参数说出