Skip to content

Commit 684beee

Browse files
author
ankus
committed
Merge branch 'dev/ankus/pad_ConvTranspose' of github.com:CodeLinaro/onnxruntime into dev/ankus/pad_ConvTranspose
2 parents 7463ec5 + d81834d commit 684beee

File tree

2 files changed

+118
-10
lines changed

2 files changed

+118
-10
lines changed

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -672,13 +672,52 @@ Status ConvOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wra
672672
ORT_RETURN_IF(auto_pad != "NOTSET" && auto_pad != "SAME_LOWER" && auto_pad != "SAME_UPPER" && auto_pad != "VALID",
673673
"QNN Conv operators do not support 'auto_pad' value: ", auto_pad.c_str());
674674

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

onnxruntime/test/providers/qnn/conv_test.cc

Lines changed: 77 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,65 @@ 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+
TEST_F(QnnHTPBackendTests, ConvTransposeU8U8S32_OutputShape) {
2187+
std::vector<int64_t> output_shape = {6, 6};
2188+
RunHTPConvOpTest<uint8_t, uint8_t>("ConvTranspose",
2189+
TestInputDef<float>({1, 1, 4, 4}, false, 0.f, 10.f), // Dynamic input
2190+
TestInputDef<float>({1, 1, 2, 2}, true, -1.f, 1.f), // Static weights
2191+
TestInputDef<float>({1}, true, {1.0f}), // Initializer bias
2192+
{2, 2}, // strides
2193+
{0, 0, 0, 0}, // pads
2194+
{1, 1}, // dilations
2195+
1, // group
2196+
"SAME_UPPER", // auto_pad
2197+
ExpectedEPNodeAssignment::All,
2198+
false, // use_contrib_qdq
2199+
13, // opset
2200+
QDQTolerance(),
2201+
std::nullopt, // No output activation
2202+
output_shape); // Pass the output_shape attribute
2203+
2204+
std::vector<int64_t> output_shape_3d = {6, 6, 6};
2205+
RunHTPConvOpTest<uint8_t, uint8_t>("ConvTranspose",
2206+
TestInputDef<float>({1, 1, 4, 4, 4}, false, 0.f, 10.f), // Dynamic input
2207+
TestInputDef<float>({1, 1, 2, 2, 2}, true, -1.f, 1.f), // Static weights
2208+
TestInputDef<float>({1}, true, {1.0f}), // Initializer bias
2209+
{2, 2, 2}, // strides
2210+
{0, 0, 0, 0, 0, 0}, // pads
2211+
{1, 1, 1}, // dilations
2212+
1, // group
2213+
"SAME_UPPER", // auto_pad
2214+
ExpectedEPNodeAssignment::All,
2215+
false, // use_contrib_qdq
2216+
13, // opset
2217+
QDQTolerance(),
2218+
std::nullopt, // No output activation
2219+
output_shape_3d); // Pass the output_shape attribute
2220+
}
2221+
2222+
TEST_F(QnnHTPBackendTests, ConvTranspose1DU8U8S32_OutputShape) {
2223+
std::vector<int64_t> output_shape = {6};
2224+
RunHTPConvOpTest<uint8_t, uint8_t>("ConvTranspose",
2225+
TestInputDef<float>({1, 1, 4}, false, 0.f, 10.f), // Dynamic input
2226+
TestInputDef<float>({1, 1, 2}, true, -1.f, 1.f), // Static weights
2227+
TestInputDef<float>({1}, true, {1.0f}), // Initializer bias
2228+
{2}, // strides
2229+
{0, 0}, // pads
2230+
{1}, // dilations
2231+
1, // group
2232+
"SAME_UPPER", // auto_pad
2233+
ExpectedEPNodeAssignment::All,
2234+
false, // use_contrib_qdq
2235+
13, // opset
2236+
QDQTolerance(),
2237+
std::nullopt, // No output activation
2238+
output_shape); // Pass the output_shape attribute
2239+
}
2240+
21722241
// Tests Conv1d auto_pad value "VALID" on HTP backend (compares to CPU EP).
21732242
TEST_F(QnnHTPBackendTests, Conv1DU8U8S32_AutoPadValid) {
21742243
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)