[moco/tf] Introduce DepthwiseConv2D Canonicalizer (#6047)
author채성우/On-Device Lab(SR)/Engineer/삼성전자 <sw4670.chae@samsung.com>
Thu, 1 Aug 2019 01:50:47 +0000 (01:50 +0000)
committer박종현/On-Device Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Thu, 1 Aug 2019 01:50:47 +0000 (10:50 +0900)
* [moco/tf] Introduce DepthwiseConv2D Canonicalizer

This commit introcude DepthwiseConv2D Canonicalizer to moco-tf.

Signed-off-by: seongwoo <sw4670.chae@samsung.com>
* apply comment.

* add codes for log.

compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp [new file with mode: 0644]
compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h [new file with mode: 0644]
compiler/moco-tf/src/Canonicalizer.cpp

diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
new file mode 100644 (file)
index 0000000..10d33c5
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2019 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 "DepthwiseConv2dNativeCanonicalizer.h"
+
+#include "Annotations/PadData.h"
+#include "Annotations/ShapeInferenceData.h"
+#include "Annotations/StrideData.h"
+
+#include "Dialect/TFDialect.h"
+#include "Dialect/TFNodes.h"
+#include "Dialect/TFNodeVisitor.h"
+#include "Dialect/TFNodeImpl.h"
+
+#include <moco/Log.h>
+#include <plier/tf/Convert.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using plier::tf::DataLayout;
+
+void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
+{
+  auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+
+  if (data_layout == DataLayout::NHWC)
+  {
+    enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+    enc->perm()->axis(loco::FeatureAxis::Height) = 1;
+    enc->perm()->axis(loco::FeatureAxis::Width) = 2;
+    enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
+  }
+  else if (data_layout == DataLayout::NCHW)
+  {
+    enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+    enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
+    enc->perm()->axis(loco::FeatureAxis::Height) = 2;
+    enc->perm()->axis(loco::FeatureAxis::Width) = 3;
+  }
+
+  feature_enc->encoder(std::move(enc));
+}
+
+void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc)
+{
+  auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>();
+
+  // In TensorFlow, depthwiseconv2dnative filter is a 4-D tensor of following shape:
+  // [filter_height, filter_width, in_channels, channel_multiplier] -> HWCM
+  enc->perm()->axis(loco::DepthwiseFilterAxis::Height) = 0;
+  enc->perm()->axis(loco::DepthwiseFilterAxis::Width) = 1;
+  enc->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 2;
+  enc->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+
+  filter_enc->encoder(std::move(enc));
+}
+
+void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
+{
+  auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+
+  if (data_layout == DataLayout::NHWC)
+  {
+    dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+    dec->perm()->axis(loco::FeatureAxis::Height) = 1;
+    dec->perm()->axis(loco::FeatureAxis::Width) = 2;
+    dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
+  }
+  else if (data_layout == DataLayout::NCHW)
+  {
+    dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+    dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
+    dec->perm()->axis(loco::FeatureAxis::Height) = 2;
+    dec->perm()->axis(loco::FeatureAxis::Width) = 3;
+  }
+
+  feature_dec->decoder(std::move(dec));
+}
+
+bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::tf::TFDepthwiseConv2dNative *node)
+{
+  LOGGER(l);
+
+  /**
+ * @note This will replace TFDepthwiseConv2dNative node with Canonical FeatureEncode +
+ *       DepthwiseFilterEncode + DepthwiseConv2D + FeatureDecode
+ *
+ *       Before
+ *              A -+- TFDepthwiseConv2dNative - C
+ *                 |
+ *              B -+
+ *
+ *       After
+ *
+ *            A -+ FeatureEncode ----------------+- DepthwiseConv2D - FeatureDecode - C
+ *               |                               |
+ *               +-(TFDepthwiseConv2dNative)     |
+ *               |                               |
+ *            B -+ DepthwiseFilterEncode --------+
+ *
+ *       Where
+ *                 A : ifm of TFDepthwiseConv2dNative
+ *                 B : ker of TFDepthwiseConv2dNative
+ *                 C : a node that uses TFDepthwiseConv2dNative as an input
+ *                 TFDepthwiseConv2dNative is disconnected from other nodes
+ */
+
+  INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative begin";
+
+  auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+  auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+  auto filter_enc = graph->nodes()->create<loco::DepthwiseFilterEncode>();
+  auto depthwiseconv2d = graph->nodes()->create<loco::DepthwiseConv2D>();
+  auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+  set_feature_enc(feature_enc, data_layout);
+  set_filter_enc(filter_enc);
+  set_feature_dec(feature_dec, data_layout);
+
+  // Set DetphwiseConv2D attributes from TFDepthwiseConv2dNative
+  auto pad_data = node->annot<moco::tf::PadData>();
+  assert(pad_data != nullptr);
+
+  depthwiseconv2d->pad()->top(pad_data->pad()->top());
+  depthwiseconv2d->pad()->bottom(pad_data->pad()->bottom());
+  depthwiseconv2d->pad()->left(pad_data->pad()->left());
+  depthwiseconv2d->pad()->right(pad_data->pad()->right());
+
+  auto stride_data = node->annot<moco::tf::StrideData>();
+  assert(stride_data != nullptr);
+
+  depthwiseconv2d->stride()->vertical(stride_data->stride()->vertical());
+  depthwiseconv2d->stride()->horizontal(stride_data->stride()->horizontal());
+
+  // update graph
+  auto node_A = node->ifm();
+  auto node_B = node->ker();
+
+  // update connections
+  feature_enc->input(node_A);
+  filter_enc->input(node_B);
+  depthwiseconv2d->ifm(feature_enc);
+  depthwiseconv2d->ker(filter_enc);
+  feature_dec->input(depthwiseconv2d);
+
+  // replace and disconnect old node
+  replace(node).with(feature_dec);
+
+  INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative done";
+
+  return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool DepthwiseConv2dNativeCanonicalizer::run(loco::Graph *graph)
+{
+  auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+  bool changed = false;
+
+  for (auto node : active_nodes)
+  {
+    if (node->dialect() == TFDialect::get())
+    {
+      auto tf_node = dynamic_cast<moco::tf::TFDepthwiseConv2dNative *>(node);
+      if (tf_node != nullptr)
+      {
+        if (canonicalize_depthwiseconv2dnative(graph, tf_node))
+          changed = true;
+      }
+    }
+  }
+
+  return changed;
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
new file mode 100644 (file)
index 0000000..9bb8c5a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
+#define __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
+
+#include "Transform.h"
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief  Convert TFDepthwiseConv2dNative to Canonical DepthwiseConv2D
+ */
+class DepthwiseConv2dNativeCanonicalizer : public Transform
+{
+public:
+  const char *name(void) const final { return "DepthwiseConv2dNativeCanonicalizer"; }
+
+public:
+  bool run(loco::Graph *graph) override;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
index f56e27e..56c3aa6 100644 (file)
@@ -24,6 +24,7 @@
 #include "Canonicalization/BiasAddCanonicalizer.h"
 #include "Canonicalization/ConstCanonicalizer.h"
 #include "Canonicalization/Conv2DCanonicalizer.h"
+#include "Canonicalization/DepthwiseConv2dNativeCanonicalizer.h"
 #include "Canonicalization/IdentityCanonicalizer.h"
 #include "Canonicalization/MaxPoolCanonicalizer.h"
 
@@ -69,6 +70,7 @@ void Canonicalizer::canonicalize(loco::Graph *g) const
   phase.emplace_back(stdex::make_unique<BiasAddCanonicalizer>());
   phase.emplace_back(stdex::make_unique<ConstCanonicalizer>());
   phase.emplace_back(stdex::make_unique<Conv2DCanonicalizer>());
+  phase.emplace_back(stdex::make_unique<DepthwiseConv2dNativeCanonicalizer>());
   phase.emplace_back(stdex::make_unique<IdentityCanonicalizer>());
   phase.emplace_back(stdex::make_unique<MaxPoolCanonicalizer>());
   /* TRANSFORM DECLARATION END */