[NNAPI QDQ] Add QDQTranspose op support (#10495)

* Squashed commit of the following:

commit 12380491a9
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Feb 7 12:59:04 2022 -0800

    Add qdq mul support

commit 9cadda7f2c
Merge: 7a32847761 0f5d0a091a
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Feb 7 11:24:47 2022 -0800

    Merge remote-tracking branch 'origin/master' into gwang-msft/qdq_mul

commit 7a32847761
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Feb 7 00:41:30 2022 -0800

    move test case to util

commit c1a8f0d81e
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Fri Feb 4 13:04:26 2022 -0800

    update input/output check

commit a6f0a0d504
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Thu Feb 3 18:37:21 2022 -0800

    update quantized io check functions

commit 87f4d1dcfe
Merge: 7849f07109 97b8f6f394
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Wed Feb 2 17:22:58 2022 -0800

    Merge remote-tracking branch 'origin/master' into gwang-msft/qdq_mul

commit 7849f07109
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Wed Feb 2 17:22:55 2022 -0800

    minor update

commit 7196cdf419
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Wed Feb 2 10:50:10 2022 -0800

    init change

commit 84c00772a1
Merge: a8c7dce22f 7318361645
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Tue Feb 1 18:21:17 2022 -0800

    Merge remote-tracking branch 'origin/master' into gwang-msft/qdq_mul

commit a8c7dce22f
Merge: 55e536c182 ef7b4dc05c
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Tue Feb 1 13:51:04 2022 -0800

    Merge remote-tracking branch 'origin/master' into gwang-msft/qdq_mul

commit 55e536c182
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Tue Feb 1 11:44:34 2022 -0800

    address cr comments

commit d460f5b776
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Tue Feb 1 00:33:54 2022 -0800

    fix android UT failure

commit 52146cf06f
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Jan 31 16:01:13 2022 -0800

    fix build break

commit ec6d07df8b
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Jan 31 15:41:52 2022 -0800

    minor update to UT

commit 8ec8490b4f
Author: Guoyu Wang <wanggy@outlook.com>
Date:   Mon Jan 31 15:01:30 2022 -0800

    Add NNAPI support of QDQ Resize

* Update qdq add/mul test case, fix build break

* Address CR comments

* Add QLinearMul support

* remove unused params

* Address CR comments

* wip

* save

* minor fix

* fix

* fix build

* address pr comments

* fix wrong ut tests

* address comments

* minor update

* fix addinitializersskip

Co-authored-by: Guoyu Wang <wanggy@outlook.com>
Co-authored-by: rachguo <rachguo@rachguos-Mini.attlocal.net>
This commit is contained in:
Rachel Guo 2022-02-11 10:42:08 -08:00 committed by GitHub
parent 318d31ea12
commit 5cfde7af29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 22 deletions

View file

@ -78,9 +78,10 @@ QuantizedOpType GetQuantizedOpType(const NodeUnit& node_unit) {
return QuantizedOpType::QDQAdd;
else if (op_type == "Mul")
return QuantizedOpType::QDQMul;
else if (op_type == "Transpose")
return QuantizedOpType::QDQTranspose;
} else {
// throw?
// Do we want to throw here? seems got neglected last time
}
return QuantizedOpType::Unknown;

View file

@ -90,6 +90,7 @@ enum class QuantizedOpType : uint8_t {
QDQAveragePool,
QDQAdd,
QDQMul,
QDQTranspose,
// TODO, add other QDQ NodeUnit types
};

View file

@ -788,12 +788,29 @@ Status ReluOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N
#pragma region op_transpose
class TransposeOpBuilder : public BaseOpBuilder {
public:
void AddInitializersToSkip(ModelBuilder& model_builder, const NodeUnit& node_unit) const override;
private:
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const NodeUnit& node_unit) const override;
static bool IsQuantizedOp(const NodeUnit& node_unit) ORT_MUST_USE_RESULT; // TODO, see if we want to move this to BaseOpBuilder
};
void TransposeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const NodeUnit& node_unit) const {
if (!IsQuantizedOp(node_unit))
return;
AddQuantizationScaleAndZeroPointToSkip(model_builder, *node_unit.Inputs()[0].quant_param); // x_scale, x_zp
AddQuantizationScaleAndZeroPointToSkip(model_builder, *node_unit.Outputs()[0].quant_param); // y_scale, y_zp
}
/* static */ bool TransposeOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) {
return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQTranspose;
}
Status TransposeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const NodeUnit& node_unit) const {
auto& shaper(model_builder.GetShaper());
const auto& initializers(model_builder.GetInitializerTensors());
const auto& input = node_unit.Inputs()[0].node_arg.Name();
const auto& output = node_unit.Outputs()[0].node_arg.Name();
@ -816,6 +833,15 @@ Status TransposeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, co
perm[i] = axis_nchw_to_nhwc[perm[i]];
}
// Check if the quantization scale and ZP are correct
if (IsQuantizedOp(node_unit)) {
float x_scale = 0.0f;
int32_t x_zero_point = 0;
ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint(
initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point));
ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point));
}
std::string perm_name = model_builder.GetUniqueName(node_unit.Name() + input + "perm");
// It is possible this onnx transpose operator can be nchw->nhwc, but so far I don't see

View file

@ -592,8 +592,14 @@ class TransposeOpSupportChecker : public BaseOpSupportChecker {
bool HasSupportedInputOutputsImpl(
const InitializedTensorSet& initializers, const NodeUnit& node_unit,
const OpSupportCheckParams& params) const override;
bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; }
static bool IsQuantizedOp(const NodeUnit& node_unit) ORT_MUST_USE_RESULT; // TODO, see if we want to move this to BaseOpBuilder
};
/* static */ bool TransposeOpSupportChecker::IsQuantizedOp(const NodeUnit& node_unit) {
return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQTranspose;
}
bool TransposeOpSupportChecker::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit,
const OpSupportCheckParams& /* params */) const {
Shape input_shape;
@ -611,8 +617,8 @@ bool TransposeOpSupportChecker::IsOpSupportedImpl(const InitializedTensorSet& /*
}
bool TransposeOpSupportChecker::HasSupportedInputOutputsImpl(
const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit,
const OpSupportCheckParams& /* params */) const {
const InitializedTensorSet& initializers, const NodeUnit& node_unit,
const OpSupportCheckParams& params) const {
int32_t input_type;
if (!GetType(node_unit.Inputs()[0].node_arg, input_type))
return false;
@ -625,6 +631,14 @@ bool TransposeOpSupportChecker::HasSupportedInputOutputsImpl(
return false;
}
if (IsQuantizedOp(node_unit)) {
if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, IOKind::Input))
return false;
if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, IOKind::Output))
return false;
}
return true;
}

View file

@ -187,5 +187,30 @@ GetQDQTestCaseFn BuildBinaryOpTestCase(const std::vector<int64_t>& input_shape,
output_arg);
};
}
template <typename InputType, typename OutputType>
GetQDQTestCaseFn BuildQDQTransposeTestCase(
const std::vector<int64_t>& input_shape,
const std::vector<int64_t>& perms) {
return [input_shape, perms](ModelTestBuilder& builder) {
auto* input_arg = builder.MakeInput<InputType>(input_shape, -128, 127);
auto* output_arg = builder.MakeOutput();
InputType dq_zp = std::numeric_limits<InputType>::max() / 2;
OutputType q_zp = std::numeric_limits<OutputType>::max() / 2;
// add DQ
auto* dq_output = builder.MakeIntermediate();
builder.AddDequantizeLinearNode<InputType>(input_arg, .003f, dq_zp, dq_output);
// add Transpose
auto* transpose_output = builder.MakeIntermediate();
Node& transpose_node = builder.AddNode("Transpose", {dq_output}, {transpose_output});
transpose_node.AddAttribute("perm", perms);
// add Q
builder.AddQuantizeLinearNode<OutputType>(transpose_output, .003f, q_zp, output_arg);
};
}
} // namespace test
} // namespace onnxruntime

View file

@ -665,24 +665,7 @@ TEST(QDQTransformerTests, Gather) {
}
TEST(QDQTransformerTests, Transpose) {
auto test_case = [&](const std::vector<int64_t>& input1_shape, const std::vector<int64_t>& perms) {
auto build_test_case = [&](ModelTestBuilder& builder) {
auto* input1_arg = builder.MakeInput<int8_t>(input1_shape, -128, 127);
auto* output_arg = builder.MakeOutput();
// add DQ
auto* dq_output = builder.MakeIntermediate();
builder.AddDequantizeLinearNode<int8_t>(input1_arg, .003f, 1, dq_output);
// add Transpose
auto* transpose_output = builder.MakeIntermediate();
Node& transpose_node = builder.AddNode("Transpose", {dq_output}, {transpose_output});
transpose_node.AddAttribute("perm", perms);
// add Q
builder.AddQuantizeLinearNode<int8_t>(transpose_output, .003f, 1, output_arg);
};
auto test_case = [&](const std::vector<int64_t>& input_shape, const std::vector<int64_t>& perms) {
auto check_graph = [&](InferenceSessionWrapper& session) {
auto op_to_count = CountOpsInGraph(session.GetGraph());
EXPECT_EQ(op_to_count["Transpose"], 1);
@ -690,7 +673,10 @@ TEST(QDQTransformerTests, Transpose) {
EXPECT_EQ(op_to_count["DequantizeLinear"], 0);
};
TransformerTester(build_test_case, check_graph, TransformerLevel::Level1, TransformerLevel::Level2);
TransformerTester(BuildQDQTransposeTestCase<int8_t, int8_t>(input_shape, perms),
check_graph,
TransformerLevel::Level1,
TransformerLevel::Level2);
};
test_case({2, 13, 12, 37}, {0, 3, 1, 2});

View file

@ -359,6 +359,17 @@ TEST(NnapiExecutionProviderTest, TestQDQMul) {
});
}
TEST(NnapiExecutionProviderTest, TestQDQTranspose) {
RunQDQModelTest(BuildQDQTransposeTestCase<uint8_t /* InputType */,
uint8_t /* OutputType */>(
{1, 3, 32, 32} /* input_shape */,
{0, 3, 1, 2} /* perms */),
"nnapi_qdq_test_graph_transpose",
{
true /* verify_entire_graph_use_ep */
});
}
#endif // !(ORT_MINIMAL_BUILD)
TEST(NnapiExecutionProviderTest, NNAPIFlagsTest) {