Fix output range for int8_t QuantizeLinear op (#3445)

This commit is contained in:
Tracy Sharpe 2020-04-07 15:01:20 -07:00 committed by GitHub
parent aabf47b107
commit de60a14c16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 94 deletions

View file

@ -420,24 +420,15 @@ MlasNchwcUpsample(
// Linear quantization routines.
//
template<typename OutputType>
void
MLASCALL
MlasQuantizeLinear(
const float* Input,
uint8_t* Output,
OutputType* Output,
size_t N,
float Scale,
uint8_t ZeroPoint
);
void
MLASCALL
MlasQuantizeLinear(
const float* Input,
int8_t* Output,
size_t N,
float Scale,
int8_t ZeroPoint
OutputType ZeroPoint
);
void

View file

@ -130,17 +130,44 @@ MlasQuantizeLinearPackBytes<int8_t>(
#endif
template<typename OutputType, int32_t MinimumValue, int32_t MaximumValue>
MLAS_FORCEINLINE
template<typename OutputType>
void
MlasQuantizeLinearKernel(
MLASCALL
MlasQuantizeLinear(
const float* Input,
OutputType* Output,
size_t N,
float Scale,
int32_t ZeroPoint
OutputType ZeroPoint
)
/*++
Routine Description:
This routine quantizes the input buffer using the supplied quantization
parameters.
Arguments:
Input - Supplies the input buffer.
Output - Supplies the output buffer.
N - Supplies the number of elements to process.
Scale - Supplies the quantization scale.
ZeroPoint - Supplies the quantization zero point value.
Return Value:
None.
--*/
{
constexpr int32_t MinimumValue = std::numeric_limits<OutputType>::min();
constexpr int32_t MaximumValue = std::numeric_limits<OutputType>::max();
auto ScaleVector = MlasBroadcastFloat32x4(Scale);
auto MinimumValueVector = MlasBroadcastFloat32x4(float(MinimumValue - ZeroPoint));
auto MaximumValueVector = MlasBroadcastFloat32x4(float(MaximumValue - ZeroPoint));
@ -189,17 +216,44 @@ MlasQuantizeLinearKernel(
// QuantizeLinear implementation using the C++ runtime.
//
template<typename OutputType, int32_t MinimumValue, int32_t MaximumValue>
MLAS_FORCEINLINE
template<typename OutputType>
void
MlasQuantizeLinearKernel(
MLASCALL
MlasQuantizeLinear(
const float* Input,
OutputType* Output,
size_t N,
float Scale,
int32_t ZeroPoint
OutputType ZeroPoint
)
/*++
Routine Description:
This routine quantizes the input buffer using the supplied quantization
parameters.
Arguments:
Input - Supplies the input buffer.
Output - Supplies the output buffer.
N - Supplies the number of elements to process.
Scale - Supplies the quantization scale.
ZeroPoint - Supplies the quantization zero point value.
Return Value:
None.
--*/
{
constexpr int32_t MinimumValue = std::numeric_limits<OutputType>::min();
constexpr int32_t MaximumValue = std::numeric_limits<OutputType>::max();
for (size_t n = 0; n < N; n++) {
float FloatValue = std::nearbyintf(Input[n] / Scale) + float(ZeroPoint);
@ -211,79 +265,27 @@ MlasQuantizeLinearKernel(
#endif
template
void
MLASCALL
MlasQuantizeLinear(
const float* Input,
uint8_t* Output,
size_t N,
float Scale,
uint8_t ZeroPoint
)
/*++
Routine Description:
This routine quantizes the input buffer using the supplied quantization
parameters.
Arguments:
Input - Supplies the input buffer.
Output - Supplies the output buffer.
N - Supplies the number of elements to process.
Scale - Supplies the quantization scale.
ZeroPoint - Supplies the quantization zero point value.
Return Value:
None.
--*/
{
return MlasQuantizeLinearKernel<uint8_t, 0, 255>(Input, Output, N, Scale, ZeroPoint);
}
void
MLASCALL
MlasQuantizeLinear(
MlasQuantizeLinear<int8_t>(
const float* Input,
int8_t* Output,
size_t N,
float Scale,
int8_t ZeroPoint
)
/*++
);
Routine Description:
This routine quantizes the input buffer using the supplied quantization
parameters.
Arguments:
Input - Supplies the input buffer.
Output - Supplies the output buffer.
N - Supplies the number of elements to process.
Scale - Supplies the quantization scale.
ZeroPoint - Supplies the quantization zero point value.
Return Value:
None.
--*/
{
return MlasQuantizeLinearKernel<int8_t, -127, 127>(Input, Output, N, Scale, ZeroPoint);
}
template
void
MLASCALL
MlasQuantizeLinear<uint8_t>(
const float* Input,
uint8_t* Output,
size_t N,
float Scale,
uint8_t ZeroPoint
);
#if defined(MLAS_SSE2_INTRINSICS)

View file

@ -16,7 +16,7 @@ __global__ void QuantizeLinearKernel(const float* input, int8_t* output, const f
for (int i = 0; i < NumElementsPerThread; i++) {
if (id < N) {
int value = __float2int_rn(input[id] / (*scale)) + *zero_point;
output[id] = static_cast<int8_t>(max(-127, min(127, value)));
output[id] = static_cast<int8_t>(max(-128, min(127, value)));
id += NumThreadsPerBlock;
}
}

View file

@ -75,10 +75,8 @@ TEST(QuantizeLinearOpTest, QuantizeLinear_int8_NegativeZeroPoint) {
test.AddInput<float>("x", dims, {0, 2, 3, 5, 6, -2, -5, -6});
test.AddInput<float>("y_scale", {}, {.039215686f});
test.AddInput<int8_t>("y_zero_point", {}, {-23});
test.AddOutput<int8_t>("y", dims, {-23, 28, 53, 104, 127, -74, -127, -127});
// TODO: nGraph returns a range of [-128,127] but the default CPU provider
// returns a range of [-127,127]. Resolve this difference in behavior later.
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider});
test.AddOutput<int8_t>("y", dims, {-23, 28, 53, 104, 127, -74, -128, -128});
test.Run();
}
// quantize with scalar zero point and scale
@ -88,10 +86,8 @@ TEST(QuantizeLinearOpTest, QuantizeLinear_int8_PositiveZeroPoint) {
test.AddInput<float>("x", dims, {0, 2, 3, 5, 6, -2, -5, -6});
test.AddInput<float>("y_scale", {}, {.039215686f});
test.AddInput<int8_t>("y_zero_point", {}, {23});
test.AddOutput<int8_t>("y", dims, {23, 74, 99, 127, 127, -28, -104, -127});
// TODO: nGraph returns a range of [-128,127] but the default CPU provider
// returns a range of [-127,127]. Resolve this difference in behavior later.
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider});
test.AddOutput<int8_t>("y", dims, {23, 74, 99, 127, 127, -28, -104, -128});
test.Run();
}
// quantize with 2D data