[pooling2d layer] Maintain pooling2d layer property with props
authorhyeonseok lee <hs89.lee@samsung.com>
Thu, 9 Sep 2021 06:15:49 +0000 (15:15 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Mon, 13 Sep 2021 11:59:57 +0000 (20:59 +0900)
 - All pooling2d layer property will be maintain with props

**Self evaluation:**
1. Build test: [X]Passed [ ]Failed [ ]Skipped
2. Run test: [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: hyeonseok lee <hs89.lee@samsung.com>
nntrainer/layers/common_properties.cpp
nntrainer/layers/common_properties.h
nntrainer/layers/pooling2d_layer.cpp
nntrainer/layers/pooling2d_layer.h
nntrainer/utils/parse_util.cpp

index ab648c4..62eb92c 100644 (file)
@@ -108,6 +108,8 @@ bool SplitDimension::isValid(const unsigned int &value) const {
   return value > 0 && value < ml::train::TensorDim::MAXDIM;
 }
 
+PoolSize::PoolSize(unsigned int value) { set(value); }
+
 Stride::Stride(unsigned int value) { set(value); }
 
 /**
index 33e8435..9d99bd0 100644 (file)
@@ -285,6 +285,27 @@ public:
 };
 
 /**
+ * @brief PoolSize property, pool size is used to measure the pooling size
+ *
+ */
+class PoolSize : public nntrainer::PositiveIntegerProperty {
+public:
+  /**
+   * @brief Construct a new PoolSize object
+   *
+   */
+  PoolSize() {}
+
+  /**
+   * @brief Construct a new PoolSize object with default value
+   *
+   */
+  PoolSize(unsigned int value);
+  static constexpr const char *key = "pool_size"; /**< unique key to access */
+  using prop_tag = uint_prop_tag;                 /**< property type */
+};
+
+/**
  * @brief Stride property, stride is used to measure how much it will be slide
  * the filter
  *
@@ -488,6 +509,38 @@ public:
 };
 
 /**
+ * @brief     Enumeration of pooling type
+ */
+struct PoolingTypeInfo {
+  /**
+   * @brief   Pooling operation type class
+   */
+  enum class Enum {
+    max = 0,
+    average = 1,
+    global_max = 2,
+    global_average = 3,
+    unknown = 4
+  };
+  static constexpr std::initializer_list<Enum> EnumList = {
+    Enum::max, Enum::average, Enum::global_max, Enum::global_average,
+    Enum::unknown};
+
+  static constexpr const char *EnumStr[] = {"max", "average", "global_max",
+                                            "global_average", "unknown"};
+};
+
+/**
+ * @brief Pooling Type Enumeration Information
+ *
+ */
+class PoolingType final : public EnumProperty<PoolingTypeInfo> {
+public:
+  using prop_tag = enum_class_prop_tag;
+  static constexpr const char *key = "pooling";
+};
+
+/**
  * @brief     Enumeration of flip direction
  */
 struct FlipDirectionInfo {
index 494f7b9..cc65b3d 100644 (file)
@@ -17,6 +17,7 @@
 #include <lazy_tensor.h>
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
+#include <node_exporter.h>
 #include <parse_util.h>
 #include <pooling2d_layer.h>
 #include <util_func.h>
@@ -25,6 +26,15 @@ namespace nntrainer {
 
 static constexpr size_t SINGLE_INOUT_IDX = 0;
 
+Pooling2DLayer::Pooling2DLayer(
+  const std::array<unsigned int, POOLING2D_DIM * 2> &padding_) :
+  Layer(),
+  padding(padding_),
+  pooling2d_props(props::PoolingType(), std::vector<props::PoolSize>(),
+                  std::array<props::Stride, POOLING2D_DIM>(),
+                  props::Padding2D()),
+  pool_helper_idx(0) {}
+
 void Pooling2DLayer::finalize(InitLayerContext &context) {
   if (context.getNumInputs() != 1) {
     throw std::invalid_argument(
@@ -34,23 +44,29 @@ void Pooling2DLayer::finalize(InitLayerContext &context) {
   const TensorDim &in_dim = context.getInputDimensions()[SINGLE_INOUT_IDX];
   TensorDim out_dim;
 
-  if (pooling_type == PoolingType::global_max ||
-      pooling_type == PoolingType::global_average) {
-    if (pool_size[0] != 0 || pool_size[1] != 0) {
-      throw std::invalid_argument(
-        "[Pooling2D] global_max, global_average does not accept pool size");
-    }
-    pool_size[0] = in_dim.height();
-    pool_size[1] = in_dim.width();
+  auto &pool_size = std::get<std::vector<props::PoolSize>>(pooling2d_props);
+  NNTR_THROW_IF(!(pool_size.empty() || pool_size.size() == 2),
+                std::invalid_argument)
+    << "[Pooling2D] the number of pool size should be 0 or 2";
+  auto &stride =
+    std::get<std::array<props::Stride, POOLING2D_DIM>>(pooling2d_props);
+  auto &pooling_type = std::get<props::PoolingType>(pooling2d_props).get();
+
+  if (pooling_type == props::PoolingTypeInfo::Enum::global_max ||
+      pooling_type == props::PoolingTypeInfo::Enum::global_average) {
+    NNTR_THROW_IF(!pool_size.empty(), std::invalid_argument)
+      << "[Pooling2D] global_max, global_average does not accept pool size";
+    pool_size.emplace_back(props::PoolSize(in_dim.height()));
+    pool_size.emplace_back(props::PoolSize(in_dim.width()));
   }
 
-  padding = std::get<props::Padding2D>(pool2d_props)
+  padding = std::get<props::Padding2D>(pooling2d_props)
               .compute(in_dim, {pool_size[0], pool_size[1]});
 
   auto [pt, pb, pl, pr] = padding;
 
-  if (pooling_type == PoolingType::global_max ||
-      pooling_type == PoolingType::global_average) {
+  if (pooling_type == props::PoolingTypeInfo::Enum::global_max ||
+      pooling_type == props::PoolingTypeInfo::Enum::global_average) {
     if (pt + pb + pl + pr != 0) {
       throw std::invalid_argument(
         "[Pooling2D] global_max, global_average does not accept padding");
@@ -97,7 +113,7 @@ void Pooling2DLayer::finalize(InitLayerContext &context) {
    * = 12 / 4 = 3
    * // clang-format on
    */
-  if (pooling_type == PoolingType::global_max) {
+  if (pooling_type == props::PoolingTypeInfo::Enum::global_max) {
     pool_helper_idx = context.requestTensor(
       in_dim, context.getName() + ":helper_idx", Tensor::Initializer::NONE,
       false, ITERATION_LIFESPAN);
@@ -125,6 +141,11 @@ void Pooling2DLayer::forwarding(RunLayerContext &context, bool training) {
 }
 
 void Pooling2DLayer::calcDerivative(RunLayerContext &context) {
+  auto &pool_size = std::get<std::vector<props::PoolSize>>(pooling2d_props);
+  auto &stride =
+    std::get<std::array<props::Stride, POOLING2D_DIM>>(pooling2d_props);
+  auto &pooling_type = std::get<props::PoolingType>(pooling2d_props).get();
+
   Tensor &deriv = context.getIncomingDerivative(SINGLE_INOUT_IDX);
   Tensor &result = context.getOutgoingDerivative(SINGLE_INOUT_IDX);
   Tensor &pool_helper = context.getTensor(pool_helper_idx);
@@ -148,7 +169,7 @@ void Pooling2DLayer::calcDerivative(RunLayerContext &context) {
   unsigned int in_map_size = height * width;
 
   switch (pooling_type) {
-  case PoolingType::max: {
+  case props::PoolingTypeInfo::Enum::max: {
     const int *iter = pool_helper.getData<int>();
     const float *deriv_data = deriv.getData();
     for (unsigned int b = 0; b < batch; ++b) {
@@ -166,8 +187,8 @@ void Pooling2DLayer::calcDerivative(RunLayerContext &context) {
       }
     }
   } break;
-  case PoolingType::global_average:
-  case PoolingType::average: {
+  case props::PoolingTypeInfo::Enum::global_average:
+  case props::PoolingTypeInfo::Enum::average: {
     int heigth_stride_end = height - p_height + pb;
     int width_stride_end = width - p_width + pr;
     const int *iter = pool_helper.getData<int>();
@@ -197,7 +218,7 @@ void Pooling2DLayer::calcDerivative(RunLayerContext &context) {
       }
     }
   } break;
-  case PoolingType::global_max: {
+  case props::PoolingTypeInfo::Enum::global_max: {
     float *deriv_data = deriv.getData();
     for (unsigned int b = 0; b < batch; b++) {
       for (unsigned int c = 0; c < channel; c++) {
@@ -219,70 +240,26 @@ void Pooling2DLayer::calcDerivative(RunLayerContext &context) {
   }
 }
 
-void Pooling2DLayer::setProperty(const std::vector<std::string> &values) {
-  /// @todo: deprecate this in favor of loadProperties
-  for (unsigned int i = 0; i < values.size(); ++i) {
-    std::string key;
-    std::string value;
-    std::stringstream ss;
-
-    if (getKeyValue(values[i], key, value) != ML_ERROR_NONE) {
-      throw std::invalid_argument("Error parsing the property: " + values[i]);
-    }
-
-    if (value.empty()) {
-      ss << "value is empty: key: " << key << ", value: " << value;
-      throw std::invalid_argument(ss.str());
-    }
-
-    /// @note this calls derived setProperty if available
-    setProperty(key, value);
-  }
+void Pooling2DLayer::exportTo(Exporter &exporter,
+                              const ExportMethods &method) const {
+  exporter.saveResult(pooling2d_props, method, this);
 }
 
-void Pooling2DLayer::setProperty(const std::string &type_str,
-                                 const std::string &value) {
-  using PropertyType = nntrainer::Layer::PropertyType;
-  int status = ML_ERROR_NONE;
-  nntrainer::Layer::PropertyType type =
-    static_cast<nntrainer::Layer::PropertyType>(parseLayerProperty(type_str));
-
-  switch (type) {
-  case PropertyType::pooling:
-    pooling_type = (PoolingType)parseType(value, TOKEN_POOLING);
-    if (pooling_type == PoolingType::unknown) {
-      throw std::invalid_argument("[Pooling2d_layer]: Unknown pooling type");
-    }
-    break;
-  case PropertyType::pool_size:
-    status = getValues(POOLING2D_DIM, value, (int *)(pool_size.data()));
-    throw_status(status);
-    if (pool_size[0] == 0 || pool_size[1] == 0) {
-      throw std::invalid_argument(
-        "[Pooling2d_layer] pool_size must be greater than 0");
-    }
-    break;
-  case PropertyType::stride:
-    status = getValues(POOLING2D_DIM, value, (int *)(stride.data()));
-    throw_status(status);
-    if (stride[0] == 0 || stride[1] == 0) {
-      throw std::invalid_argument(
-        "[Pooling2d_layer] stride must be greater than 0");
-    }
-    break;
-  case PropertyType::padding:
-    from_string(value, std::get<props::Padding2D>(pool2d_props));
-    break;
-  default:
-    std::string msg = "[Pooling2DLayer] Unknown Layer Property Key for value " +
-                      std::string(value);
-    throw exception::not_supported(msg);
-  }
+void Pooling2DLayer::setProperty(const std::vector<std::string> &values) {
+  auto remain_props = loadProperties(values, pooling2d_props);
+  NNTR_THROW_IF(!remain_props.empty(), std::invalid_argument)
+    << "[Pooling2dLayer] Unknown Layer Properties count " +
+         std::to_string(values.size());
 }
 
 void Pooling2DLayer::pooling2d(Tensor &in, bool training, Tensor &output,
                                Tensor &pool_helper, int batch_idx) {
 
+  auto &pool_size = std::get<std::vector<props::PoolSize>>(pooling2d_props);
+  auto &stride =
+    std::get<std::array<props::Stride, POOLING2D_DIM>>(pooling2d_props);
+  auto &pooling_type = std::get<props::PoolingType>(pooling2d_props).get();
+
   unsigned int channel = in.channel();
   auto [pt, pb, pl, pr] = padding;
 
@@ -307,7 +284,7 @@ void Pooling2DLayer::pooling2d(Tensor &in, bool training, Tensor &output,
 
   unsigned int max_idx_count = 0;
   switch (pooling_type) {
-  case PoolingType::max: {
+  case props::PoolingTypeInfo::Enum::max: {
     pool_fn = [&, this](const float *in_data, int channel_idx, int start_h,
                         int start_w) {
       int end_h = start_h + patch_height;
@@ -340,7 +317,7 @@ void Pooling2DLayer::pooling2d(Tensor &in, bool training, Tensor &output,
     };
     break;
   }
-  case PoolingType::global_max: {
+  case props::PoolingTypeInfo::Enum::global_max: {
     pool_fn = [&, this](const float *in_data, int channel_idx, int start_h,
                         int start_w) {
       int end_h = start_h + patch_height;
@@ -370,8 +347,8 @@ void Pooling2DLayer::pooling2d(Tensor &in, bool training, Tensor &output,
     };
     break;
   }
-  case PoolingType::global_average:
-  case PoolingType::average: {
+  case props::PoolingTypeInfo::Enum::global_average:
+  case props::PoolingTypeInfo::Enum::average: {
     pool_fn = [&, this](const float *in_data, int channel_idx, int start_h,
                         int start_w) {
       int end_h = start_h + patch_height;
@@ -398,7 +375,7 @@ void Pooling2DLayer::pooling2d(Tensor &in, bool training, Tensor &output,
     };
     break;
   }
-  case PoolingType::unknown:
+  case props::PoolingTypeInfo::Enum::unknown:
   default:
     throw std::invalid_argument("unknown pooling type given");
     break;
index d8e257b..38c4d7c 100644 (file)
@@ -32,17 +32,6 @@ constexpr const unsigned int POOLING2D_DIM = 2;
 class Pooling2DLayer : public Layer {
 public:
   /**
-   * @brief   Pooling operation type class
-   */
-  enum class PoolingType {
-    max = 0,
-    average = 1,
-    global_max = 2,
-    global_average = 3,
-    unknown = 4,
-  };
-
-  /**
    * @brief PaddingType Class
    * @todo support keras type of padding
    */
@@ -56,19 +45,8 @@ public:
   /**
    * @brief     Constructor of Pooling 2D Layer
    */
-  Pooling2DLayer(
-    PoolingType pooling_type_ = PoolingType::average,
-    const std::array<unsigned int, POOLING2D_DIM> &pool_size_ = {0, 0},
-    const std::array<unsigned int, POOLING2D_DIM> &stride_ = {1, 1},
-    const std::array<unsigned int, POOLING2D_DIM * 2> &padding_ = {0, 0, 0,
-                                                                   0}) :
-    Layer(),
-    pool_size(pool_size_),
-    stride(stride_),
-    padding(padding_),
-    pool2d_props(),
-    pool_helper_idx(0),
-    pooling_type(pooling_type_) {}
+  Pooling2DLayer(const std::array<unsigned int, POOLING2D_DIM * 2> &padding_ = {
+                   0, 0, 0, 0});
 
   /**
    * @brief     Destructor of Pooling 2D Layer
@@ -105,10 +83,7 @@ public:
   /**
    * @copydoc Layer::exportTo(Exporter &exporter, ExportMethods method)
    */
-  void exportTo(Exporter &exporter,
-                const ExportMethods &method) const override {
-    Layer::exportTo(exporter, method);
-  }
+  void exportTo(Exporter &exporter, const ExportMethods &method) const override;
 
   /**
    * @copydoc Layer::getType()
@@ -132,7 +107,9 @@ public:
    */
   void setBatch(InitLayerContext &context, unsigned int batch) override {
     context.updateTensorSpec(pool_helper_idx, batch);
-    if (pooling_type == PoolingType::global_max)
+    props::PoolingTypeInfo::Enum pooling_type =
+      std::get<props::PoolingType>(pooling2d_props).get();
+    if (pooling_type == props::PoolingTypeInfo::Enum::global_max)
       pool_helper_size.resize(batch *
                               context.getInputDimensions()[0].channel());
   }
@@ -142,21 +119,22 @@ public:
    */
   void setBatch(RunLayerContext &context, unsigned int batch) override {
     context.updateTensor(pool_helper_idx, batch);
-    if (pooling_type == PoolingType::global_max)
+    props::PoolingTypeInfo::Enum pooling_type =
+      std::get<props::PoolingType>(pooling2d_props).get();
+    if (pooling_type == props::PoolingTypeInfo::Enum::global_max)
       pool_helper_size.resize(batch * context.getInput(0).channel());
   }
 
 private:
-  std::array<unsigned int, POOLING2D_DIM> pool_size;
-  std::array<unsigned int, POOLING2D_DIM> stride;
   std::array<unsigned int, POOLING2D_DIM * 2> padding;
-  std::tuple<props::Padding2D> pool2d_props;
+  std::tuple<props::PoolingType, std::vector<props::PoolSize>,
+             std::array<props::Stride, POOLING2D_DIM>, props::Padding2D>
+    pooling2d_props;
 
   unsigned int pool_helper_idx; /**< helper tensor idx */
   std::vector<unsigned int>
     pool_helper_size; /**< helper size for each elements in the case of
                          global_max pooling */
-  PoolingType pooling_type;
 
   /**
    * @brief     calculation convolution
@@ -168,16 +146,6 @@ private:
    */
   void pooling2d(Tensor &in, bool training, Tensor &output, Tensor &pool_helper,
                  int batch_idx);
-
-  /**
-   * @brief setProperty by type and value separated
-   * @param[in] type property type to be passed
-   * @param[in] value value to be passed
-   * @exception exception::not_supported     when property type is not valid for
-   * the particular layer
-   * @exception std::invalid_argument invalid argument
-   */
-  void setProperty(const std::string &type_str, const std::string &value);
 };
 
 } // namespace nntrainer
index 176e4e0..f168e90 100644 (file)
@@ -168,7 +168,7 @@ unsigned int parseType(std::string ll, InputType t) {
         return (i);
       }
     }
-    ret = (unsigned int)Pooling2DLayer::PoolingType::unknown;
+    ret = (unsigned int)props::PoolingTypeInfo::Enum::unknown;
     break;
   case TOKEN_UNKNOWN:
   default: