Unverified Commit 1587c533 authored by Lakhinder Walia's avatar Lakhinder Walia Committed by GitHub
Browse files

qlinearconv operator (#2225)

parent b66d58ac
......@@ -47,7 +47,7 @@ void cal_auto_padding_size(onnx_parser::node_info info,
return;
}
auto auto_pad = info.attributes["auto_pad"].s();
auto auto_pad = to_upper(info.attributes["auto_pad"].s());
if(auto_pad.find("SAME") != std::string::npos)
{
bool is_same_upper = (auto_pad.find("SAME_UPPER") != std::string::npos);
......
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <migraphx/onnx/op_parser.hpp>
#include <migraphx/onnx/padding.hpp>
#include <migraphx/onnx/conv.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/make_op.hpp>
#include <migraphx/onnx/checks.hpp>
#include <migraphx/onnx/broadcast_qdq.hpp>
#include <migraphx/instruction.hpp>
#include <migraphx/stringutils.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
namespace onnx {
/*
*********************************************************************************
* Reference: see QLinearConv in *
* https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md *
*********************************************************************************
com.microsoft.QLinearConv
Version
This version of the operator has been available since version 1 of the 'com.microsoft' operator set.
ATTRIBUTES:
auto_pad : string
channels_last : int
dilations : list of ints
group : int
kernel_shape : list of ints
pads : list of ints
strides : list of ints
INPUTS (8 - 9):
x : T1
x_scale : tensor(float)
x_zero_point : T1
w : T2
w_scale : tensor(float)
w_zero_point : T2
y_scale : tensor(float)
y_zero_point : T3
B (optional) : T4
OUTPUTS:
y : T3
Type Constraints:
T1 : tensor(int8), tensor(uint8)
T2 : tensor(int8), tensor(uint8)
T3 : tensor(int8), tensor(uint8)
T4 : tensor(int32)
More details also at:
https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__QLinearConv.html
*/
struct parse_qlinearconv : op_parser<parse_qlinearconv>
{
std::vector<op_desc> operators() const { return {{"QLinearConv"}}; }
// basic type checking for QLinearConv Operator
void check_inputs(const std::vector<instruction_ref>& inp_arg) const
{
if(inp_arg.size() < 8)
MIGRAPHX_THROW("QLINEARCONV: missing inputs");
const instruction_ref& in_x = inp_arg[0];
const instruction_ref& in_scale_x = inp_arg[1];
const instruction_ref& in_w = inp_arg[3];
const instruction_ref& in_scale_w = inp_arg[4];
const instruction_ref& in_scale_y = inp_arg[6];
auto sh_x = in_x->get_shape();
auto sh_w = in_w->get_shape();
auto type_x = sh_x.type();
auto type_w = sh_w.type();
assert(in_x->get_shape().ndim() > 2);
if(type_x != shape::int8_type and type_x != shape::uint8_type)
MIGRAPHX_THROW("QLINEARCONV: unsupported input type");
if(type_w != shape::int8_type and type_w != shape::uint8_type)
MIGRAPHX_THROW("QLINEARCONV: unsupported weight type");
if(in_scale_x->get_shape().type() != shape::float_type)
MIGRAPHX_THROW("QLINEARCONV x scale type should be float");
if(in_scale_w->get_shape().type() != shape::float_type)
MIGRAPHX_THROW("QLINEARCONV: wt scale type should be float");
if(in_scale_y->get_shape().type() != shape::float_type)
MIGRAPHX_THROW("QLINEARCONV: y scale type should be float");
if(inp_arg.size() > 8 and inp_arg[8]->get_shape().type() != shape::int32_type)
MIGRAPHX_THROW("QLINEARCONV y bias should be int32");
}
// process all attributes of QLinearConv Operator..
value process_attributes(const onnx_parser& parser,
const onnx_parser::node_info& info,
const std::vector<instruction_ref>& args) const
{
value values;
const auto& in_x = args[0];
const auto& wt = args[3];
size_t kdims = in_x->get_shape().ndim() - 2;
check_padding_mode(info, "QLINEARCONV");
values["stride"] = std::vector<int>(kdims, 1);
values["dilation"] = std::vector<int>(kdims, 1);
values["padding"] = std::vector<int>(kdims, 0);
values["group"] = 1;
if(contains(info.attributes, "group"))
values["group"] = parser.parse_value(info.attributes.at("group")).template at<int>();
if(contains(info.attributes, "strides"))
{
std::vector<int> st;
copy(info.attributes.at("strides").ints(), std::back_inserter(st));
check_attr_sizes(kdims, st.size(), "QLINEARCONV: inconsistent strides");
values["stride"] = st;
}
if(contains(info.attributes, "dilations"))
{
std::vector<int> dil;
copy(info.attributes.at("dilations").ints(), std::back_inserter(dil));
check_attr_sizes(kdims, dil.size(), "QLINEARCONV: inconsistent dilations");
values["dilation"] = dil;
}
if(contains(info.attributes, "pads"))
{
std::vector<int> pads;
copy(info.attributes.at("pads").ints(), std::back_inserter(pads));
check_attr_sizes(kdims, pads.size() / 2, "QLINEARCONV: inconsistent padding");
values["padding"] = pads;
}
else if(contains(info.attributes, "auto_pad"))
{
auto in_lens = in_x->get_shape().lens();
auto wt_lens = wt->get_shape().lens();
std::vector<std::size_t> k_lens(wt_lens.begin() + 2, wt_lens.end());
std::vector<int64_t> pads = values["padding"].to_vector<std::int64_t>();
cal_auto_padding_size(
info, values, k_lens, values["dilation"].to_vector<std::size_t>(), in_lens, pads);
values["padding"] = pads;
}
recalc_conv_attributes(values, kdims);
return values;
}
instruction_ref add_bias_to_conv(const instruction_ref bias_arg,
const instruction_ref conv_instr,
const onnx_parser::node_info& info) const
{
auto conv_sh = conv_instr->get_shape();
auto conv_lens = conv_sh.lens();
auto conv_type = conv_sh.type();
auto broadcast_bias = info.add_instruction(
migraphx::make_op("broadcast", {{"axis", 1}, {"out_lens", conv_lens}}), bias_arg);
auto f_bias =
info.add_instruction(make_op("convert", {{"target_type", conv_type}}), broadcast_bias);
return info.add_instruction(migraphx::make_op("add"), conv_instr, f_bias);
};
instruction_ref parse(const op_desc& /* opd */,
const onnx_parser& parser,
const onnx_parser::node_info& info,
const std::vector<instruction_ref>& args) const
{
check_inputs(args);
auto values = process_attributes(parser, info, args);
// input: quantized x, scale, zero_pt
const instruction_ref& in_x = args[0];
const instruction_ref& in_scale_x = args[1];
const instruction_ref& in_zero_pt_x = args[2];
// input: quantized weights, scale, zero_pt
const instruction_ref& in_w = args[3];
const instruction_ref& in_scale_w = args[4];
const instruction_ref& in_zero_pt_w = args[5];
// for the dequantized output y: scale & zero_pt
const instruction_ref& in_scale_y = args[6];
const instruction_ref& in_zero_pt_y = args[7];
auto dquant_x = bcast_qdq_instr("dequantizelinear", in_x, in_scale_x, in_zero_pt_x, info);
auto dquant_w = bcast_qdq_instr("dequantizelinear", in_w, in_scale_w, in_zero_pt_w, info);
auto conv_op = migraphx::make_op("convolution", values);
auto conv_x_w = info.add_instruction(conv_op, dquant_x, dquant_w);
// Biases, if any.. : is an optional argument.
if(args.size() > 8)
conv_x_w = add_bias_to_conv(args[8], conv_x_w, info);
auto quant_conv =
bcast_qdq_instr("quantizelinear", conv_x_w, in_scale_y, in_zero_pt_y, info);
return quant_conv;
}
};
} // namespace onnx
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
......@@ -5151,6 +5151,116 @@ def qlinearadd_bcast_test():
[sc_a, zero_pt_a, sc_b, zero_pt_b, sc_c, zero_pt_c])
@onnx_test()
def qlinearconv_test():
# https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__QLinearConv.html
x = helper.make_tensor_value_info('X', TensorProto.UINT8, [1, 1, 7, 7])
sc_x = helper.make_tensor('1', TensorProto.FLOAT, [], [0.00369204697])
zero_pt_x = helper.make_tensor('2', TensorProto.UINT8, [], [132])
wt = helper.make_tensor('3', TensorProto.UINT8, [1, 1, 1, 1], [0])
sc_wt = helper.make_tensor('4', TensorProto.FLOAT, [], [0.00172794575])
zero_pt_wt = helper.make_tensor('5', TensorProto.UINT8, [], [255])
sc_y = helper.make_tensor('6', TensorProto.FLOAT, [], [0.00162681262])
zero_pt_y = helper.make_tensor('7', TensorProto.UINT8, [], [123])
out = helper.make_tensor_value_info('out', TensorProto.UINT8, [1, 1, 7, 7])
node = onnx.helper.make_node(
'QLinearConv',
inputs=['X', '1', '2', '3', '4', '5', '6', '7'],
outputs=['out'],
)
return ([node], [x], [out],
[sc_x, zero_pt_x, wt, sc_wt, zero_pt_wt, sc_y, zero_pt_y])
@onnx_test()
def qlinearconv_pad_1_test():
# https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
x = helper.make_tensor_value_info('X', TensorProto.UINT8, [1, 1, 5, 5])
sc_x = helper.make_tensor('1', TensorProto.FLOAT, [],
[0.09411764705882353])
zero_pt_x = helper.make_tensor('2', TensorProto.UINT8, [], [0])
wt = helper.make_tensor('3', TensorProto.UINT8, [1, 1, 3, 3],
[1, 1, 1, 1, 1, 1, 1, 1, 1])
sc_wt = helper.make_tensor('4', TensorProto.FLOAT, [], [1.0])
zero_pt_wt = helper.make_tensor('5', TensorProto.UINT8, [], [0])
sc_y = helper.make_tensor('6', TensorProto.FLOAT, [], [0.6352941176470588])
zero_pt_y = helper.make_tensor('7', TensorProto.UINT8, [], [0])
out = helper.make_tensor_value_info('out', TensorProto.UINT8, [1, 1, 5, 5])
node = onnx.helper.make_node(
'QLinearConv',
inputs=['X', '1', '2', '3', '4', '5', '6', '7'],
outputs=['out'],
pads=[1, 1, 1, 1],
)
return ([node], [x], [out],
[sc_x, zero_pt_x, wt, sc_wt, zero_pt_wt, sc_y, zero_pt_y])
@onnx_test()
def qlinearconv_pad_0_test():
# https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
x = helper.make_tensor_value_info('X', TensorProto.UINT8, [1, 1, 5, 5])
sc_x = helper.make_tensor('1', TensorProto.FLOAT, [],
[0.09411764705882353])
zero_pt_x = helper.make_tensor('2', TensorProto.UINT8, [], [0])
wt = helper.make_tensor('3', TensorProto.UINT8, [1, 1, 3, 3],
[1, 1, 1, 1, 1, 1, 1, 1, 1])
sc_wt = helper.make_tensor('4', TensorProto.FLOAT, [], [1.0])
zero_pt_wt = helper.make_tensor('5', TensorProto.UINT8, [], [0])
sc_y = helper.make_tensor('6', TensorProto.FLOAT, [], [0.6352941176470588])
zero_pt_y = helper.make_tensor('7', TensorProto.INT8, [], [-128])
out = helper.make_tensor_value_info('out', TensorProto.INT8, [1, 1, 3, 3])
node = onnx.helper.make_node(
'QLinearConv',
inputs=['X', '1', '2', '3', '4', '5', '6', '7'],
outputs=['out'],
pads=[0, 0, 0, 0],
)
return ([node], [x], [out],
[sc_x, zero_pt_x, wt, sc_wt, zero_pt_wt, sc_y, zero_pt_y])
@onnx_test()
def qlinearconv_scale_1D_test():
# https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
x = helper.make_tensor_value_info('X', TensorProto.UINT8, [1, 1, 5, 5])
sc_x = helper.make_tensor('1', TensorProto.FLOAT, [],
[0.09411764705882353])
zero_pt_x = helper.make_tensor('2', TensorProto.UINT8, [], [0])
wt = helper.make_tensor(
'3', TensorProto.UINT8, [2, 1, 3, 3],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2])
sc_wt = helper.make_tensor('4', TensorProto.FLOAT, [2], [1.0, 0.5])
zero_pt_wt = helper.make_tensor('5', TensorProto.UINT8, [2], [0, 0])
sc_y = helper.make_tensor('6', TensorProto.FLOAT, [], [0.6352941176470588])
zero_pt_y = helper.make_tensor('7', TensorProto.INT8, [], [-128])
out = helper.make_tensor_value_info('out', TensorProto.INT8, [1, 2, 3, 3])
node = onnx.helper.make_node(
'QLinearConv',
inputs=['X', '1', '2', '3', '4', '5', '6', '7'],
outputs=['out'],
pads=[0, 0, 0, 0],
)
return ([node], [x], [out],
[sc_x, zero_pt_x, wt, sc_wt, zero_pt_wt, sc_y, zero_pt_y])
@onnx_test()
def quantizelinear_test():
arg0 = helper.make_tensor_value_info('0', TensorProto.FLOAT, [5])
......
......@@ -4904,6 +4904,61 @@ TEST_CASE(qlinearadd_test)
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(qlinearconv_test)
{
migraphx::program p;
auto* mm = p.get_main_module();
auto x = mm->add_parameter("X", {migraphx::shape::uint8_type, {1, 1, 7, 7}});
auto sc_x = mm->add_literal(migraphx::literal{migraphx::shape::float_type, {0.00369204697}});
auto z_pt_x = mm->add_literal(migraphx::literal{migraphx::shape::uint8_type, {132}});
auto w = mm->add_literal(
migraphx::literal{migraphx::shape{migraphx::shape::uint8_type, {1, 1, 1, 1}}, {0}});
auto sc_w = mm->add_literal(migraphx::literal{migraphx::shape::float_type, {0.00172794575}});
auto z_pt_w = mm->add_literal(migraphx::literal{migraphx::shape::uint8_type, {255}});
auto sc_y = mm->add_literal(migraphx::literal{migraphx::shape::float_type, {0.00162681262}});
auto z_pt_y = mm->add_literal(migraphx::literal{migraphx::shape::uint8_type, {123}});
auto scale_x_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 7, 7}}}), sc_x);
auto z_pt_x_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 7, 7}}}), z_pt_x);
auto fp_x =
mm->add_instruction(migraphx::make_op("dequantizelinear"), x, scale_x_bcast, z_pt_x_bcast);
auto scale_w_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 1, 1}}}), sc_w);
auto z_pt_w_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 1, 1}}}), z_pt_w);
auto fp_w =
mm->add_instruction(migraphx::make_op("dequantizelinear"), w, scale_w_bcast, z_pt_w_bcast);
auto fp_y = mm->add_instruction(migraphx::make_op("convolution"), fp_x, fp_w);
auto scale_y_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 7, 7}}}), sc_y);
auto z_pt_y_bcast = mm->add_instruction(
migraphx::make_op("multibroadcast", {{"out_lens", {1, 1, 7, 7}}}), z_pt_y);
auto y =
mm->add_instruction(migraphx::make_op("quantizelinear"), fp_y, scale_y_bcast, z_pt_y_bcast);
mm->add_return({y});
auto prog = migraphx::parse_onnx("qlinearconv_test.onnx");
EXPECT(p.sort() == prog.sort());
}
TEST_CASE(quantizelinear_test)
{
migraphx::program p;
......
......@@ -1318,6 +1318,112 @@ TEST_CASE(qlinearadd_bcast_test)
EXPECT(migraphx::verify::verify_rms_range(result_vector, gold));
}
TEST_CASE(qlinearconv_test)
{
// https://xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__QLinearConv.html
migraphx::program p = migraphx::parse_onnx("qlinearconv_test.onnx");
p.compile(migraphx::make_target("ref"));
migraphx::shape sx{migraphx::shape::uint8_type, {1, 1, 7, 7}};
std::vector<uint8_t> x_data = {255, 174, 162, 25, 203, 168, 58, 15, 59, 237, 95, 129, 0,
64, 56, 242, 153, 221, 168, 12, 166, 232, 178, 186, 195, 237,
162, 237, 188, 39, 124, 77, 80, 102, 43, 127, 230, 21, 83,
41, 40, 134, 255, 154, 92, 141, 42, 148, 247};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, x_data.data());
auto result = p.eval(pp).back();
std::vector<uint8_t> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
std::vector<uint8_t> gold = {0, 81, 93, 230, 52, 87, 197, 240, 196, 18, 160, 126, 255,
191, 199, 13, 102, 34, 87, 243, 89, 23, 77, 69, 60, 18,
93, 18, 67, 216, 131, 178, 175, 153, 212, 128, 25, 234, 172,
214, 215, 121, 0, 101, 163, 114, 213, 107, 8};
EXPECT(migraphx::verify::verify_rms_range(result_vector, gold));
}
TEST_CASE(qlinearconv_pad_0_test)
{
// https:xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
migraphx::program p = migraphx::parse_onnx("qlinearconv_pad_0_test.onnx");
p.compile(migraphx::make_target("ref"));
migraphx::shape sx{migraphx::shape::uint8_type, {1, 1, 5, 5}};
std::vector<uint8_t> x_data = {0, 11, 21, 32, 42, 53, 64, 74, 85, 96, 106, 117, 128,
138, 149, 159, 170, 181, 191, 202, 212, 223, 234, 244, 255};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, x_data.data());
auto result = p.eval(pp).back();
std::vector<int8_t> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
// # (1, 1, 3, 3) output tensor
std::vector<int8_t> gold = {-43, -29, -15, 28, 42, 56, 99, 113, 127};
EXPECT(migraphx::verify::verify_rms_range(result_vector, gold));
}
TEST_CASE(qlinearconv_pad_1_test)
{
// https:xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
migraphx::program p = migraphx::parse_onnx("qlinearconv_pad_1_test.onnx");
p.compile(migraphx::make_target("ref"));
migraphx::shape sx{migraphx::shape::uint8_type, {1, 1, 5, 5}};
std::vector<uint8_t> x_data = {0, 11, 21, 32, 42, 53, 64, 74, 85, 96, 106, 117, 128,
138, 149, 159, 170, 181, 191, 202, 212, 223, 234, 244, 255};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, x_data.data());
auto result = p.eval(pp).back();
std::vector<uint8_t> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
// # (1, 1, 5, 5) output tensor
std::vector<uint8_t> gold = {19, 33, 43, 52, 38, 52, 85, 99, 113, 80, 99, 156, 170,
184, 128, 146, 227, 241, 255, 175, 113, 175, 184, 194, 132};
EXPECT(migraphx::verify::verify_rms_range(result_vector, gold));
}
TEST_CASE(qlinearconv_scale_1D_test)
{
// https:xadupre.github.io/draft/onnx/onnx_doc_folder/onnx__Conv.html
migraphx::program p = migraphx::parse_onnx("qlinearconv_scale_1D_test.onnx");
p.compile(migraphx::make_target("ref"));
migraphx::shape sx{migraphx::shape::uint8_type, {1, 1, 5, 5}};
std::vector<uint8_t> x_data = {0, 11, 21, 32, 42, 53, 64, 74, 85, 96, 106, 117, 128,
138, 149, 159, 170, 181, 191, 202, 212, 223, 234, 244, 255};
migraphx::parameter_map pp;
pp["X"] = migraphx::argument(sx, x_data.data());
auto result = p.eval(pp).back();
std::vector<int8_t> result_vector;
result.visit([&](auto output) { result_vector.assign(output.begin(), output.end()); });
// # (1, 2, 3, 3) output tensor
std::vector<int8_t> gold = {
-43, -29, -15, 28, 42, 56, 99, 113, 127, -43, -29, -15, 28, 42, 56, 99, 113, 127};
EXPECT(migraphx::verify::verify_rms_range(result_vector, gold));
}
TEST_CASE(resize_downsample_f_test)
{
migraphx::program p = migraphx::parse_onnx("resize_downsample_f_test.onnx");
......
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