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:
Hector Li 2024-06-10 09:16:25 -07:00 committed by GitHub
parent 855c1cffc9
commit 3c6d409937
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 225 additions and 262 deletions

View file

@ -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) {

View file

@ -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},

View file

@ -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

View file

@ -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") {

View file

@ -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);