[Op] Squeeze update - no throw on non-squeezable dims (#24715)
### Details:
- Changes needed to enable Squeeze (and models) with axes pointing to
non-squeezable dims (currently ov throws an error for such case) -
considered as backward compatible change
- If agreed to update functionality within the same version of the op,
actual changes are needed only in shape infer related functions to cover
**Template, CPU and GPU plugins**:
- src/core/shape_inference/include/squeeze_shape_inference.hpp
- src/plugins/intel_cpu/src/shape_inference/custom/reshape.cpp
- The rest of the changes are test related.
- Pytorch FE Squeeze test enabled - passed
### Tickets:
- 142387
This commit is contained in:
parent
56d184dbb1
commit
142114ccbc
|
|
@ -17,7 +17,10 @@ Squeeze
|
|||
**Detailed description**: *Squeeze* can be used with or without the second input tensor.
|
||||
|
||||
* If only the first input is provided, every dimension that is equal to 1 will be removed from it.
|
||||
* With the second input provided, each value is an index of a dimension from the first tensor that is to be removed. Specified dimension has to be equal to 1, otherwise an error will be raised. Dimension indices can be specified directly, or by negative indices (counting dimensions from the end).
|
||||
* With the second input provided, each value is an index of a dimension from the first tensor that is to be removed. Specified dimension should be equal to 1, otherwise it will be ignored and copied as is.
|
||||
Dimension indices can be specified directly, or by negative indices (counting dimensions from the end).
|
||||
|
||||
Note: Updated behavior since 2024.3, request of squeezing dimension not equal to 1 is expected to be ignored instead of causing an error.
|
||||
|
||||
**Attributes**: *Squeeze* operation doesn't have attributes.
|
||||
|
||||
|
|
@ -87,4 +90,3 @@ Squeeze
|
|||
</port>
|
||||
</output>
|
||||
</layer>
|
||||
|
||||
|
|
|
|||
|
|
@ -54,17 +54,15 @@ std::vector<TRShape> shape_infer(const Squeeze* op,
|
|||
unique_axes.reset(new std::set<int64_t>(axes->cbegin(), axes->cend()));
|
||||
} else if (arg_rank.get_length() > 0 && shape_size(axes_shape.to_shape()) == 1) {
|
||||
// The `axes` input is a single element tensor which is unique by definition, deducing output rank
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
std::any_of(arg_shape.cbegin(),
|
||||
arg_shape.cend(),
|
||||
[](const DimType& dim) {
|
||||
return dim.compatible(1);
|
||||
}),
|
||||
"Data input shape ",
|
||||
arg_shape,
|
||||
" doesn't contain squeezable dimension,"
|
||||
" but axes input is expected to have one element.");
|
||||
output_shape = PartialShape::dynamic(arg_rank.get_length() - 1);
|
||||
const auto has_squeezable_dim =
|
||||
std::any_of(arg_shape.cbegin(), arg_shape.cend(), [](const DimType& dim) {
|
||||
return dim.compatible(1);
|
||||
});
|
||||
if (has_squeezable_dim) {
|
||||
output_shape = PartialShape::dynamic(arg_rank.get_length() - 1);
|
||||
} else {
|
||||
output_shape = arg_shape;
|
||||
}
|
||||
return output_shapes;
|
||||
}
|
||||
}
|
||||
|
|
@ -97,13 +95,11 @@ std::vector<TRShape> shape_infer(const Squeeze* op,
|
|||
auto rm_axis_end = unique_axes->cend();
|
||||
|
||||
// Returns true if dimension not squeezable on axis from input axes.
|
||||
const auto not_squeezable_at_axis = [&op, &rm_axis_iter, &rm_axis_end, &idx](const DimType& dim) {
|
||||
const auto not_squeezable_at_axis = [&rm_axis_iter, &rm_axis_end, &idx](const DimType& dim) {
|
||||
if ((rm_axis_iter != rm_axis_end) && (*rm_axis_iter == idx++)) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
dim.compatible(1),
|
||||
"provided axis value is invalid. Only axes of size 1 may be removed.");
|
||||
++rm_axis_iter;
|
||||
return false;
|
||||
// Ignore: Pointed by axis, but not squeezable
|
||||
return !dim.compatible(1);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ using namespace testing;
|
|||
TEST(type_prop, squeeze_axes_invalid_value) {
|
||||
auto param = make_shared<ov::op::v0::Parameter>(element::f32, Shape{1, 2, 3, 4});
|
||||
auto axes_node = make_shared<ov::op::v0::Constant>(element::u64, Shape{2}, vector<int64_t>{0, 2});
|
||||
const auto squeeze = std::make_shared<op::v0::Squeeze>(param, axes_node);
|
||||
|
||||
OV_EXPECT_THROW(auto s = make_shared<op::v0::Squeeze>(param, axes_node),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("provided axis value is invalid. Only axes of size 1 may be removed."));
|
||||
EXPECT_EQ(squeeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(squeeze->get_output_partial_shape(0), (PartialShape{2, 3, 4}));
|
||||
}
|
||||
|
||||
TEST(type_prop, squeeze_single_input) {
|
||||
|
|
@ -53,10 +53,10 @@ TEST(type_prop, squeeze_incorrect_negative_axes) {
|
|||
TEST(type_prop, squeeze_data_static_param_axes_1D_single_elem_static_shape_no_squeezable_dims) {
|
||||
auto param = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, PartialShape{2, 2, 4});
|
||||
const auto axes_node = std::make_shared<ov::op::v0::Parameter>(element::u64, PartialShape{1});
|
||||
const auto squeeze = std::make_shared<op::v0::Squeeze>(param, axes_node);
|
||||
|
||||
OV_EXPECT_THROW(auto s = make_shared<op::v0::Squeeze>(param, axes_node),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("doesn't contain squeezable dimension"));
|
||||
EXPECT_EQ(squeeze->get_element_type(), element::f32);
|
||||
EXPECT_EQ(squeeze->get_output_partial_shape(0), (PartialShape{2, 2, 4}));
|
||||
}
|
||||
|
||||
TEST(type_prop, squeeze_data_static_param_axes_1D_two_elem_static_shape_squeezable_dims_two) {
|
||||
|
|
|
|||
|
|
@ -83,30 +83,19 @@ Result SqueezeShapeInfer::infer(const std::vector<std::reference_wrapper<const V
|
|||
ov::util::Cast<int64_t>());
|
||||
std::vector<int64_t> originOutPattern = outPattern;
|
||||
std::vector<bool> removeMask(inputShapeSize, false);
|
||||
bool existError = false;
|
||||
for (size_t i = 0; i < outputPatternSize; i++) {
|
||||
if (outPattern[i] < 0) {
|
||||
outPattern[i] = inputShapeSize + outPattern[i];
|
||||
}
|
||||
if (outPattern[i] >= 0 && outPattern[i] < static_cast<int64_t>(inputShapeSize)) {
|
||||
if (outPattern[i] >= 0 && outPattern[i] < static_cast<int64_t>(inputShapeSize) && inputShape[outPattern[i]] == 1) {
|
||||
removeMask[outPattern[i]] = true;
|
||||
} else {
|
||||
existError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < inputShapeSize; i++) {
|
||||
if (!removeMask[i]) {
|
||||
outputShape.push_back(inputShape[i]);
|
||||
} else if (inputShape[i] != 1) {
|
||||
existError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existError) {
|
||||
OPENVINO_THROW("[cpu]squeeze: the shape of input data ", ov::intel_cpu::vec2str(inputShape),
|
||||
" conflicts with the squeeze pattern ", ov::intel_cpu::vec2str(originOutPattern));
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < inputShapeSize; i++) {
|
||||
if (inputShape[i] != 1) {
|
||||
|
|
@ -189,4 +178,3 @@ ShapeInferPtr ReshapeShapeInferFactory::makeShapeInfer() const {
|
|||
} // namespace node
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
|
||||
|
|
|
|||
|
|
@ -87,50 +87,30 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
make_tuple(unit_test::ShapeVector{{2, 6, 7, 8, 1}, {2}}, std::vector<int64_t>{-1, -1}, StaticShape({2, 6, 7, 8}))),
|
||||
SqueezeCpuShapeInferenceTest::getTestCaseName);
|
||||
|
||||
using SqueezeCpuShapeInferenceThrowExceptionTest = SqueezeCpuShapeInferenceTest;
|
||||
TEST_P(SqueezeCpuShapeInferenceThrowExceptionTest, wrong_pattern) {
|
||||
// Tests with non-squeezable dims pointed by axes (no throw, ignore)
|
||||
class SqueezeCpuShapeInferenceTestNonSqueeezable : public SqueezeCpuShapeInferenceTest {};
|
||||
TEST_P(SqueezeCpuShapeInferenceTestNonSqueeezable, shape_inference_non_squeezable_with_const_map) {
|
||||
const auto axes_node = std::make_shared<op::v0::Parameter>(element::i64, PartialShape::dynamic());
|
||||
const auto op = make_op(arg, axes_node);
|
||||
|
||||
const auto axes_tensor = ov::Tensor(element::i64, ov::Shape{axes.size()}, axes.data());
|
||||
const std::unordered_map<size_t, ov::Tensor> constant_data = {{1, axes_tensor}};
|
||||
std::ostringstream os;
|
||||
os << "[cpu]squeeze: the shape of input data ";
|
||||
os << "(";
|
||||
for (size_t i = 0; i < input_shapes[0].size(); i++) {
|
||||
os << input_shapes[0][i];
|
||||
if (i < input_shapes[0].size() - 1) {
|
||||
os << ".";
|
||||
}
|
||||
}
|
||||
os << ")";
|
||||
os << " conflicts with the squeeze pattern ";
|
||||
os << "(";
|
||||
for (size_t i = 0; i < axes.size(); i++) {
|
||||
os << axes[i];
|
||||
if (i < axes.size() - 1) {
|
||||
os << ".";
|
||||
}
|
||||
}
|
||||
os << ")";
|
||||
|
||||
OV_EXPECT_THROW(unit_test::cpu_test_shape_infer(op.get(), input_shapes, output_shapes, constant_data),
|
||||
ov::Exception,
|
||||
HasSubstr(os.str()));
|
||||
unit_test::cpu_test_shape_infer(op.get(), input_shapes, output_shapes, constant_data);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CpuShapeInfer,
|
||||
SqueezeCpuShapeInferenceThrowExceptionTest,
|
||||
Values(make_tuple(unit_test::ShapeVector{{1, 2, 3, 1}, {1}}, std::vector<int64_t>{1}, StaticShape({})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {1}}, std::vector<int64_t>{2}, StaticShape({})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{1, 2}, StaticShape({})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {1}}, std::vector<int64_t>{-1}, StaticShape({})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{-1, -1}, StaticShape({})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{-1, -2}, StaticShape({}))),
|
||||
SqueezeCpuShapeInferenceThrowExceptionTest::getTestCaseName);
|
||||
CpuShapeInfer_Squeeze_non_squeezable,
|
||||
SqueezeCpuShapeInferenceTestNonSqueeezable,
|
||||
Values(
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 1}, {1}}, std::vector<int64_t>{1}, StaticShape({1, 2, 3, 1})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 1}, {2}}, std::vector<int64_t>{0, 1}, StaticShape({2, 3, 1})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {1}}, std::vector<int64_t>{2}, StaticShape({1, 2, 3, 8})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{1, 2}, StaticShape({1, 2, 3, 8})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {1}}, std::vector<int64_t>{-1}, StaticShape({1, 2, 3, 8})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{-1, -1}, StaticShape({1, 2, 3, 8})),
|
||||
make_tuple(unit_test::ShapeVector{{1, 2, 3, 8}, {2}}, std::vector<int64_t>{-1, -2}, StaticShape({1, 2, 3, 8}))),
|
||||
SqueezeCpuShapeInferenceTest::getTestCaseName);
|
||||
|
||||
} // namespace cpu_shape_infer
|
||||
} // namespace unit_test
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
} // namespace cpu_shape_infer
|
||||
} // namespace unit_test
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "squeeze_shape_inference.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "common_test_utils/test_assertions.hpp"
|
||||
#include "openvino/op/constant.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/squeeze.hpp"
|
||||
#include "squeeze_shape_inference.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace ov;
|
||||
|
|
@ -69,6 +70,7 @@ protected:
|
|||
INSTANTIATE_TEST_SUITE_P(1d_shapes,
|
||||
SqueezeStaticShapeInferenceTest,
|
||||
Values(make_tuple(ShapeVector{{1}, {1}}, std::vector<int64_t>{-1}, StaticShape({})),
|
||||
make_tuple(ShapeVector{{6}, {1}}, std::vector<int64_t>{-1}, StaticShape({6})),
|
||||
make_tuple(ShapeVector{{1}, {1}}, std::vector<int64_t>{0}, StaticShape({}))),
|
||||
PrintToStringParamName());
|
||||
|
||||
|
|
@ -77,6 +79,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
SqueezeStaticShapeInferenceTest,
|
||||
Values(make_tuple(ShapeVector{{1, 2, 3, 1}, {2}}, std::vector<int64_t>{0, 3}, StaticShape({2, 3})),
|
||||
make_tuple(ShapeVector{{2, 1, 1, 4}, {2}}, std::vector<int64_t>{2, 1}, StaticShape({2, 4})),
|
||||
make_tuple(ShapeVector{{2, 1, 1, 4, 1}, {2}}, std::vector<int64_t>{0, 1, -2, -1}, StaticShape({2, 1, 4})),
|
||||
make_tuple(ShapeVector{{1, 3, 1, 2, 1}, {3}}, std::vector<int64_t>{0, 2, 4}, StaticShape({3, 2})),
|
||||
make_tuple(ShapeVector{{1, 3, 1, 2, 1}, {3}}, std::vector<int64_t>{4, 2, 0}, StaticShape({3, 2})),
|
||||
make_tuple(ShapeVector{{1, 3, 1, 2, 1}, {3}}, std::vector<int64_t>{2, 0, 4}, StaticShape({3, 2})),
|
||||
|
|
|
|||
|
|
@ -116,6 +116,15 @@ std::vector<SqueezeParams> generateParamsForSqueeze() {
|
|||
using T2 = typename element_type_traits<Axes_T>::value_type;
|
||||
|
||||
std::vector<SqueezeParams> params{
|
||||
SqueezeParams(Shape{1, 4, 1, 1, 2},
|
||||
Shape{4, 1, 2},
|
||||
IO_T,
|
||||
IO_T,
|
||||
std::vector<T1>{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
std::vector<T1>{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
Shape{4},
|
||||
Axes_T,
|
||||
std::vector<T2>{0, 1, 3, -1}),
|
||||
SqueezeParams(Shape{1, 4, 1, 1, 2},
|
||||
Shape{4, 1, 2},
|
||||
IO_T,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ class TestSqueeze(PytorchLayerTest):
|
|||
pytest.xfail(reason="export fails if dim is not provided")
|
||||
self._test(*self.create_model(dim), ie_device, precision, ir_version, dynamic_shapes=dynamic_shapes)
|
||||
|
||||
@pytest.mark.xfail(reason='OpenVINO squeeze does not support dimension is not equal to 1.')
|
||||
@pytest.mark.parametrize("dim", [-1, 2])
|
||||
@pytest.mark.nightly
|
||||
@pytest.mark.precommit
|
||||
|
|
|
|||
Loading…
Reference in New Issue