Skip to content

Commit d81834d

Browse files
author
ankus-qti
committed
[QNN-EP]ConvTranspose not calculating "pad" if "output_shape" is given.
Signed-off-by: ankus <[email protected]>
1 parent 17b2725 commit d81834d

File tree

2 files changed

+108
-9
lines changed

2 files changed

+108
-9
lines changed

onnxruntime/core/providers/qnn/builder/opbuilder/conv_op_builder.cc

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,51 @@ Status ConvOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wra
668668
ORT_RETURN_IF(auto_pad != "NOTSET" && auto_pad != "SAME_LOWER" && auto_pad != "SAME_UPPER" && auto_pad != "VALID",
669669
"QNN Conv operators do not support 'auto_pad' value: ", auto_pad.c_str());
670670

671-
if (auto_pad != "NOTSET") {
671+
std::vector<int64_t> output_shape_attribute_value = node_helper.Get("output_shape", std::vector<int64_t>());
672+
bool has_output_shape_attr = !output_shape_attribute_value.empty();
673+
674+
if (conv_type == OnnxConvType::kConvTranspose && has_output_shape_attr) {
675+
// Pads are auto generated using the formula:
676+
// total_padding[i] = stride[i] * (input_size[i] - 1) + output_padding[i] + ((kernel_shape[i] - 1) * dilations[i] + 1) - output_shape[i]
677+
// Then distributed using auto_pad rules.
678+
679+
LOGS(logger, VERBOSE) << "ConvTranspose with 'output_shape' attribute. Calculating pads since output_shape is specified, pad values are ignored";
680+
681+
// input_dims for calculation are (H, W, D...) excluding N, C
682+
std::vector<uint32_t> input_dims(input_0_shape.begin() + 1, input_0_shape.end() - 1);
683+
// output_dims for calculation are from the 'output_shape' attribute
684+
std::vector<uint32_t> output_dims_from_attr;
685+
output_dims_from_attr.reserve(output_shape_attribute_value.size()); // Use the new name
686+
for (int64_t dim : output_shape_attribute_value) { // Use the new name
687+
output_dims_from_attr.push_back(narrow<uint32_t>(dim));
688+
}
689+
690+
if (is_1d_conv) { // Adjust input_dims and output_dims_from_attr for 1D conv logic
691+
input_dims.insert(input_dims.begin(), 1);
692+
output_dims_from_attr.insert(output_dims_from_attr.begin(), 1);
693+
}
694+
695+
pads.assign(kernel_shape.size() * 2, 0); // Reset pads before filling
696+
size_t rank = input_dims.size();
697+
698+
ORT_RETURN_IF_NOT(rank == output_dims_from_attr.size(),
699+
"QNN EP: ConvTranspose 'output_shape' attribute rank mismatch "
700+
"with input dims for padding calculation.");
701+
702+
for (size_t dim = 0; dim < rank; ++dim) {
703+
int64_t pad_head = 0;
704+
int64_t pad_tail = 0;
705+
AutoPadType pad_type = StringToAutoPadType(auto_pad); // Use current auto_pad for distribution
706+
707+
auto total_pad = ComputeTotalPad(input_dims[dim], strides[dim], output_padding[dim],
708+
kernel_shape[dim], dilations[dim], output_dims_from_attr[dim]);
709+
DistributePadding(pad_type, total_pad, pad_head, pad_tail);
710+
711+
pads[dim] = narrow<uint32_t>(pad_head);
712+
pads[rank + dim] = narrow<uint32_t>(pad_tail);
713+
}
714+
715+
} else if (auto_pad != "NOTSET") { // Case: auto_pad is SAME_UPPER/LOWER/VALID, no output_shape attribute
672716
auto pad_type = StringToAutoPadType(auto_pad);
673717
// skip N, C, input0 shape NHWC
674718
std::vector<uint32_t> input_dims(input_0_shape.begin() + 1, input_0_shape.end() - 1);

onnxruntime/test/providers/qnn/conv_test.cc

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ static GetTestModelFn BuildF32ConvTestCase(const std::string& conv_op_type, cons
3030
const std::vector<int64_t>& dilations,
3131
std::optional<int64_t> group,
3232
const std::string& auto_pad = "NOTSET",
33-
std::optional<OutputActivationInfo> output_activation = std::nullopt) {
33+
std::optional<OutputActivationInfo> output_activation = std::nullopt,
34+
std::optional<std::vector<int64_t>> output_shape = std::nullopt) {
3435
return [conv_op_type, input_def, weights_def, bias_def, strides, pads,
35-
dilations, group, auto_pad, output_activation](ModelTestBuilder& builder) {
36+
dilations, group, auto_pad, output_activation, output_shape](ModelTestBuilder& builder) {
3637
std::vector<NodeArg*> conv_inputs = {
3738
MakeTestInput(builder, input_def),
3839
MakeTestInput(builder, weights_def)};
@@ -62,6 +63,10 @@ static GetTestModelFn BuildF32ConvTestCase(const std::string& conv_op_type, cons
6263
conv_node.AddAttribute("dilations", dilations);
6364
}
6465

66+
if (output_shape.has_value()) {
67+
conv_node.AddAttribute("output_shape", output_shape.value());
68+
}
69+
6570
if (output_activation.has_value()) {
6671
NodeArg* output = builder.MakeOutput();
6772
std::vector<NodeArg*> activation_inputs = {conv_output};
@@ -113,11 +118,12 @@ static GetTestQDQModelFn<ActivationQType> BuildQDQConvTestCase(
113118
std::optional<int64_t> group,
114119
const std::string& auto_pad = "NOTSET",
115120
bool use_contrib_qdq = false,
116-
std::optional<OutputActivationInfo> output_activation = std::nullopt) {
121+
std::optional<OutputActivationInfo> output_activation = std::nullopt,
122+
std::optional<std::vector<int64_t>> output_shape = std::nullopt) {
117123
return [conv_op_type, input_def, weights_def, bias_def, strides, pads,
118124
dilations, group, auto_pad,
119-
use_contrib_qdq, output_activation](ModelTestBuilder& builder,
120-
std::vector<QuantParams<ActivationQType>>& output_qparams) {
125+
use_contrib_qdq, output_activation, output_shape](ModelTestBuilder& builder,
126+
std::vector<QuantParams<ActivationQType>>& output_qparams) {
121127
std::vector<NodeArg*> conv_inputs;
122128

123129
// input -> Q/DQ ->
@@ -160,6 +166,9 @@ static GetTestQDQModelFn<ActivationQType> BuildQDQConvTestCase(
160166
if (!dilations.empty()) {
161167
conv_node.AddAttribute("dilations", dilations);
162168
}
169+
if (output_shape.has_value()) {
170+
conv_node.AddAttribute("output_shape", output_shape.value());
171+
}
163172

164173
NodeArg* q_input = conv_output;
165174
if (output_activation.has_value()) {
@@ -307,17 +316,18 @@ static void RunHTPConvOpTest(const std::string& conv_op_type, const TestInputDef
307316
bool use_contrib_qdq = false,
308317
int opset = 13,
309318
QDQTolerance tolerance = QDQTolerance(),
310-
std::optional<OutputActivationInfo> output_activation = std::nullopt) {
319+
std::optional<OutputActivationInfo> output_activation = std::nullopt,
320+
std::optional<std::vector<int64_t>> output_shape = std::nullopt) {
311321
ProviderOptions provider_options;
312322
provider_options["backend_type"] = "htp";
313323
provider_options["offload_graph_io_quantization"] = "0";
314324

315325
TestQDQModelAccuracy(BuildF32ConvTestCase(conv_op_type, input_def, weights_def, bias_def, strides, pads, dilations,
316-
group, auto_pad, output_activation),
326+
group, auto_pad, output_activation, output_shape),
317327
BuildQDQConvTestCase<ActivationQType, WeightQType>(conv_op_type, input_def, weights_def,
318328
bias_def, strides, pads, dilations,
319329
group, auto_pad, use_contrib_qdq,
320-
output_activation),
330+
output_activation, output_shape),
321331
provider_options,
322332
opset,
323333
expected_ep_assignment,
@@ -2169,6 +2179,51 @@ TEST_F(QnnHTPBackendTests, ConvTransposeU8U8S32_AutoPadValid) {
21692179
13);
21702180
}
21712181

2182+
// Test ConvTranspose with output_shape attribute
2183+
// This test verifies that when 'output_shape' is provided, the QNN EP correctly
2184+
// calculates and applies padding for ConvTranspose, overriding any 'pads' attribute,
2185+
// and correctly distributes the padding according to 'auto_pad' rules.
2186+
// CPU does not use "output_shape" in the node config and caluculates it with pad=0
2187+
// qnn_test_utils.h(652): error: Expected equality of these values: num_vals Which is: 36 cpu_qdq_vals.size() Which is: 64
2188+
TEST_F(QnnHTPBackendTests, DISABLED_ConvTransposeU8U8S32_OutputShape) {
2189+
// Explicit output shape: [N, C_out, H_out, W_out] = [1, 1, 6, 6]
2190+
// This implies a total padding of 1 per spatial dim (H/W), distributed as (0, 1) for SAME_UPPER
2191+
std::vector<int64_t> output_shape = {1, 1, 6, 6};
2192+
RunHTPConvOpTest<uint8_t, uint8_t>("ConvTranspose",
2193+
TestInputDef<float>({1, 1, 4, 4}, false, 0.f, 10.f), // Dynamic input
2194+
TestInputDef<float>({1, 1, 2, 2}, true, -1.f, 1.f), // Static weights
2195+
TestInputDef<float>({1}, true, {1.0f}), // Initializer bias
2196+
{2, 2}, // strides
2197+
{0, 0, 0, 0}, // pads
2198+
{1, 1}, // dilations
2199+
1, // group
2200+
"SAME_UPPER", // auto_pad
2201+
ExpectedEPNodeAssignment::All,
2202+
false, // use_contrib_qdq
2203+
13, // opset
2204+
QDQTolerance(),
2205+
std::nullopt, // No output activation
2206+
output_shape); // Pass the output_shape attribute
2207+
2208+
// Explicit output shape: [N, C_out, H_out, W_out] = [1, 1, 6, 6, 6]
2209+
std::vector<int64_t> output_shape_3d = {1, 1, 6, 6, 6};
2210+
RunHTPConvOpTest<uint8_t, uint8_t>("ConvTranspose",
2211+
TestInputDef<float>({1, 1, 4, 4, 4}, false, 0.f, 10.f), // Dynamic input
2212+
TestInputDef<float>({1, 1, 2, 2, 2}, true, -1.f, 1.f), // Static weights
2213+
TestInputDef<float>({1}, true, {1.0f}), // Initializer bias
2214+
{2, 2, 2}, // strides
2215+
{0, 0, 0, 0, 0, 0}, // pads
2216+
{1, 1, 1}, // dilations
2217+
1, // group
2218+
"SAME_UPPER", // auto_pad
2219+
ExpectedEPNodeAssignment::All,
2220+
false, // use_contrib_qdq
2221+
13, // opset
2222+
QDQTolerance(),
2223+
std::nullopt, // No output activation
2224+
output_shape_3d); // Pass the output_shape attribute
2225+
}
2226+
21722227
// Tests Conv1d auto_pad value "VALID" on HTP backend (compares to CPU EP).
21732228
TEST_F(QnnHTPBackendTests, Conv1DU8U8S32_AutoPadValid) {
21742229
std::vector<float> input_data = {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f};

0 commit comments

Comments
 (0)