Unverified Commit 404f416d authored by kahmed10's avatar kahmed10 Committed by GitHub
Browse files

Add Quantize and Dequantize Linear op (#703)

* initial testing

* initial testing

* add dequantize

* formatting

* add tests

* formatting

* revert file

* add parse files

* formatting

* add axis tuning and fix tests

* formatting

* add tests and fix int8

* formatting

* fix tidy

* test with int32

* add default name and change string to upper

* formatting

* remove boost call
parent 85ed5718
#ifndef MIGRAPHX_GUARD_OPERATORS_TUNE_AXIS_HPP
#define MIGRAPHX_GUARD_OPERATORS_TUNE_AXIS_HPP
#include <utility>
#include <cstdint>
#include <migraphx/stringutils.hpp>
#include <migraphx/errors.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
inline int tune_axis(const int n_dim, const int axis, const std::string& op_name = "OPERATOR")
{
if(axis >= n_dim || abs(axis) > n_dim)
{
MIGRAPHX_THROW(to_upper(op_name) + ": axis is out of range.");
}
return (axis < 0) ? axis + n_dim : axis;
}
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#endif
File mode changed from 100755 to 100644
#include <migraphx/onnx/op_parser.hpp>
#include <migraphx/instruction.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/make_op.hpp>
#include <migraphx/tune_axis.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
namespace onnx {
struct parse_dequantizelinear : op_parser<parse_dequantizelinear>
{
std::vector<op_desc> operators() const { return {{"DequantizeLinear"}}; }
instruction_ref parse(const op_desc& opd,
const onnx_parser& /*parser*/,
const onnx_parser::node_info& info,
std::vector<instruction_ref> args) const
{
int axis = 1;
if(contains(info.attributes, "axis"))
axis = info.attributes.at("axis").i();
auto input_lens = args[0]->get_shape().lens();
int n_dim = static_cast<int>(input_lens.size());
auto sub_zero_point = args[0];
if(args.size() == 3)
{
auto zero_point = args[2];
if(not(zero_point->get_shape().elements() == 1))
{
axis = tune_axis(n_dim, axis, opd.op_name);
zero_point = info.add_instruction(
make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), zero_point);
}
auto zero_point_int32 = info.add_instruction(
make_op("convert", {{"target_type", shape::int32_type}}), zero_point);
auto sub_zero_point_int32 = info.add_instruction(
make_op("convert", {{"target_type", shape::int32_type}}), sub_zero_point);
sub_zero_point =
info.add_broadcastable_binary_op("sub", sub_zero_point_int32, zero_point_int32);
}
auto dequant_input = info.add_instruction(
make_op("convert", {{"target_type", shape::float_type}}), sub_zero_point);
auto scale = args[1];
if(not(scale->get_shape().elements() == 1))
{
axis = tune_axis(n_dim, axis, opd.op_name);
scale = info.add_instruction(
make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), scale);
}
return info.add_broadcastable_binary_op("mul", dequant_input, scale);
}
};
} // namespace onnx
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#include <migraphx/onnx/op_parser.hpp>
#include <migraphx/instruction.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/make_op.hpp>
#include <migraphx/tune_axis.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
namespace onnx {
struct parse_quantizelinear : op_parser<parse_quantizelinear>
{
std::vector<op_desc> operators() const { return {{"QuantizeLinear"}}; }
// y = saturate(round(x / y_scale) + zero_point)
instruction_ref parse(const op_desc& opd,
const onnx_parser& /*parser*/,
const onnx_parser::node_info& info,
std::vector<instruction_ref> args) const
{
auto quant_type = shape::uint8_type;
int nargs = args.size();
int max_quant = 255;
int min_quant = 0;
if(nargs == 3)
quant_type = args[2]->get_shape().type();
if(quant_type == shape::int8_type)
{
max_quant = 127;
min_quant = -128;
}
auto max_arg = info.add_literal(max_quant);
auto min_arg = info.add_literal(min_quant);
int axis = 1;
if(contains(info.attributes, "axis"))
axis = info.attributes.at("axis").i();
auto input_lens = args[0]->get_shape().lens();
int n_dim = static_cast<int>(input_lens.size());
auto scale = args[1];
if(not(scale->get_shape().elements() == 1))
{
axis = tune_axis(n_dim, axis, opd.op_name);
scale = info.add_instruction(
make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), scale);
}
auto div = info.add_broadcastable_binary_op("div", args[0], scale);
auto div_round = info.add_instruction(make_op("round"), div);
auto add_zero_point = div_round;
if(nargs == 3)
{
auto zero_point = args[2];
if(not(zero_point->get_shape().elements() == 1))
{
axis = tune_axis(n_dim, axis, opd.op_name);
zero_point = info.add_instruction(
make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), zero_point);
}
zero_point = info.add_instruction(
make_op("convert", {{"target_type", shape::int32_type}}), zero_point);
add_zero_point = info.add_instruction(
make_op("convert", {{"target_type", shape::int32_type}}), add_zero_point);
add_zero_point = info.add_broadcastable_binary_op("add", add_zero_point, zero_point);
}
auto saturated = info.add_instruction(make_op("clip"), add_zero_point, min_arg, max_arg);
return info.add_instruction(make_op("convert", {{"target_type", quant_type}}), saturated);
}
};
} // namespace onnx
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
dequantizelinear_axis_test:
-
0
1
2out"DequantizeLinear*
axisdequantizelinear_axis_testZ
0




