From 85ade38cf3a15bbd717cb19fa74a97ea015d653a Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=B2=9C=EA=B5=90/On-Device=20Lab=28SR=29/Enginee?= =?utf8?q?r/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Tue, 16 Jul 2019 11:57:21 +0900 Subject: [PATCH] [loco] Permuting encoder for DepthwiseFilter (#4284) This commit introduces permuting encoder for depthwise filter domain. Signed-off-by: Cheongyo Bahk --- contrib/loco/include/loco/IR/PermutingCodec.h | 25 +++++++++++ contrib/loco/src/IR/PermutingCodec.cpp | 63 +++++++++++++++++++++++++++ contrib/loco/src/IR/PermutingCodec.test.cpp | 60 +++++++++++++++++++++++++ 3 files changed, 148 insertions(+) diff --git a/contrib/loco/include/loco/IR/PermutingCodec.h b/contrib/loco/include/loco/IR/PermutingCodec.h index ade5b66..e9c7b6a 100644 --- a/contrib/loco/include/loco/IR/PermutingCodec.h +++ b/contrib/loco/include/loco/IR/PermutingCodec.h @@ -24,6 +24,7 @@ #include "loco/IR/FilterAxis.h" #include "loco/IR/FilterCodec.h" #include "loco/IR/DepthwiseFilterAxis.h" +#include "loco/IR/DepthwiseFilterCodec.h" #include @@ -223,6 +224,30 @@ private: std::map _map; }; +/** + * @brief Permutation-based Tensor-to-DepthwiseFilter converter + */ +template <> class PermutingEncoder final : public DepthwiseFilterEncoder +{ +public: + PermutingEncoder() = default; + +public: + bool valid(void) const; + +public: + DepthwiseFilterShape shape(const TensorShape &tensor_shape) const override; + TensorIndex value(const DepthwiseFilterIndex &index) const override; + +public: + const Permutation *perm(void) const { return &_perm; } + Permutation *perm(void) { return &_perm; } + void perm(const Permutation &p) { _perm = p; } + +private: + Permutation _perm; +}; + } // namespace loco #endif // __LOCO_IR_PERMUTING_CODEC_H__ diff --git a/contrib/loco/src/IR/PermutingCodec.cpp b/contrib/loco/src/IR/PermutingCodec.cpp index 0564fe0..e91d5e7 100644 --- a/contrib/loco/src/IR/PermutingCodec.cpp +++ b/contrib/loco/src/IR/PermutingCodec.cpp @@ -332,6 +332,34 @@ inline bool valid(const DepthwiseFilterAxis &axis) return false; } +inline bool valid(const loco::Permutation &perm) +{ + auto check = [&perm](DepthwiseFilterAxis axis_f) { + if (!perm.mapped(axis_f)) + return false; + return perm.axis(axis_f) < 4; + }; + + if (!check(DepthwiseFilterAxis::Depth)) + return false; + if (!check(DepthwiseFilterAxis::Multiplier)) + return false; + if (!check(DepthwiseFilterAxis::Height)) + return false; + if (!check(DepthwiseFilterAxis::Width)) + return false; + + // Check whether tensor axes are all distinct + std::set values; + + values.insert(perm[DepthwiseFilterAxis::Depth]); + values.insert(perm[DepthwiseFilterAxis::Multiplier]); + values.insert(perm[DepthwiseFilterAxis::Height]); + values.insert(perm[DepthwiseFilterAxis::Width]); + + return values.size() == 4; +} + } // namespace namespace loco @@ -359,4 +387,39 @@ uint32_t &Permutation::axis(const DepthwiseFilterAxis & return _map[axis_f]; } +// +// Permuting Encoder +// +DepthwiseFilterShape PermutingEncoder::shape(const TensorShape &in) const +{ + assert(valid() && "invalid permutation"); + + DepthwiseFilterShape out; + + out.depth() = in.dim(_perm[DepthwiseFilterAxis::Depth]); + out.multiplier() = in.dim(_perm[DepthwiseFilterAxis::Multiplier]); + out.height() = in.dim(_perm[DepthwiseFilterAxis::Height]); + out.width() = in.dim(_perm[DepthwiseFilterAxis::Width]); + + return out; +} + +TensorIndex PermutingEncoder::value(const DepthwiseFilterIndex &in) const +{ + assert(valid() && "invalid permutation"); + + TensorIndex out; + + out.resize(4); + + out.at(_perm[DepthwiseFilterAxis::Depth]) = in.channel(); + out.at(_perm[DepthwiseFilterAxis::Multiplier]) = in.nth(); + out.at(_perm[DepthwiseFilterAxis::Height]) = in.row(); + out.at(_perm[DepthwiseFilterAxis::Width]) = in.column(); + + return out; +} + +bool PermutingEncoder::valid(void) const { return ::valid(_perm); } + } // namespace loco diff --git a/contrib/loco/src/IR/PermutingCodec.test.cpp b/contrib/loco/src/IR/PermutingCodec.test.cpp index 690f397..8c674b5 100644 --- a/contrib/loco/src/IR/PermutingCodec.test.cpp +++ b/contrib/loco/src/IR/PermutingCodec.test.cpp @@ -254,6 +254,66 @@ TEST(PermutingEncoderTest, filter) ASSERT_EQ(tensor_index.at(3), 2); // CHANNEL(DEPTH) } +TEST(PermutingEncoderTest, depthwise_filter) +{ + PermutingEncoder enc; + + // Encoder is invalid at the beginning + ASSERT_FALSE(enc.valid()); + + // Set "invalid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Depth) = 0; + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 6; + enc.perm()->axis(DepthwiseFilterAxis::Height) = 1; + enc.perm()->axis(DepthwiseFilterAxis::Width) = 2; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set another "invalid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 1; + + // Encoder is still invalid + ASSERT_FALSE(enc.valid()); + + // Set "valid" mapping + enc.perm()->axis(DepthwiseFilterAxis::Multiplier) = 3; + + // Encoder is now valid + ASSERT_TRUE(enc.valid()); + + TensorShape tensor_shape; + + tensor_shape.rank(4); + tensor_shape.dim(0) = 8; // DEPTH + tensor_shape.dim(1) = 1; // HEIGHT + tensor_shape.dim(2) = 7; // WIDTH + tensor_shape.dim(3) = 4; // MULTIPLIER + + // Get the corresponding depthwise filter shape + auto filter_shape = enc.shape(tensor_shape); + + ASSERT_EQ(filter_shape.depth(), 8); + ASSERT_EQ(filter_shape.multiplier(), 4); + ASSERT_EQ(filter_shape.height(), 1); + ASSERT_EQ(filter_shape.width(), 7); + + // Let's find a source tensor index! + DepthwiseFilterIndex filter_index; + + filter_index.channel() = 1; + filter_index.nth() = 2; + filter_index.row() = 0; + filter_index.column() = 3; + + auto tensor_index = enc.value(filter_index); + + ASSERT_EQ(tensor_index.at(0), 1); // CHANNEL(DEPTH) + ASSERT_EQ(tensor_index.at(1), 0); // ROW(HEIGHT) + ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH) + ASSERT_EQ(tensor_index.at(3), 2); // NTH(MULTIPLIER) +} + TEST(PermutingDecoderTest, feature) { PermutingDecoder dec; -- 2.7.4