mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-10 17:37:14 +00:00
[QNN EP] Fix an issue for Conv with dynamic weights (#16235)
### Description Fix an issue for Conv with dynamic weights Root cause: Conv op builder create the weight input tensor with wrong name. With dynamic weight, Transpose node is inserted. Conv op builder should use the new name which is Transpose output. It cause the weight producer has wrong output shape.
This commit is contained in:
parent
ac8444f299
commit
d1e8d4a261
6 changed files with 84 additions and 10 deletions
|
|
@ -53,7 +53,6 @@ class ConvOpBuilder : public BaseOpBuilder {
|
|||
// The nodes from 1st call of GetCapability do not get layout transformer applied, it's still NCHW
|
||||
// The nodes from 2nd call of GetCapability get layout transformer applied, it's NHWC
|
||||
// Need to do op validation in 1st call of GetCapability
|
||||
// TODO: Check if node domain == kMSInternalNHWCDomain to determine if the layout has been transformed.
|
||||
Status ConvOpBuilder::IsOpSupported(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
const logging::Logger& logger,
|
||||
|
|
@ -128,7 +127,6 @@ Status ConvOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
|||
bool is_quantized_model,
|
||||
std::vector<std::string>& input_names,
|
||||
bool do_op_validation) const {
|
||||
ORT_UNUSED_PARAMETER(do_op_validation);
|
||||
Qnn_QuantizeParams_t quantize_param = QNN_QUANTIZE_PARAMS_INIT;
|
||||
InitializeQuantizeParam(quantize_param, is_quantized_model);
|
||||
Qnn_DataType_t qnn_data_type = QNN_DATATYPE_FLOAT_32;
|
||||
|
|
@ -192,6 +190,7 @@ Status ConvOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
|||
new_input_shape,
|
||||
qnn_data_type,
|
||||
quantize_param,
|
||||
do_op_validation,
|
||||
is_graph_input));
|
||||
} else if (node_unit.OpType() == "ConvTranspose") {
|
||||
ORT_RETURN_IF_ERROR(qnn_model_wrapper.AddCnhwToHwcnTranspose(node_unit.Index(),
|
||||
|
|
@ -201,6 +200,7 @@ Status ConvOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
|||
new_input_shape,
|
||||
qnn_data_type,
|
||||
quantize_param,
|
||||
do_op_validation,
|
||||
is_graph_input));
|
||||
} else {
|
||||
ORT_THROW("Unexpected operator %s", node_unit.OpType());
|
||||
|
|
@ -211,14 +211,14 @@ Status ConvOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
|||
}
|
||||
input_names.push_back(input_tensor_name);
|
||||
|
||||
if (qnn_model_wrapper.IsQnnTensorWrapperExist(input_name)) {
|
||||
LOGS(logger, VERBOSE) << "Tensor already added, skip it: " << input_name;
|
||||
if (qnn_model_wrapper.IsQnnTensorWrapperExist(input_tensor_name)) {
|
||||
LOGS(logger, VERBOSE) << "Tensor already added, skip it: " << input_tensor_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
Qnn_TensorType_t tensor_type = GetInputTensorType(qnn_model_wrapper, input_name);
|
||||
Qnn_TensorType_t tensor_type = GetInputTensorType(qnn_model_wrapper, input_tensor_name);
|
||||
|
||||
QnnTensorWrapper input_tensorwrapper(input_name, tensor_type, qnn_data_type, quantize_param,
|
||||
QnnTensorWrapper input_tensorwrapper(input_tensor_name, tensor_type, qnn_data_type, quantize_param,
|
||||
std::move(input_shape), std::move(unpacked_tensor));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(input_tensorwrapper)), "Failed to add tensor.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ Status GemmOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
|||
std::vector<uint32_t> perm{1, 0};
|
||||
ORT_RETURN_IF_ERROR(qnn_model_wrapper.AddTransposeNode(node_unit.Index(), node_input_name, input_tensor_name,
|
||||
old_input_shape, perm, input_shape,
|
||||
qnn_data_type, quantize_param));
|
||||
qnn_data_type, quantize_param, do_op_validation));
|
||||
}
|
||||
|
||||
if (2 == input_i && 2 == input_shape.size()) {
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ void QnnBackendManager::InitializeQnnLog() {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
LOGS(*logger_, VERBOSE) << "Set Qnn log level: " << qnn_log_level;
|
||||
|
||||
if (QNN_SUCCESS != qnn_interface_.logCreate(QnnLogging, qnn_log_level, &log_handle_)) {
|
||||
LOGS(*logger_, WARNING) << "Unable to initialize logging in the QNN backend.";
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ bool QnnModelWrapper::AddTensorWrapper(QnnTensorWrapper&& tensor_wrapper) {
|
|||
}
|
||||
|
||||
if (IsQnnTensorWrapperExist(tensor_name) == true) {
|
||||
LOGS(logger_, VERBOSE) << "Tensor eist already: " << tensor_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +363,7 @@ Status QnnModelWrapper::AddTransposeNode(NodeIndex node_index,
|
|||
const std::vector<uint32_t>& output_shape,
|
||||
const Qnn_DataType_t& tensor_data_type,
|
||||
const Qnn_QuantizeParams_t& quantize_param,
|
||||
bool do_op_validation,
|
||||
const bool is_for_input,
|
||||
const bool is_for_output) {
|
||||
// No need to add this for output nodes as it is added as output tensor for previous node
|
||||
|
|
@ -397,7 +399,8 @@ Status QnnModelWrapper::AddTransposeNode(NodeIndex node_index,
|
|||
qnn_node_type,
|
||||
{input_name},
|
||||
{output_name},
|
||||
{param_tensor_name});
|
||||
{param_tensor_name},
|
||||
do_op_validation);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ class QnnModelWrapper {
|
|||
const std::vector<uint32_t>& output_shape,
|
||||
const Qnn_DataType_t& tensor_data_type,
|
||||
const Qnn_QuantizeParams_t& quantize_param,
|
||||
bool do_op_validation,
|
||||
const bool is_for_input = true,
|
||||
const bool is_for_output = false);
|
||||
|
||||
|
|
@ -124,12 +125,13 @@ class QnnModelWrapper {
|
|||
const std::vector<uint32_t>& output_shape,
|
||||
const Qnn_DataType_t& tensor_data_type,
|
||||
const Qnn_QuantizeParams_t& quantize_param,
|
||||
bool do_op_validation,
|
||||
const bool is_for_input = true,
|
||||
const bool is_for_output = false) {
|
||||
LOGS(logger_, VERBOSE) << "Add NCHW->HWCN Transpose node after Conv weight input: " << input_name
|
||||
<< " -> " << output_name;
|
||||
return AddTransposeNode(node_index, input_name, output_name, input_shape, nchw2hwcn_perm_, output_shape,
|
||||
tensor_data_type, quantize_param, is_for_input, is_for_output);
|
||||
tensor_data_type, quantize_param, do_op_validation, is_for_input, is_for_output);
|
||||
}
|
||||
|
||||
// Tranpose CNHW->HWCN for QNN weight
|
||||
|
|
@ -140,12 +142,13 @@ class QnnModelWrapper {
|
|||
const std::vector<uint32_t>& output_shape,
|
||||
const Qnn_DataType_t& tensor_data_type,
|
||||
const Qnn_QuantizeParams_t& quantize_param,
|
||||
bool do_op_validation,
|
||||
const bool is_for_input = true,
|
||||
const bool is_for_output = false) {
|
||||
LOGS(logger_, VERBOSE) << "Add CNHW->HWCN Transpose node after ConvTranspose weight input: " << input_name
|
||||
<< " -> " << output_name;
|
||||
return AddTransposeNode(node_index, input_name, output_name, input_shape, cnhw2hwcn_perm_, output_shape,
|
||||
tensor_data_type, quantize_param, is_for_input, is_for_output);
|
||||
tensor_data_type, quantize_param, do_op_validation, is_for_input, is_for_output);
|
||||
}
|
||||
|
||||
Status UnpackInitializerData(const ONNX_NAMESPACE::TensorProto& initializer,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,73 @@
|
|||
namespace onnxruntime {
|
||||
namespace test {
|
||||
|
||||
// The bug is from a QDQ model, and Conv node gets processed before it's producer Mul node
|
||||
// A Transpose node gets inserted between Mul and the dynamic weight tensor shape on Conv
|
||||
// to make Conv weight with shape HWNC
|
||||
// However it changes Mul output shape to HWNC and cause issue
|
||||
// It has to be QDQ model, because the DQ node with initializer on Conv gets processed first
|
||||
// and DQ node requires its node unit to be processed
|
||||
// So, Conv gets processed before Mul node
|
||||
TEST_F(QnnCPUBackendTests, Test_QDQConvWithDynamicWeightsFromMul) {
|
||||
ProviderOptions provider_options;
|
||||
|
||||
#if defined(_WIN32)
|
||||
provider_options["backend_path"] = "QnnHtp.dll";
|
||||
#else
|
||||
provider_options["backend_path"] = "libQnnHtp.so";
|
||||
#endif
|
||||
|
||||
auto BuildConvMulGraph = [](ModelTestBuilder& builder) {
|
||||
// DQ node for Conv input
|
||||
auto* dq_i_output = builder.MakeIntermediate();
|
||||
auto* conv_dq_input = builder.MakeInitializer<uint8_t>({1, 32, 16, 113}, static_cast<uint8_t>(0), static_cast<uint8_t>(127));
|
||||
|
||||
// DQ node for Conv bias
|
||||
auto* dq_bias_output = builder.MakeIntermediate();
|
||||
auto* bias = builder.MakeInitializer<int32_t>({16}, static_cast<int32_t>(0), static_cast<int32_t>(127));
|
||||
|
||||
// Mul node
|
||||
// DQ nodes for Mul
|
||||
auto* mul_dq1_output = builder.MakeIntermediate();
|
||||
auto* mul_input1 = builder.MakeInput<uint8_t>({16, 32, 1, 1}, static_cast<uint8_t>(0), static_cast<uint8_t>(127));
|
||||
|
||||
auto* mul_dq2_output = builder.MakeIntermediate();
|
||||
auto* mul_input2 = builder.MakeInitializer<uint8_t>({16, 1, 1, 1}, static_cast<uint8_t>(0), static_cast<uint8_t>(127));
|
||||
builder.AddDequantizeLinearNode<uint8_t>(mul_input1, .03f, 0, mul_dq1_output);
|
||||
builder.AddDequantizeLinearNode<uint8_t>(mul_input2, .03f, 0, mul_dq2_output);
|
||||
|
||||
auto* mul_output = builder.MakeIntermediate();
|
||||
builder.AddNode("Mul", {mul_dq1_output, mul_dq2_output}, {mul_output});
|
||||
|
||||
auto* mul_dq_output = AddQDQNodePair<uint8_t>(builder, mul_output, .03f, 0);
|
||||
|
||||
builder.AddDequantizeLinearNode<uint8_t>(conv_dq_input, .04f, 0, dq_i_output);
|
||||
builder.AddDequantizeLinearNode<int32_t>(bias, .0012f, 0, dq_bias_output);
|
||||
// Conv node
|
||||
auto* conv_output = builder.MakeIntermediate();
|
||||
|
||||
Node& conv_node = builder.AddNode("Conv", {dq_i_output, mul_dq_output, dq_bias_output}, {conv_output});
|
||||
conv_node.AddAttribute("auto_pad", "NOTSET");
|
||||
conv_node.AddAttribute("pads", std::vector<int64_t>{0, 0, 0, 0});
|
||||
conv_node.AddAttribute("strides", std::vector<int64_t>{1, 1});
|
||||
conv_node.AddAttribute("dilations", std::vector<int64_t>{1, 1});
|
||||
|
||||
auto* q_output = builder.MakeIntermediate();
|
||||
builder.AddQuantizeLinearNode<uint8_t>(conv_output, .039f, 0, q_output);
|
||||
|
||||
auto* dq_output = builder.MakeOutput();
|
||||
builder.AddDequantizeLinearNode<uint8_t>(q_output, .039f, 0, dq_output);
|
||||
};
|
||||
|
||||
constexpr int expected_nodes_in_partition = 1;
|
||||
RunQnnModelTest(BuildConvMulGraph,
|
||||
provider_options,
|
||||
13,
|
||||
ExpectedEPNodeAssignment::All,
|
||||
expected_nodes_in_partition,
|
||||
"Test_ConvWithDynamicWeightsFromMul");
|
||||
}
|
||||
|
||||
// Creates a graph with a single Conv operator. Used for testing CPU backend.
|
||||
static GetTestModelFn BuildConvTestCase(const std::vector<int64_t>& input_shape,
|
||||
const std::vector<int64_t>& weights_shape,
|
||||
|
|
|
|||
Loading…
Reference in a new issue