mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-20 19:12:24 +00:00
add implementation for dynamic quantize linear (#1697)
This commit is contained in:
parent
4b5b037289
commit
e54904e6a3
8 changed files with 160 additions and 17 deletions
|
|
@ -33,4 +33,13 @@ inline bool IsScalarOr1ElementVector(const Tensor* input) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Clamps input between provided min and max values
|
||||
**/
|
||||
inline float clamp(float v, float lo, float hi) {
|
||||
if (v < lo) return lo;
|
||||
if (v > hi) return hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
|
|
@ -303,6 +303,7 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 10, Re
|
|||
|
||||
// opset 11
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Clip);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, uint8_t, DynamicQuantizeLinear);
|
||||
|
||||
void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
|
||||
static const BuildKernelCreateInfoFn function_table[] = {
|
||||
|
|
@ -591,6 +592,7 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
|
|||
|
||||
//opset 11
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Clip)>,
|
||||
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, uint8_t, DynamicQuantizeLinear)>,
|
||||
};
|
||||
|
||||
for (auto& function_table_entry : function_table) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "dynamicquantizelinear.h"
|
||||
#include "core/providers/common.h"
|
||||
#include "core/util/math_cpuonly.h"
|
||||
#include <cmath>
|
||||
#include <cfenv>
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
ONNX_CPU_OPERATOR_TYPED_KERNEL(
|
||||
DynamicQuantizeLinear,
|
||||
11,
|
||||
uint8_t,
|
||||
KernelDefBuilder()
|
||||
.TypeConstraint("T2", DataTypeImpl::GetTensorType<uint8_t>()),
|
||||
DynamicQuantizeLinear<uint8_t>);
|
||||
|
||||
|
||||
static float RoundHalfToEven(float input) {
|
||||
std::fesetround(FE_TONEAREST);
|
||||
auto result = std::nearbyintf(input);
|
||||
return result;
|
||||
}
|
||||
|
||||
// formula is Y = X / Scale + ZeroPoint
|
||||
template <typename T>
|
||||
Status DynamicQuantizeLinear<T>::Compute(OpKernelContext* ctx) const {
|
||||
auto x_ptr = ctx->Input<Tensor>(0);
|
||||
ORT_ENFORCE(x_ptr != nullptr);
|
||||
auto& x = *x_ptr;
|
||||
const auto* x_data = x.template Data<float>();
|
||||
|
||||
auto& y = *ctx->Output(0, x.Shape());
|
||||
std::vector<int64_t> shape({});
|
||||
auto& y_scale = *ctx->Output(1, shape);
|
||||
auto& y_zeropoint = *ctx->Output(2, shape);
|
||||
|
||||
// find quantization range min and max
|
||||
float qmax = std::numeric_limits<T>::max();
|
||||
float qmin = std::numeric_limits<T>::min();
|
||||
// Adjust the int8 range to -127 to 127 so that zero point can be 0
|
||||
if (qmin == -128) {
|
||||
qmin = -127;
|
||||
}
|
||||
|
||||
// find input range min and max
|
||||
auto min = ConstEigenVectorMap<float>(x_data, x.Shape().Size()).minCoeff();
|
||||
min = std::min(min, qmin);
|
||||
auto max = ConstEigenVectorMap<float>(x_data, x.Shape().Size()).maxCoeff();
|
||||
max = std::max(max, qmin);
|
||||
|
||||
// find scale and zero point
|
||||
auto scale = (max - min) / (qmax - qmin);
|
||||
auto* output_scale = y_scale.template MutableData<float>();
|
||||
*output_scale = scale;
|
||||
|
||||
const auto initial_zero_point = qmin - min / scale;
|
||||
auto zero_point = static_cast<T>(RoundHalfToEven(std::max(qmin, std::min(qmax, initial_zero_point))));
|
||||
auto* output_zp = y_zeropoint.template MutableData<T>();
|
||||
*output_zp = zero_point;
|
||||
|
||||
// quantize the data
|
||||
auto* output = y.template MutableData<T>();
|
||||
const auto num_of_elements = x.Shape().Size();
|
||||
|
||||
for (int i = 0; i < num_of_elements; ++i) {
|
||||
output[i] = static_cast<T>(clamp(RoundHalfToEven(static_cast<float>(x_data[i] / scale)) + zero_point, qmin, qmax));
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/common/common.h"
|
||||
#include "core/framework/op_kernel.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
template <typename T>
|
||||
class DynamicQuantizeLinear final : public OpKernel {
|
||||
public:
|
||||
DynamicQuantizeLinear(const OpKernelInfo& info) : OpKernel(info) {
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
};
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -80,14 +80,6 @@ ONNX_CPU_OPERATOR_TYPED_KERNEL(
|
|||
.TypeConstraint("y", DataTypeImpl::GetTensorType<int8_t>()),
|
||||
QuantizeLinear<int8_t>);
|
||||
|
||||
// clamp doesn't exist in the version of <algorithm> that we're using, so
|
||||
// make a local one.
|
||||
static float clamp(float v, float lo, float hi) {
|
||||
if (v < lo) return lo;
|
||||
if (v > hi) return hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
static float RoundHalfToEven(float input) {
|
||||
std::fesetround(FE_TONEAREST);
|
||||
auto result = std::nearbyintf(input);
|
||||
|
|
|
|||
|
|
@ -380,12 +380,9 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
|
|||
{"maxpool_with_argmax_2d_precomputed_strides", "ShapeInferenceError"},
|
||||
{"tf_inception_v2", "result mismatch"},
|
||||
{"mxnet_arcface", "result mismatch"},
|
||||
{"dynamicquantizelinear", "not implemented yet"},
|
||||
{"dynamicquantizelinear_expanded", "not implemented yet"},
|
||||
{"dynamicquantizelinear_max_adjusted", "not implemented yet"},
|
||||
{"dynamicquantizelinear_max_adjusted_expanded", "not implemented yet"},
|
||||
{"dynamicquantizelinear_min_adjusted", "not implemented yet"},
|
||||
{"dynamicquantizelinear_min_adjusted_expanded", "not implemented yet"},
|
||||
{"dynamicquantizelinear_expanded", "Round(11) not implemented yet"},
|
||||
{"dynamicquantizelinear_max_adjusted_expanded", "Round(11) not implemented yet"},
|
||||
{"dynamicquantizelinear_min_adjusted_expanded", "Round(11) not implemented yet"},
|
||||
{"top_k", "not implemented yet for opset 11", {"onnxtip"}},
|
||||
{"top_k_smallest", "not implemented yet for opset 11", {"onnxtip"}},
|
||||
{"unique_not_sorted_without_axis", "not implemented yet"},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/providers/provider_test_utils.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace test {
|
||||
|
||||
// range = [-ve, +ve]
|
||||
TEST(QuantizeLinearOpTest, DynamicQuantizeLinear) {
|
||||
OpTester test("DynamicQuantizeLinear", 11);
|
||||
std::vector<int64_t> dims{6};
|
||||
test.AddInput<float>("x", dims, {0, 2, -3, -2.5f, 1.34f, 0.5f});
|
||||
test.AddOutput<uint8_t>("y", dims, {153, 255, 0, 26, 221, 179});
|
||||
test.AddOutput<float>("y_scale", {}, {0.0196078438f});
|
||||
test.AddOutput<uint8_t>("y_zero_point", {}, {153});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
// quantize with 2D data with min adjustment to include 0 in the input range.
|
||||
TEST(QuantizeLinearOpTest, DynamicQuantizeLinear_Min_Adjusted) {
|
||||
OpTester test("DynamicQuantizeLinear", 11);
|
||||
std::vector<int64_t> dims{3, 4};
|
||||
test.AddInput<float>("x", dims,
|
||||
{1, 2.1f, 1.3f, 2.5f,
|
||||
3.34f, 4.0f, 1.5f, 2.6f,
|
||||
3.9f, 4.0f, 3.0f, 2.345f});
|
||||
|
||||
test.AddOutput<uint8_t>("y", dims,
|
||||
{64, 134, 83, 159,
|
||||
213, 255, 96, 166,
|
||||
249, 255, 191, 149});
|
||||
test.AddOutput<float>("y_scale", {}, {0.01568628f});
|
||||
test.AddOutput<uint8_t>("y_zero_point", {}, {0});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
// quantize max adjustment to include 0 in the input range.
|
||||
TEST(QuantizeLinearOpTest, DynamicQuantizeLinear_Max_Adjusted) {
|
||||
OpTester test("DynamicQuantizeLinear", 11);
|
||||
std::vector<int64_t> dims{6};
|
||||
test.AddInput<float>("x", dims, {-1.0f, -2.1f, -1.3f, -2.5f, -3.34f, -4.0f});
|
||||
test.AddOutput<uint8_t>("y", dims, {191, 121, 172, 96, 42, 0});
|
||||
test.AddOutput<float>("y_scale", {}, {0.01568628f});
|
||||
test.AddOutput<uint8_t>("y_zero_point", {}, {255});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -110,11 +110,8 @@ def create_backend_test(testname=None):
|
|||
'^test_cumsum_1d_reverse_exclusive_cpu.*',
|
||||
'^test_cumsum_2d_axis_0_cpu.*',
|
||||
'^test_cumsum_2d_axis_1_cpu.*',
|
||||
'^test_dynamicquantizelinear*',
|
||||
'^test_dynamicquantizelinear_expanded*',
|
||||
'^test_dynamicquantizelinear_max_adjusted*',
|
||||
'^test_dynamicquantizelinear_max_adjusted_expanded*',
|
||||
'^test_dynamicquantizelinear_min_adjusted*',
|
||||
'^test_dynamicquantizelinear_min_adjusted_expanded*',
|
||||
'^test_depthtospace*',
|
||||
'^test_gather_elements*',
|
||||
|
|
|
|||
Loading…
Reference in a new issue