mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-09 17:28:58 +00:00
Enable Hardsigmoid for QNN EP using SDK support direct support (#20956)
### Description Enable Hardsigmoid for QNN EP using SDK support direct support instead of decomposing to its constituent ops so it can support the quantized model
This commit is contained in:
parent
855c1cffc9
commit
3c6d409937
5 changed files with 225 additions and 262 deletions
|
|
@ -58,6 +58,7 @@ OpBuilderRegistrations::OpBuilderRegistrations() {
|
|||
CreateSimpleOpBuilder("DequantizeLinear", *this);
|
||||
|
||||
CreateSimpleOpBuilder("HardSwish", *this);
|
||||
CreateSimpleOpBuilder("HardSigmoid", *this);
|
||||
|
||||
CreateSimpleOpBuilder("DepthToSpace", *this);
|
||||
CreateSimpleOpBuilder("SpaceToDepth", *this);
|
||||
|
|
@ -167,10 +168,6 @@ OpBuilderRegistrations::OpBuilderRegistrations() {
|
|||
{
|
||||
CreateExpandOpBuilder("Expand", *this);
|
||||
}
|
||||
|
||||
{
|
||||
CreateHardSigmoidOpBuilder("HardSigmoid", *this);
|
||||
}
|
||||
}
|
||||
|
||||
const IOpBuilder* GetOpBuilder(const std::string& onnx_op_type) {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ class BaseOpBuilder : public IOpBuilder {
|
|||
{"Relu", QNN_OP_RELU},
|
||||
{"Gelu", QNN_OP_GELU},
|
||||
|
||||
{"HardSigmoid", QNN_OP_ELEMENT_WISE_NEURON},
|
||||
{"HardSwish", QNN_OP_HARD_SWISH},
|
||||
{"DepthToSpace", QNN_OP_DEPTH_TO_SPACE},
|
||||
{"SpaceToDepth", QNN_OP_SPACE_TO_DEPTH},
|
||||
|
|
|
|||
|
|
@ -1,239 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "core/framework/float16.h"
|
||||
#include "core/providers/qnn/builder/opbuilder/base_op_builder.h"
|
||||
#include "core/providers/qnn/builder/qnn_utils.h"
|
||||
#include "core/providers/shared/utils/utils.h"
|
||||
#include "core/providers/qnn/builder/qnn_model_wrapper.h"
|
||||
#include "core/providers/qnn/builder/op_builder_factory.h"
|
||||
|
||||
#include "QnnOpDef.h"
|
||||
#include "QnnTypes.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace qnn {
|
||||
|
||||
class HardSigmoidOpBuilder : public BaseOpBuilder {
|
||||
public:
|
||||
HardSigmoidOpBuilder() : BaseOpBuilder("HardSigmoidOpBuilder") {}
|
||||
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(HardSigmoidOpBuilder);
|
||||
|
||||
Status IsOpSupported(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
const logging::Logger& logger) const override ORT_MUST_USE_RESULT;
|
||||
|
||||
protected:
|
||||
Status ProcessInputs(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit,
|
||||
const logging::Logger& logger,
|
||||
std::vector<std::string>& input_names,
|
||||
bool do_op_validation = false) const override ORT_MUST_USE_RESULT;
|
||||
|
||||
Status ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const override ORT_MUST_USE_RESULT;
|
||||
|
||||
private:
|
||||
static const OnnxAttrInfo<float> onnx_alpha_attr;
|
||||
static const OnnxAttrInfo<float> onnx_beta_attr;
|
||||
};
|
||||
|
||||
const OnnxAttrInfo<float> HardSigmoidOpBuilder::onnx_alpha_attr = {"alpha", 0.2f};
|
||||
const OnnxAttrInfo<float> HardSigmoidOpBuilder::onnx_beta_attr = {"beta", 0.5};
|
||||
|
||||
// HardSigmoid is not natively supported by QNN. This builder must decompose HardSigmoid into
|
||||
// HardSigmoid(X) = max(0, min(1, alpha*X + beta)). This is only valid for float (non-quantized) HardSigmoid ops
|
||||
// because we don't compute internal quantization parameters (scale/zp) for any new nodes.
|
||||
Status HardSigmoidOpBuilder::IsOpSupported(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
const logging::Logger& logger) const {
|
||||
ORT_RETURN_IF_NOT(node_unit.UnitType() == NodeUnit::Type::SingleNode,
|
||||
"QNN EP does not support quantized (QDQ) HardSigmoid");
|
||||
|
||||
const auto& inputs = node_unit.Inputs();
|
||||
ORT_RETURN_IF(inputs.size() != 1, "HardSigmoid operator must have 1 input.");
|
||||
const auto& input = inputs[0];
|
||||
|
||||
int32_t onnx_data_type = 0;
|
||||
ORT_RETURN_IF_ERROR(utils::GetOnnxTensorElemDataType(input.node_arg, onnx_data_type));
|
||||
|
||||
const bool is_float_type = (onnx_data_type == ONNX_NAMESPACE::TensorProto_DataType_FLOAT) ||
|
||||
(onnx_data_type == ONNX_NAMESPACE::TensorProto_DataType_FLOAT16);
|
||||
ORT_RETURN_IF_NOT(is_float_type, "QNN EP only supports HardSigmoid with float/float16 inputs");
|
||||
|
||||
return AddToModelBuilder(qnn_model_wrapper, node_unit, logger, true);
|
||||
}
|
||||
|
||||
Status HardSigmoidOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
const logging::Logger& logger,
|
||||
std::vector<std::string>& input_names,
|
||||
bool do_op_validation) const {
|
||||
ORT_UNUSED_PARAMETER(do_op_validation);
|
||||
const auto& inputs = node_unit.Inputs();
|
||||
|
||||
return ProcessInput(qnn_model_wrapper, inputs[0], logger, input_names);
|
||||
}
|
||||
|
||||
static Status GetFloatBytes(float f32_val, Qnn_DataType_t qnn_data_type, std::vector<uint8_t>& bytes) {
|
||||
switch (qnn_data_type) {
|
||||
case QNN_DATATYPE_FLOAT_32: {
|
||||
bytes.resize(sizeof(float));
|
||||
std::memcpy(bytes.data(), &f32_val, bytes.size());
|
||||
break;
|
||||
}
|
||||
case QNN_DATATYPE_FLOAT_16: {
|
||||
bytes.resize(sizeof(MLFloat16));
|
||||
const MLFloat16 f16_val(f32_val);
|
||||
std::memcpy(bytes.data(), &f16_val, bytes.size());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Qnn Data Type: ", qnn_data_type, " is not supported");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status HardSigmoidOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const {
|
||||
ORT_UNUSED_PARAMETER(logger);
|
||||
const auto& onnx_node_name = utils::GetNodeName(node_unit);
|
||||
const auto& input = node_unit.Inputs()[0];
|
||||
const auto& output = node_unit.Outputs()[0];
|
||||
|
||||
std::vector<uint32_t> input_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(input.node_arg, input_shape), "Cannot get shape of input 0");
|
||||
|
||||
Qnn_DataType_t qnn_data_type = QNN_DATATYPE_FLOAT_32;
|
||||
ORT_RETURN_IF_ERROR(utils::GetQnnDataType(false /*is_quantized*/, input.node_arg.TypeAsProto(), qnn_data_type));
|
||||
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
|
||||
//
|
||||
// Create Mul node.
|
||||
//
|
||||
|
||||
std::string alpha_input_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_alpha");
|
||||
std::vector<uint8_t> alpha_bytes;
|
||||
ORT_RETURN_IF_ERROR(GetFloatBytes(GetOnnxAttr(node_helper, onnx_alpha_attr), qnn_data_type, alpha_bytes));
|
||||
|
||||
QnnTensorWrapper alpha_input(alpha_input_name,
|
||||
QNN_TENSOR_TYPE_STATIC,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
{1}, // shape
|
||||
std::move(alpha_bytes));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(alpha_input)), "Failed to add alpha input tensor.");
|
||||
|
||||
std::string mul_output_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_output");
|
||||
std::string mul_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_node");
|
||||
QnnTensorWrapper mul_output(mul_output_name,
|
||||
QNN_TENSOR_TYPE_NATIVE,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(mul_output)), "Failed to add Mul output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(mul_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_ELEMENT_WISE_MULTIPLY,
|
||||
{input_names[0], alpha_input_name}, // input names
|
||||
{mul_output_name}, // output names
|
||||
{},
|
||||
do_op_validation),
|
||||
"Failed to add Mul node.");
|
||||
|
||||
//
|
||||
// Create Add node.
|
||||
//
|
||||
|
||||
std::string beta_input_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_beta");
|
||||
std::vector<uint8_t> beta_bytes;
|
||||
ORT_RETURN_IF_ERROR(GetFloatBytes(GetOnnxAttr(node_helper, onnx_beta_attr), qnn_data_type, beta_bytes));
|
||||
|
||||
QnnTensorWrapper beta_input(beta_input_name,
|
||||
QNN_TENSOR_TYPE_STATIC,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
{1}, // shape
|
||||
std::move(beta_bytes));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(beta_input)), "Failed to add beta input tensor.");
|
||||
|
||||
std::string add_output_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Add_output");
|
||||
std::string add_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Add_node");
|
||||
QnnTensorWrapper add_output(add_output_name,
|
||||
QNN_TENSOR_TYPE_NATIVE,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(add_output)), "Failed to add Add output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(add_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_ELEMENT_WISE_ADD,
|
||||
{mul_output_name, beta_input_name}, // input names
|
||||
{add_output_name}, // output names
|
||||
{},
|
||||
do_op_validation),
|
||||
"Failed to add Add node.");
|
||||
|
||||
//
|
||||
// Create ReluMinMax node.
|
||||
//
|
||||
|
||||
std::vector<std::string> param_tensor_names;
|
||||
|
||||
// Parameter 'min_value'
|
||||
{
|
||||
Qnn_Scalar_t min_value = QNN_SCALAR_INIT;
|
||||
min_value.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
min_value.floatValue = 0.0f;
|
||||
|
||||
QnnParamWrapper qnn_param(node_unit.Index(), node_unit.Name(), QNN_OP_RELU_MIN_MAX_PARAM_MIN_VALUE, min_value);
|
||||
param_tensor_names.push_back(qnn_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_param));
|
||||
}
|
||||
|
||||
// Parameter 'max_value'
|
||||
{
|
||||
Qnn_Scalar_t max_value = QNN_SCALAR_INIT;
|
||||
max_value.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
max_value.floatValue = 1.0f;
|
||||
|
||||
QnnParamWrapper qnn_param(node_unit.Index(), node_unit.Name(), QNN_OP_RELU_MIN_MAX_PARAM_MAX_VALUE, max_value);
|
||||
param_tensor_names.push_back(qnn_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_param));
|
||||
}
|
||||
|
||||
const std::string& output_name = output.node_arg.Name();
|
||||
std::string relu_min_max_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_ReluMinMax_node");
|
||||
QnnTensorWrapper output_tensor(output_name,
|
||||
qnn_model_wrapper.GetTensorType(output_name),
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(output_tensor)), "Failed to add output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(relu_min_max_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_RELU_MIN_MAX,
|
||||
{add_output_name}, // input names
|
||||
{output_name}, // output names
|
||||
std::move(param_tensor_names),
|
||||
do_op_validation),
|
||||
"Failed to add ReluMinMax node.");
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void CreateHardSigmoidOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations) {
|
||||
op_registrations.AddOpBuilder(op_type, std::make_unique<HardSigmoidOpBuilder>());
|
||||
}
|
||||
|
||||
} // namespace qnn
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -161,16 +161,20 @@ Status SimpleOpBuilder::ExplicitOpCheck(const NodeUnit& node_unit) const {
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ProcessAlphaAttribute(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>& param_tensor_names) {
|
||||
// Limit to float type for now
|
||||
Status ProcessNodeAttribute(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
const std::string& onnx_attr_key,
|
||||
const std::string& qnn_param_key,
|
||||
std::vector<std::string>& param_tensor_names,
|
||||
const float default_value = 1.0f) {
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
float alpha = node_helper.Get("alpha", 1.0f);
|
||||
Qnn_Scalar_t alpha_qnn_scalar = QNN_SCALAR_INIT;
|
||||
alpha_qnn_scalar.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
alpha_qnn_scalar.floatValue = alpha;
|
||||
float attr_value = node_helper.Get(onnx_attr_key, default_value);
|
||||
Qnn_Scalar_t attr_qnn_scalar = QNN_SCALAR_INIT;
|
||||
attr_qnn_scalar.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
attr_qnn_scalar.floatValue = attr_value;
|
||||
|
||||
QnnParamWrapper alpha_param(node_unit.Index(), node_unit.Name(), QNN_OP_ELU_PARAM_ALPHA, alpha_qnn_scalar);
|
||||
QnnParamWrapper alpha_param(node_unit.Index(), node_unit.Name(), qnn_param_key, attr_qnn_scalar);
|
||||
param_tensor_names.push_back(alpha_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(alpha_param));
|
||||
|
||||
|
|
@ -306,6 +310,159 @@ Status ProcessGridSampleAttributes(QnnModelWrapper& qnn_model_wrapper,
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status GetFloatBytes(float f32_val, Qnn_DataType_t qnn_data_type, std::vector<uint8_t>& bytes) {
|
||||
switch (qnn_data_type) {
|
||||
case QNN_DATATYPE_FLOAT_32: {
|
||||
bytes.resize(sizeof(float));
|
||||
std::memcpy(bytes.data(), &f32_val, bytes.size());
|
||||
break;
|
||||
}
|
||||
case QNN_DATATYPE_FLOAT_16: {
|
||||
bytes.resize(sizeof(MLFloat16));
|
||||
const MLFloat16 f16_val(f32_val);
|
||||
std::memcpy(bytes.data(), &f16_val, bytes.size());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Qnn Data Type: ", qnn_data_type, " is not supported");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status DecomposeHardSigmoid(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) {
|
||||
ORT_UNUSED_PARAMETER(logger);
|
||||
const auto& onnx_node_name = utils::GetNodeName(node_unit);
|
||||
const auto& input = node_unit.Inputs()[0];
|
||||
const auto& output = node_unit.Outputs()[0];
|
||||
|
||||
std::vector<uint32_t> input_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(input.node_arg, input_shape), "Cannot get shape of input 0");
|
||||
|
||||
Qnn_DataType_t qnn_data_type = QNN_DATATYPE_FLOAT_32;
|
||||
ORT_RETURN_IF_ERROR(utils::GetQnnDataType(false /*is_quantized*/, input.node_arg.TypeAsProto(), qnn_data_type));
|
||||
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
|
||||
//
|
||||
// Create Mul node.
|
||||
//
|
||||
const OnnxAttrInfo<float> onnx_alpha_attr{"alpha", 0.2f};
|
||||
const OnnxAttrInfo<float> onnx_beta_attr{"beta", 0.5};
|
||||
std::string alpha_input_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_alpha");
|
||||
std::vector<uint8_t> alpha_bytes;
|
||||
ORT_RETURN_IF_ERROR(GetFloatBytes(GetOnnxAttr(node_helper, onnx_alpha_attr), qnn_data_type, alpha_bytes));
|
||||
|
||||
QnnTensorWrapper alpha_input(alpha_input_name,
|
||||
QNN_TENSOR_TYPE_STATIC,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
{1}, // shape
|
||||
std::move(alpha_bytes));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(alpha_input)), "Failed to add alpha input tensor.");
|
||||
|
||||
std::string mul_output_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_output");
|
||||
std::string mul_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_node");
|
||||
QnnTensorWrapper mul_output(mul_output_name,
|
||||
QNN_TENSOR_TYPE_NATIVE,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(mul_output)), "Failed to add Mul output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(mul_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_ELEMENT_WISE_MULTIPLY,
|
||||
{input_names[0], alpha_input_name}, // input names
|
||||
{mul_output_name}, // output names
|
||||
{},
|
||||
do_op_validation),
|
||||
"Failed to add Mul node.");
|
||||
|
||||
//
|
||||
// Create Add node.
|
||||
//
|
||||
|
||||
std::string beta_input_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Mul_beta");
|
||||
std::vector<uint8_t> beta_bytes;
|
||||
ORT_RETURN_IF_ERROR(GetFloatBytes(GetOnnxAttr(node_helper, onnx_beta_attr), qnn_data_type, beta_bytes));
|
||||
|
||||
QnnTensorWrapper beta_input(beta_input_name,
|
||||
QNN_TENSOR_TYPE_STATIC,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
{1}, // shape
|
||||
std::move(beta_bytes));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(beta_input)), "Failed to add beta input tensor.");
|
||||
|
||||
std::string add_output_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Add_output");
|
||||
std::string add_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_Add_node");
|
||||
QnnTensorWrapper add_output(add_output_name,
|
||||
QNN_TENSOR_TYPE_NATIVE,
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(add_output)), "Failed to add Add output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(add_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_ELEMENT_WISE_ADD,
|
||||
{mul_output_name, beta_input_name}, // input names
|
||||
{add_output_name}, // output names
|
||||
{},
|
||||
do_op_validation),
|
||||
"Failed to add Add node.");
|
||||
|
||||
//
|
||||
// Create ReluMinMax node.
|
||||
//
|
||||
|
||||
std::vector<std::string> param_tensor_names;
|
||||
|
||||
// Parameter 'min_value'
|
||||
{
|
||||
Qnn_Scalar_t min_value = QNN_SCALAR_INIT;
|
||||
min_value.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
min_value.floatValue = 0.0f;
|
||||
|
||||
QnnParamWrapper qnn_param(node_unit.Index(), node_unit.Name(), QNN_OP_RELU_MIN_MAX_PARAM_MIN_VALUE, min_value);
|
||||
param_tensor_names.push_back(qnn_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_param));
|
||||
}
|
||||
|
||||
// Parameter 'max_value'
|
||||
{
|
||||
Qnn_Scalar_t max_value = QNN_SCALAR_INIT;
|
||||
max_value.dataType = QNN_DATATYPE_FLOAT_32;
|
||||
max_value.floatValue = 1.0f;
|
||||
|
||||
QnnParamWrapper qnn_param(node_unit.Index(), node_unit.Name(), QNN_OP_RELU_MIN_MAX_PARAM_MAX_VALUE, max_value);
|
||||
param_tensor_names.push_back(qnn_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_param));
|
||||
}
|
||||
|
||||
const std::string& output_name = output.node_arg.Name();
|
||||
std::string relu_min_max_node_name = MakeString("ort_qnn_ep_", onnx_node_name, "_HardSigmoid_ReluMinMax_node");
|
||||
QnnTensorWrapper output_tensor(output_name,
|
||||
qnn_model_wrapper.GetTensorType(output_name),
|
||||
qnn_data_type,
|
||||
QnnQuantParamsWrapper(),
|
||||
std::vector<uint32_t>(input_shape));
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.AddTensorWrapper(std::move(output_tensor)), "Failed to add output tensor.");
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.CreateQnnNode(relu_min_max_node_name,
|
||||
QNN_OP_PACKAGE_NAME_QTI_AISW,
|
||||
QNN_OP_RELU_MIN_MAX,
|
||||
{add_output_name}, // input names
|
||||
{output_name}, // output names
|
||||
std::move(param_tensor_names),
|
||||
do_op_validation),
|
||||
"Failed to add ReluMinMax node.");
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status SimpleOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
|
|
@ -369,7 +526,34 @@ Status SimpleOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_w
|
|||
}
|
||||
|
||||
if (op_type == "Elu") {
|
||||
ORT_RETURN_IF_ERROR(ProcessAlphaAttribute(qnn_model_wrapper, node_unit, param_tensor_names));
|
||||
ORT_RETURN_IF_ERROR(ProcessNodeAttribute(qnn_model_wrapper, node_unit, "alpha",
|
||||
QNN_OP_ELU_PARAM_ALPHA, param_tensor_names));
|
||||
}
|
||||
|
||||
if (op_type == "HardSigmoid") {
|
||||
// direct conversion to ElementWiseNeuron has issue to finalize the graph for FP16 data type
|
||||
// still decompose it to Mul, Add, ReluMinMax
|
||||
int32_t onnx_data_type = 0;
|
||||
ORT_RETURN_IF_ERROR(utils::GetOnnxTensorElemDataType(node_unit.Inputs()[0].node_arg, onnx_data_type));
|
||||
if (onnx_data_type == ONNX_NAMESPACE::TensorProto_DataType_FLOAT16) {
|
||||
return DecomposeHardSigmoid(qnn_model_wrapper, node_unit, std::move(input_names), logger, do_op_validation);
|
||||
}
|
||||
|
||||
ORT_RETURN_IF_ERROR(ProcessNodeAttribute(qnn_model_wrapper, node_unit, "alpha",
|
||||
QNN_OP_ELEMENT_WISE_NEURON_PARAM_ALPHA,
|
||||
param_tensor_names, 0.2f));
|
||||
ORT_RETURN_IF_ERROR(ProcessNodeAttribute(qnn_model_wrapper, node_unit, "beta",
|
||||
QNN_OP_ELEMENT_WISE_NEURON_PARAM_BETA,
|
||||
param_tensor_names, 0.5f));
|
||||
Qnn_Scalar_t neuron_operation = QNN_SCALAR_INIT;
|
||||
neuron_operation.dataType = QNN_DATATYPE_UINT_32;
|
||||
neuron_operation.uint32Value = QNN_OP_ELEMENT_WISE_NEURON_OPERATION_HARD_SIGMOID;
|
||||
|
||||
QnnParamWrapper operation_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_ELEMENT_WISE_NEURON_PARAM_OPERATION,
|
||||
neuron_operation);
|
||||
param_tensor_names.push_back(operation_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(operation_param));
|
||||
}
|
||||
|
||||
if (op_type == "DepthToSpace") {
|
||||
|
|
|
|||
|
|
@ -323,11 +323,6 @@ TEST_F(QnnHTPBackendTests, UnaryOp_HardSwish) {
|
|||
}
|
||||
|
||||
// Tests accuracy of 16-bit QDQ HardSwish
|
||||
// TODO(adrianlizarraga): Inaccuracy detected for output 'output', element 5.
|
||||
// Output quant params: scale=0.00015259021893143654, zero_point=0.
|
||||
// Expected val: 10
|
||||
// QNN QDQ val: 9.999237060546875 (err 0.000762939453125)
|
||||
// CPU QDQ val: 9.999847412109375 (err 0.000152587890625)
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSwish_U16) {
|
||||
const std::vector<float> input_data = {-10.0f, -8.4f, 0.0f, 4.3f, 7.1f, 10.0f};
|
||||
RunQDQOpTest<uint16_t>("HardSwish",
|
||||
|
|
@ -1211,16 +1206,33 @@ TEST_F(QnnHTPBackendTests, Add_U8_U16_Convert) {
|
|||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test that QDQ HardSigmoid is *not* supported by QNN EP.
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_QDQ_NotSupported) {
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_QU8) {
|
||||
RunQDQOpTest<uint8_t>("HardSigmoid",
|
||||
{TestInputDef<float>({1, 2, 3}, false, GetFloatDataInRange(-10.0f, 10.0f, 6))},
|
||||
{utils::MakeAttribute("alpha", 0.1f),
|
||||
utils::MakeAttribute("beta", 0.4f)},
|
||||
21,
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_QU16) {
|
||||
RunQDQOpTest<uint16_t>("HardSigmoid",
|
||||
{TestInputDef<float>({1, 2, 3}, false, GetFloatDataInRange(-10.0f, 10.0f, 6))},
|
||||
{},
|
||||
21,
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test that QDQ HardSigmoid is supported by QNN EP.
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_QDQ_Supported) {
|
||||
RunQDQOpTest<uint8_t>("HardSigmoid",
|
||||
{TestInputDef<float>({1, 2, 2, 2}, false, -10.0f, 10.0f)},
|
||||
{},
|
||||
19,
|
||||
ExpectedEPNodeAssignment::None); // Not assigned to QNN EP
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Check that QNN EP can support float32 HardSigmoid on HTP by decomposing to its constituent ops.
|
||||
// Check that QNN EP can support float32 HardSigmoid on HTP.
|
||||
// Enables running f32 ops using fp16 precision.
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_F32_as_FP16) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-5.0f, 5.0f, 16);
|
||||
|
|
@ -1246,7 +1258,15 @@ TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_F32_as_FP16) {
|
|||
true); // enable_htp_fp16_precision
|
||||
}
|
||||
|
||||
// Check that QNN EP can support float16 HardSigmoid on HTP by decomposing to its constituent ops.
|
||||
// Check that QNN EP can support float16 HardSigmoid on HTP
|
||||
// It is using decompose way for FP16 since ElementWiseNeuron failed to finalize the graph with the error below:
|
||||
// \HTP\src\hexagon\prepare\tcm_migration.cc:1829:ERROR:no properties registered for q::QNN_HardSigmoid
|
||||
// \HTP\HTP\src\hexagon\prepare\graph_prepare.cc:203:ERROR:could not create op: q::QNN_HardSigmoid
|
||||
// \HTP\HTP\src\hexagon\prepare\graph_prepare.cc:1238:ERROR:Op 0x101000000010 preparation failed with err:-1
|
||||
// Completed stage: Graph Transformations and Optimizations (16361 us)
|
||||
// QnnDsp <E> "node" generated: could not create op
|
||||
// QnnDsp <E> RouterWindows graph prepare failed 12
|
||||
// QnnDsp <E> Failed to finalize graph (id: 1) with err 1002
|
||||
TEST_F(QnnHTPBackendTests, UnaryOp_HardSigmoid_FP16) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-5.0f, 5.0f, 16);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue