为什么TF-lite量化后精度不对
Why the accuracy of TF-lite is not correct after quantization
我正在尝试使用 TF1.12 进行 TF-lite 转换器。
并且发现量化后TF-lite的精度不对。
以MNIST为例,如果用下面的命令转成f32,当运行 convolution_test_lite.py时还是可以正确判断的 与 conv_net_f32.tflite.
*tflite_convert --output_file model_lite/conv_net_f32.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE*
但是当我使用下面的脚本来转换和输入0-255的数据时。当 运行 convolution_test_lite.py 和 [=41= 时,准确性似乎不正确].
*UINT8:
tflite_convert --output_file model_lite/conv_net_uint8.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE \
--mean_values 128 \
--std_dev_values 128 \
--default_ranges_min 0 \
--default_ranges_max 6 \
--inference_type QUANTIZED_UINT8 \
--inference_input_type QUANTIZED_UINT8*
进一步的代码上传到这里:
https://github.com/mvhsin/TF-lite/blob/master/mnist/convolution_test_lite.py
有谁知道原因吗?非常感谢您的帮助!
我相信这其中隐藏着多个问题。让我一一解决。
1。输入值应该被量化。
您的测试代码 (convolution_test_lite.py
) 没有正确量化输入值。
在 QUANTIZED_UINT8
量化的情况下:
real_input_value = (quantized_input_value - mean_value) / std_dev_value
这意味着,要将输入值 [0,1] 转换为量化的 int 值,您需要:
quantized_input_value = real_input_value * std_dev_value + mean_value
并将其应用于 所有 个输入值。
因此,在您的 convolution_test_lite.py
中,尝试更改:
input_data = input_data.astype(np.uint8)
到
# Use the same values provided to the converter
mean_value = 0
std_dev_value = 255
input_data = input_data * std_dev_value + mean_value
input_data = input_data.astype(np.uint8)
同样适用于输出。您应该通过以下方式对输出进行反量化:
real_output_value = (quantized_output_value - mean_value) / std_dev_value
也就是说,由于您刚刚获得 argmax,去量化步骤并不那么重要。如果你想看到实际的 softmaxed 值加起来为 1,你应该对输出进行反量化。
2。缺少真正的最小-最大范围值
即使你正确地进行了输入量化,模型的准确性也会显着下降。这是因为该模型未使用量化感知训练技术(您在评论中链接)进行训练。量化感知训练可让您实际捕获正确的全整数量化所需的中间值的实际最小-最大范围。
由于模型没有使用这种技术进行训练,我们能做的最好的就是提供默认的最小-最大范围,即 --default_ranges_min
、--default_ranges_max
值。这称为虚拟量化,预计模型的准确率会显着下降。
如果您使用量化感知训练,则不需要提供默认值,完全量化的模型会产生准确的结果。
3。量化范围
这将是一个相对较小的问题,但由于 MNIST 输入值范围是 [0, 1],因此最好使用:
mean_value 0
std_dev_value 255
这样int值0
映射到0.0
,255
映射到1.0
.
备选方案:尝试 post-训练量化
post-training quantization 仅量化权重值,从而显着减小模型大小。因为在这种情况下 input/outputs 没有量化,所以您基本上可以使用 post-training-quantized tflite 模型代替 float32 模型。
你可以试试:
tflite_convert --output_file model_lite/conv_net_post_quant.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE \
--post_training_quantize 1
通过提供 --post_training_quantize 1
选项,您可以看到它生成的模型比常规的 float32 版本小得多。
您可以 运行 这个模型,就像 运行 convolution_test_lite.py
中的 float32 模型一样。
希望对您有所帮助。
我正在尝试使用 TF1.12 进行 TF-lite 转换器。 并且发现量化后TF-lite的精度不对。 以MNIST为例,如果用下面的命令转成f32,当运行 convolution_test_lite.py时还是可以正确判断的 与 conv_net_f32.tflite.
*tflite_convert --output_file model_lite/conv_net_f32.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE*
但是当我使用下面的脚本来转换和输入0-255的数据时。当 运行 convolution_test_lite.py 和 [=41= 时,准确性似乎不正确].
*UINT8:
tflite_convert --output_file model_lite/conv_net_uint8.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE \
--mean_values 128 \
--std_dev_values 128 \
--default_ranges_min 0 \
--default_ranges_max 6 \
--inference_type QUANTIZED_UINT8 \
--inference_input_type QUANTIZED_UINT8*
进一步的代码上传到这里: https://github.com/mvhsin/TF-lite/blob/master/mnist/convolution_test_lite.py
有谁知道原因吗?非常感谢您的帮助!
我相信这其中隐藏着多个问题。让我一一解决。
1。输入值应该被量化。
您的测试代码 (convolution_test_lite.py
) 没有正确量化输入值。
在 QUANTIZED_UINT8
量化的情况下:
real_input_value = (quantized_input_value - mean_value) / std_dev_value
这意味着,要将输入值 [0,1] 转换为量化的 int 值,您需要:
quantized_input_value = real_input_value * std_dev_value + mean_value
并将其应用于 所有 个输入值。
因此,在您的 convolution_test_lite.py
中,尝试更改:
input_data = input_data.astype(np.uint8)
到
# Use the same values provided to the converter
mean_value = 0
std_dev_value = 255
input_data = input_data * std_dev_value + mean_value
input_data = input_data.astype(np.uint8)
同样适用于输出。您应该通过以下方式对输出进行反量化:
real_output_value = (quantized_output_value - mean_value) / std_dev_value
也就是说,由于您刚刚获得 argmax,去量化步骤并不那么重要。如果你想看到实际的 softmaxed 值加起来为 1,你应该对输出进行反量化。
2。缺少真正的最小-最大范围值
即使你正确地进行了输入量化,模型的准确性也会显着下降。这是因为该模型未使用量化感知训练技术(您在评论中链接)进行训练。量化感知训练可让您实际捕获正确的全整数量化所需的中间值的实际最小-最大范围。
由于模型没有使用这种技术进行训练,我们能做的最好的就是提供默认的最小-最大范围,即 --default_ranges_min
、--default_ranges_max
值。这称为虚拟量化,预计模型的准确率会显着下降。
如果您使用量化感知训练,则不需要提供默认值,完全量化的模型会产生准确的结果。
3。量化范围
这将是一个相对较小的问题,但由于 MNIST 输入值范围是 [0, 1],因此最好使用:
mean_value 0
std_dev_value 255
这样int值0
映射到0.0
,255
映射到1.0
.
备选方案:尝试 post-训练量化
post-training quantization 仅量化权重值,从而显着减小模型大小。因为在这种情况下 input/outputs 没有量化,所以您基本上可以使用 post-training-quantized tflite 模型代替 float32 模型。
你可以试试:
tflite_convert --output_file model_lite/conv_net_post_quant.tflite \
--graph_def_file frozen_graphs/conv_net.pb \
--input_arrays "input" \
--input_shapes "1,784" \
--output_arrays output \
--output_format TFLITE \
--post_training_quantize 1
通过提供 --post_training_quantize 1
选项,您可以看到它生成的模型比常规的 float32 版本小得多。
您可以 运行 这个模型,就像 运行 convolution_test_lite.py
中的 float32 模型一样。
希望对您有所帮助。