[caffegen] Add 'ConvolutionLayer' (#111)
author박종현/동작제어Lab(SR)/Senior Engineer/삼성전자 <jh1302.park@samsung.com>
Fri, 20 Apr 2018 02:24:07 +0000 (11:24 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Fri, 20 Apr 2018 02:24:07 +0000 (11:24 +0900)
This commit adds 'ConvolutionLayer' which allows us to easily access the
internal of ConvolutionLayerParameter.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
contrib/caffegen/include/ConvolutionLayer.h [new file with mode: 0644]
contrib/caffegen/include/LayerAnalysisPass.h
contrib/caffegen/include/LayerTransformPass.h
contrib/caffegen/src/ConvolutionLayer.cpp [new file with mode: 0644]
contrib/caffegen/src/ParameterRandomizePass.cpp
contrib/caffegen/src/ParameterRandomizePass.h

diff --git a/contrib/caffegen/include/ConvolutionLayer.h b/contrib/caffegen/include/ConvolutionLayer.h
new file mode 100644 (file)
index 0000000..3c5e3ca
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __CONVOLUTION_LAYER_H__
+#define __CONVOLUTION_LAYER_H__
+
+#include "Layer.h"
+#include "Network.h"
+
+#include "BlobShape.h"
+
+#include <caffe.pb.h>
+
+class ConvolutionLayer final : public Layer
+{
+public:
+  ConvolutionLayer(const Network *net, caffe::LayerParameter *p);
+
+public:
+  uint32_t bottom_size(void) const override;
+  const std::string &bottom_name(uint32_t n) const override;
+  const BlobShape &bottom_shape(uint32_t n) const override;
+
+public:
+  uint32_t top_size(void) const override;
+  const std::string &top_name(uint32_t n) const override;
+  BlobShape top_shape(uint32_t n) const override;
+
+public:
+  void accept(LayerAnalysisPass &&) const override;
+  void accept(LayerTransformPass &&) override;
+
+public:
+  const caffe::LayerParameter &param(void) const { return *_param; }
+  caffe::LayerParameter &param(void) { return *_param; }
+
+public:
+  caffe::ConvolutionParameter &conv_param(void)
+  {
+    return *param().mutable_convolution_param();
+  }
+
+  const caffe::ConvolutionParameter &conv_param(void) const
+  {
+    return param().convolution_param();
+  }
+
+public:
+  const std::string &input_name(void) const;
+  const BlobShape &input_shape(void) const;
+
+public:
+  const std::string &output_name(void) const;
+  BlobShape output_shape(void) const;
+
+public:
+  uint32_t channel_axis(void) const;
+  uint32_t num_effective_output(void) const;
+
+public:
+  uint32_t num_spatial_axes(void) const;
+  uint32_t num_batch_axes(void) const;
+
+public:
+  uint32_t pad(uint32_t spatial_axe) const;
+  uint32_t kernel_size(uint32_t spatial_axe) const;
+  uint32_t stride(uint32_t spatial_axe) const;
+  uint32_t dilation(uint32_t spatial_axe) const;
+
+private:
+  const Network * const _net;
+  caffe::LayerParameter * const _param;
+};
+
+#endif // __CONVOLUTION_LAYER_H__
index a3c282c..40244ef 100644 (file)
@@ -2,12 +2,14 @@
 #define __LAYER_ANALYSIS_PASS_H__
 
 #include "InputLayer.h"
+#include "ConvolutionLayer.h"
 
 struct LayerAnalysisPass
 {
   virtual ~LayerAnalysisPass() = default;
 
   virtual void visit(const InputLayer &) = 0;
+  virtual void visit(const ConvolutionLayer &) = 0;
 };
 
 #endif // __LAYER_ANALYSIS_PASS_H__
index 18ad0f9..4afbc02 100644 (file)
@@ -2,12 +2,14 @@
 #define __LAYER_TRANSFORM_PASS_H__
 
 #include "InputLayer.h"
+#include "ConvolutionLayer.h"
 
 struct LayerTransformPass
 {
   virtual ~LayerTransformPass() = default;
 
   virtual void visit(InputLayer &) = 0;
+  virtual void visit(ConvolutionLayer &) = 0;
 };
 
 #endif // __LAYER_TRANSFORM_PASS_H__
diff --git a/contrib/caffegen/src/ConvolutionLayer.cpp b/contrib/caffegen/src/ConvolutionLayer.cpp
new file mode 100644 (file)
index 0000000..04cf779
--- /dev/null
@@ -0,0 +1,145 @@
+#include "ConvolutionLayer.h"
+#include "LayerAnalysisPass.h"
+#include "LayerTransformPass.h"
+
+#include <cassert>
+
+ConvolutionLayer::ConvolutionLayer(const Network *net, caffe::LayerParameter *p)
+    : _net{net}, _param{p}
+{
+  assert(param().type() == "Convolution");
+  assert(param().mutable_convolution_param() != nullptr);
+
+  // These constratins come from Convolution layer's definition
+  assert(param().bottom_size() == 1);
+  assert(param().top_size() == 1);
+  assert(num_batch_axes() + 1 /*channel axis*/ + num_spatial_axes() == input_shape().rank());
+
+  // TODO Support force_nd_im2col option
+  assert(!conv_param().force_nd_im2col());
+  // TODO Support negative axis
+  assert(conv_param().axis() > 0);
+  // TODO Support multi-group convolution
+  assert(conv_param().group() == 1);
+
+  // Comment on ConvolutionParameter (in caffe.proto)
+  //   Pad, kernel size, and stride are all given as a single value for equal
+  //   dimensions in all spatial dimensions, or once per spatial dimension.
+  //
+  // NOTE 'equal dimensions in all spatial dimensions' schema is supported
+  // TODO Support 'once per spatial dimension'
+  assert(conv_param().pad_size() == 1);
+  assert(conv_param().kernel_size_size() == 1);
+  assert(conv_param().stride_size() == 1);
+
+  // NOTE 'dilation' is not supported yet
+  // TODO Support 'dilation'
+  assert(conv_param().dilation_size() == 0);
+}
+
+uint32_t ConvolutionLayer::bottom_size(void) const { return 1; }
+
+const std::string &ConvolutionLayer::bottom_name(uint32_t n) const
+{
+  assert(n == 0);
+  return input_name();
+}
+
+const BlobShape &ConvolutionLayer::bottom_shape(uint32_t n) const
+{
+  assert(n == 0);
+  return input_shape();
+}
+
+uint32_t ConvolutionLayer::top_size(void) const { return 1; }
+
+const std::string &ConvolutionLayer::top_name(uint32_t n) const
+{
+  assert(n == 0);
+  return output_name();
+}
+
+BlobShape ConvolutionLayer::top_shape(uint32_t n) const
+{
+  assert(n == 0);
+  return output_shape();
+}
+
+void ConvolutionLayer::accept(LayerAnalysisPass &&v) const { v.visit(*this); }
+void ConvolutionLayer::accept(LayerTransformPass &&v) { v.visit(*this); }
+
+const std::string &ConvolutionLayer::input_name(void) const { return param().bottom(0); }
+const BlobShape &ConvolutionLayer::input_shape(void) const
+{
+  return _net->blobs().at(input_name());
+}
+
+const std::string &ConvolutionLayer::output_name(void) const { return param().top(0); }
+BlobShape ConvolutionLayer::output_shape(void) const
+{
+  // The code below is derived from Caffe
+  //  - Please refer to 'compute_output_shape' method in 'caffe::ConvolutionLayer' for details
+  BlobShape res{};
+
+  res.resize(num_batch_axes() + 1 + num_spatial_axes());
+
+  for (uint32_t batch_axis = 0; batch_axis < num_batch_axes(); ++batch_axis)
+  {
+    res.dim(batch_axis) = input_shape().dim(batch_axis);
+  }
+
+  res.dim(num_batch_axes()) = num_effective_output();
+
+  for (int spatial_axis = 0; spatial_axis < num_spatial_axes(); ++spatial_axis)
+  {
+    const uint32_t axis = num_batch_axes() + 1 + spatial_axis;
+    const int64_t kernel_ext =
+      dilation(spatial_axis) * (kernel_size(spatial_axis) - 1) + 1;
+
+    res.dim(axis) =
+      (input_shape().dim(axis) + 2 * pad(spatial_axis) - kernel_ext) / stride(spatial_axis);
+  }
+
+  return res;
+}
+
+uint32_t ConvolutionLayer::channel_axis(void) const
+{
+  return conv_param().axis();
+}
+
+uint32_t ConvolutionLayer::num_effective_output(void) const
+{
+  return conv_param().num_output();
+}
+
+uint32_t ConvolutionLayer::num_spatial_axes(void) const
+{
+  assert(input_shape().rank() > channel_axis());
+  return input_shape().rank() - channel_axis() - 1;
+}
+
+uint32_t ConvolutionLayer::num_batch_axes(void) const
+{
+  return input_shape().rank() - num_spatial_axes() - 1;
+}
+
+uint32_t ConvolutionLayer::pad(uint32_t spatial_axis) const
+{
+  return conv_param().pad(0);
+}
+
+uint32_t ConvolutionLayer::kernel_size(uint32_t spatial_axis) const
+{
+  return conv_param().kernel_size(0);
+}
+
+uint32_t ConvolutionLayer::stride(uint32_t spatial_axis) const
+{
+  return conv_param().stride(0);
+}
+
+uint32_t ConvolutionLayer::dilation(uint32_t spatial_axis) const
+{
+  return 1;
+}
index a760142..1d63ab2 100644 (file)
@@ -1,5 +1,7 @@
 #include "ParameterRandomizePass.h"
 
+#include <stdexcept>
+
 ParameterRandomizePass::ParameterRandomizePass(std::default_random_engine &generator)
     : _generator{generator}
 {
@@ -11,3 +13,8 @@ void ParameterRandomizePass::visit(InputLayer &)
   // InputLayer has no parameter to be randomized
   return;
 }
+
+void ParameterRandomizePass::visit(ConvolutionLayer &)
+{
+  throw std::runtime_error{"Not supported, yet"};
+}
index 7c9ae8b..a192b1f 100644 (file)
@@ -12,6 +12,7 @@ public:
 
 public:
   void visit(InputLayer &) override;
+  void visit(ConvolutionLayer &) override;
 
 private:
   std::default_random_engine &_generator;