Skip to content

Commit

Permalink
[luci/service] Migrate shape inference for Conv2D, DepthwiseConv2D
Browse files Browse the repository at this point in the history
This commit migrates shape inference of Conv2D and DepthwiseConv2D to sinf::Algorithm.

ONE-DCO-1.0-Signed-off-by: Chansu Park <[email protected]>
  • Loading branch information
pcs1265 committed Sep 29, 2024
1 parent eb72364 commit e0fd3b1
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ class Algorithm final : public luci::CircleNodeVisitor<loco::TensorShape>
// loco::TensorShape visit(const luci::CircleCeil *node) final;
loco::TensorShape visit(const luci::CircleConcatenation *node) final;
// loco::TensorShape visit(const luci::CircleConst *node) final;
// loco::TensorShape visit(const luci::CircleConv2D *node) final;
loco::TensorShape visit(const luci::CircleConv2D *node) final;
// loco::TensorShape visit(const luci::CircleCos *node) final;
// loco::TensorShape visit(const luci::CircleCustom *node) final;
// loco::TensorShape visit(const luci::CircleDepthToSpace *node) final;
// loco::TensorShape visit(const luci::CircleDepthwiseConv2D *node) final;
loco::TensorShape visit(const luci::CircleDepthwiseConv2D *node) final;
// loco::TensorShape visit(const luci::CircleDequantize *node) final;
loco::TensorShape visit(const luci::CircleDiv *node) final;
// loco::TensorShape visit(const luci::CircleElu *node) final;
Expand Down
108 changes: 0 additions & 108 deletions compiler/luci/service/src/CircleShapeInferenceRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,57 +411,6 @@ loco::NodeShape infer_batch_to_space_nd(const luci::CircleBatchToSpaceND *node)
return loco::NodeShape{shape_output};
}

struct OutputSize
{
uint32_t height = 0;
uint32_t width = 0;
};

template <class Conv2DType> OutputSize infer_conv2d_type(const Conv2DType *node)
{
auto ifm_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
auto ker_shape = luci::shape_get(node->filter()).template as<loco::TensorShape>();
assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ifm_shape.dim(1).known());
assert(ifm_shape.dim(2).known());
assert(ker_shape.dim(1).known());
assert(ker_shape.dim(2).known());

uint32_t input_height = ifm_shape.dim(1).value();
uint32_t input_width = ifm_shape.dim(2).value();
uint32_t stride_height = node->stride()->h();
uint32_t stride_width = node->stride()->w();
uint32_t ker_height = ker_shape.dim(1).value();
uint32_t ker_width = ker_shape.dim(2).value();
uint32_t dilation_height = node->dilation()->h();
uint32_t dilation_width = node->dilation()->w();
uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;

uint32_t output_height = 0;
uint32_t output_width = 0;

if (node->padding() == luci::Padding::VALID)
{
LUCI_ASSERT(input_height + stride_height > effective_ker_height, "Invalid shape");
LUCI_ASSERT(input_width + stride_width > effective_ker_width, "Invalid shape");
output_height = (input_height + stride_height - effective_ker_height) / stride_height;
output_width = (input_width + stride_width - effective_ker_width) / stride_width;
}
else if (node->padding() == luci::Padding::SAME)
{
output_height = (input_height + stride_height - 1) / stride_height;
output_width = (input_width + stride_width - 1) / stride_width;
}
else
LUCI_ASSERT(false, "Wrong padding type");

OutputSize os{output_height, output_width};

return os;
}

loco::NodeShape infer_broadcast_to(const luci::CircleBroadcastTo *node)
{
const loco::DataType S32 = loco::DataType::S32;
Expand Down Expand Up @@ -492,34 +441,6 @@ loco::NodeShape infer_broadcast_to(const luci::CircleBroadcastTo *node)
return loco::NodeShape{shape_by_input};
}

loco::NodeShape infer_conv2d(const luci::CircleConv2D *node)
{
LOGGER(l);

auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI

assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ifm_shape.dim(3) == ker_shape.dim(3));

auto os = infer_conv2d_type(node);

loco::TensorShape ofm_shape;
ofm_shape.rank(4);
ofm_shape.dim(0) = ifm_shape.dim(0);
ofm_shape.dim(1) = os.height;
ofm_shape.dim(2) = os.width;
ofm_shape.dim(3) = ker_shape.dim(0);

INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" << ker_shape.rank()
<< ") output(" << ofm_shape.dim(0).value() << "," << ofm_shape.dim(1).value() << ","
<< ofm_shape.dim(2).value() << "," << ofm_shape.dim(3).value() << ") " << node->name()
<< std::endl;

return loco::NodeShape{ofm_shape};
}

loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node)
{
auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
Expand Down Expand Up @@ -552,28 +473,6 @@ loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node)
return loco::NodeShape{output_shape};
}

loco::NodeShape infer_depthwise_conv2d(const luci::CircleDepthwiseConv2D *node)
{
auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM

assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ker_shape.dim(0).value() == 1);
assert(ifm_shape.dim(3).value() * node->depthMultiplier() == ker_shape.dim(3).value());

auto os = infer_conv2d_type(node);

loco::TensorShape ofm_shape;
ofm_shape.rank(4);
ofm_shape.dim(0) = ifm_shape.dim(0);
ofm_shape.dim(1) = os.height;
ofm_shape.dim(2) = os.width;
ofm_shape.dim(3) = ker_shape.dim(3);

return loco::NodeShape{ofm_shape};
}

