diff --git a/src/tim/transform/ops/activation_layout_inference.h b/src/tim/transform/ops/activation_layout_inference.h index 64adf31..d2ea49a 100644 --- a/src/tim/transform/ops/activation_layout_inference.h +++ b/src/tim/transform/ops/activation_layout_inference.h @@ -25,6 +25,7 @@ #define TIM_LAYOUT_INFER_ACTIVATION_LAYOUT_INFERENCE_H_ #include "tim/vx/ops/activations.h" +#include "tim/vx/ops/reshape.h" #include "ops/op_layout_inference.h" #include "permute_vector.h" @@ -67,49 +68,54 @@ class PReluLayoutInfer : public OpLayoutInfer { void OnInputs( std::vector>& next_tensors) override { auto src_input = op_->impl()->InputsTensor()[0]; + auto input_shape = src_input->GetShape(); auto src_slope = op_->impl()->InputsTensor()[1]; + auto slope_shape = src_slope->GetShape(); auto input_pv = context_->GetPermuteVector(src_input); + std::vector boardcast_shape; + for (uint32_t i = 0; i < input_shape.size(); ++i) { + if (i < slope_shape.size()) { + boardcast_shape.push_back(slope_shape[i]); + } else { + boardcast_shape.push_back(1); + } + } if (src_slope->IsConstTensor()) { - std::shared_ptr infer_tensor; - std::shared_ptr slope_pv; - std::vector dataRef(src_slope->GetSpec().GetByteSize()); - src_slope->CopyDataFromTensor(dataRef.data()); - auto infer_slope = context_->infer_graph_->CreateTensor( - src_slope->GetSpec(), (const void*)dataRef.data()); - slope_pv = MakeShared(src_slope->GetShape().size()); + std::vector dataRef(src_slope->GetSpec().GetByteSize()); + src_slope->CopyDataFromTensor(dataRef.data()); + auto infer_slope_spec = src_slope->GetSpec(); + infer_slope_spec.SetShape(boardcast_shape); + auto infer_slope = context_->infer_graph_->CreateTensor( + infer_slope_spec, (const void*)dataRef.data()); - if(!input_pv->IsAligned()){ - // compute transpose param - std::vector perm; - for(uint32_t i = 0,j=0; i< input_pv->Rank(); i++,j++){ - if(j == slope_pv->Rank()) break; - if(input_pv->At(i) < slope_pv->Rank()){ - perm.push_back(input_pv->At(i)); - } - else i++; // if dims of input is higher than slope - } - auto out_slope = context_->infer_graph_->CreateTensor(src_slope->GetSpec().AsTransientSpec()); - auto permute = context_->infer_graph_->CreateOperation(perm); - (*permute).BindInput(infer_slope).BindOutput(out_slope); - context_->UpdateTensorMap(src_slope, out_slope); - } - else { - context_->UpdateTensorMap(src_slope, infer_slope); - } - context_->SetPermuteVector(src_slope,slope_pv); + if (!input_pv->IsAligned()) { + //The dimension of slop is already the same as input, directly use input_pv to convert + auto out_slope = PermuteConstTensor(infer_slope, input_pv); + context_->UpdateTensorMap(src_slope, out_slope); + } else { + context_->UpdateTensorMap(src_slope, infer_slope); + } + } else { + auto infer_slope_spec = src_slope->GetSpec().AsTransientSpec(); + auto reshape_out = context_->infer_graph_->CreateTensor(infer_slope_spec); + boardcast_shape = MapMultipleAxis(input_pv->AsStdVec(), boardcast_shape); + auto reshape = context_->infer_graph_->CreateOperation(boardcast_shape); + (*reshape) + .BindInput(context_->GetMapedTensor(src_slope)) + .BindOutput(reshape_out); + context_->UpdateTensorMap(src_slope, reshape_out); } - else{ - VSILOGE("Slope tensor cannot be handled yet if not constant."); - assert(false); - } - auto axis = MapAxis(input_pv->AsStdVec(), - op_->impl()->node()->nn_param.prelu.axis); + context_->SetPermuteVector(src_slope, input_pv); + + auto axis = + MapAxis(input_pv->AsStdVec(), op_->impl()->node()->nn_param.prelu.axis); auto prelu = context_->infer_graph_->CreateOperation(axis); auto out_infer = CreateOutputsTensor(input_pv); - (*prelu).BindInput(context_->GetMapedTensor(src_input)).BindInput( - context_->GetMapedTensor(src_slope)); + (*prelu) + .BindInput(context_->GetMapedTensor(src_input)) + .BindInput(context_->GetMapedTensor(src_slope)); (*prelu).BindOutput(out_infer[0]); context_->SetPermuteVector(op_->impl()->OutputsTensor()[0], input_pv); next_tensors.push_back(op_->impl()->OutputsTensor()[0]); diff --git a/src/tim/transform/prelu_layout_inference_test.cc b/src/tim/transform/prelu_layout_inference_test.cc new file mode 100644 index 0000000..2690350 --- /dev/null +++ b/src/tim/transform/prelu_layout_inference_test.cc @@ -0,0 +1,121 @@ +/**************************************************************************** +* +* Copyright (c) 2020-2023 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +*****************************************************************************/ +#include "tim/vx/context.h" +#include "tim/vx/graph.h" +#include "tim/vx/ops.h" +#include "test_utils.h" +#include "gtest/gtest.h" +#include "tim/transform/layout_inference.h" +#include "permute_vector.h" + +TEST(Prelu, alpha_boardcast) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType input_shape({2,2,3,1}); //input_pv={1,2,0,3} + tim::vx::ShapeType alph_shape({3,1,1}); //pv={0,1,2} + tim::vx::ShapeType output_shape({3,2,2,1}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, input_shape, + tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec alpha_spec(tim::vx::DataType::FLOAT32, alph_shape, + tim::vx::TensorAttribute::CONSTANT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::FLOAT32, output_shape, + tim::vx::TensorAttribute::OUTPUT); + std::vector in_data = {0.4, -0.2, 0.3, -0.4, 0.5, -0.6, 0.4, -0.2, 0.3, -0.4, 0.5, -0.6}; + std::vector alpha_data = {1,2,3}; + std::vector golden = {0.4, 0.5, 0.3, -0.2, -1.2, -1.2, 0.3, 0.4, 0.5, -0.4, -0.4, -1.8}; + + auto input = graph->CreateTensor(input_spec); + auto alpha = graph->CreateTensor(alpha_spec, alpha_data.data()); + auto output = graph->CreateTensor(output_spec); + auto prelu = graph->CreateOperation(0); + (*prelu).BindInputs({input, alpha}).BindOutputs({output}); + + std::map, + std::shared_ptr> + tensor_pv_map; + std::shared_ptr pv = + std::make_shared>( + std::initializer_list({1,2,0,3})); + tensor_pv_map.insert({input, pv}); + auto transform = tim::transform::LayoutInference(graph, ctx, tensor_pv_map); + auto infer_graph = transform.first; + EXPECT_TRUE(infer_graph->Compile()); + auto graph_io_map = transform.second; + auto infer_input = graph_io_map[graph->InputsTensor()[0]]; + auto infer_output = graph_io_map[graph->OutputsTensor()[0]]; + EXPECT_TRUE(infer_input->CopyDataToTensor(in_data.data(), in_data.size())); + EXPECT_TRUE(infer_graph->Run()); + + std::vector out(golden.size()); + EXPECT_TRUE(infer_output->CopyDataFromTensor(out.data())); + EXPECT_TRUE(ArraysMatch(golden, out, 1e-5f)); +} + + +TEST(Prelu, alpha_as_input) { + auto ctx = tim::vx::Context::Create(); + auto graph = ctx->CreateGraph(); + + tim::vx::ShapeType input_shape({2,2,3,1}); //input_pv={1,2,0,3} + tim::vx::ShapeType alph_shape({3,1,1}); //pv={0,1,2} + tim::vx::ShapeType output_shape({3,2,2,1}); + tim::vx::TensorSpec input_spec(tim::vx::DataType::FLOAT32, input_shape, + tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec alpha_spec(tim::vx::DataType::FLOAT32, alph_shape, + tim::vx::TensorAttribute::INPUT); + tim::vx::TensorSpec output_spec(tim::vx::DataType::FLOAT32, output_shape, + tim::vx::TensorAttribute::OUTPUT); + std::vector in_data = {0.4, -0.2, 0.3, -0.4, 0.5, -0.6, 0.4, -0.2, 0.3, -0.4, 0.5, -0.6}; + std::vector alpha_data = {1,2,3}; + std::vector golden = {0.4, 0.5, 0.3, -0.2, -1.2, -1.2, 0.3, 0.4, 0.5, -0.4, -0.4, -1.8}; + + auto input = graph->CreateTensor(input_spec); + auto alpha = graph->CreateTensor(alpha_spec); + auto output = graph->CreateTensor(output_spec); + auto prelu = graph->CreateOperation(0); + (*prelu).BindInputs({input, alpha}).BindOutputs({output}); + + std::map, + std::shared_ptr> + tensor_pv_map; + std::shared_ptr pv = + std::make_shared>( + std::initializer_list({1,2,0,3})); + tensor_pv_map.insert({input, pv}); + auto transform = tim::transform::LayoutInference(graph, ctx, tensor_pv_map); + auto infer_graph = transform.first; + EXPECT_TRUE(infer_graph->Compile()); + auto graph_io_map = transform.second; + auto infer_input = graph_io_map[graph->InputsTensor()[0]]; + auto infer_alpha = graph_io_map[graph->InputsTensor()[1]]; + auto infer_output = graph_io_map[graph->OutputsTensor()[0]]; + EXPECT_TRUE(infer_input->CopyDataToTensor(in_data.data(), in_data.size())); + EXPECT_TRUE(infer_alpha->CopyDataToTensor(alpha_data.data(), alpha_data.size())); + EXPECT_TRUE(infer_graph->Run()); + + std::vector out(golden.size()); + EXPECT_TRUE(infer_output->CopyDataFromTensor(out.data())); + EXPECT_TRUE(ArraysMatch(golden, out, 1e-5f)); +} \ No newline at end of file