关于tensorflow lite量化代码、论文和文档不一致的问题
Question about inconsistency between tensorflow lite quantization code, paper and documentation
在google发表的this论文(Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference)中描述了量化方案如下:
其中,
M = S1 * S2 / S3
S1、S2和S3分别是输入和输出的尺度.
S1(和零点Z1)和S2(和零点 Z2) 可以很容易地确定,无论是“离线”还是“在线”。但是 S3(和零点 Z3)呢?这些参数取决于“实际”输出比例(即没有量化的 float
值)。但是输出比例在计算之前是未知的。
根据tensorflow documentation:
At inference, weights are converted from 8-bits of precision to floating point and computed using floating-point kernels. This conversion is done once and cached to reduce latency.
但是下面的code说的是不同的东西:
tensor_utils::BatchQuantizeFloats(
input_ptr, batch_size, input_size, quant_data, scaling_factors_ptr,
input_offset_ptr, params->asymmetric_quantize_inputs);
for (int b = 0; b < batch_size; ++b) {
// Incorporate scaling of the filter.
scaling_factors_ptr[b] *= filter->params.scale;
}
// Compute output += weight * quantized_input
int32_t* scratch = GetTensorData<int32_t>(accum_scratch);
tensor_utils::MatrixBatchVectorMultiplyAccumulate(
filter_data, num_units, input_size, quant_data, scaling_factors_ptr,
batch_size, GetTensorData<float>(output), /*per_channel_scale=*/nullptr,
input_offset_ptr, scratch, row_sums_ptr, &data->compute_row_sums,
CpuBackendContext::GetFromContext(context));
这里可以看到:
scaling_factors_ptr[b] *= filter->params.scale;
我认为这意味着:
- S1 * S2 计算出来。
- 权重仍然是整数。只是最后的结果是浮点数。
- 好像S3和Z3不用计算。但如果是这样的话,最后的float结果怎么可能接近未量化的结果呢?
论文、文档和代码之间的这种不一致让我很困惑。我说不出我错过了什么。谁能帮帮我?
让我回答我自己的问题。突然间我看到了我错过的东西
骑自行车。上面问题中的代码来自函数
tflite::ops::builtin::fully_connected::EvalHybrid()
。这里的
名字已经说明了一切!矩阵乘法输出的值为
在论文的 2.2 节中表示为 r3。就方程而言
(2) 在 2.2 节中,我们有:
如果我们想得到矩阵乘法的浮点数结果,我们可以使用2.2节中的等式(4),然后将结果转换回浮点数,或者我们可以使用等式(3)的左边替换为r3,如:
如果我们选择所有的零点都是0
,那么上面的公式就变成:
这正是 EvalHybrid()
所做的(暂时忽略偏差)。原来这篇论文给出了量化算法的概述,而实现使用了不同的变体。
在google发表的this论文(Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference)中描述了量化方案如下:
其中,
M = S1 * S2 / S3
S1、S2和S3分别是输入和输出的尺度.
S1(和零点Z1)和S2(和零点 Z2) 可以很容易地确定,无论是“离线”还是“在线”。但是 S3(和零点 Z3)呢?这些参数取决于“实际”输出比例(即没有量化的 float
值)。但是输出比例在计算之前是未知的。
根据tensorflow documentation:
At inference, weights are converted from 8-bits of precision to floating point and computed using floating-point kernels. This conversion is done once and cached to reduce latency.
但是下面的code说的是不同的东西:
tensor_utils::BatchQuantizeFloats(
input_ptr, batch_size, input_size, quant_data, scaling_factors_ptr,
input_offset_ptr, params->asymmetric_quantize_inputs);
for (int b = 0; b < batch_size; ++b) {
// Incorporate scaling of the filter.
scaling_factors_ptr[b] *= filter->params.scale;
}
// Compute output += weight * quantized_input
int32_t* scratch = GetTensorData<int32_t>(accum_scratch);
tensor_utils::MatrixBatchVectorMultiplyAccumulate(
filter_data, num_units, input_size, quant_data, scaling_factors_ptr,
batch_size, GetTensorData<float>(output), /*per_channel_scale=*/nullptr,
input_offset_ptr, scratch, row_sums_ptr, &data->compute_row_sums,
CpuBackendContext::GetFromContext(context));
这里可以看到:
scaling_factors_ptr[b] *= filter->params.scale;
我认为这意味着:
- S1 * S2 计算出来。
- 权重仍然是整数。只是最后的结果是浮点数。
- 好像S3和Z3不用计算。但如果是这样的话,最后的float结果怎么可能接近未量化的结果呢?
论文、文档和代码之间的这种不一致让我很困惑。我说不出我错过了什么。谁能帮帮我?
让我回答我自己的问题。突然间我看到了我错过的东西
骑自行车。上面问题中的代码来自函数
tflite::ops::builtin::fully_connected::EvalHybrid()
。这里的
名字已经说明了一切!矩阵乘法输出的值为
在论文的 2.2 节中表示为 r3。就方程而言
(2) 在 2.2 节中,我们有:
如果我们想得到矩阵乘法的浮点数结果,我们可以使用2.2节中的等式(4),然后将结果转换回浮点数,或者我们可以使用等式(3)的左边替换为r3,如:
如果我们选择所有的零点都是0
,那么上面的公式就变成:
这正是 EvalHybrid()
所做的(暂时忽略偏差)。原来这篇论文给出了量化算法的概述,而实现使用了不同的变体。