loco::NodeShape infer_expand_dims(const luci::CircleExpandDims *node)
{
const loco::DataType S32 = loco::DataType::S32;
Expand Down Expand Up @@ -1815,8 +1714,6 @@ class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeS

loco::NodeShape visit(const luci::CircleConst *node) final { return use_own(node); }

loco::NodeShape visit(const luci::CircleConv2D *node) final { return infer_conv2d(node); }

loco::NodeShape visit(const luci::CircleCos *node) final { return use_x(node); }

loco::NodeShape visit(const luci::CircleCumSum *node) final { return use_input(node); }
Expand All @@ -1830,11 +1727,6 @@ class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeS
return infer_depth_to_space(node);
}

loco::NodeShape visit(const luci::CircleDepthwiseConv2D *node) final
{
return infer_depthwise_conv2d(node);
}

loco::NodeShape visit(const luci::CircleDequantize *node) final
{
const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
Expand Down
79 changes: 79 additions & 0 deletions compiler/luci/service/src/HelperConv2Ds.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. 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 "Check.h"
#include "CircleShapeInferenceHelper.h"

namespace luci
{

namespace sinf
{

struct OutputSize
{
uint32_t height = 0;
uint32_t width = 0;
};

template <class Conv2DType> OutputSize infer_conv2d_type(const Conv2DType *node)
{
auto ifm_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
auto ker_shape = luci::shape_get(node->filter()).template as<loco::TensorShape>();
assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ifm_shape.dim(1).known());
assert(ifm_shape.dim(2).known());
assert(ker_shape.dim(1).known());
assert(ker_shape.dim(2).known());

uint32_t input_height = ifm_shape.dim(1).value();
uint32_t input_width = ifm_shape.dim(2).value();
uint32_t stride_height = node->stride()->h();
uint32_t stride_width = node->stride()->w();
uint32_t ker_height = ker_shape.dim(1).value();
uint32_t ker_width = ker_shape.dim(2).value();
uint32_t dilation_height = node->dilation()->h();
uint32_t dilation_width = node->dilation()->w();
uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;

uint32_t output_height = 0;
uint32_t output_width = 0;

if (node->padding() == luci::Padding::VALID)
{
LUCI_ASSERT(input_height + stride_height > effective_ker_height, "Invalid shape");
LUCI_ASSERT(input_width + stride_width > effective_ker_width, "Invalid shape");
output_height = (input_height + stride_height - effective_ker_height) / stride_height;
output_width = (input_width + stride_width - effective_ker_width) / stride_width;
}
else if (node->padding() == luci::Padding::SAME)
{
output_height = (input_height + stride_height - 1) / stride_height;
output_width = (input_width + stride_width - 1) / stride_width;
}
else
LUCI_ASSERT(false, "Wrong padding type");

OutputSize os{output_height, output_width};

return os;
}

} // namespace sinf

} // namespace luci
39 changes: 39 additions & 0 deletions compiler/luci/service/src/Nodes/CircleConv2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
* limitations under the License.
*/

#include "HelperConv2Ds.h"
#include "luci/Service/CircleShapeInference.h"

#include "CircleCloneNode.h"
#include "CircleShapeInferenceHelper.h"

#include <luci/Log.h>

namespace luci
{
Expand All @@ -39,4 +45,37 @@ luci::CircleNode *CloneNodeLet<CN::ABC>::visit(const luci::CircleConv2D *node)
return cloned;
}

namespace sinf
{

loco::TensorShape Algorithm::visit(const luci::CircleConv2D *node)
{
LOGGER(l);

auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI

assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ifm_shape.dim(3) == ker_shape.dim(3));

auto os = infer_conv2d_type(node);

loco::TensorShape ofm_shape;
ofm_shape.rank(4);
ofm_shape.dim(0) = ifm_shape.dim(0);
ofm_shape.dim(1) = os.height;
ofm_shape.dim(2) = os.width;
ofm_shape.dim(3) = ker_shape.dim(0);

INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" << ker_shape.rank()
<< ") output(" << ofm_shape.dim(0).value() << "," << ofm_shape.dim(1).value() << ","
<< ofm_shape.dim(2).value() << "," << ofm_shape.dim(3).value() << ") " << node->name()
<< std::endl;

return ofm_shape;
}

} // namespace sinf

} // namespace luci
31 changes: 31 additions & 0 deletions compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
* limitations under the License.
*/

#include "HelperConv2Ds.h"
#include "luci/Service/CircleShapeInference.h"

#include "CircleCloneNode.h"
#include "CircleShapeInferenceHelper.h"

namespace luci
{
Expand All @@ -40,4 +44,31 @@ luci::CircleNode *CloneNodeLet<CN::DEF>::visit(const luci::CircleDepthwiseConv2D
return cloned;
}

namespace sinf
{

loco::TensorShape Algorithm::visit(const luci::CircleDepthwiseConv2D *node)
{
auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM

assert(ifm_shape.rank() == 4);
assert(ker_shape.rank() == 4);
assert(ker_shape.dim(0).value() == 1);
assert(ifm_shape.dim(3).value() * node->depthMultiplier() == ker_shape.dim(3).value());

auto os = infer_conv2d_type(node);

loco::TensorShape ofm_shape;
ofm_shape.rank(4);
ofm_shape.dim(0) = ifm_shape.dim(0);
ofm_shape.dim(1) = os.height;
ofm_shape.dim(2) = os.width;
ofm_shape.dim(3) = ker_shape.dim(3);

return ofm_shape;
}

} // namespace sinf

} // namespace luci

0 comments on commit e0fd3b1

Please sign in to comment.