Z
1

Z
2

b
out




B
\ No newline at end of file
dequantizelinear_neg_axis_test:
6
0
1
2out"DequantizeLinear*
axisdequantizelinear_neg_axis_testZ
0




Z
1

Z
2

b
out




B
\ No newline at end of file
dequantizelinear_test:
0
1
2out"DequantizeLineardequantizelinear_testZ
0

Z
1

Z
2

b
out

B
\ No newline at end of file
......@@ -1016,6 +1016,47 @@ def deconv_stride_test():
return ([node], [x, w], [y])
@onnx_test
def dequantizelinear_test():
arg0 = helper.make_tensor_value_info('0', TensorProto.INT8, [5])
arg1 = helper.make_tensor_value_info('1', TensorProto.FLOAT, [1])
arg2 = helper.make_tensor_value_info('2', TensorProto.INT8, [1])
arg_out = helper.make_tensor_value_info('out', TensorProto.FLOAT, [5])
node = onnx.helper.make_node(
'DequantizeLinear',
inputs=['0', '1', '2'],
outputs=['out'],
)
return ([node], [arg0, arg1, arg2], [arg_out])
def make_dequantizelinear_axis_graph(axis):
arg0 = helper.make_tensor_value_info('0', TensorProto.INT8, [1, 1, 5, 1])
arg1 = helper.make_tensor_value_info('1', TensorProto.FLOAT, [5])
arg2 = helper.make_tensor_value_info('2', TensorProto.INT8, [5])
arg_out = helper.make_tensor_value_info('out', TensorProto.FLOAT,
[1, 1, 5, 1])
node = onnx.helper.make_node('DequantizeLinear',
inputs=['0', '1', '2'],
outputs=['out'],
axis=axis)
return ([node], [arg0, arg1, arg2], [arg_out])
@onnx_test
def dequantizelinear_axis_test():
return make_dequantizelinear_axis_graph(2)
@onnx_test
def dequantizelinear_neg_axis_test():
return make_dequantizelinear_axis_graph(-2)
@onnx_test
def dropout_test():
x = helper.make_tensor_value_info('0', TensorProto.FLOAT, [1, 3, 2, 2])
......@@ -2162,6 +2203,47 @@ def prelu_brcst_test():
return ([node], [arg0, arg1], [arg_out])
@onnx_test
def quantizelinear_test():
arg0 = helper.make_tensor_value_info('0', TensorProto.FLOAT, [5])
arg1 = helper.make_tensor_value_info('1', TensorProto.FLOAT, [1])
arg2 = helper.make_tensor_value_info('2', TensorProto.INT8, [1])
arg_out = helper.make_tensor_value_info('out', TensorProto.INT8, [5])
node = onnx.helper.make_node(
'QuantizeLinear',
inputs=['0', '1', '2'],
outputs=['out'],
)
return ([node], [arg0, arg1, arg2], [arg_out])
def make_quantizelinear_axis_graph(axis):
arg0 = helper.make_tensor_value_info('0', TensorProto.FLOAT, [1, 1, 5, 1])
arg1 = helper.make_tensor_value_info('1', TensorProto.FLOAT, [5])
arg2 = helper.make_tensor_value_info('2', TensorProto.INT8, [5])
arg_out = helper.make_tensor_value_info('out', TensorProto.INT8,
[1, 1, 5, 1])
node = onnx.helper.make_node('QuantizeLinear',
inputs=['0', '1', '2'],
outputs=['out'],
axis=axis)
return ([node], [arg0, arg1, arg2], [arg_out])
@onnx_test
def quantizelinear_axis_test():
return make_quantizelinear_axis_graph(2)
@onnx_test
def quantizelinear_neg_axis_test():
return make_quantizelinear_axis_graph(-2)
@onnx_test
def range_test():
......
......@@ -896,6 +896,85 @@ TEST_CASE(deconv_output_shape_3d_test)
EXPECT(p == prog);
}
TEST_CASE(dequantizelinear_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
auto l0 = mm->add_parameter("0", {migraphx::shape::int8_type, {5}});
auto l1 = mm->add_parameter("1", {migraphx::shape::float_type, {1}});
auto l2 = mm->add_parameter("2", {migraphx::shape::int8_type, {1}});
auto l1_mbcast =
mm->add_instruction(migraphx::make_op("multibroadcast", {{"output_lens", {5}}}), l1);
l2 = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l2);
auto l2_mbcast =
mm->add_instruction(migraphx::make_op("multibroadcast", {{"output_lens", {5}}}), l2);
l0 = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l0);
auto sub = mm->add_instruction(migraphx::make_op("sub"), l0, l2_mbcast);
auto dequant = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::float_type)}}),
sub);
mm->add_instruction(migraphx::make_op("mul"), dequant, l1_mbcast);
auto prog = optimize_onnx("dequantizelinear_test.onnx");
EXPECT(p.sort() == prog.sort());
}
migraphx::program make_dequantizelinear_axis_prog()
{
migraphx::program p;
std::vector<size_t> input_lens{1, 1, 5, 1};
int axis = 2;
auto* mm = p.get_main_module();
auto l0 = mm->add_parameter("0", {migraphx::shape::int8_type, input_lens});
auto l1 = mm->add_parameter("1", {migraphx::shape::float_type, {5}});
auto l2 = mm->add_parameter("2", {migraphx::shape::int8_type, {5}});
auto l1_bcast = mm->add_instruction(
migraphx::make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), l1);
auto l2_bcast = mm->add_instruction(
migraphx::make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), l2);
l2_bcast = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l2_bcast);
l0 = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l0);
auto sub = mm->add_instruction(migraphx::make_op("sub"), l0, l2_bcast);
auto dequant = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::float_type)}}),
sub);
mm->add_instruction(migraphx::make_op("mul"), dequant, l1_bcast);
return p;
}
TEST_CASE(dequantizelinear_axis_test)
{
migraphx::program p = make_dequantizelinear_axis_prog();
auto prog = optimize_onnx("dequantizelinear_axis_test.onnx");
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(dequantizelinear_neg_axis_test)
{
migraphx::program p = make_dequantizelinear_axis_prog();
auto prog = optimize_onnx("dequantizelinear_neg_axis_test.onnx");
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(dropout_test)
{
migraphx::program p;
......@@ -1955,6 +2034,96 @@ TEST_CASE(prelu_brcst_test)
EXPECT(p == prog);
}
TEST_CASE(quantizelinear_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
auto l0 = mm->add_parameter("0", {migraphx::shape::float_type, {5}});
auto l1 = mm->add_parameter("1", {migraphx::shape::float_type, {1}});
auto l2 = mm->add_parameter("2", {migraphx::shape::int8_type, {1}});
auto min_val = mm->add_literal(-128);
auto max_val = mm->add_literal(127);
auto l1_mbcast =
mm->add_instruction(migraphx::make_op("multibroadcast", {{"output_lens", {5}}}), l1);
auto div = mm->add_instruction(migraphx::make_op("div"), l0, l1_mbcast);
auto round = mm->add_instruction(migraphx::make_op("round"), div);
l2 = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l2);
auto l2_mbcast =
mm->add_instruction(migraphx::make_op("multibroadcast", {{"output_lens", {5}}}), l2);
round = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
round);
auto add = mm->add_instruction(migraphx::make_op("add"), round, l2_mbcast);
auto clip = mm->add_instruction(migraphx::make_op("clip"), add, min_val, max_val);
mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int8_type)}}),
clip);
auto prog = optimize_onnx("quantizelinear_test.onnx");
EXPECT(p.sort() == prog.sort());
}
migraphx::program make_quantizelinear_axis_prog()
{
migraphx::program p;
std::vector<size_t> input_lens{1, 1, 5, 1};
int axis = 2;
auto* mm = p.get_main_module();
auto l0 = mm->add_parameter("0", {migraphx::shape::float_type, input_lens});
auto l1 = mm->add_parameter("1", {migraphx::shape::float_type, {5}});
auto l2 = mm->add_parameter("2", {migraphx::shape::int8_type, {5}});
auto min_val = mm->add_literal(-128);
auto max_val = mm->add_literal(127);
auto l1_bcast = mm->add_instruction(
migraphx::make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), l1);
auto div = mm->add_instruction(migraphx::make_op("div"), l0, l1_bcast);
auto round = mm->add_instruction(migraphx::make_op("round"), div);
auto l2_bcast = mm->add_instruction(
migraphx::make_op("broadcast", {{"axis", axis}, {"dims", input_lens}}), l2);
l2_bcast = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
l2_bcast);
round = mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int32_type)}}),
round);
auto add = mm->add_instruction(migraphx::make_op("add"), round, l2_bcast);
auto clip = mm->add_instruction(migraphx::make_op("clip"), add, min_val, max_val);
mm->add_instruction(
migraphx::make_op("convert",
{{"target_type", migraphx::to_value(migraphx::shape::int8_type)}}),
clip);
return p;
}
TEST_CASE(quantizelinear_axis_test)
{
migraphx::program p = make_quantizelinear_axis_prog();
auto prog = optimize_onnx("quantizelinear_axis_test.onnx");
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(quantizelinear_neg_axis_test)
{
migraphx::program p = make_quantizelinear_axis_prog();
auto prog = optimize_onnx("quantizelinear_neg_axis_test.onnx");
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(range_test)
{
migraphx::program p;
......
quantizelinear_axis_test:
+
0
1
2out"QuantizeLinear*
axisquantizelinear_axis_testZ
0




Z
1

Z
2

b
out




B
\ No newline at end of file
quantizelinear_neg_axis_test:
4
0
1
2out"QuantizeLinear*
axisquantizelinear_neg_axis_testZ
0




Z
1

Z
2

b
out




B
\ No newline at end of file
quantizelinear_test:{

0
1
2out"QuantizeLinearquantizelinear_testZ
0

Z
1

Z
2

b
out

B
\ No newline at end of file
......@@ -71,6 +71,7 @@ def create_backend_test(testname=None, target_device=None):
backend_test.include(r'.*test_cos.*')
backend_test.include(r'.*test_cosh.*')
backend_test.include(r'.*test_depthtospace.*')
backend_test.include(r'.*test_dequantizelinear')
backend_test.include(r'.*test_div.*')
backend_test.include(r'.*test_dropout.*')
backend_test.include(r'.*test_ELU*')
......@@ -132,6 +133,7 @@ def create_backend_test(testname=None, target_device=None):
backend_test.include(r'.*test_operator_view.*')
backend_test.include(r'.*test_pow.*')
backend_test.include(r'.*test_PoissonNLLLLoss_no_reduce*')
backend_test.include(r'.*test_quantizelinear')
backend_test.include(r'.*test_reciprocal.*')
backend_test.include(r'.*test_reduce.*')
backend_test.include(r'.*test_ReLU*')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment