[QNN EP] Fix auto_pad handling for Conv operator (#16299)

### Description
Correctly sets padding when the `auto_pad` attribute is specified for
Conv operator.

### Motivation and Context
Needed to correctly translate ONNX Conv to QNN Conv2d.
This commit is contained in:
Adrian Lizarraga 2023-06-09 09:23:08 -07:00 committed by GitHub
parent b668a6da96
commit 1a22d245e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 8 deletions

View file

@ -311,15 +311,26 @@ Status ConvOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wra
total_padding[0] = stride_values[0] * (input_0_shape[1] - 1) + output_padding_0 + (input_1_shape[2] - 1) * dilations_0 + 1 - output_shape[1];
total_padding[1] = stride_values[1] * (input_0_shape[2] - 1) + output_padding_1 + (input_1_shape[3] - 1) * dilations_1 + 1 - output_shape[2];
} else {
total_padding[0] = output_shape[1] * stride_values[0] - input_0_shape[1] + 1;
total_padding[1] = output_shape[1] * stride_values[0] - input_0_shape[2] + 1;
// dilated_filter_height = (shape(in[1])[height] - 1) * dilation[0] + 1
// height_out = floor((pad_amount[0,0] + shape(in[0])[height] + pad_amount[0,1] - dilated_filter_height) / stride[0] + 1)
//
// Set total_height_padding equal to pad_amount[0,0] + pad_amount[0,1], and solve for it.
uint32_t dilated_filter_height = (input_1_shape[2] - 1) * dilations_0 + 1;
total_padding[0] = (output_shape[1] - 1) * stride_values[0] + dilated_filter_height - input_0_shape[1]; // Total height padding
// dilated_filter_width = (shape(in[1])[width] - 1) * dilation[1] + 1
// width_out = floor((pad_amount[1,0] + shape(in[0])[width] + pad_amount[1,1] - dilated_filter_width) / stride[1] + 1)
//
// Set total_width_padding equal to pad_amount[1,0] + pad_amount[1,1], and solve for it.
uint32_t dilated_filter_width = (input_1_shape[3] - 1) * dilations_1 + 1;
total_padding[1] = (output_shape[2] - 1) * stride_values[1] + dilated_filter_width - input_0_shape[2]; // Total width padding
}
if (auto_pad.compare("SAME_UPPER")) {
if (auto_pad.compare("SAME_UPPER") == 0) {
pad_values[0] = total_padding[0] / 2;
pad_values[1] = total_padding[1] / 2;
pad_values[2] = total_padding[0] - pad_values[0];
pad_values[3] = total_padding[1] - pad_values[1];
} else if (auto_pad.compare("SAME_LOWER")) {
} else if (auto_pad.compare("SAME_LOWER") == 0) {
pad_values[2] = total_padding[0] / 2;
pad_values[3] = total_padding[1] / 2;
pad_values[0] = total_padding[0] - pad_values[2];

View file

@ -196,11 +196,11 @@ GetTestModelFn BuildQDQConvTestCase(const std::vector<int64_t>& input_shape,
Node& convNode = builder.AddNode("Conv", {dq_output, dq_w_output, dq_bias_output}, {conv_output});
auto* q_output = builder.MakeIntermediate();
builder.AddQuantizeLinearNode<OutputType>(conv_output, .039f,
builder.AddQuantizeLinearNode<OutputType>(conv_output, 1e-4f,
(OutputLimits::min() + OutputLimits::max()) / 2 + 1,
q_output);
builder.AddDequantizeLinearNode<OutputType>(q_output, .039f,
builder.AddDequantizeLinearNode<OutputType>(q_output, 1e-4f,
(OutputLimits::min() + OutputLimits::max()) / 2 + 1,
output_arg);
convNode.AddAttribute("auto_pad", auto_pad);
@ -228,7 +228,8 @@ static void RunHTPConvOpTest(const std::vector<int64_t>& input_shape,
const std::vector<int64_t>& dilations,
const std::string& auto_pad,
ExpectedEPNodeAssignment expected_ep_assignment, const char* test_description,
int opset = 13) {
int opset = 13,
float fp32_abs_err = 1e-5f) {
ProviderOptions provider_options;
#if defined(_WIN32)
@ -245,7 +246,8 @@ static void RunHTPConvOpTest(const std::vector<int64_t>& input_shape,
opset,
expected_ep_assignment,
expected_nodes_in_partition,
test_description);
test_description,
fp32_abs_err);
}
// Check that QNN compiles DQ -> Conv -> Q as a single unit.
@ -263,6 +265,32 @@ TEST_F(QnnCPUBackendTests, TestCPUConvf32_bias_initializer) {
RunCPUConvOpTest({1, 1, 3, 3}, {2, 1, 2, 2}, true, {1, 1}, {0, 0, 0, 0}, {1, 1}, "NOTSET", ExpectedEPNodeAssignment::All, "TestCPUConvf32_bias_initializer");
}
// Tests auto_pad value "SAME_UPPER" (compares to CPU EP).
TEST_F(QnnCPUBackendTests, TestCPUConvf32_AutoPadUpper) {
RunCPUConvOpTest({1, 1, 3, 3}, // Input 0 shape
{2, 1, 2, 2}, // Input 1 (weights) shape
true, // is_bias_initializer
{1, 1}, // strides
{}, // pads
{1, 1}, // dilations
"SAME_UPPER", // auto_pad
ExpectedEPNodeAssignment::All,
"TestCPUConvf32_AutoPadUpper");
}
// Tests auto_pad value "SAME_LOWER" (compares to CPU EP).
TEST_F(QnnCPUBackendTests, TestCPUConvf32_AutoPadLower) {
RunCPUConvOpTest({1, 1, 3, 3}, // Input 0 shape
{2, 1, 2, 2}, // Input 1 (weights) shape
true, // is_bias_initializer
{1, 1}, // strides
{}, // pads
{1, 1}, // dilations
"SAME_LOWER", // auto_pad
ExpectedEPNodeAssignment::All,
"TestCPUConvf32_AutoPadLower");
}
// large input,output, pads
// TODO: re-enable tests once Padding issues are resolved
TEST_F(QnnCPUBackendTests, DISABLED_TestCPUConvf32_large_input1_pad_bias_initializer) {
@ -307,6 +335,38 @@ TEST_F(QnnHTPBackendTests, TestQDQConvU8U8S32_bias_initializer) {
"TestQDQConvU8U8S32_bias_initializer");
}
// Tests auto_pad value "SAME_UPPER" on HTP backend (compares to CPU EP).
TEST_F(QnnHTPBackendTests, TestConvU8U8S32_AutoPadUpper) {
RunHTPConvOpTest<uint8_t, uint8_t, int32_t, uint8_t>(
{1, 1, 5, 5}, // input_shape
{1, 1, 4, 4}, // weights_shape
true, // is_bias_initializer
{1, 1}, // strides
{}, // pads
{1, 1}, // dilations
"SAME_UPPER", // auto_pad
ExpectedEPNodeAssignment::All,
"TestConvU8U8S32_AutoPadUpper",
13,
1e-4f);
}
// Tests auto_pad value "SAME_LOWER" on HTP backend (compares to CPU EP).
TEST_F(QnnHTPBackendTests, TestConvU8U8S32_AutoPadLower) {
RunHTPConvOpTest<uint8_t, uint8_t, int32_t, uint8_t>(
{1, 1, 5, 5}, // input_shape
{1, 1, 4, 4}, // weights_shape
true, // is_bias_initializer
{1, 1}, // strides
{}, // pads
{1, 1}, // dilations
"SAME_LOWER", // auto_pad
ExpectedEPNodeAssignment::All,
"TestConvU8U8S32_AutoPadLower",
13,
1e-4f);
}
// TODO: re-enable tests once HTP issues are resolved
TEST_F(QnnHTPBackendTests, DISABLED_TestQDQConvU8U8S32_large_input1_padding_bias_initializer) {
RunHTPConvOpTest<uint8_t, uint8_t, int32_t, uint8_t>({1, 3, 60, 452}, {16, 3, 3, 3}, true, {1, 1}, {1, 1, 1, 1}, {1, 1}, "NOTSET", ExpectedEPNodeAssignment::All,