Skip to content

Commit 4afdced

Browse files
authored
[NNAPI EP] Update squeeze ops (#5946)
* [NNAPI EP] Update squeeze ops
1 parent d52b9ac commit 4afdced

File tree

6 files changed

+78
-51
lines changed

6 files changed

+78
-51
lines changed

onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
//
2-
// Created by daquexian on 8/3/18.
3-
//
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
43

54
#include <iostream>
65
#include <string>

onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//
2-
// Created by daquexian on 5/21/18.
3-
//
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
44
#pragma once
55

66
#include <string>

onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder.cc

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,10 @@ Status ConcatOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const
17671767
class SqueezeOpBuilder : public BaseOpBuilder {
17681768
public:
17691769
void AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) const override;
1770+
static Status AddSqueezeOp(ModelBuilder& model_builder,
1771+
const std::string& node_name,
1772+
const std::string& input, const std::string& output,
1773+
vector<int32_t> axes) ORT_MUST_USE_RESULT;
17701774

17711775
private:
17721776
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) const override ORT_MUST_USE_RESULT;
@@ -1779,6 +1783,49 @@ void SqueezeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const
17791783
}
17801784
}
17811785

1786+
/* static */ Status SqueezeOpBuilder::AddSqueezeOp(ModelBuilder& model_builder,
1787+
const std::string& node_name,
1788+
const std::string& input, const std::string& output,
1789+
vector<int32_t> axes) {
1790+
auto& shaper(model_builder.GetShaper());
1791+
const auto& operand_indices(model_builder.GetOperandIndices());
1792+
const auto& operand_types(model_builder.GetOperandTypes());
1793+
1794+
const auto& input_shape(shaper[input]);
1795+
auto input_dims = input_shape.size();
1796+
for (auto& axis : axes) {
1797+
axis = static_cast<int32_t>(HandleNegativeAxis(axis, input_dims));
1798+
}
1799+
1800+
// Despite the spec of ANEURALNETWORKS_SQUEEZE at
1801+
// https://developer.android.com/ndk/reference/group/neural-networks
1802+
// states, that the axes (input 1 of ANEURALNETWORKS_SQUEEZE) is optional.
1803+
//
1804+
// The actual code of NNAPI requires the axes to be provided
1805+
// https://android.googlesource.com/platform/frameworks/ml/+/master/nn/common/operations/Squeeze.cpp#31
1806+
if (axes.empty()) { // Squeeze all
1807+
for (size_t i = 0; i < input_dims; i++) {
1808+
if (input_shape[i] == 1)
1809+
axes.push_back(i);
1810+
}
1811+
}
1812+
1813+
const auto axes_name = model_builder.GetUniqueName(node_name + input + "_axes");
1814+
Shape axes_dimen = {static_cast<uint32_t>(axes.size())};
1815+
const OperandType axes_operand_type(Type::TENSOR_INT32, axes_dimen);
1816+
ORT_RETURN_IF_ERROR(model_builder.AddOperandFromPersistMemoryBuffer(axes_name, axes.data(), axes_operand_type));
1817+
1818+
std::vector<uint32_t> input_indices;
1819+
input_indices.push_back(operand_indices.at(input)); // input
1820+
input_indices.push_back(operand_indices.at(axes_name)); // axes
1821+
1822+
ORT_RETURN_IF_ERROR(shaper.Squeeze(input, axes, output));
1823+
const OperandType output_operand_type(operand_types.at(input).type, shaper[output]);
1824+
ORT_RETURN_IF_ERROR(model_builder.AddOperation(ANEURALNETWORKS_SQUEEZE, input_indices,
1825+
{output}, {output_operand_type}, {false}));
1826+
return Status::OK();
1827+
}
1828+
17821829
/* static */ vector<int32_t> SqueezeOpBuilder::GetAxes(ModelBuilder& model_builder, const Node& node) {
17831830
vector<int32_t> axes;
17841831
// Squeeze opset 13 use input as axes
@@ -1804,47 +1851,13 @@ void SqueezeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const
18041851
}
18051852

18061853
Status SqueezeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) const {
1807-
auto& shaper(model_builder.GetShaper());
1808-
const auto& operand_indices(model_builder.GetOperandIndices());
1809-
const auto& operand_types(model_builder.GetOperandTypes());
1810-
18111854
auto input = node.InputDefs()[0]->Name();
18121855
if (model_builder.IsOperandNHWC(input)) {
18131856
// We want to transpose nhwc operand back to nchw before squeeze
18141857
ORT_RETURN_IF_ERROR(GetNCHWInput(model_builder, node, 0, input));
18151858
}
18161859

1817-
NodeAttrHelper helper(node);
1818-
vector<int32_t> axes = GetAxes(model_builder, node);
1819-
const auto& input_shape(shaper[input]);
1820-
auto input_dims = input_shape.size();
1821-
for (auto& axis : axes) {
1822-
axis = static_cast<int32_t>(HandleNegativeAxis(axis, input_dims));
1823-
}
1824-
1825-
if (axes.empty()) { // Squeeze all
1826-
for (size_t i = 0; i < input_dims; i++) {
1827-
if (input_shape[i] == 1)
1828-
axes.push_back(i);
1829-
}
1830-
}
1831-
1832-
const auto axes_name = model_builder.GetUniqueName(node.Name() + input + "_axes");
1833-
Shape axes_dimen = {static_cast<uint32_t>(axes.size())};
1834-
shaper.AddShape(axes_name, axes_dimen);
1835-
const OperandType axes_operand_type(Type::TENSOR_INT32, axes_dimen);
1836-
ORT_RETURN_IF_ERROR(model_builder.AddOperandFromPersistMemoryBuffer(axes_name, axes.data(), axes_operand_type));
1837-
1838-
std::vector<uint32_t> input_indices;
1839-
input_indices.push_back(operand_indices.at(input)); // input
1840-
input_indices.push_back(operand_indices.at(axes_name)); // axes
1841-
1842-
const auto& output = node.OutputDefs()[0]->Name();
1843-
ORT_RETURN_IF_ERROR(shaper.Squeeze(input, axes, output));
1844-
const OperandType output_operand_type(operand_types.at(input).type, shaper[output]);
1845-
ORT_RETURN_IF_ERROR(model_builder.AddOperation(ANEURALNETWORKS_SQUEEZE, input_indices,
1846-
{output}, {output_operand_type}, {false}));
1847-
return Status::OK();
1860+
return AddSqueezeOp(model_builder, node.Name(), input, node.OutputDefs()[0]->Name(), GetAxes(model_builder, node));
18481861
}
18491862

18501863
#pragma endregion

onnxruntime/core/providers/nnapi/nnapi_builtin/builders/shaper.cc

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
14
#include "core/providers/common.h"
25
#include "core/providers/nnapi/nnapi_builtin/nnapi_lib/NeuralNetworksWrapper.h"
36

@@ -374,17 +377,12 @@ Status Shaper::SqueezeImpl(const std::string& input_name,
374377
const std::string& output_name) {
375378
const Shape& input_dimen = shape_map_.at(input_name);
376379
int32_t input_size = input_dimen.size();
377-
size_t axes_size = axes.size();
378380
std::unordered_set<int32_t> axes_to_be_squeezed;
379-
if (axes_size == 0) {
380-
for (int32_t idx = 0; idx < input_size; ++idx) {
381-
if (input_dimen[idx] == 1)
382-
axes_to_be_squeezed.insert(idx);
383-
}
384-
} else {
385-
for (const auto& axis : axes)
386-
axes_to_be_squeezed.insert(axis);
387-
}
381+
382+
// If the Op is squeezing all by not specifying axes, the axes is pre-populate
383+
// with axes of all single dimensions by the caller
384+
for (const auto& axis : axes)
385+
axes_to_be_squeezed.insert(axis);
388386

389387
// Make output dimensions
390388
std::vector<uint32_t> output_dimen;
@@ -394,6 +392,11 @@ Status Shaper::SqueezeImpl(const std::string& input_name,
394392
output_dimen.push_back(input_dimen[i]);
395393
}
396394

395+
// In case of a tensor has all 1's in dimension such as {1,1,1,1} and gets squeezed all
396+
// the output shape will be {1}
397+
if (output_dimen.empty())
398+
output_dimen.push_back(1);
399+
397400
shape_map_[output_name] = output_dimen;
398401
return Status::OK();
399402
}

onnxruntime/core/providers/nnapi/nnapi_builtin/builders/shaper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
14
#pragma once
25

36
#include <string>

onnxruntime/test/providers/cpu/tensor/squeeze_op_test.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ TEST(SqueezeOpTest, Squeeze_Empty_Axes_2) {
3535
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
3636
}
3737

38+
TEST(SqueezeOpTest, Squeeze_Empty_Axes_3) {
39+
OpTester test("Squeeze");
40+
// Squeeze all for all 1's shape will end up as a scalar
41+
test.AddInput<float>("data", {1, 1, 1, 1}, std::vector<float>{1.0f});
42+
test.AddOutput<float>("squeezed", {}, std::vector<float>{1.0f});
43+
// TensorRT doesn't seem to support missing 'axes'
44+
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});
45+
}
46+
3847
TEST(SqueezeOpTest, Squeeze_1_int32) {
3948
OpTester test("Squeeze");
4049
test.AddAttribute("axes", std::vector<int64_t>{0});

0 commit comments

Comments
 (0)