mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-29 20:14:01 +00:00
[QNN EP] Integrate Resize op fixes from QNN 2.14.1 (#17641)
### Description QNN SDK version 2.14.1 fixed several issues with the QNN Resize operator. This PR integrates the fixes and simplifies the implementation. ### Motivation and Context Improve Resize operator and test coverage.
This commit is contained in:
parent
6d7bc2a097
commit
e70a23f8dc
3 changed files with 313 additions and 338 deletions
|
|
@ -2,7 +2,8 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "core/providers/common.h"
|
||||
#include "core/providers/shared/utils/utils.h"
|
||||
|
|
@ -42,76 +43,6 @@ class ResizeOpBuilder : public BaseOpBuilder {
|
|||
bool do_op_validation) const override ORT_MUST_USE_RESULT;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the QNN integer value that corresponds to the given ONNX mode (string).
|
||||
*
|
||||
* /param onnx_modes Array of ONNX modes supported by QNN. The index of each mode corresponds to the QNN value.
|
||||
* /param onnx_mode The ONNX mode for which to get the corresponding QNN value.
|
||||
* /param onnx_model_label Mode label to print out in case of error (e.g., "nearest_mode").
|
||||
* /param qnn_mode Output parameter that is set to the appropriate QNN value from the given ONNX mode.
|
||||
*
|
||||
* /returns A status indicating failure or success.
|
||||
*/
|
||||
template <typename QnnValType, std::size_t N>
|
||||
Status GetQnnModeFromString(const std::array<std::string_view, N>& onnx_modes, std::string_view onnx_mode,
|
||||
const char* onnx_mode_label, QnnValType& qnn_mode) const ORT_MUST_USE_RESULT;
|
||||
|
||||
/**
|
||||
* Called by IsOpSupported to validate the op for non-quantized models.
|
||||
*
|
||||
* /param qnn_model_wrapper The QNN model wrapper instance.
|
||||
* /param node_unit The node unit containing metadata for the ONNX Resize operator.
|
||||
*
|
||||
* /returns A status indicating failure or success.
|
||||
*/
|
||||
Status ValidateOp(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const ORT_MUST_USE_RESULT;
|
||||
|
||||
/**
|
||||
* Called by IsOpSupported to validate the op for quantized models.
|
||||
*
|
||||
* /param qnn_model_wrapper The QNN model wrapper instance.
|
||||
* /param node_unit The node unit containing metadata for the ONNX Resize operator and its Q/DQ nodes.
|
||||
*
|
||||
* /returns A status indicating failure or success.
|
||||
*/
|
||||
Status ValidateQDQOp(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const ORT_MUST_USE_RESULT;
|
||||
|
||||
/**
|
||||
* Called by ProcessAttributesAndOutputs to process the op's attributes and outputs
|
||||
* for non-quantized models.
|
||||
*
|
||||
* /param qnn_model_wrapper The QNN model wrapper instance.
|
||||
* /param node_unit The node unit containing metadata for the ONNX Resize operator.
|
||||
* /param input_names The operator's input names.
|
||||
* /param logger A logger.
|
||||
* /param do_op_validation Set to true if the op should be validated using QNN's validation API.
|
||||
*
|
||||
* /returns A status indicating failure or success.
|
||||
*/
|
||||
Status ProcessOpAttrsAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const ORT_MUST_USE_RESULT;
|
||||
|
||||
/**
|
||||
* Called by ProcessAttributesAndOutputs to process the op's attributes and outputs
|
||||
* for quantized models.
|
||||
*
|
||||
* /param qnn_model_wrapper The QNN model wrapper instance.
|
||||
* /param node_unit The node unit containing metadata for the ONNX Resize operator and its Q/DQ nodes.
|
||||
* /param input_names The operator's input names.
|
||||
* /param logger A logger.
|
||||
* /param do_op_validation Set to true if the op should be validated using QNN's validation API.
|
||||
*
|
||||
* /returns A status indicating failure or success.
|
||||
*/
|
||||
Status ProcessQDQOpAttrsAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const ORT_MUST_USE_RESULT;
|
||||
|
||||
// Info for each ONNX attribute of interest (attribute name + default value)
|
||||
static const OnnxAttrInfo<std::string> onnx_mode_attr;
|
||||
static const OnnxAttrInfo<std::string> onnx_coord_transf_mode_attr;
|
||||
|
|
@ -119,22 +50,30 @@ class ResizeOpBuilder : public BaseOpBuilder {
|
|||
static const OnnxAttrInfo<int64_t> onnx_antialias_attr;
|
||||
static const OnnxAttrInfo<int64_t> onnx_exclude_outside_attr;
|
||||
|
||||
// Arrays of supported QNN modes for QNN's Resize op. The index of each mode is used as the corresponding
|
||||
// QNN parameter value. Ex: The "nearest" mode is represented as the value 0 in QNN. Note, that
|
||||
// not all modes are supported by every QNN backend.
|
||||
|
||||
// QNN values: NEAREST = 0, LINEAR = 1
|
||||
static constexpr std::array<std::string_view, 2> supported_modes = {"nearest", "linear"};
|
||||
|
||||
// QNN values: HALF_PIXEL = 0, PYTORCH_HALF_PIXEL = 1, ALIGN_CORNERS = 2, ASYMMETRIC = 3
|
||||
static constexpr std::array<std::string_view, 4> supported_coord_transf_modes = {"half_pixel", "pytorch_half_pixel",
|
||||
"align_corners", "asymmetric"};
|
||||
|
||||
// QNN values: ROUND_PREFER_FLOOR = 0, ROUND_PREFER_CEIL = 1, FLOOR = 2, CEIL = 3
|
||||
static constexpr std::array<std::string_view, 4> supported_nearest_modes = {"round_prefer_floor", "round_prefer_ceil",
|
||||
"floor", "ceil"};
|
||||
// Tables that map an ONNX attribute value (string) to the corresponding integer (enum) QNN parameter value.
|
||||
// Ex: The "half_pixel" coordinate_transformation_mode is represented as the value 0 in QNN.
|
||||
// Only the modes supported by QNN Resize are mapped by these tables.
|
||||
static const std::unordered_map<std::string, uint32_t> supported_modes;
|
||||
static const std::unordered_map<std::string, uint32_t> supported_coord_transf_modes;
|
||||
static const std::unordered_map<std::string, uint32_t> supported_nearest_modes;
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, uint32_t> ResizeOpBuilder::supported_modes = {
|
||||
{"nearest", QNN_OP_RESIZE_INTERPOLATION_MODE_NEAREST},
|
||||
{"linear", QNN_OP_RESIZE_INTERPOLATION_MODE_LINEAR}};
|
||||
|
||||
const std::unordered_map<std::string, uint32_t> ResizeOpBuilder::supported_coord_transf_modes = {
|
||||
{"half_pixel", QNN_OP_RESIZE_TRANSFORMATION_MODE_HALF_PIXEL},
|
||||
{"pytorch_half_pixel", QNN_OP_RESIZE_TRANSFORMATION_MODE_PYTORCH_HALF_PIXEL},
|
||||
{"align_corners", QNN_OP_RESIZE_TRANSFORMATION_MODE_ALIGN_CORNERS},
|
||||
{"asymmetric", QNN_OP_RESIZE_TRANSFORMATION_MODE_ASYMMETRIC}};
|
||||
|
||||
const std::unordered_map<std::string, uint32_t> ResizeOpBuilder::supported_nearest_modes = {
|
||||
{"round_prefer_floor", QNN_OP_RESIZE_NEAREST_MODE_ROUND_PREFER_FLOOR},
|
||||
{"round_prefer_ceil", QNN_OP_RESIZE_NEAREST_MODE_ROUND_PREFER_CEIL},
|
||||
{"floor", QNN_OP_RESIZE_NEAREST_MODE_FLOOR},
|
||||
{"ceil", QNN_OP_RESIZE_NEAREST_MODE_CEIL}};
|
||||
|
||||
const OnnxAttrInfo<std::string> ResizeOpBuilder::onnx_mode_attr = {"mode", "nearest"};
|
||||
const OnnxAttrInfo<std::string> ResizeOpBuilder::onnx_coord_transf_mode_attr = {"coordinate_transformation_mode",
|
||||
"half_pixel"};
|
||||
|
|
@ -143,19 +82,26 @@ const OnnxAttrInfo<std::string> ResizeOpBuilder::onnx_nearest_mode_attr = {"near
|
|||
const OnnxAttrInfo<int64_t> ResizeOpBuilder::onnx_antialias_attr = {"antialias", 0};
|
||||
const OnnxAttrInfo<int64_t> ResizeOpBuilder::onnx_exclude_outside_attr = {"exclude_outside", 0};
|
||||
|
||||
template <typename QnnValType, std::size_t N>
|
||||
Status ResizeOpBuilder::GetQnnModeFromString(const std::array<std::string_view, N>& onnx_modes,
|
||||
std::string_view onnx_mode, const char* onnx_mode_label,
|
||||
QnnValType& qnn_mode) const {
|
||||
for (size_t i = 0; i < onnx_modes.size(); ++i) {
|
||||
if (onnx_modes[i] == onnx_mode) {
|
||||
qnn_mode = SafeInt<QnnValType>(i);
|
||||
return Status::OK();
|
||||
}
|
||||
// Returns the QNN parameter integer value that corresponds to the given ONNX attribute mode string value.
|
||||
static Status GetQnnModeValFromOnnxString(const std::unordered_map<std::string, uint32_t>& supported_qnn_modes,
|
||||
const std::string& onnx_attr_value,
|
||||
const char* onnx_attr_name,
|
||||
uint32_t& qnn_mode_value) {
|
||||
auto it = supported_qnn_modes.find(onnx_attr_value);
|
||||
if (it != supported_qnn_modes.end()) {
|
||||
qnn_mode_value = it->second;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "QNN EP: Resize operator does not support ", onnx_mode_label,
|
||||
" ", std::string(onnx_mode));
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "QNN EP: Resize operator does not support ", onnx_attr_name,
|
||||
" ", std::string(onnx_attr_value));
|
||||
}
|
||||
|
||||
// Returns true if the given ONNX attribute mode value is generally supported on QNN. Note that
|
||||
// different QNN backends may support a smaller subset of modes.
|
||||
static bool IsOnnxAttrModeSupported(const std::unordered_map<std::string, uint32_t>& supported_qnn_modes,
|
||||
const std::string& onnx_attr_value) {
|
||||
return supported_qnn_modes.find(onnx_attr_value) != supported_qnn_modes.end();
|
||||
}
|
||||
|
||||
// Resize ops are sensitive with data layout, no special validation so far
|
||||
|
|
@ -169,118 +115,95 @@ Status ResizeOpBuilder::IsOpSupported(QnnModelWrapper& qnn_model_wrapper,
|
|||
return AddToModelBuilder(qnn_model_wrapper, node_unit, logger, true);
|
||||
}
|
||||
|
||||
const bool is_npu_backend = IsNpuBackend(qnn_model_wrapper.GetQnnBackendType());
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
|
||||
// QNN doesn't support anti-aliasing (added in opset 18)
|
||||
if (node_unit.SinceVersion() >= 18) {
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
const bool antialias = GetOnnxAttr(node_helper, onnx_antialias_attr) != 0;
|
||||
ORT_RETURN_IF(antialias, "QNN EP: Resize doesn't support anti-aliasing.");
|
||||
}
|
||||
|
||||
// The QNN Resize op does not currently work with the QNN cpu backend, but works with the HTP backend. Therefore, we
|
||||
// currently use QNN's Resize op for quantized models and either ResizeBilinear or ResizeNearestNeighbor for
|
||||
// non-quantized models. This requires separate validation for quantized models.
|
||||
// TODO: Use only Resize once QNN's Resize op works in the QNN cpu backend.
|
||||
bool is_npu_backend = IsNpuBackend(qnn_model_wrapper.GetQnnBackendType());
|
||||
return is_npu_backend ? ValidateQDQOp(qnn_model_wrapper, node_unit) : ValidateOp(qnn_model_wrapper, node_unit);
|
||||
}
|
||||
|
||||
Status ResizeOpBuilder::ValidateOp(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const {
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
const std::string resize_mode = GetOnnxAttr(node_helper, onnx_mode_attr);
|
||||
ORT_RETURN_IF((resize_mode != "nearest") && (resize_mode != "linear"),
|
||||
"QNN EP: Resize doesn't support mode '", resize_mode.c_str(), "'.",
|
||||
"Only 'nearest' and 'linear' are supported.");
|
||||
|
||||
const std::string coordinate_mode = GetOnnxAttr(node_helper, onnx_coord_transf_mode_attr);
|
||||
ORT_RETURN_IF((coordinate_mode != "half_pixel") && (coordinate_mode != "align_corners"),
|
||||
"QNN EP: coordinate transformation mode '", coordinate_mode.c_str(), "' not supported for Resize op.",
|
||||
"Only 'align_corners' and 'half_pixel' are supported.");
|
||||
|
||||
// Check for a valid "nearest_mode" if the mode is "nearest".
|
||||
if (resize_mode == "nearest") {
|
||||
// NOTE: QNN's ResizeNearestNeighbor operator does not have a way to specify rounding (i.e., "nearest_mode").
|
||||
// The output of the QNN ResizeNearestNeighbor operator is not always equivalent to ONNX's Resize
|
||||
// operator with any single specific "nearest_mode".
|
||||
//
|
||||
// For some input/output shapes, QNN's ResizeNearestNeighbor is equivalent to ONNX's Resize with "round_prefer_floor".
|
||||
// For other shapes, QNN's ResizeNearestNeighbor is equivalent to ONNX Resize with "round_prefer_ceil".
|
||||
//
|
||||
// From unit tests, I've found a relationship between input/output shapes and the equivalent ONNX "nearest_mode".
|
||||
// If the new and old spatial dimensions are evenly divisible, the "nearest_mode" is "round_prefer_floor".
|
||||
// Otherwise, the "nearest_mode" is "round_prefer_ceil".
|
||||
//
|
||||
// This relationship is probably incomplete/wrong.
|
||||
//
|
||||
// TODO: Ask Qualcomm what the correct "nearest_mode" should be,
|
||||
// OR use QNN's own Resize operator once it works on QnnCpu.
|
||||
const std::string& nearest_mode = GetOnnxAttr(node_helper, onnx_nearest_mode_attr);
|
||||
ORT_RETURN_IF_NOT("floor" == nearest_mode, "QNN Resize only supports nearest_mode: floor!"); // This is wrong!
|
||||
}
|
||||
|
||||
auto& input_0 = node_unit.Inputs()[0];
|
||||
std::vector<uint32_t> input_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(input_0.node_arg, input_shape),
|
||||
"QNN EP: Cannot get input shape for Resize op");
|
||||
|
||||
const auto& output_0 = node_unit.Outputs()[0];
|
||||
std::vector<uint32_t> output_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(output_0.node_arg, output_shape),
|
||||
"QNN EP: Cannot get output shape for Resize op");
|
||||
|
||||
ORT_RETURN_IF(input_shape.size() != 4 || output_shape.size() != 4, "QNN Resize only supports 4D!");
|
||||
|
||||
ONNX_NAMESPACE::DataType input_data_type = input_0.node_arg.Type();
|
||||
ORT_RETURN_IF(input_data_type != ONNX_NAMESPACE::Utils::DataTypeUtils::ToType("float"),
|
||||
"QNN EP: Data type ", input_data_type->c_str(),
|
||||
" is not supported for Resize operator in CPU backend.");
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ResizeOpBuilder::ValidateQDQOp(QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit) const {
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
|
||||
using namespace onnxruntime::qnn::utils;
|
||||
// Check mode
|
||||
const std::string interp_mode = GetOnnxAttr(node_helper, onnx_mode_attr);
|
||||
ORT_RETURN_IF_NOT(ArrayHasString(supported_modes, interp_mode), "QNN EP: Resize does not support mode ",
|
||||
ORT_RETURN_IF_NOT(IsOnnxAttrModeSupported(supported_modes, interp_mode), "QNN EP: Resize does not support mode ",
|
||||
interp_mode.c_str());
|
||||
|
||||
// Check coordinate transformation mode
|
||||
const std::string transformation_mode = GetOnnxAttr(node_helper, onnx_coord_transf_mode_attr);
|
||||
ORT_RETURN_IF_NOT(ArrayHasString(supported_coord_transf_modes, transformation_mode),
|
||||
ORT_RETURN_IF_NOT(IsOnnxAttrModeSupported(supported_coord_transf_modes, transformation_mode),
|
||||
"QNN EP: Resize does not support coordinate_transformation_mode ", transformation_mode.c_str());
|
||||
|
||||
// Check nearest mode
|
||||
if (interp_mode == "nearest") {
|
||||
const std::string nearest_mode = GetOnnxAttr(node_helper, onnx_nearest_mode_attr);
|
||||
ORT_RETURN_IF_NOT(ArrayHasString(supported_nearest_modes, nearest_mode),
|
||||
"QNN EP: Resize does not support nearest_mode ", nearest_mode.c_str());
|
||||
|
||||
// TODO: Support 'asymmetric' transformation mode with nearest_mode != 'floor'.
|
||||
//
|
||||
// QNN's ONNX converter tool translates 'nearest' + 'asymmetric' (regardless of rounding mode)
|
||||
// to QNN's ResizeNearestNeighbor with {align_corners: 0, half_pixel: 0}.
|
||||
// This is only accurate if the rounding mode is "floor". Need to investigate how to handle
|
||||
// other rounding modes with Qualcomm. Ideally, we would use QNN's Resize operator, but it doesn't support
|
||||
// the "asymmetric" coordinate transformation mode on HTP.
|
||||
ORT_RETURN_IF(transformation_mode == "asymmetric" && nearest_mode != "floor",
|
||||
"QNN EP: Resize with coordinate_transformation_mode 'asymmetric' and nearest_mode '", nearest_mode,
|
||||
"' is not currently supported on the HTP backend.");
|
||||
}
|
||||
|
||||
// Check that input shape has at least a rank of 3.
|
||||
const auto& input_0 = node_unit.Inputs()[0];
|
||||
std::vector<uint32_t> input_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(input_0.node_arg, input_shape),
|
||||
"QNN EP: Cannot get shape for Resize input");
|
||||
ORT_RETURN_IF(input_shape.size() < 3, "QNN EP: Resize input must have a rank >= 3.");
|
||||
const size_t input_rank = input_shape.size();
|
||||
|
||||
// Validate Resize w/ "nearest" mode.
|
||||
// Translation matrix of ONNX Resize w/ "nearest" mode on HTP backend.
|
||||
// Table entries correspond to the QNN operator used for the given configuration
|
||||
// (Resize = QNN Resize op, RNN = QNN ResizeNearestNeighbor op, X = Unsupported).
|
||||
//
|
||||
// nearest_mode:
|
||||
// coordinate_transformation_mode: | round_prefer_floor round_prefer_ceil floor ceil
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// half_pixel | Resize X RNN X
|
||||
// pytorch_half_pixel | Resize X X X
|
||||
// align_corners | Resize X RNN X
|
||||
// asymmetric | Resize X RNN X
|
||||
|
||||
if (interp_mode == "nearest") {
|
||||
const std::string nearest_mode = GetOnnxAttr(node_helper, onnx_nearest_mode_attr);
|
||||
ORT_RETURN_IF_NOT(IsOnnxAttrModeSupported(supported_nearest_modes, nearest_mode),
|
||||
"QNN EP: Resize does not support nearest_mode ", nearest_mode.c_str());
|
||||
|
||||
if (is_npu_backend) {
|
||||
// QNN only supports the following nearest_mode values on HTP:
|
||||
// - "round_prefer_floor" via QNN's Resize operator
|
||||
// - "floor" via QNN's ResizeNearestNeighbor operator
|
||||
//
|
||||
// QNN validation does not throw an error if unsupported nearest_mode values are used, so we have to
|
||||
// catch them here. Otherwise, accuracy is significantly degraded.
|
||||
ORT_RETURN_IF_NOT(nearest_mode == "round_prefer_floor" || nearest_mode == "floor",
|
||||
"QNN EP: Resize on the NPU does not support nearest_mode ", nearest_mode.c_str());
|
||||
|
||||
const bool use_resize_nn_op = nearest_mode == "floor";
|
||||
|
||||
// If HTP uses ResizeNearestNeighbor ("floor"), then the "pytorch_half_pixel" coordinate_transformation_mode
|
||||
// is not supported.
|
||||
ORT_RETURN_IF(use_resize_nn_op && transformation_mode == "pytorch_half_pixel",
|
||||
"QNN EP: Resize on the NPU does not support the combination of nearest_mode == 'floor' ",
|
||||
" and coordinate_transformation_mode == 'pytorch_half_pixel'.");
|
||||
|
||||
// QNN's ResizeNearestNeighbor requires rank 4 inputs.
|
||||
ORT_RETURN_IF(use_resize_nn_op && input_rank != 4,
|
||||
"QNN EP: Resize on the NPU with nearest_mode == 'floor' requires an input with rank 4.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the input shape has at least a rank of 3 (and a max of 5 on HTP).
|
||||
ORT_RETURN_IF(input_rank < 3 || (is_npu_backend && input_rank > 5),
|
||||
"QNN EP: Resize input must have a rank >= 3. The maximum rank is 5 on the NPU.");
|
||||
|
||||
const auto& output_0 = node_unit.Outputs()[0];
|
||||
std::vector<uint32_t> output_shape;
|
||||
ORT_RETURN_IF_NOT(qnn_model_wrapper.GetOnnxShape(output_0.node_arg, output_shape),
|
||||
"QNN EP: Cannot get shape for Resize output");
|
||||
ORT_RETURN_IF(output_shape.size() < 3, "QNN EP: Resize output must have a rank >= 3.");
|
||||
|
||||
// Check that only the spatial dimensions (width, height) are resized. The batch_size (N) and channels (C) should
|
||||
// be untouched. This code runs before layout transformation, so we know that the current layout is "channel first"
|
||||
// (e.g., N, C, S1, S2, ..., SN), and that the minimum rank is 3.
|
||||
assert(node_unit.Domain() != kMSInternalNHWCDomain);
|
||||
ORT_RETURN_IF_NOT(input_shape[0] == output_shape[0] && input_shape[1] == output_shape[1],
|
||||
"QNN EP: Resize may only change the spatial dimensions.");
|
||||
|
||||
if (!is_npu_backend) {
|
||||
ONNX_NAMESPACE::DataType input_data_type = input_0.node_arg.Type();
|
||||
ORT_RETURN_IF(input_data_type != ONNX_NAMESPACE::Utils::DataTypeUtils::ToType("float"),
|
||||
"QNN EP: Data type ", input_data_type->c_str(),
|
||||
" is not supported for Resize operator in CPU backend.");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
|
@ -305,92 +228,34 @@ Status ResizeOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_w
|
|||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const {
|
||||
// The QNN Resize op does not currently work with the QNN cpu backend, but works with the HTP backend. Therefore, we
|
||||
// currently use QNN's Resize op for quantized models and either ResizeBilinear or ResizeNearestNeighbor for
|
||||
// non-quantized models. This requires separate handling for quantized models.
|
||||
// TODO: Use only Resize once QNN's Resize op works in the QNN cpu backend.
|
||||
bool is_quantized_node = NodeUnit::Type::QDQGroup == node_unit.UnitType();
|
||||
return is_quantized_node ? ProcessQDQOpAttrsAndOutputs(qnn_model_wrapper, node_unit, std::move(input_names), logger, do_op_validation) : ProcessOpAttrsAndOutputs(qnn_model_wrapper, node_unit, std::move(input_names), logger, do_op_validation);
|
||||
}
|
||||
|
||||
Status ResizeOpBuilder::ProcessOpAttrsAndOutputs(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);
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
const std::string resize_mode = GetOnnxAttr(node_helper, onnx_mode_attr);
|
||||
std::string qnn_node_type = "ResizeNearestNeighbor";
|
||||
if ("linear" == resize_mode) {
|
||||
qnn_node_type = "ResizeBilinear";
|
||||
}
|
||||
|
||||
const std::string coordinate_mode = GetOnnxAttr(node_helper, onnx_coord_transf_mode_attr);
|
||||
|
||||
Qnn_Scalar_t qnn_align_corners = QNN_SCALAR_INIT;
|
||||
qnn_align_corners.dataType = QNN_DATATYPE_BOOL_8;
|
||||
qnn_align_corners.bool8Value = static_cast<uint8_t>(0);
|
||||
|
||||
Qnn_Scalar_t qnn_half_pixel = QNN_SCALAR_INIT;
|
||||
qnn_half_pixel.dataType = QNN_DATATYPE_BOOL_8;
|
||||
qnn_half_pixel.bool8Value = static_cast<uint8_t>(0);
|
||||
|
||||
if ("align_corners" == coordinate_mode) {
|
||||
qnn_align_corners.bool8Value = static_cast<uint8_t>(1);
|
||||
} else if ("half_pixel" == coordinate_mode) {
|
||||
qnn_half_pixel.bool8Value = static_cast<uint8_t>(1);
|
||||
}
|
||||
QnnParamWrapper qnn_align_corners_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_RESIZE_BILINEAR_PARAM_ALIGN_CORNERS, qnn_align_corners);
|
||||
QnnParamWrapper qnn_half_pixel_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_RESIZE_BILINEAR_PARAM_HALF_PIXEL_CENTERS, qnn_half_pixel);
|
||||
|
||||
std::vector<std::string> param_tensor_names;
|
||||
param_tensor_names.push_back(qnn_align_corners_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_align_corners_param));
|
||||
param_tensor_names.push_back(qnn_half_pixel_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_half_pixel_param));
|
||||
|
||||
return ProcessOutputs(qnn_model_wrapper, node_unit, std::move(input_names), std::move(param_tensor_names),
|
||||
logger, do_op_validation, qnn_node_type);
|
||||
}
|
||||
|
||||
Status ResizeOpBuilder::ProcessQDQOpAttrsAndOutputs(QnnModelWrapper& qnn_model_wrapper,
|
||||
const NodeUnit& node_unit,
|
||||
std::vector<std::string>&& input_names,
|
||||
const logging::Logger& logger,
|
||||
bool do_op_validation) const {
|
||||
std::vector<std::string> param_tensor_names;
|
||||
NodeAttrHelper node_helper(node_unit);
|
||||
|
||||
const std::string interp_mode = GetOnnxAttr(node_helper, onnx_mode_attr);
|
||||
const std::string transformation_mode = GetOnnxAttr(node_helper, onnx_coord_transf_mode_attr);
|
||||
const std::string nearest_mode = GetOnnxAttr(node_helper, onnx_nearest_mode_attr);
|
||||
const bool is_npu_backend = IsNpuBackend(qnn_model_wrapper.GetQnnBackendType());
|
||||
std::string qnn_op_type = "Resize";
|
||||
|
||||
// Handle Resize with {mode: "nearest", coordinate_transformation_mode: "asymmetric"} uniquely.
|
||||
// QNN's ONNX converter tool translates this configuration (regardless of rounding mode)
|
||||
// to QNN's ResizeNearestNeighbor with {align_corners: 0, half_pixel: 0}.
|
||||
//
|
||||
// NOTE: This is only accurate if the rounding mode is "floor". Need to investigate how to handle
|
||||
// other rounding modes with Qualcomm. Ideally, we would use QNN's Resize operator, but it doesn't support
|
||||
// the "asymmetric" coordinate transformation mode on HTP.
|
||||
if (interp_mode == "nearest" && transformation_mode == "asymmetric") {
|
||||
// Translate Resize with {mode: "nearest", nearest_mode: "floor", coordinate_transformation_mode: XXX} to
|
||||
// QNN's ResizeNearestNeighbor operator on the HTP backend. This combination of parameters is not supported on HTP
|
||||
// via QNN's Resize operator. Note that QNN's ResizeNearestNeighbor operator always uses "floor" rounding.
|
||||
if (is_npu_backend && interp_mode == "nearest" && nearest_mode == "floor") {
|
||||
qnn_op_type = "ResizeNearestNeighbor";
|
||||
|
||||
// Set parameter 'align_corners' to 0
|
||||
// Parameter 'align_corners'
|
||||
Qnn_Scalar_t qnn_align_corners = QNN_SCALAR_INIT;
|
||||
qnn_align_corners.dataType = QNN_DATATYPE_BOOL_8;
|
||||
qnn_align_corners.bool8Value = static_cast<uint8_t>(0);
|
||||
qnn_align_corners.bool8Value = static_cast<uint8_t>(transformation_mode == "align_corners");
|
||||
QnnParamWrapper qnn_align_corners_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_RESIZE_BILINEAR_PARAM_ALIGN_CORNERS, qnn_align_corners);
|
||||
param_tensor_names.push_back(qnn_align_corners_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_align_corners_param));
|
||||
|
||||
// Set parameter 'half_pixel_centers' to 0
|
||||
// Parameter 'half_pixel_centers'
|
||||
Qnn_Scalar_t qnn_half_pixel = QNN_SCALAR_INIT;
|
||||
qnn_half_pixel.dataType = QNN_DATATYPE_BOOL_8;
|
||||
qnn_half_pixel.bool8Value = static_cast<uint8_t>(0);
|
||||
qnn_half_pixel.bool8Value = static_cast<uint8_t>(transformation_mode == "half_pixel");
|
||||
QnnParamWrapper qnn_half_pixel_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_RESIZE_BILINEAR_PARAM_HALF_PIXEL_CENTERS, qnn_half_pixel);
|
||||
param_tensor_names.push_back(qnn_half_pixel_param.GetParamTensorName());
|
||||
|
|
@ -399,11 +264,12 @@ Status ResizeOpBuilder::ProcessQDQOpAttrsAndOutputs(QnnModelWrapper& qnn_model_w
|
|||
// Parameter 'transformation_mode'
|
||||
Qnn_Scalar_t qnn_transformation_mode = QNN_SCALAR_INIT;
|
||||
qnn_transformation_mode.dataType = QNN_DATATYPE_UINT_32;
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeFromString(supported_coord_transf_modes, transformation_mode,
|
||||
"coordinate_transformation_mode", qnn_transformation_mode.uint32Value));
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeValFromOnnxString(supported_coord_transf_modes, transformation_mode,
|
||||
"coordinate_transformation_mode",
|
||||
qnn_transformation_mode.uint32Value));
|
||||
|
||||
QnnParamWrapper qnn_transformation_mode_param(node_unit.Index(), node_unit.Name(), QNN_OP_RESIZE_PARAM_TRANSFORMATION_MODE,
|
||||
qnn_transformation_mode);
|
||||
QnnParamWrapper qnn_transformation_mode_param(node_unit.Index(), node_unit.Name(),
|
||||
QNN_OP_RESIZE_PARAM_TRANSFORMATION_MODE, qnn_transformation_mode);
|
||||
param_tensor_names.push_back(qnn_transformation_mode_param.GetParamTensorName());
|
||||
qnn_model_wrapper.AddParamWrapper(std::move(qnn_transformation_mode_param));
|
||||
|
||||
|
|
@ -420,7 +286,7 @@ Status ResizeOpBuilder::ProcessQDQOpAttrsAndOutputs(QnnModelWrapper& qnn_model_w
|
|||
// Parameter 'interpolation_mode'
|
||||
Qnn_Scalar_t qnn_interp_mode = QNN_SCALAR_INIT;
|
||||
qnn_interp_mode.dataType = QNN_DATATYPE_UINT_32;
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeFromString(supported_modes, interp_mode, "mode", qnn_interp_mode.uint32Value));
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeValFromOnnxString(supported_modes, interp_mode, "mode", qnn_interp_mode.uint32Value));
|
||||
|
||||
QnnParamWrapper qnn_interp_mode_param(node_unit.Index(), node_unit.Name(), QNN_OP_RESIZE_PARAM_INTERPOLATION_MODE,
|
||||
qnn_interp_mode);
|
||||
|
|
@ -429,11 +295,10 @@ Status ResizeOpBuilder::ProcessQDQOpAttrsAndOutputs(QnnModelWrapper& qnn_model_w
|
|||
|
||||
// Parameter 'nearest_mode'. Processed only when 'interpolation_mode' is NEAREST(0).
|
||||
if (qnn_interp_mode.uint32Value == 0) {
|
||||
const std::string nearest_mode = GetOnnxAttr(node_helper, onnx_nearest_mode_attr);
|
||||
Qnn_Scalar_t qnn_nearest_mode = QNN_SCALAR_INIT;
|
||||
qnn_nearest_mode.dataType = QNN_DATATYPE_UINT_32;
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeFromString(supported_nearest_modes, nearest_mode, "nearest_mode",
|
||||
qnn_nearest_mode.uint32Value));
|
||||
ORT_RETURN_IF_ERROR(GetQnnModeValFromOnnxString(supported_nearest_modes, nearest_mode, "nearest_mode",
|
||||
qnn_nearest_mode.uint32Value));
|
||||
|
||||
QnnParamWrapper qnn_nearest_mode_param(node_unit.Index(), node_unit.Name(), QNN_OP_RESIZE_PARAM_NEAREST_MODE,
|
||||
qnn_nearest_mode);
|
||||
|
|
|
|||
|
|
@ -99,9 +99,8 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extr
|
|||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// TensorRT: results mismatch
|
||||
// ROCm: results mismatch
|
||||
// QNN: conflict with layout transformer, need furture investigation
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
|
||||
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
{kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapolation_uint8) {
|
||||
|
|
@ -131,7 +130,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extr
|
|||
test.AddOutput<uint8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapolation_int8) {
|
||||
|
|
@ -159,7 +158,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extr
|
|||
10, 10, 10};
|
||||
|
||||
test.AddOutput<int8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_without_extrapolation_uint8) {
|
||||
|
|
@ -188,7 +187,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_without_e
|
|||
test.AddOutput<uint8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_without_extrapolation_int8) {
|
||||
|
|
@ -215,7 +214,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_without_e
|
|||
0, 0, 0};
|
||||
|
||||
test.AddOutput<int8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear) {
|
||||
|
|
@ -261,9 +260,8 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear) {
|
|||
test.AddOutput<float>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
// QNN: conflict with layout transformer, need furture investigation
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
|
||||
{kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
{kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_uint8) {
|
||||
|
|
@ -287,7 +285,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_uint8) {
|
|||
test.AddOutput<uint8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_int8) {
|
||||
|
|
@ -309,7 +307,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_int8) {
|
|||
std::vector<int8_t> Y = {0, 0};
|
||||
|
||||
test.AddOutput<int8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider});
|
||||
test.Run();
|
||||
}
|
||||
|
||||
// Since NNAPI(TFLite) only using the scale calculate using the input/output size
|
||||
|
|
@ -399,7 +397,9 @@ TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear_align_corners) {
|
|||
std::vector<float> Y = {1.0f, 4.0f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
|
||||
// QNN: result mismatch ("NaN" instead of 1.0f on QNN CPU backend)
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
|
@ -435,7 +435,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_align_corners_uin
|
|||
test.AddOutput<uint8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
|
@ -465,7 +465,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_align_corners_int
|
|||
|
||||
test.AddOutput<int8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C}, Y);
|
||||
// TensorRT: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
|
@ -532,7 +532,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_pytorch_half_pixe
|
|||
test.AddOutput<uint8_t>("Y", {N, sizes[1], sizes[2], C}, Y);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_pytorch_half_pixel_int8) {
|
||||
|
|
@ -560,7 +560,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_4DBilinear_pytorch_half_pixe
|
|||
std::vector<int8_t> Y = {0, 2, -9};
|
||||
|
||||
test.AddOutput<int8_t>("Y", {N, sizes[1], sizes[2], C}, Y);
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider}); // TensorRT: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); // TensorRT: results mismatch
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearUpSampleTest_4DBilinear_asymmetric) {
|
||||
|
|
@ -641,7 +641,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearUpSampleTest_4DBilinear_asymmetric_uint8) {
|
|||
Y, false, .0f, 1.0f);
|
||||
// CUDA: result mismatch due to not implementing NHWC support
|
||||
// ROCm: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kRocmExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
|
@ -683,7 +683,7 @@ TEST(ResizeOpTest, NhwcResizeOpLinearUpSampleTest_4DBilinear_asymmetric_int8) {
|
|||
test.AddOutput<int8_t>("Y", {N, static_cast<int64_t>(H * scales[1]), static_cast<int64_t>(W * scales[2]), C},
|
||||
Y, false, .0f, 1.0f);
|
||||
// TensorRT: results mismatch
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
|
@ -1079,7 +1079,7 @@ TEST(ResizeOpTest, ResizeOpNearestUpSample_Floor_Align_Corners) {
|
|||
13.0f, 13.0f, 13.0f, 14.0f, 14.0f, 15.0f, 15.0f, 16.0f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kQnnExecutionProvider}); // QNN: result diff
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpNearest_OneToOneMappingBetweenInputAndOutputDataDims) {
|
||||
|
|
@ -1887,7 +1887,7 @@ void TestAntialiasing(std::map<std::string, std::string> attributes,
|
|||
|
||||
test.AddOutput<T>("Y", output_shape, output_data);
|
||||
// TensorRT 8.5 supports operators up to Opset 17. Temporarily exclude TensorRT EP due to accurarcy issue.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, Antialias_Bilinear_No_ExcludeOutside) {
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ static void RunCPUResizeOpTest(const TestInputDef<float>& input_def, const std::
|
|||
const std::string& mode, const std::string& coordinate_transformation_mode,
|
||||
const std::string& nearest_mode,
|
||||
ExpectedEPNodeAssignment expected_ep_assignment,
|
||||
int opset = 11) {
|
||||
int opset = 19) {
|
||||
ProviderOptions provider_options;
|
||||
#if defined(_WIN32)
|
||||
provider_options["backend_path"] = "QnnCpu.dll";
|
||||
|
|
@ -138,7 +138,7 @@ static void RunCPUResizeOpTestWithScales(const TestInputDef<float>& input_def, c
|
|||
const std::string& mode, const std::string& coordinate_transformation_mode,
|
||||
const std::string& nearest_mode,
|
||||
ExpectedEPNodeAssignment expected_ep_assignment,
|
||||
int opset = 11) {
|
||||
int opset = 19) {
|
||||
ProviderOptions provider_options;
|
||||
#if defined(_WIN32)
|
||||
provider_options["backend_path"] = "QnnCpu.dll";
|
||||
|
|
@ -157,7 +157,8 @@ static void RunQDQResizeOpTest(const TestInputDef<float>& input_def,
|
|||
const std::vector<int64_t>& sizes_data,
|
||||
const std::string& mode, const std::string& coordinate_transformation_mode,
|
||||
const std::string& nearest_mode,
|
||||
ExpectedEPNodeAssignment expected_ep_assignment) {
|
||||
ExpectedEPNodeAssignment expected_ep_assignment,
|
||||
int opset = 19) {
|
||||
ProviderOptions provider_options;
|
||||
#if defined(_WIN32)
|
||||
provider_options["backend_path"] = "QnnHtp.dll";
|
||||
|
|
@ -169,27 +170,20 @@ static void RunQDQResizeOpTest(const TestInputDef<float>& input_def,
|
|||
GetQDQResizeModelBuilder<QuantType>(input_def, sizes_data, mode, coordinate_transformation_mode,
|
||||
nearest_mode),
|
||||
provider_options,
|
||||
18, // opset
|
||||
expected_ep_assignment,
|
||||
1e-5f);
|
||||
opset,
|
||||
expected_ep_assignment);
|
||||
}
|
||||
|
||||
//
|
||||
// CPU tests:
|
||||
//
|
||||
|
||||
// TODO: Our QNN CPU translation of ONNX Resize with "nearest" mode uses QNN's ResizeNearestNeighbor
|
||||
// operator, which does not have a way to specify rounding (i.e., "nearest_mode" in ONNX). It is not clear
|
||||
// what kind of rounding QNN's ResizeNearestNeighbor uses. Therefore, we do not yet know how to compare
|
||||
// ONNX Resize to QNN ResizeNearestNeighbor. These tests should remain disabled until this behavior is
|
||||
// clarified. If, for example, it turns out that ResizeNearestNeighbor uses "floor" rounding, then we should
|
||||
// only compare against ONNX resize with "floor" rounding.
|
||||
|
||||
// Upsample that uses "round_prefer_floor" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "half_pixel"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeUpsampleNearestHalfPixel_rpf) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 2, 7, 5}, false, -10.0f, 10.0f), // Random input w/ range [-10, 10]
|
||||
{1, 2, 21, 10}, // Sizes
|
||||
TEST_F(QnnCPUBackendTests, ResizeUpsampleNearestHalfPixel_rpf) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 70);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 2, 7, 5}, false, input_data),
|
||||
{1, 2, 21, 10}, // Sizes
|
||||
"nearest",
|
||||
"half_pixel",
|
||||
"round_prefer_floor",
|
||||
|
|
@ -198,57 +192,72 @@ TEST_F(QnnCPUBackendTests, DISABLED_ResizeUpsampleNearestHalfPixel_rpf) {
|
|||
|
||||
// Upsample that uses "round_prefer_ceil" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "half_pixel"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeUpsampleNearestHalfPixel_rpc) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeUpsampleNearestHalfPixel_rpc) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 7, 5}, "nearest", "half_pixel", "round_prefer_ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Downsample that uses "round_prefer_ceil" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "half_pixel"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeDownsampleNearestHalfPixel_rpc) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeDownsampleNearestHalfPixel_rpc) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 3}, "nearest", "half_pixel", "round_prefer_ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Downsample that uses "round_prefer_floor" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "half_pixel"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeDownsampleNearestHalfPixel_rpf) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeDownsampleNearestHalfPixel_rpf) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 2}, "nearest", "half_pixel", "round_prefer_ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Upsample that uses "round_prefer_floor" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "align_corners"
|
||||
// QNN v2.13: index #50 don't match, which is 4.67152 from -1.93515
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeUpsampleNearestAlignCorners_rpf) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 2, 7, 5}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeUpsampleNearestAlignCorners_rpf) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 70);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 2, 7, 5}, false, input_data),
|
||||
{1, 2, 21, 10}, "nearest", "align_corners", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Upsample that uses "round_prefer_floor" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "asymmetric"
|
||||
TEST_F(QnnCPUBackendTests, ResizeUpsampleNearestAsymmetric_rpf) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 70);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 2, 7, 5}, false, input_data),
|
||||
{1, 2, 21, 10}, "nearest", "asymmetric", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Upsample that uses "round_prefer_ceil" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "align_corners"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeUpsampleNearestAlignCorners_rpc) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeUpsampleNearestAlignCorners_rpc) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 7, 5}, "nearest", "align_corners", "round_prefer_ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Downsample that uses "round_prefer_ceil" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "align_corners"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeDownsampleNearestAlignCorners_rpc) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeDownsampleNearestAlignCorners_rpc) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 3}, "nearest", "align_corners", "round_prefer_ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Downsample that uses "round_prefer_floor" as the "nearest_mode".
|
||||
// coordinate_transformation_mode: "align_corners"
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_ResizeDownsampleNearestAlignCorners_rpf) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, -10.0f, 10.0f),
|
||||
TEST_F(QnnCPUBackendTests, ResizeDownsampleNearestAlignCorners_rpf) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 2}, "nearest", "align_corners", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
|
@ -258,76 +267,177 @@ TEST_F(QnnCPUBackendTests, DISABLED_ResizeDownsampleNearestAlignCorners_rpf) {
|
|||
//
|
||||
|
||||
TEST_F(QnnCPUBackendTests, Resize2xLinearHalfPixel) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 3, 4, 5}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 60);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 3, 4, 5}, false, input_data),
|
||||
{1, 3, 8, 10}, "linear", "half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
TEST_F(QnnCPUBackendTests, Resize2xLinearHalfPixel_scales) {
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 3, 4, 5}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 60);
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 3, 4, 5}, false, input_data),
|
||||
{1.0f, 1.0f, 2.0f, 2.0f}, "linear", "half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
TEST_F(QnnCPUBackendTests, Resize2xLinearAlignCorners) {
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 3, 4, 5}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 60);
|
||||
RunCPUResizeOpTest(TestInputDef<float>({1, 3, 4, 5}, false, input_data),
|
||||
{1, 3, 8, 10}, "linear", "align_corners", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
TEST_F(QnnCPUBackendTests, Resize2xLinearAlignCorners_scales) {
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 3, 4, 5}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 60);
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 3, 4, 5}, false, input_data),
|
||||
{1.0f, 1.0f, 2.0f, 2.0f}, "linear", "align_corners", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test Resize downsample with mode: "linear", coordinate_transformation_mode: "align_corners"
|
||||
// TODO: Enable ResizeOpTest.ResizeOpLinearDownSampleTest_4DBilinear_align_corners in cpu resize_op tests when fixed.
|
||||
//
|
||||
// Input f32[1,1,2,4]: 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0
|
||||
// Expected output f32[1, 1, 1, 2]: 1.0, 4.0
|
||||
// Actual output f32[1, 1, 1, 2]: NaN, NaN
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_Resize_DownSample_Linear_AlignCorners_scales) {
|
||||
std::vector<float> input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1.0f, 1.0f, 0.6f, 0.6f}, "linear", "align_corners", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test Resize downsample with mode: "linear", coordinate_transformation_mode: "half_pixel"
|
||||
// TODO: Enable ResizeOpTest.ResizeOpLinearDownSampleTest_4DBilinear cpu resize_op tests when fixed.
|
||||
//
|
||||
// Input f32[1,1,2,4]: 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0
|
||||
// Expected output f32[1, 1, 1, 2]: 2.6666 4.3333
|
||||
// Actual output f32[1, 1, 1, 2]: NaN, NaN
|
||||
TEST_F(QnnCPUBackendTests, DISABLED_Resize_DownSample_Linear_HalfPixel_scales) {
|
||||
std::vector<float> input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
RunCPUResizeOpTestWithScales(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1.0f, 1.0f, 0.6f, 0.6f}, "linear", "half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__)
|
||||
//
|
||||
// HTP tests:
|
||||
//
|
||||
|
||||
// Test QDQ Resize downsample with mode: "linear", coordinate_transformation_mode: "align_corners"
|
||||
TEST_F(QnnHTPBackendTests, Resize_DownSample_Linear_AlignCorners) {
|
||||
std::vector<float> input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 2}, "linear", "align_corners", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test QDQ Resize downsample with mode: "linear", coordinate_transformation_mode: "half_pixel"
|
||||
TEST_F(QnnHTPBackendTests, Resize_DownSample_Linear_HalfPixel) {
|
||||
std::vector<float> input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 1, 2, 4}, false, input_data),
|
||||
{1, 1, 1, 2}, "linear", "half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "linear", coordinate_transformation_mode: "pytorch_half_pixel"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xLinearPytorchHalfPixel) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "linear", "pytorch_half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "linear", coordinate_transformation_mode: "half_pixel"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xLinearHalfPixel) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "linear", "half_pixel", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "linear", coordinate_transformation_mode: "align_corners"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xLinearAlignCorners) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "linear", "align_corners", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "linear", coordinate_transformation_mode: "asymmetric"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xLinearAsymmetric) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "linear", "asymmetric", "",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "nearest", coordinate_transformation_mode: "half_pixel", nearest_mode: "round_prefer_floor"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xNearestHalfPixelRoundPreferFloor) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "nearest", "half_pixel", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xNearestAsymmetricFloor) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
{1, 3, 8, 8}, "nearest", "asymmetric", "floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// TODO: Investigate with Qualcomm. The qnn-onnx-converter tool translates ONNX Resize [nearest, asymmetric, ceil] to
|
||||
// QNN ResizeNearestNeighbor {align_corners: 0, half_pixel: 0}, which is NOT equivalent. It would be better to use
|
||||
// QNN's own Resize operator (instead of ResizeNearestNeighbor), but it doesn't support the "asymmetric" coordinate
|
||||
// transform mode.
|
||||
//
|
||||
// QNN v2.13: Inaccuracy detected for output 'output', element 189.
|
||||
// Output quant params: scale=0.078431375324726105, zero_point=127.
|
||||
// Expected val: -2.663428783416748
|
||||
// QNN QDQ val: 7.4509806632995605 (err 10.114409446716309)
|
||||
// CPU QDQ val: -2.6666667461395264 (err 0.0032379627227783203)
|
||||
TEST_F(QnnHTPBackendTests, DISABLED_ResizeU8_2xNearestAsymmetricCeil) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
// Test that the nearest_mode "ceil" is not supported on the HTP backend.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_NearestModeCeil_Unsupported) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 8, 8}, "nearest", "asymmetric", "ceil",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
ExpectedEPNodeAssignment::None);
|
||||
}
|
||||
|
||||
// Test 3x QDQ Resize mode: "nearest", coordinate_transformation_mode: "asymmetric", nearest_mode: "floor".
|
||||
// QNN EP uses QNN's ResizeNearestNeighbor op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_3xNearestAsymmetricFloor) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 12, 12}, "nearest", "asymmetric", "floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 2x QDQ Resize mode: "nearest", coordinate_transformation_mode: "asymmetric", nearest_mode: "round_prefer_floor"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_2xNearestAsymmetricRoundPreferFloor) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 8);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 2, 2, 2}, false, input_data),
|
||||
{1, 2, 4, 4}, "nearest", "asymmetric", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 3x QDQ Resize mode: "nearest", coordinate_transformation_mode: "asymmetric", nearest_mode: "round_prefer_floor"
|
||||
// QNN EP uses QNN's Resize op.
|
||||
//
|
||||
// TODO: Inaccuracy detected for output 'output_0', element 2.
|
||||
// Output quant params: scale=0.078431375324726105, zero_point=127.
|
||||
// Expected val: -3.3333334922790527
|
||||
// QNN QDQ val: -9.960784912109375 (err 6.6274514198303223)
|
||||
// CPU QDQ val: -3.2941176891326904 (err 0.039215803146362305)
|
||||
//
|
||||
// More debugging info:
|
||||
// Input elements f32[1,1,2,2] = -10.0000000 -3.33333349 3.33333302 10.0000000
|
||||
// ORT CPU EP (f32 model) outputs: -10.0000000 -10.0000000 -3.33333349 -3.33333349 -3.33333349 -3.33333349 -10.00 ...
|
||||
// ORT CPU EP (qdq model) outputs: -9.96078491 -9.96078491 -3.29411769 -3.29411769 -3.29411769 -3.29411769 -9.961 ...
|
||||
// ORT QNN EP (qdq model) outputs: -9.96078491 -9.96078491 -9.96078491 -3.37254906 -3.37254906 -3.37254906 -9.961 ...
|
||||
TEST_F(QnnHTPBackendTests, DISABLED_ResizeU8_3xNearestAsymmetricRoundPreferFloor) {
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 4);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 1, 2, 2}, false, input_data),
|
||||
{1, 1, 6, 6}, "nearest", "asymmetric", "round_prefer_floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
||||
// Test 0.5x QDQ Resize mode: "nearest", coordinate_transformation_mode: "asymmetric", nearest_mode: "floor"
|
||||
// QNN EP uses QNN's ResizeNearestNeighbor op.
|
||||
TEST_F(QnnHTPBackendTests, ResizeU8_HalfNearestAsymmetricFloor) {
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, -10.0f, 10.0f),
|
||||
std::vector<float> input_data = GetFloatDataInRange(-10.0f, 10.0f, 48);
|
||||
RunQDQResizeOpTest<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, input_data),
|
||||
{1, 3, 2, 2}, "nearest", "asymmetric", "floor",
|
||||
ExpectedEPNodeAssignment::All);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue