From 833aa8a7c94ad1354639a431d4015a672a55c152 Mon Sep 17 00:00:00 2001 From: zhupengyang Date: Sat, 7 Dec 2019 15:52:43 +0800 Subject: [PATCH] [NPU] add unsqueeze op bridge and unit test (#2570) test=develop --- lite/kernels/npu/bridges/CMakeLists.txt | 3 + .../npu/bridges/paddle_use_npu_bridges.h | 2 + lite/kernels/npu/bridges/unsqueeze_op.cc | 64 ++++++++ lite/kernels/npu/bridges/unsqueeze_op_test.cc | 139 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 lite/kernels/npu/bridges/unsqueeze_op.cc create mode 100644 lite/kernels/npu/bridges/unsqueeze_op_test.cc diff --git a/lite/kernels/npu/bridges/CMakeLists.txt b/lite/kernels/npu/bridges/CMakeLists.txt index 79d1bf2fd5..988544ed2d 100644 --- a/lite/kernels/npu/bridges/CMakeLists.txt +++ b/lite/kernels/npu/bridges/CMakeLists.txt @@ -22,6 +22,7 @@ lite_cc_library(npu_bridge_pad2d_op SRCS pad2d_op.cc DEPS ${npu_bridge_deps}) lite_cc_library(npu_bridge_square_op SRCS square_op.cc DEPS ${npu_bridge_deps}) lite_cc_library(npu_bridge_sqrt_op SRCS sqrt_op.cc DEPS ${npu_bridge_deps}) lite_cc_library(npu_bridge_reduce_mean_op SRCS reduce_mean_op.cc DEPS ${npu_bridge_deps}) +lite_cc_library(npu_bridge_unsqueeze_op SRCS unsqueeze_op.cc DEPS ${npu_bridge_deps}) set(npu_bridges npu_bridge_registry @@ -45,6 +46,7 @@ set(npu_bridges npu_bridge_square_op npu_bridge_sqrt_op npu_bridge_reduce_mean_op + npu_bridge_unsqueeze_op CACHE INTERNAL "npu_bridges") set(npu_bridge_test_deps ${npu_bridges} ${npu_kernels} ${ops}) @@ -69,5 +71,6 @@ lite_cc_test(test_npu_bridge_pad2d_op SRCS pad2d_op_test.cc test_helper.cc DEPS lite_cc_test(test_npu_bridge_square_op SRCS square_op_test.cc test_helper.cc DEPS ${npu_bridge_test_deps}) lite_cc_test(test_npu_bridge_sqrt_op SRCS sqrt_op_test.cc test_helper.cc DEPS ${npu_bridge_test_deps}) lite_cc_test(test_npu_bridge_reduce_mean_op SRCS reduce_mean_op_test.cc test_helper.cc DEPS ${npu_bridge_test_deps}) +lite_cc_test(test_npu_bridge_unsqueeze_op SRCS unsqueeze_op_test.cc test_helper.cc DEPS ${npu_bridge_test_deps}) message(STATUS "+++++ npu_bridges: ${npu_bridges}") diff --git a/lite/kernels/npu/bridges/paddle_use_npu_bridges.h b/lite/kernels/npu/bridges/paddle_use_npu_bridges.h index 9a432d17e5..40b1a5e31f 100644 --- a/lite/kernels/npu/bridges/paddle_use_npu_bridges.h +++ b/lite/kernels/npu/bridges/paddle_use_npu_bridges.h @@ -53,3 +53,5 @@ USE_NPU_BRIDGE(sqrt); USE_NPU_BRIDGE(square); USE_NPU_BRIDGE(transpose); USE_NPU_BRIDGE(transpose2); +USE_NPU_BRIDGE(unsqueeze); +USE_NPU_BRIDGE(unsqueeze2); diff --git a/lite/kernels/npu/bridges/unsqueeze_op.cc b/lite/kernels/npu/bridges/unsqueeze_op.cc new file mode 100644 index 0000000000..0721711466 --- /dev/null +++ b/lite/kernels/npu/bridges/unsqueeze_op.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lite/backends/npu/builder.h" +#include "lite/kernels/npu/bridges/registry.h" + +namespace paddle { +namespace lite { +namespace kernels { +namespace npu { +namespace bridges { + +node_map_type UnsqueezeConverter( + const std::shared_ptr unsqueeze_op, + const node_map_type& inputs_map) { + auto scope = unsqueeze_op->scope(); + auto op_info = unsqueeze_op->op_info(); + auto op_type = op_info->Type(); + auto unique_op_type = lite::npu::UniqueName(op_type); + LOG(INFO) << "[NPU] Converting " + op_type + "..."; + + std::shared_ptr unsqueeze_node = + std::make_shared(unique_op_type); + + auto x_var_name = op_info->Input("X").front(); + CHECK(inputs_map.count(x_var_name)); + unsqueeze_node->set_input_tensor(*inputs_map.at(x_var_name)); + + lite::npu::OpList::Global().add(inputs_map.at(x_var_name)); + lite::npu::OpList::Global().add(unsqueeze_node); + + CHECK(op_info->HasAttr("axes")) + << "[NPU] unsqueeze not support axes from tensor now"; + auto out_var_name = op_info->Output("Out").front(); + auto out_shape = scope->FindTensor(out_var_name)->dims().Vectorize(); + unsqueeze_node->set_attr_shape( + ge::AttrValue::LIST_INT(out_shape.begin(), out_shape.end())); + + node_map_type outputs_map; + outputs_map[op_info->Output("Out").front()] = unsqueeze_node; + return outputs_map; +} + +} // namespace bridges +} // namespace npu +} // namespace kernels +} // namespace lite +} // namespace paddle + +REGISTER_NPU_BRIDGE(unsqueeze, + paddle::lite::kernels::npu::bridges::UnsqueezeConverter); +REGISTER_NPU_BRIDGE(unsqueeze2, + paddle::lite::kernels::npu::bridges::UnsqueezeConverter); diff --git a/lite/kernels/npu/bridges/unsqueeze_op_test.cc b/lite/kernels/npu/bridges/unsqueeze_op_test.cc new file mode 100644 index 0000000000..c59843f614 --- /dev/null +++ b/lite/kernels/npu/bridges/unsqueeze_op_test.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lite/operators/unsqueeze_op.h" +#include +#include +#include "lite/core/op_registry.h" +#include "lite/kernels/npu/bridges/registry.h" +#include "lite/kernels/npu/bridges/test_helper.h" + +namespace paddle { +namespace lite { +namespace kernels { +namespace npu { +namespace bridges { + +static DDim GetOutputShape(const std::vector& unsqz_dims, + const DDim& in_dims) { + int output_size = in_dims.size() + static_cast(unsqz_dims.size()); + int cur_output_size = in_dims.size(); + std::vector output_shape(output_size, 0); + + // Validate Check: rank range. + CHECK_LE(output_size, 6) << "The output tensor's rank should be less than 6."; + + for (int axis : unsqz_dims) { + int cur = axis < 0 ? axis + cur_output_size + 1 : axis; + // Validate Check: the axis bound + CHECK((cur >= 0) && (cur <= cur_output_size)) + << "The unsqueeze dims must be within range of current rank."; + // Move old axis, and insert new axis + for (int i = cur_output_size; i >= cur; --i) { + if (output_shape[i] == 1) { + // Move axis + output_shape[i + 1] = 1; + output_shape[i] = 0; + } + } + + output_shape[cur] = 1; + // Add the output size. + cur_output_size++; + } + + // Make output shape + for (int in_idx = 0, out_idx = 0; out_idx < output_size; ++out_idx) { + if (output_shape[out_idx] == 0) { + output_shape[out_idx] = in_dims[in_idx++]; + } + } + + return DDim(output_shape); +} + +template +void unsqueeze_ref(const std::shared_ptr op) { + Scope* scope = op->scope(); + const OpInfo* op_info = op->op_info(); + + auto x = scope->FindTensor("x"); + auto out = scope->FindMutableTensor("out_ref"); + auto axes = op_info->GetAttr>("axes"); + auto y_dims = GetOutputShape(axes, x->dims()); + out->Resize(y_dims); + + auto x_data = x->data(); + auto out_data = out->mutable_data(); + + memcpy(out_data, x_data, x->numel() * sizeof(float)); +} + +void test_unsqueeze(const std::vector& input_shape, + std::vector axes) { + // prepare input&output variables + Scope scope; + std::string x_var_name = "x"; + std::string out_var_name = "out"; + std::string out_ref_var_name = "out_ref"; + auto* x = scope.NewTensor(x_var_name); + auto* out = scope.NewTensor(out_var_name); + auto* out_ref = scope.NewTensor(out_ref_var_name); + x->Resize(input_shape); + + // initialize input&output data + FillTensor(x); + + // initialize op desc + cpp::OpDesc opdesc; + opdesc.SetType("unsqueeze"); + opdesc.SetInput("X", {x_var_name}); + opdesc.SetOutput("Out", {out_var_name}); + opdesc.SetAttr("axes", axes); + + // create and convert op to NPU model, then run it on NPU + auto op = CreateOp(opdesc, &scope); + LauchOp(op, {x_var_name}, {out_var_name}); + + // execute reference implementation and save to output tensor + unsqueeze_ref(op); + + // compare results + CHECK_EQ(out->dims().size(), out_ref->dims().size()); + for (int i = 0; i < out->dims().size(); i++) { + CHECK_EQ(out->dims()[i], out_ref->dims()[i]); + } + + auto* out_data = out->mutable_data(); + auto* out_ref_data = out_ref->mutable_data(); + for (int i = 0; i < out->dims().production(); i++) { + EXPECT_NEAR(out_data[i], out_ref_data[i], 1e-2); + } +} + +TEST(NPUBridges, unsqueeze) { + test_unsqueeze({2}, {0, 2}); + test_unsqueeze({2, 3}, {1, 3}); + test_unsqueeze({1, 2, 3}, {3}); + test_unsqueeze({5, 6, 7}, {1}); +} + +} // namespace bridges +} // namespace npu +} // namespace kernels +} // namespace lite +} // namespace paddle + +USE_LITE_OP(unsqueeze); +USE_NPU_BRIDGE(unsqueeze); -- GitLab