mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-05-14 20:48:00 +00:00
[C++] Correctly handle scalar inputs in reduction ops, enforce Transpose perm attribute matches input rank. (#17041)
### Description This PR addresses the following issues related to the use of the functions in ORT. - https://github.com/microsoft/onnxruntime/issues/16492 - https://github.com/microsoft/onnxruntime/issues/16997 - https://github.com/microsoft/onnxruntime/issues/14678 - Partially addresses https://github.com/microsoft/onnxruntime/issues/16813 The optimization case for a scalar input did not correctly recognize it as such. Transpose kernel assumed that `perm` attribute would always match input tensor rank. ### Motivation and Context The issues causes crashes and erratic behavior.
This commit is contained in:
parent
fb11c67368
commit
c424e42594
5 changed files with 53 additions and 9 deletions
|
|
@ -1008,6 +1008,8 @@ ONNX_NAMESPACE::TensorProto TensorToTensorProto(const Tensor& tensor, const std:
|
|||
common::Status ConstantNodeProtoToTensorProto(const ONNX_NAMESPACE::NodeProto& node,
|
||||
const Path& model_path,
|
||||
ONNX_NAMESPACE::TensorProto& tensor, const std::string& tensor_name) {
|
||||
ORT_RETURN_IF_NOT(node.attribute_size() > 0, "Constant node: ", node.name(), " has no data attributes");
|
||||
|
||||
const AttributeProto& constant_attribute = node.attribute(0);
|
||||
|
||||
switch (constant_attribute.type()) {
|
||||
|
|
|
|||
|
|
@ -595,10 +595,14 @@ FastReduceKind OptimizeShapeForFastReduce(gsl::span<const int64_t> input_shape,
|
|||
TensorShapeVector& fast_axes,
|
||||
bool keep_dims, bool noop_with_empty_axes) {
|
||||
if (input_shape.empty()) {
|
||||
fast_shape.assign(input_shape.begin(), input_shape.end());
|
||||
fast_output_shape = fast_shape;
|
||||
fast_axes.assign(reduced_axes.begin(), reduced_axes.end());
|
||||
return FastReduceKind::kNone;
|
||||
fast_shape.clear();
|
||||
fast_output_shape.clear();
|
||||
// XXX: Should we enforce the absence of the axes in the scalar input case?
|
||||
// The operator spec refers to Numpy which returns error because axes can not possibly contain any valid
|
||||
// value in scalar case, but pytorch simply ignores it.
|
||||
// ORT_ENFORCE(reduced_axes.empty(), "With scalar input shape, axis can not contain valid values");
|
||||
fast_axes.clear();
|
||||
return FastReduceKind::kEmpty;
|
||||
}
|
||||
|
||||
InlinedHashSet<int64_t> axes;
|
||||
|
|
|
|||
|
|
@ -62,20 +62,24 @@ class TransposeBase {
|
|||
|
||||
Status ComputeOutputShape(const Tensor& X, TensorShapeVector& output_dims, InlinedVector<size_t>& default_perm,
|
||||
const InlinedVector<size_t>*& p_perm) const {
|
||||
size_t rank = X.Shape().NumDimensions();
|
||||
const size_t rank = X.Shape().NumDimensions();
|
||||
const auto& input_dims = X.Shape().GetDims();
|
||||
|
||||
// Determine permutation to use:
|
||||
// If no permutation was specified in the attributes, the default is [rank-1, ..., 0]
|
||||
default_perm.resize(rank);
|
||||
|
||||
if (perm_specified_)
|
||||
p_perm = &perm_;
|
||||
else {
|
||||
// Determine permutation to use:
|
||||
// If no permutation was specified in the attributes, the default is [rank-1, ..., 0]
|
||||
default_perm.resize(rank);
|
||||
for (size_t i = 0; i < rank; ++i) default_perm[i] = rank - i - 1;
|
||||
p_perm = &default_perm;
|
||||
}
|
||||
|
||||
if (p_perm->size() != rank) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
|
||||
"perm size: ", p_perm->size(), " does not match input rank: ", std::to_string(rank));
|
||||
}
|
||||
|
||||
// Determine shape of output
|
||||
output_dims.resize(rank);
|
||||
for (size_t i = 0; i < rank; i++) {
|
||||
|
|
|
|||
|
|
@ -3215,6 +3215,24 @@ TEST(ReductionOpTest, OptimizeShapeForFastReduce_ReduceDimWithZero1) {
|
|||
ASSERT_EQ(fast_axes, expected_fast_axes);
|
||||
}
|
||||
|
||||
TEST(ReductionOpTest, OptimizeShapeForFastReduce_ReduceDimWithScalarInputAxesPresent) {
|
||||
FastReduceKind fast_kind;
|
||||
TensorShapeVector fast_shape, fast_output_shape, fast_axes;
|
||||
TensorShapeVector expected_fast_shape, expected_fast_output_shape, expected_fast_axes;
|
||||
|
||||
// R - keep_dims=1 - noop=false
|
||||
fast_kind = OptimizeShapeForFastReduce(
|
||||
EmptySpan<int64_t>(), AsSpan<int64_t>({1, 2, 3}),
|
||||
fast_shape, fast_output_shape, fast_axes, true);
|
||||
expected_fast_shape = {};
|
||||
expected_fast_axes = {};
|
||||
expected_fast_output_shape = {};
|
||||
ASSERT_EQ(fast_kind, FastReduceKind::kEmpty);
|
||||
ASSERT_EQ(fast_output_shape, expected_fast_output_shape);
|
||||
ASSERT_EQ(fast_shape, expected_fast_shape);
|
||||
ASSERT_EQ(fast_axes, expected_fast_axes);
|
||||
}
|
||||
|
||||
TEST(ReductionOpTest, OptimizeShapeForFastReduce_ReduceDimWithZero1b) {
|
||||
FastReduceKind fast_kind;
|
||||
TensorShapeVector fast_shape, fast_output_shape, fast_axes;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,22 @@ TEST(TransposeOpTest, IsTransposeReshapeTest) {
|
|||
ASSERT_FALSE(IsTransposeReshape(perm, input_dims));
|
||||
}
|
||||
|
||||
// Negative test, making sure it fails
|
||||
TEST(TransposeOpTest, PermRankDoesNotMatchTensorRank) {
|
||||
const std::vector<float> input_vals(1 * 2 * 3 * 4, 0.f);
|
||||
const std::vector<int64_t> perm{0, 2, 1};
|
||||
|
||||
OpTester test("Transpose");
|
||||
test.AddAttribute("perm", perm);
|
||||
test.AddInput<float>("X", {1, 2, 3, 4}, input_vals);
|
||||
// Output is not very relevant
|
||||
test.AddOutput<float>("Y", {1, 3, 2, 4}, input_vals);
|
||||
// This failure comes from shape inference, because in this case it knows the input dims.
|
||||
// But in the real world, the model can supply different input dims at runtime.
|
||||
test.Run(OpTester::ExpectResult::kExpectFailure,
|
||||
"Node:node1 Output:Y [ShapeInferenceError] Mismatch between number of source and target dimensions. Source=3 Target=4");
|
||||
}
|
||||
|
||||
// Some of the tests can't run on TensorrtExecutionProvider because of errors.
|
||||
// Those tests will fallback to other EPs.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue