mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-05-26 22:35:43 +00:00
[CoreML] Add Softmax and Split op support (#18358)
### Description <!-- Describe your changes. --> As title. ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> Added for yolov8 model missing operator support. https://github.com/microsoft/onnxruntime/issues/17654 Now the model support info looks like: _CoreMLExecutionProvider::GetCapability, number of partitions supported by CoreML: 3 number of nodes in the graph: 233 number of nodes supported by CoreML: 230_ (only missing 3 concat op support due to input 3d shape is not currently support in CoreML EP Concat). --------- Co-authored-by: rachguo <rachguo@rachguos-Mini.attlocal.net> Co-authored-by: rachguo <rachguo@rachguos-Mac-mini.local> Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com>
This commit is contained in:
parent
6f3c1f9dc9
commit
62f00ad8e7
9 changed files with 394 additions and 7 deletions
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/providers/coreml/builders/impl/base_op_builder.h"
|
||||
|
||||
#include "core/framework/tensorprotoutils.h"
|
||||
#include "core/providers/common.h"
|
||||
#include "core/providers/coreml/shape_utils.h"
|
||||
#include "core/providers/shared/utils/utils.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "core/providers/coreml/builders/model_builder.h"
|
||||
#endif
|
||||
#include "core/providers/coreml/builders/op_builder_factory.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace coreml {
|
||||
|
||||
class SoftmaxOpBuilder : public BaseOpBuilder {
|
||||
// Add operator related
|
||||
#ifdef __APPLE__
|
||||
private:
|
||||
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node,
|
||||
const logging::Logger& logger) const override;
|
||||
#endif
|
||||
|
||||
// Operator support related
|
||||
private:
|
||||
bool IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& input_params,
|
||||
const logging::Logger& logger) const override;
|
||||
};
|
||||
|
||||
// Add operator related
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
Status SoftmaxOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
|
||||
const Node& node,
|
||||
const logging::Logger& logger) const {
|
||||
std::unique_ptr<COREML_SPEC::NeuralNetworkLayer> layer = CreateNNLayer(model_builder, node);
|
||||
const auto& input_name = node.InputDefs()[0]->Name();
|
||||
const auto& output_name = node.OutputDefs()[0]->Name();
|
||||
|
||||
std::vector<int64_t> data_shape;
|
||||
ORT_RETURN_IF_NOT(GetStaticShape(*node.InputDefs()[0], data_shape, logger), "Failed to get input shape.");
|
||||
|
||||
NodeAttrHelper helper(node);
|
||||
int32_t axis_default_value = (node.SinceVersion() < 13) ? 1 : -1;
|
||||
const auto axis = helper.Get("axis", axis_default_value);
|
||||
const auto axis_nonnegative = HandleNegativeAxis(axis, data_shape.size());
|
||||
|
||||
if (node.SinceVersion() >= 13 || (data_shape.size() == 2)) {
|
||||
auto* coreml_softmaxnd = layer->mutable_softmaxnd();
|
||||
coreml_softmaxnd->set_axis(axis);
|
||||
*layer->mutable_input()->Add() = input_name;
|
||||
*layer->mutable_output()->Add() = output_name;
|
||||
model_builder.AddLayer(std::move(layer));
|
||||
} else {
|
||||
// note: if opsets < 13, onnx Softmax coerces the input shape to be 2D based on axis.
|
||||
// we need to manually reshape to 2D and apply SoftmaxND to axis -1 to achieve equivalent results for CoreML.
|
||||
TensorShape input_shape(data_shape);
|
||||
const auto size_to_dimension = input_shape.SizeToDimension(axis_nonnegative);
|
||||
const auto size_from_dimension = input_shape.SizeFromDimension(axis_nonnegative);
|
||||
|
||||
TensorShapeVector target_shape;
|
||||
target_shape.push_back(size_to_dimension);
|
||||
target_shape.push_back(size_from_dimension);
|
||||
|
||||
const auto reshape1_output_name = model_builder.GetUniqueName(MakeString(node.Name(), "reshape1_output"));
|
||||
{ // Add reshape layer
|
||||
const auto softmax_reshape1_layer_name =
|
||||
model_builder.GetUniqueName(MakeString(node.Name(), "_Softmax_reshape1"));
|
||||
auto reshape_layer = CreateNNLayer(softmax_reshape1_layer_name);
|
||||
*reshape_layer->mutable_reshapestatic()->mutable_targetshape() = {target_shape.cbegin(), target_shape.cend()};
|
||||
*reshape_layer->mutable_input()->Add() = input_name;
|
||||
*reshape_layer->mutable_output()->Add() = reshape1_output_name;
|
||||
model_builder.AddLayer(std::move(reshape_layer));
|
||||
}
|
||||
const auto softmax_output_name = model_builder.GetUniqueName(MakeString(node.Name(), "softmax_output"));
|
||||
{
|
||||
auto* coreml_softmaxnd = layer->mutable_softmaxnd();
|
||||
coreml_softmaxnd->set_axis(-1);
|
||||
*layer->mutable_input()->Add() = reshape1_output_name;
|
||||
*layer->mutable_output()->Add() = softmax_output_name;
|
||||
model_builder.AddLayer(std::move(layer));
|
||||
}
|
||||
{
|
||||
// Add reshape back layer
|
||||
const auto softmax_reshape2_layer_name =
|
||||
model_builder.GetUniqueName(MakeString(node.Name(), "_Softmax_reshape2"));
|
||||
auto reshape_layer = CreateNNLayer(softmax_reshape2_layer_name);
|
||||
*reshape_layer->mutable_reshapestatic()->mutable_targetshape() = {data_shape.cbegin(), data_shape.cend()};
|
||||
*reshape_layer->mutable_input()->Add() = softmax_output_name;
|
||||
*reshape_layer->mutable_output()->Add() = output_name;
|
||||
model_builder.AddLayer(std::move(reshape_layer));
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Operator support related
|
||||
|
||||
bool SoftmaxOpBuilder::IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& /* input_params */,
|
||||
const logging::Logger& logger) const {
|
||||
const auto& input_defs = node.InputDefs();
|
||||
std::vector<int64_t> input_shape;
|
||||
if (!GetStaticShape(*input_defs[0], input_shape, logger))
|
||||
return false;
|
||||
|
||||
const TensorShape shape(input_shape);
|
||||
if (shape.Size() == 0) {
|
||||
LOGS(logger, VERBOSE) << "Empty input data is not supported.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateSoftmaxOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations) {
|
||||
op_registrations.builders.push_back(std::make_unique<SoftmaxOpBuilder>());
|
||||
op_registrations.op_builder_map.emplace(op_type, op_registrations.builders.back().get());
|
||||
}
|
||||
|
||||
} // namespace coreml
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/providers/coreml/builders/impl/base_op_builder.h"
|
||||
|
||||
#include "core/optimizer/initializer.h"
|
||||
#include "core/providers/common.h"
|
||||
#include "core/providers/coreml/builders/helper.h"
|
||||
#include "core/providers/coreml/builders/op_builder_factory.h"
|
||||
#include "core/providers/coreml/shape_utils.h"
|
||||
#include "core/providers/shared/utils/utils.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "core/providers/coreml/builders/model_builder.h"
|
||||
#endif
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace coreml {
|
||||
|
||||
class SplitOpBuilder : public BaseOpBuilder {
|
||||
// Add operator related
|
||||
#ifdef __APPLE__
|
||||
private:
|
||||
void AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) const override;
|
||||
|
||||
private:
|
||||
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node,
|
||||
const logging::Logger& logger) const override;
|
||||
#endif
|
||||
|
||||
// Operator support related
|
||||
private:
|
||||
bool IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& input_params,
|
||||
const logging::Logger& logger) const override;
|
||||
|
||||
// Split opset 13- uses "split" as attribute. Currently it's not supported.
|
||||
int GetMinSupportedOpSet(const Node& /* node */) const override { return 13; }
|
||||
};
|
||||
|
||||
// Add operator related
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
void SplitOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) const {
|
||||
const auto& input_defs = node.InputDefs();
|
||||
|
||||
if (input_defs.size() > 1 && input_defs[1]->Exists()) { // optional second input "split"
|
||||
model_builder.AddInitializerToSkip(input_defs[1]->Name());
|
||||
}
|
||||
}
|
||||
|
||||
Status SplitOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder,
|
||||
const Node& node,
|
||||
const logging::Logger& logger) const {
|
||||
const auto& input_defs = node.InputDefs();
|
||||
|
||||
std::vector<int64_t> data_shape;
|
||||
ORT_RETURN_IF_NOT(GetShape(*node.InputDefs()[0], data_shape, logger), "Failed to get input shape.");
|
||||
|
||||
NodeAttrHelper helper(node);
|
||||
const auto axis = helper.Get("axis", 0);
|
||||
|
||||
// attribute introduced since opset 18
|
||||
uint64_t num_outputs;
|
||||
|
||||
std::unique_ptr<COREML_SPEC::NeuralNetworkLayer> layer = CreateNNLayer(model_builder, node);
|
||||
auto* coreml_splitnd = layer->mutable_splitnd();
|
||||
coreml_splitnd->set_axis(axis);
|
||||
|
||||
if (input_defs.size() > 1) {
|
||||
// if "split" is explicitly provided as an input
|
||||
const auto& split_tensor = *model_builder.GetInitializerTensors().at(input_defs[1]->Name());
|
||||
Initializer unpacked_tensor(split_tensor);
|
||||
auto split_span = unpacked_tensor.DataAsSpan<uint64_t>();
|
||||
auto split_sizes = split_span.size();
|
||||
num_outputs = narrow<uint64_t>(split_sizes);
|
||||
for (size_t i = 0; i < split_sizes; i++) {
|
||||
coreml_splitnd->add_splitsizes(split_span[i]);
|
||||
}
|
||||
} else if (node.SinceVersion() < 18) {
|
||||
num_outputs = narrow<uint64_t>(node.OutputDefs().size());
|
||||
coreml_splitnd->set_numsplits(num_outputs);
|
||||
} else {
|
||||
// note: for opset 18+ 'num_outputs' is a required attribute
|
||||
num_outputs = narrow<uint64_t>(helper.GetInt("num_outputs").value());
|
||||
// note: checked in IsOpSupportedImpl that ensures the dim value at splitting axis exists
|
||||
auto split_dim_size = data_shape[HandleNegativeAxis(axis, data_shape.size())];
|
||||
uint64_t chunk_size = narrow<uint64_t>((split_dim_size + num_outputs - 1) / num_outputs);
|
||||
uint64_t remainder = split_dim_size % chunk_size;
|
||||
if (remainder) {
|
||||
// uneven
|
||||
auto split_sizes = InlinedVector<uint64_t>(num_outputs, chunk_size);
|
||||
split_sizes.back() = remainder;
|
||||
for (size_t i = 0; i < split_sizes.size(); i++) {
|
||||
coreml_splitnd->add_splitsizes(split_sizes[i]);
|
||||
}
|
||||
} else {
|
||||
// even
|
||||
coreml_splitnd->set_numsplits(num_outputs);
|
||||
}
|
||||
}
|
||||
|
||||
*layer->mutable_input()->Add() = node.InputDefs()[0]->Name();
|
||||
// variadic number of outputs. Calculated based on the length of the given splitSizes if provided.
|
||||
// Otherwise, uses attribute value 'num_outputs'.
|
||||
for (uint64_t i = 0; i < num_outputs; i++) {
|
||||
*layer->mutable_output()->Add() = node.OutputDefs()[i]->Name();
|
||||
}
|
||||
model_builder.AddLayer(std::move(layer));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Operator support related
|
||||
|
||||
bool SplitOpBuilder::IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& input_params,
|
||||
const logging::Logger& logger) const {
|
||||
const auto& input_defs = node.InputDefs();
|
||||
const auto& initializers = input_params.graph_viewer.GetAllInitializedTensors();
|
||||
|
||||
NodeAttrHelper helper(node);
|
||||
const auto axis = helper.Get("axis", 0);
|
||||
|
||||
std::vector<int64_t> input_shape;
|
||||
if (!GetShape(*input_defs[0], input_shape, logger))
|
||||
return false;
|
||||
|
||||
const auto split_dims_at_axis = input_shape[HandleNegativeAxis(axis, input_shape.size())];
|
||||
if (input_defs.size() > 1 && input_defs[1]->Exists()) {
|
||||
if (!CheckIsConstantInitializer(*input_defs[1], input_params.graph_viewer, logger, "'split'")) {
|
||||
return false;
|
||||
}
|
||||
const auto split_shape = *input_defs[1]->Shape();
|
||||
if (split_shape.dim_size() < 2) {
|
||||
LOGS(logger, VERBOSE) << "CoreML SplitND requires to produce at least 2 outputs.";
|
||||
return false;
|
||||
}
|
||||
const auto& splits_tensor = *initializers.at(input_defs[1]->Name());
|
||||
Initializer unpacked_tensor(splits_tensor);
|
||||
auto splits_span = unpacked_tensor.DataAsSpan<uint64_t>();
|
||||
int sum_of_splits = std::accumulate(splits_span.begin(), splits_span.end(), 0);
|
||||
if (sum_of_splits != split_dims_at_axis) {
|
||||
LOGS(logger, VERBOSE) << "Mismatch between the sum of 'split'. Expected: "
|
||||
<< split_dims_at_axis
|
||||
<< "Actual: "
|
||||
<< sum_of_splits;
|
||||
return false;
|
||||
}
|
||||
auto it = std::find(splits_span.begin(), splits_span.end(), 0);
|
||||
if (it != splits_span.end()) {
|
||||
LOGS(logger, VERBOSE) << "Invalid value in 'splits' input.";
|
||||
return false;
|
||||
}
|
||||
if (split_dims_at_axis == -1) {
|
||||
LOGS(logger, VERBOSE) << "Dim at the splitting axis is not allowed to be dynamic.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (node.SinceVersion() >= 18) {
|
||||
const auto num_outputs = helper.GetInt("num_outputs");
|
||||
if (!num_outputs.has_value()) {
|
||||
LOGS(logger, VERBOSE) << "No 'num_outputs' provided. For split 18+, num_outputs is a required attribute.";
|
||||
return false;
|
||||
}
|
||||
if (num_outputs.value() < 2) {
|
||||
LOGS(logger, VERBOSE) << "Invalid num_outputs. The value cannot be lower than 2.\n"
|
||||
<< "CoreML SplitND requires at least 2 outputs. num_outputs: " << num_outputs.value();
|
||||
return false;
|
||||
}
|
||||
if (num_outputs.value() != static_cast<int32_t>(node.OutputDefs().size()) || num_outputs.value() > split_dims_at_axis) {
|
||||
LOGS(logger, VERBOSE) << "Invalid num_outputs provided.\n."
|
||||
<< "The value should be smaller or equal to the size of dimension being split. num_outputs: "
|
||||
<< num_outputs.value();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateSplitOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations) {
|
||||
op_registrations.builders.push_back(std::make_unique<SplitOpBuilder>());
|
||||
op_registrations.op_builder_map.emplace(op_type, op_registrations.builders.back().get());
|
||||
}
|
||||
|
||||
} // namespace coreml
|
||||
} // namespace onnxruntime
|
||||
|
|
@ -122,6 +122,14 @@ static OpBuilderRegistrations CreateOpBuilderRegistrations() {
|
|||
CreateSliceOpBuilder("Slice", op_registrations);
|
||||
}
|
||||
|
||||
{ // Softmax
|
||||
CreateSoftmaxOpBuilder("Softmax", op_registrations);
|
||||
}
|
||||
|
||||
{ // Split
|
||||
CreateSplitOpBuilder("Split", op_registrations);
|
||||
}
|
||||
|
||||
return op_registrations;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ void CreateReshapeOpBuilder(const std::string& op_type, OpBuilderRegistrations&
|
|||
void CreateResizeOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateShapeOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateSliceOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateSoftmaxOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateSplitOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateSqueezeOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateTransposeOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
void CreateUnaryOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations);
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ std::vector<float> NodeAttrHelper::Get(const std::string& key, const std::vector
|
|||
return std::vector<float>{source.cbegin(), source.cend()};
|
||||
}
|
||||
|
||||
std::optional<int64_t> NodeAttrHelper::GetInt(const std::string& key) const {
|
||||
if (!HasAttr(key))
|
||||
return std::nullopt;
|
||||
return node_attributes_.at(key).i();
|
||||
}
|
||||
|
||||
bool NodeAttrHelper::HasAttr(const std::string& key) const {
|
||||
return Contains(node_attributes_, key);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "core/graph/basic_types.h"
|
||||
|
||||
|
|
@ -57,6 +58,8 @@ class NodeAttrHelper {
|
|||
uint32_t Get(const std::string& key, uint32_t def_val) const;
|
||||
std::vector<uint32_t> Get(const std::string& key, const std::vector<uint32_t>& def_val) const;
|
||||
|
||||
std::optional<int64_t> GetInt(const std::string& key) const;
|
||||
|
||||
bool HasAttr(const std::string& key) const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ TEST(SoftmaxOperator, GH15949_regression_test) {
|
|||
{0.00032932f, 0.01798029f, 0.9816904f});
|
||||
|
||||
// disable TRT as it does not support axis=0 as used by the model
|
||||
tester.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kCoreMLExecutionProvider});
|
||||
tester.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ constexpr T ValueFromIdx(size_t idx) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void SplitTestAxis0EqualSplit(bool use_opset_13 = false) {
|
||||
void SplitTestAxis0EqualSplit() {
|
||||
SCOPED_TRACE(onnxruntime::MakeString("data type: ", utils::ToTensorProtoElementType<T>()));
|
||||
|
||||
constexpr int64_t axis = 0;
|
||||
|
|
@ -117,11 +117,20 @@ void SplitTestAxis0EqualSplit(bool use_opset_13 = false) {
|
|||
{V(5), V(6),
|
||||
V(7), V(8)}});
|
||||
|
||||
// BFloat16 added in opset 13
|
||||
if constexpr (!std::is_same_v<T, BFloat16>) {
|
||||
RunTest<T>(axis, {}, input, outputs,
|
||||
// TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
{kTensorrtExecutionProvider}, // is_tensorrt_supported
|
||||
false, // expect_failure
|
||||
false /*split_as_input*/);
|
||||
}
|
||||
|
||||
RunTest<T>(axis, {}, input, outputs,
|
||||
// TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
{kTensorrtExecutionProvider}, // is_tensorrt_supported
|
||||
false, // expect_failure
|
||||
use_opset_13); // split_as_input
|
||||
true /*split_as_input*/);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -130,7 +139,7 @@ TEST(SplitOperatorTest, Axis0EqualSplit) {
|
|||
SplitTestAxis0EqualSplit<float>();
|
||||
SplitTestAxis0EqualSplit<double>();
|
||||
SplitTestAxis0EqualSplit<MLFloat16>();
|
||||
SplitTestAxis0EqualSplit<BFloat16>(true); // BFloat16 added in opset 13
|
||||
SplitTestAxis0EqualSplit<BFloat16>();
|
||||
SplitTestAxis0EqualSplit<int8_t>();
|
||||
SplitTestAxis0EqualSplit<int16_t>();
|
||||
SplitTestAxis0EqualSplit<int32_t>();
|
||||
|
|
@ -162,8 +171,11 @@ TEST(SplitOperatorTest, Axis0UnequalSplitFloat) {
|
|||
{3.f, 4.f,
|
||||
5.f, 6.f,
|
||||
7.f, 8.f}});
|
||||
|
||||
// TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
// CoreML EP, etc. requires split to be an input. Same applies to below sets of tests.
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
}
|
||||
|
||||
TEST(SplitOperatorTest, Axis0UnequalSplitString) {
|
||||
|
|
@ -186,6 +198,7 @@ TEST(SplitOperatorTest, Axis0UnequalSplitString) {
|
|||
"e", "f",
|
||||
"g", "h"}});
|
||||
// TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
RunTest<std::string>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<std::string>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +218,7 @@ TEST(SplitOperatorTest, Axis1EqualSplitFloat) {
|
|||
outputs.push_back({{2, 2},
|
||||
{3.f, 4.f,
|
||||
7.f, 8.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -226,6 +239,7 @@ TEST(SplitOperatorTest, Axis1EqualSplitString) {
|
|||
{"c", "d",
|
||||
"g", "h"}});
|
||||
|
||||
RunTest<std::string>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<std::string>(axis, {}, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -248,6 +262,7 @@ TEST(SplitOperatorTest, Axis1UnequalSplitFloat) {
|
|||
{4.f,
|
||||
8.f}});
|
||||
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +285,7 @@ TEST(SplitOperatorTest, Axis1UnequalSplitString) {
|
|||
{"d",
|
||||
"h"}});
|
||||
|
||||
RunTest<std::string>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<std::string>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -312,6 +328,7 @@ TEST(SplitOperatorTest, Axis2EqualSplit) {
|
|||
17.f, 18.f,
|
||||
23.f, 24.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -344,6 +361,9 @@ TEST(SplitOperatorTest, Axis2UnequalSplit) {
|
|||
16.f, 17.f, 18.f,
|
||||
22.f, 23.f, 24.f}});
|
||||
|
||||
// Note: temporarily marked qnn ep as excluded when running tests with split_as_input=true.
|
||||
// TODO: Need to resolve to see if it's not supported or test case failure.
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +373,7 @@ TEST(SplitOperatorTest, ZeroSizeInput) {
|
|||
|
||||
ShapeAndFloatData input = CreateInput<float>({0, 2});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider});
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider, kCoreMLExecutionProvider});
|
||||
}
|
||||
|
||||
// test a split of a dimension that has leading and trailing dimensions
|
||||
|
|
@ -377,6 +397,7 @@ TEST(SplitOperatorTest, Axis1SplitMiddleDimensionEqually) {
|
|||
25.f, 26.f, 27.f, 28.f,
|
||||
29.f, 30.f, 31.f, 32.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -403,6 +424,7 @@ TEST(SplitOperatorTest, Axis1SplitMiddleDimensionUnequally) {
|
|||
25.f, 26.f, 27.f, 28.f,
|
||||
29.f, 30.f, 31.f, 32.f}});
|
||||
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -423,6 +445,7 @@ TEST(SplitOperatorTest, NegativeAxis) {
|
|||
{3.f, 4.f,
|
||||
7.f, 8.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true);
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider});
|
||||
}
|
||||
|
||||
|
|
@ -439,6 +462,7 @@ TEST(SplitOperatorTest, InvalidAxis) {
|
|||
|
||||
outputs.push_back({{1}, {0.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {}, true, true, -1, true, "Invalid value of attribute 'axis'");
|
||||
RunTest<float>(axis, {}, input, outputs, {}, true, false, -1, true, "Invalid value of attribute 'axis'");
|
||||
}
|
||||
|
||||
|
|
@ -459,6 +483,8 @@ TEST(SplitOperatorTest, SplitAttributeSumTooSmall) {
|
|||
outputs.push_back({{1, 2}, {1.f, 2.f}});
|
||||
outputs.push_back({{2, 2}, {3.f, 4.f, 5.f, 6.f}});
|
||||
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, true, true, -1, true,
|
||||
"[ShapeInferenceError] Mismatch between the sum of 'split'");
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, true, false, -1, true,
|
||||
"[ShapeInferenceError] Mismatch between the sum of 'split'"); // TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
}
|
||||
|
|
@ -478,6 +504,8 @@ TEST(SplitOperatorTest, InvalidValueInSplitAttribute) {
|
|||
outputs.push_back({{1, 2}, {1.f, 2.f}});
|
||||
outputs.push_back({{3, 2}, {3.f, 4.f, 5.f, 6.f, 7.f, 8.f}});
|
||||
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, true, true, -1, true,
|
||||
"[ShapeInferenceError] Mismatch between number of splits");
|
||||
RunTest<float>(axis, splits, input, outputs, {kTensorrtExecutionProvider}, true, false, -1, true,
|
||||
"[ShapeInferenceError] Mismatch between number of splits"); // TensorRT parser: Assertion failed: axis != BATCH_DIM
|
||||
}
|
||||
|
|
@ -654,7 +682,8 @@ TEST(SplitOperatorTest, MissingOptionalInputAdded) {
|
|||
{3.f, 4.f,
|
||||
7.f, 8.f}});
|
||||
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true, -1, false, {}, false);
|
||||
// CoreML EP does not support the case when split_is_input==true but missing providing the split as initializer.
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kCoreMLExecutionProvider}, false, true, -1, false, {}, false);
|
||||
}
|
||||
|
||||
TEST(SplitOperatorTest, Split18_NumOutputs_EvenSplit) {
|
||||
|
|
@ -677,6 +706,9 @@ TEST(SplitOperatorTest, Split18_NumOutputs_EvenSplit) {
|
|||
7.f, 8.f}});
|
||||
|
||||
int64_t num_outputs = 2;
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true, num_outputs, true);
|
||||
#endif
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true, num_outputs, false);
|
||||
}
|
||||
|
||||
|
|
@ -703,6 +735,9 @@ TEST(SplitOperatorTest, Split18_NumOutputs_UnevenSplit) {
|
|||
outputs.push_back({{1, 2}, {9.f, 10.f}});
|
||||
|
||||
int64_t num_outputs = 3;
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider}, false, true, num_outputs, true);
|
||||
#endif
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider}, false, true, num_outputs, false);
|
||||
}
|
||||
|
||||
|
|
@ -728,6 +763,10 @@ TEST(SplitOperatorTest, Split18_InvalidNumOutputs) {
|
|||
};
|
||||
RunTest<float>(axis, {}, input, outputs, excluded_providers, true, true, num_outputs, false,
|
||||
"Attribute `num_outputs` value cannot be lower than 1");
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, excluded_providers, true, true, num_outputs, true,
|
||||
"Attribute `num_outputs` value cannot be lower than 1");
|
||||
#endif
|
||||
|
||||
outputs.clear();
|
||||
outputs.push_back({{1, 2},
|
||||
|
|
@ -738,6 +777,10 @@ TEST(SplitOperatorTest, Split18_InvalidNumOutputs) {
|
|||
num_outputs = 3;
|
||||
RunTest<float>(axis, {}, input, outputs, excluded_providers, true, true, num_outputs, false,
|
||||
"Invalid num_outputs value of 3. Size of dimension being split is 2");
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, excluded_providers, true, true, num_outputs, true,
|
||||
"Invalid num_outputs value of 3. Size of dimension being split is 2");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(SplitOperatorTest, Split18_NumOutputsEvenSplitAxis1) {
|
||||
|
|
@ -755,6 +798,9 @@ TEST(SplitOperatorTest, Split18_NumOutputsEvenSplitAxis1) {
|
|||
|
||||
int64_t num_outputs = 3;
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true, num_outputs, false);
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider}, false, true, num_outputs);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(SplitOperatorTest, Split18_NumOutputsUnevenSplitAxis1) {
|
||||
|
|
@ -772,6 +818,9 @@ TEST(SplitOperatorTest, Split18_NumOutputsUnevenSplitAxis1) {
|
|||
outputs.push_back({{2, 1}, {3.f, 6.f}});
|
||||
|
||||
int64_t num_outputs = 2;
|
||||
#ifdef USE_COREML
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider}, false, true, num_outputs);
|
||||
#endif
|
||||
RunTest<float>(axis, {}, input, outputs, {kTensorrtExecutionProvider, kQnnExecutionProvider}, false, true, num_outputs, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ Keep in sync with doco generated from /docs/execution-providers/CoreML-Execution
|
|||
|ai.onnx:Shape|Attribute `start` with non-default value is not supported.<br/>Attribute `end` is not supported.|
|
||||
|ai.onnx:Sigmoid||
|
||||
|ai.onnx:Slice|Inputs `starts`, `ends`, `axes`, and `steps` should be constant. Empty slice is not supported.|
|
||||
|ai.onnx:Softmax||
|
||||
|ai.onnx:Split|If provided, `splits` should be constant. num of outputs supported is at least 2.|
|
||||
|ai.onnx:Squeeze||
|
||||
|ai.onnx:Sqrt||
|
||||
|ai.onnx:Sub||
|
||||
|
|
|
|||
Loading…
Reference in a new issue