Enable int32 data support for Clip (#20590)

Enable int32 data support for Clip

fix issue: https://github.com/microsoft/onnxruntime/issues/20525
This commit is contained in:
Hector Li 2024-05-07 11:35:29 -07:00 committed by GitHub
parent d693aef39e
commit d121a1f906
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 29 deletions

View file

@ -43,15 +43,51 @@ static Status ProcessClipMinMax(QnnModelWrapper& qnn_model_wrapper,
std::vector<uint8_t> val_bytes;
ORT_RETURN_IF_ERROR(qnn_model_wrapper.GetTensorInfo(input, input_info));
assert(input_info.is_initializer); // Checked by ExplicitOpCheck().
if (QNN_DATATYPE_FLOAT_16 == input_info.qnn_data_type) {
ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes));
MLFloat16 fp16_value = *reinterpret_cast<const MLFloat16*>(val_bytes.data());
float_value = fp16_value.ToFloat();
} else {
ORT_RETURN_IF_NOT(QNN_DATATYPE_FLOAT_32 == input_info.qnn_data_type,
"QNN EP: The 'min' input of the Clip operator must be of type float32.");
ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes));
float_value = *reinterpret_cast<const float*>(val_bytes.data());
ORT_RETURN_IF_ERROR(qnn_model_wrapper.UnpackInitializerData(*input_info.initializer_tensor, val_bytes));
switch (input_info.qnn_data_type) {
case QNN_DATATYPE_INT_8: {
float_value = static_cast<float>(*reinterpret_cast<int8_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_INT_16: {
float_value = static_cast<float>(*reinterpret_cast<int16_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_INT_32: {
float_value = static_cast<float>(*reinterpret_cast<int32_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_INT_64: {
float_value = static_cast<float>(*reinterpret_cast<int64_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_UINT_8: {
float_value = static_cast<float>(*val_bytes.data());
break;
}
case QNN_DATATYPE_UINT_16: {
float_value = static_cast<float>(*reinterpret_cast<uint16_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_UINT_32: {
float_value = static_cast<float>(*reinterpret_cast<uint32_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_UINT_64: {
float_value = static_cast<float>(*reinterpret_cast<uint64_t*>(val_bytes.data()));
break;
}
case QNN_DATATYPE_FLOAT_16: {
MLFloat16 fp16_value = *reinterpret_cast<const MLFloat16*>(val_bytes.data());
float_value = fp16_value.ToFloat();
break;
}
case QNN_DATATYPE_FLOAT_32: {
float_value = *reinterpret_cast<const float*>(val_bytes.data());
break;
}
default:
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "min/max input data type not supported.");
}
return Status::OK();

View file

@ -17,16 +17,17 @@ namespace test {
// Runs a model with a Clip operator on the QNN CPU backend. Checks the graph node assignment
// and that inference outputs for QNN EP and CPU EP match.
template <typename DataType>
static void RunClipTestOnCPU(const TestInputDef<DataType>& input_def,
const std::vector<TestInputDef<DataType>>& min_max_defs,
ExpectedEPNodeAssignment expected_ep_assignment,
int opset = 13) {
static void RunClipTest(const TestInputDef<DataType>& input_def,
const std::vector<TestInputDef<DataType>>& min_max_defs,
ExpectedEPNodeAssignment expected_ep_assignment,
bool on_cpu_backend = true,
int opset = 13) {
ProviderOptions provider_options;
#if defined(_WIN32)
provider_options["backend_path"] = "QnnCpu.dll";
provider_options["backend_path"] = on_cpu_backend ? "QnnCpu.dll" : "QnnHtp.dll";
#else
provider_options["backend_path"] = "libQnnCpu.so";
provider_options["backend_path"] = on_cpu_backend ? "libQnnCpu.so" : "libQnnHtp.so";
#endif
RunQnnModelTest(BuildOpTestCase<DataType, DataType>("Clip", {input_def}, min_max_defs, {}),
@ -42,29 +43,29 @@ static void RunClipTestOnCPU(const TestInputDef<DataType>& input_def,
// Test that Clip with a dynamic min or max input is not supported by QNN EP.
TEST_F(QnnCPUBackendTests, Clip_Dynamic_MinMax_Unsupported) {
// Dynamic min input is not supported.
RunClipTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
{TestInputDef<float>({}, false /* is_initializer */, {-5.0f})},
ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP.
RunClipTest<float>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
{TestInputDef<float>({}, false /* is_initializer */, {-5.0f})},
ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP.
// Dynamic max input is not supported.
RunClipTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
{TestInputDef<float>({}, true, {-5.0f}),
TestInputDef<float>({}, false, {5.0f})},
ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP.
RunClipTest<float>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
{TestInputDef<float>({}, true, {-5.0f}),
TestInputDef<float>({}, false, {5.0f})},
ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP.
}
// Test Clip with default min/max.
TEST_F(QnnCPUBackendTests, Clip_4D_f32_DefaultMinMax) {
RunClipTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
{}, // Don't specify min/max inputs.
ExpectedEPNodeAssignment::All);
RunClipTest<float>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
{}, // Don't specify min/max inputs.
ExpectedEPNodeAssignment::All);
}
// Test Clip with 5D input.
TEST_F(QnnCPUBackendTests, Clip_5D_f32) {
RunClipTestOnCPU<float>(TestInputDef<float>({1, 1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
{TestInputDef<float>({}, true, {-5.0f}),
TestInputDef<float>({}, true, {5.0f})},
ExpectedEPNodeAssignment::All);
RunClipTest<float>(TestInputDef<float>({1, 1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
{TestInputDef<float>({}, true, {-5.0f}),
TestInputDef<float>({}, true, {5.0f})},
ExpectedEPNodeAssignment::All);
}
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__)
@ -72,6 +73,26 @@ TEST_F(QnnCPUBackendTests, Clip_5D_f32) {
// HTP tests:
//
// Test Clip with float32 on HTP
TEST_F(QnnHTPBackendTests, Clip_f32) {
bool on_cpu_backend = false;
RunClipTest<float>(TestInputDef<float>({1, 1, 3, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 12)),
{TestInputDef<float>({}, true, {-5.0f}),
TestInputDef<float>({}, true, {5.0f})},
ExpectedEPNodeAssignment::All,
on_cpu_backend);
}
// Test Clip with int32 on HTP
TEST_F(QnnHTPBackendTests, Clip_int32) {
bool on_cpu_backend = false;
RunClipTest<int32_t>(TestInputDef<int32_t>({1, 1, 3, 2}, false, {1, 2, -5, 3, -10, 25}),
{TestInputDef<int32_t>({}, true, {-5}),
TestInputDef<int32_t>({}, true, {5})},
ExpectedEPNodeAssignment::All,
on_cpu_backend);
}
// Runs a QDQ Clip model on the QNN (HTP) EP and the ORT CPU EP. Checks the graph node assignment and that inference
// running the QDQ model on QNN EP is at least as accurate as on ORT CPU EP (compared to the baseline float32 model).
template <typename QType>