diff --git a/onnxruntime/core/providers/qnn/builder/opbuilder/conv_op_builder.cc b/onnxruntime/core/providers/qnn/builder/opbuilder/conv_op_builder.cc index f7c1ae9a8d..c964996a22 100644 --- a/onnxruntime/core/providers/qnn/builder/opbuilder/conv_op_builder.cc +++ b/onnxruntime/core/providers/qnn/builder/opbuilder/conv_op_builder.cc @@ -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]; diff --git a/onnxruntime/test/providers/qnn/conv_test.cc b/onnxruntime/test/providers/qnn/conv_test.cc index 7c3e715539..133d8186e5 100644 --- a/onnxruntime/test/providers/qnn/conv_test.cc +++ b/onnxruntime/test/providers/qnn/conv_test.cc @@ -196,11 +196,11 @@ GetTestModelFn BuildQDQConvTestCase(const std::vector& input_shape, Node& convNode = builder.AddNode("Conv", {dq_output, dq_w_output, dq_bias_output}, {conv_output}); auto* q_output = builder.MakeIntermediate(); - builder.AddQuantizeLinearNode(conv_output, .039f, + builder.AddQuantizeLinearNode(conv_output, 1e-4f, (OutputLimits::min() + OutputLimits::max()) / 2 + 1, q_output); - builder.AddDequantizeLinearNode(q_output, .039f, + builder.AddDequantizeLinearNode(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& input_shape, const std::vector& 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& 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( + {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( + {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({1, 3, 60, 452}, {16, 3, 3, 3}, true, {1, 1}, {1, 1, 1, 1}, {1, 1}, "NOTSET", ExpectedEPNodeAssignment::All,