了解 tf.contrib.lite.TFLiteConverter 个量化参数
Understanding tf.contrib.lite.TFLiteConverter quantization parameters
我在将 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()
Input/output 转换模型:
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()
Input/output 转换模型:
[{'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 的答案
1) 请参见 documantation. In short, this technique allows you to get a quantized uint8 graph with an accuracy of work that is close to the original one and does not require further training of the quantized model. However, the speed is noticeably less than if 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 神经网络推理比 float32 推理快约 2 倍(基于设备)
What is happenning when only post_training_quantize = True is set? i.e. why 1st case work fine, but second don't.
在 TF 1.14 中,这似乎只是量化存储在磁盘上 .tflite 文件中的权重。这本身并没有将推理模式设置为量化推理。
即,您可以拥有一个推理类型为 float32
的 tflite 模型,但模型权重被量化(使用 post_training_quantize=True
)以减小磁盘大小并加快模型加载速度在 运行 时间。
How to estimate mean, std and range parameters for second case?
许多人对文档感到困惑。让我解释一下我经过一番研究后得出的结论:
- 不幸的是,量化 parameters/stats 在 TF 库和文档中有 3 个 等价物 forms/representations:
- A)
(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)映射到任意范围的参数,您可以从2个等式开始:
min / std_dev + mean = 0
和max / std_dev + mean = 255
,然后按照数学计算得出以上换算公式
- 从 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
Looks like in second case model inference is faster, is it depend on the fact that model input is uint8?
是的,有可能。然而,量化模型通常较慢,除非您使用特定硬件的矢量化指令。 TFLite 针对 运行 ARM 处理器的专用指令进行了优化。从 TF 1.14 或 1.15 开始,如果您 运行 在本地机器 x86 Intel 或 AMD 上使用它,那么如果量化模型 运行 更快,我会感到惊讶。 [更新:在 TFLite 的路线图上添加第一个-class 支持 x86 向量化指令以使量化推理比 float 更快]
What means 'quantization': (0.0, 0) in 1st case and 'quantization': (0.003921568859368563, 0),'quantization': (0.7843137383460999, 128) in 2nd case?
这里的格式是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_details
将 return quantization: (scale, zero_point)
,而不仅仅是形式不同(比例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
我在将 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()
Input/output 转换模型:
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()
Input/output 转换模型:
[{'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 的答案
1) 请参见 documantation. In short, this technique allows you to get a quantized uint8 graph with an accuracy of work that is close to the original one and does not require further training of the quantized model. However, the speed is noticeably less than if 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 神经网络推理比 float32 推理快约 2 倍(基于设备)
What is happenning when only post_training_quantize = True is set? i.e. why 1st case work fine, but second don't.
在 TF 1.14 中,这似乎只是量化存储在磁盘上 .tflite 文件中的权重。这本身并没有将推理模式设置为量化推理。
即,您可以拥有一个推理类型为 float32
的 tflite 模型,但模型权重被量化(使用 post_training_quantize=True
)以减小磁盘大小并加快模型加载速度在 运行 时间。
How to estimate mean, std and range parameters for second case?
许多人对文档感到困惑。让我解释一下我经过一番研究后得出的结论:
- 不幸的是,量化 parameters/stats 在 TF 库和文档中有 3 个 等价物 forms/representations:
- A)
(mean, std_dev)
- B)
(zero_point, scale)
- C)
(min,max)
- A)
- 从 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)映射到任意范围的参数,您可以从2个等式开始:
min / std_dev + mean = 0
和max / std_dev + mean = 255
,然后按照数学计算得出以上换算公式
- 从 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
Looks like in second case model inference is faster, is it depend on the fact that model input is uint8?
是的,有可能。然而,量化模型通常较慢,除非您使用特定硬件的矢量化指令。 TFLite 针对 运行 ARM 处理器的专用指令进行了优化。从 TF 1.14 或 1.15 开始,如果您 运行 在本地机器 x86 Intel 或 AMD 上使用它,那么如果量化模型 运行 更快,我会感到惊讶。 [更新:在 TFLite 的路线图上添加第一个-class 支持 x86 向量化指令以使量化推理比 float 更快]
What means 'quantization': (0.0, 0) in 1st case and 'quantization': (0.003921568859368563, 0),'quantization': (0.7843137383460999, 128) in 2nd case?
这里的格式是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_details
将 return quantization: (scale, zero_point)
,而不仅仅是形式不同(比例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