[enco.caffe] Initial PoolingSpec implementation (#1312)
author박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Tue, 4 Sep 2018 02:20:15 +0000 (11:20 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Tue, 4 Sep 2018 02:20:15 +0000 (11:20 +0900)
This commit submits the initial implementation of PoolingSpec.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
contrib/enco/frontend/caffe/src/PoolingSpec.cpp [new file with mode: 0644]
contrib/enco/frontend/caffe/src/PoolingSpec.h [new file with mode: 0644]
contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp [new file with mode: 0644]

diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.cpp b/contrib/enco/frontend/caffe/src/PoolingSpec.cpp
new file mode 100644 (file)
index 0000000..e9b316d
--- /dev/null
@@ -0,0 +1,57 @@
+#include "PoolingSpec.h"
+
+#include <cassert>
+
+PoolingSpec::PoolingSpec(const ::caffe::PoolingParameter &param) : _param(param)
+{
+  // DO NOTHING
+}
+
+uint32_t PoolingSpec::window_height(void) const
+{
+  // NOTE Global pooling is not supported, yet
+  // TODO Support global pooling
+  assert(!_param.global_pooling());
+
+  if (_param.has_kernel_h())
+  {
+    return _param.kernel_h();
+  }
+
+  assert(_param.has_kernel_size());
+  return _param.kernel_size();
+}
+
+uint32_t PoolingSpec::window_width(void) const
+{
+  // NOTE Global pooling is not supported, yet
+  // TODO Support global pooling
+  assert(!_param.global_pooling());
+
+  if (_param.has_kernel_w())
+  {
+    return _param.kernel_w();
+  }
+
+  assert(_param.has_kernel_size());
+  return _param.kernel_size();
+}
+
+nncc::core::ADT::tensor::Shape PoolingSpec::ofm_shape(void) const
+{
+  nncc::core::ADT::tensor::Shape res;
+
+  // NOTE Caffe supports only pooling over rank-4 tensor
+  assert(_ifm_shape.rank() == 4);
+  res.resize(4);
+
+  // N (= the number of bacths) SHOULD be same
+  res.dim(0) = _ifm_shape.dim(0);
+  // C (= the number of chaanels) SHOULD be same
+  res.dim(1) = _ifm_shape.dim(1);
+
+  // H and W are derived from IFM, Window, and Padding
+  res.dim(2) = _ifm_shape.dim(2) - window_height() + 1;
+  res.dim(3) = _ifm_shape.dim(3) - window_width() + 1;
+  return res;
+}
diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.h b/contrib/enco/frontend/caffe/src/PoolingSpec.h
new file mode 100644 (file)
index 0000000..8993501
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __POOLING_SPEC_H__
+#define __POOLING_SPEC_H__
+
+#include <caffe/proto/caffe.pb.h>
+
+#include <nncc/core/ADT/tensor/Shape.h>
+
+class PoolingSpec
+{
+public:
+  PoolingSpec(const ::caffe::PoolingParameter &param);
+
+public:
+  const nncc::core::ADT::tensor::Shape &ifm_shape(void) const { return _ifm_shape; }
+  void ifm_shape(const nncc::core::ADT::tensor::Shape &shape) { _ifm_shape = shape; }
+
+public:
+  uint32_t window_height(void) const;
+  uint32_t window_width(void) const;
+
+public:
+  nncc::core::ADT::tensor::Shape ofm_shape(void) const;
+
+private:
+  const ::caffe::PoolingParameter &_param;
+  nncc::core::ADT::tensor::Shape _ifm_shape;
+};
+
+#endif // __POOLING_SPEC_H__
diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp b/contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp
new file mode 100644 (file)
index 0000000..c36e9aa
--- /dev/null
@@ -0,0 +1,149 @@
+#include "PoolingSpec.h"
+#include "Importer.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/foundation/Memory.h>
+
+#include <caffe/net.hpp>
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <sstream>
+#include <stdexcept>
+
+#include <gtest/gtest.h>
+
+using namespace nncc::core::ADT;
+
+using nncc::foundation::make_unique;
+
+#define STRING(content) #content
+
+namespace
+{
+
+class SequentialBuilder
+{
+public:
+  SequentialBuilder(::caffe::NetParameter *net) : _net{net}
+  {
+    // DO NOTHING
+  }
+
+public:
+  bool addLayer(const std::string &prototxt)
+  {
+    auto layer = _net->add_layer();
+    std::stringstream ss{prototxt};
+    ::google::protobuf::io::IstreamInputStream iis{&ss};
+    return google::protobuf::TextFormat::Parse(&iis, layer);
+  }
+
+  bool addInputLayer(const tensor::Shape &shape)
+  {
+    auto param = new ::caffe::InputParameter;
+    {
+      auto s = param->add_shape();
+      for (uint32_t n = 0; n < shape.rank(); ++n)
+      {
+        s->add_dim(shape.dim(n));
+      }
+    }
+
+    auto layer = _net->add_layer();
+
+    layer->set_name("data");
+    layer->set_type("Input");
+    layer->add_top("data");
+    layer->set_allocated_input_param(param);
+
+    return true;
+  }
+
+private:
+  ::caffe::NetParameter *_net;
+};
+
+} // namespace
+
+namespace
+{
+
+class PoolingSpecTest : public ::testing::Test
+{
+protected:
+  tensor::Shape as_tensor_shape(const std::vector<int> &dims)
+  {
+    const uint32_t rank = dims.size();
+
+    tensor::Shape res;
+
+    res.resize(rank);
+
+    for (uint32_t axis = 0; axis < rank; ++axis)
+    {
+      res.dim(axis) = dims.at(axis);
+    }
+
+    return res;
+  }
+};
+} // namespace
+
+TEST_F(PoolingSpecTest, ifm_shape)
+{
+  ::caffe::PoolingParameter param;
+  PoolingSpec spec{param};
+
+  const tensor::Shape ifm_shape{1, 3, 244, 244};
+
+  spec.ifm_shape(ifm_shape);
+
+  ASSERT_EQ(spec.ifm_shape(), ifm_shape);
+}
+
+namespace
+{
+} // namespace
+
+TEST_F(PoolingSpecTest, kernel_size_same_for_all)
+{
+  const tensor::Shape ifm_shape{1, 3, 16, 16};
+
+  ::caffe::NetParameter param;
+  {
+    SequentialBuilder builder{&param};
+
+    builder.addInputLayer(ifm_shape);
+
+    // clang-format off
+    const char *prototxt = STRING(
+      name : "pool"
+      type : "Pooling"
+      bottom : "data"
+      top : "pool"
+      pooling_param { kernel_size : 3 }
+    );
+    // clang-format on
+
+    builder.addLayer(prototxt);
+  }
+
+  ::caffe::Net<float> net{param};
+
+  PoolingSpec spec{param.layer(1).pooling_param()};
+
+  spec.ifm_shape(ifm_shape);
+
+  ASSERT_EQ(spec.window_height(), 3);
+  ASSERT_EQ(spec.window_width(), 3);
+
+  // Check 'ofm_shape'
+  {
+    auto expected = as_tensor_shape(net.blob_by_name("pool")->shape());
+    auto obtained = spec.ofm_shape();
+
+    ASSERT_EQ(expected, obtained);
+  }
+}