[ TensorDim ] Add tensor format in TensorDim
authorjijoong.moon <jijoong.moon@samsung.com>
Fri, 31 Mar 2023 06:58:02 +0000 (15:58 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Thu, 6 Apr 2023 06:27:44 +0000 (15:27 +0900)
In order to support Channel Last & Channel First at the same time, we
need to define the Tensor Format in TensorDim Class.

According to the format of tensorDim, it return proper value for the
APIs such as bacch(), channel(), height() and width().

The default format is Channel First as it is now.

If nothing is provided when a Tensor is constructured, then it is set
NCHW.

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

Signed-off-by: jijoong.moon <jijoong.moon@samsung.com>
api/ccapi/include/tensor_dim.h
nntrainer/layers/fc_layer.cpp
nntrainer/tensor/tensor_dim.cpp
nntrainer/utils/node_exporter.cpp

index 2da4f9b..54858a8 100644 (file)
@@ -34,6 +34,11 @@ public:
   static constexpr const size_t MAXDIM = 4;
 
   /**
+   * @brief     Tensor Formant : Default is NCHW
+   */
+  enum class Format { NCHW, NHWC };
+
+  /**
    * @brief Get the Num Dim object
    *
    * @return unsigned int fixed value of MAXDIM
@@ -43,27 +48,32 @@ public:
   /**
    * @brief Construct a new Tensor Dim object
    *
+   * @param fm format NCHW | HNWC
    * @param eff_dim_flag_ effective dimension flag (1 means it's effective)
    * @param dyn_dim_flag_ dynamic dimension flag (1 means it's unspecified)
    */
-  explicit TensorDim(const std::bitset<MAXDIM> &eff_dim_flag_ = 0b1111,
+  explicit TensorDim(Format fm = Format::NCHW,
+                     const std::bitset<MAXDIM> &eff_dim_flag_ = 0b1111,
                      const std::bitset<MAXDIM> &dyn_dim_flag_ = 0b0000);
 
   /**
    * @brief Construct a new Tensor Dim object
    *
    * @param dims std::initialize_list
+   * @param fm format NCHW | HNWC
    *
-   * formats of {w}, {h, w}, {c, h, w}, {b, c, h, w} are accepted
+   * formats of {w}, {h, w}, {c, h, w}, {b, c, h, w} for the NCHW are accepted
+   * formats of {c}, {w, c}, {h, w, c}, {b, h, w, c} for the NHWC are accepted
    */
-  TensorDim(std::initializer_list<size_t> dims);
+  TensorDim(std::initializer_list<size_t> dims, Format fm = Format::NCHW);
 
   /**
    * @brief Construct a new Tensor Dim object without batch dimension
    *
    * @param shapes shapes without batch dimension
+   * @param fm format NCHW | HNWC
    */
-  TensorDim(const std::array<size_t, 3> &shapes);
+  TensorDim(const std::array<size_t, 3> &shapes, Format fm = Format::NCHW);
 
   /**
    * @brief Construct a new Tensor Dim object
@@ -72,10 +82,11 @@ public:
    * @param c channel
    * @param h height
    * @param w width
+   * @param fm format NCHW | HNWC
    * @param eff_dim_flag_ dimension bit flag to calculate the dynamic
    * dimension, rightmost is width
    */
-  TensorDim(size_t b, size_t c, size_t h, size_t w,
+  TensorDim(size_t b, size_t c, size_t h, size_t w, Format fm = Format::NCHW,
             const std::bitset<MAXDIM> &eff_dim_flag_ = 0b1111,
             const std::bitset<MAXDIM> &dyn_dim_flag_ = 0b0000);
 
@@ -89,9 +100,10 @@ public:
   /**
    * @brief Construct a new Tensor Dim object
    *
-   * @param shape shape of format N:C:H:W
+   * @param shape shape of format
+   * @param fm format NCHW | HNWC
    */
-  TensorDim(const std::string &shape);
+  TensorDim(const std::string &shape, Format fm = Format::NCHW);
 
   /**
    * @brief Destroy the Tensor Dim object
@@ -275,10 +287,11 @@ public:
   /**
    * @brief Set the Tensor Dim object
    *
-   * @param input_shape input_shape of format `N:C:H:W`
+   * @param input_shape input_shape
+   * @param fm NCHW | NHWC
    * @return int ML_ERROR_NONE if successs
    */
-  int setTensorDim(const std::string &input_shape);
+  int setTensorDim(const std::string &input_shape, Format fm = Format::NCHW);
 
   /**
    * @brief copy assign a dimension
@@ -366,6 +379,18 @@ public:
    */
   bool is_dynamic() const;
 
+  /**
+   * @brief getFormat
+   *
+   */
+  TensorDim::Format getFormat() const { return format; };
+
+  /**
+   * @brief setFormat
+   *
+   */
+  void setFormat(TensorDim::Format fm) { format = fm; };
+
 private:
   /**
    * @brief reset length
@@ -373,6 +398,8 @@ private:
    */
   void resetLen();
 
+  Format format;
+
   std::bitset<MAXDIM> eff_dim_flag; /**< dimension bit flag to define effective
           dimension size */
 
index f3f18a2..a2044c8 100644 (file)
@@ -73,8 +73,11 @@ void FullyConnectedLayer::finalize(InitLayerContext &context) {
   context.setOutputDimensions(output_dims);
 
   /** set weight specifications */
-  TensorDim bias_dim(1, 1, 1, unit, 0b0001);
-  TensorDim weight_dim(1, 1, in_dim.width(), unit, 0b0011);
+  // @todo : This NCHW format setting is just temporal, it needs to be set by
+  // global configuration
+  TensorDim bias_dim(1, 1, 1, unit, ml::train::TensorDim::Format::NCHW, 0b0001);
+  TensorDim weight_dim(1, 1, in_dim.width(), unit,
+                       ml::train::TensorDim::Format::NCHW, 0b0011);
 
   weight_idx[FCParams::weight] = context.requestWeight(
     weight_dim, weight_initializer, weight_regularizer,
index 7fdfa15..a37ef37 100644 (file)
 namespace ml {
 namespace train {
 
-TensorDim::TensorDim(const std::bitset<MAXDIM> &eff_dim_flag_,
+TensorDim::TensorDim(TensorDim::Format fm,
+                     const std::bitset<MAXDIM> &eff_dim_flag_,
                      const std::bitset<MAXDIM> &dyn_dim_flag_) :
+  format(fm),
   eff_dim_flag(eff_dim_flag_),
   dyn_dim_flag(dyn_dim_flag_) {
   for (size_t i = 0; i < MAXDIM; ++i) {
@@ -36,7 +38,8 @@ TensorDim::TensorDim(const std::bitset<MAXDIM> &eff_dim_flag_,
   feature_len = 0;
 }
 
-TensorDim::TensorDim(std::initializer_list<size_t> dims) : TensorDim() {
+TensorDim::TensorDim(std::initializer_list<size_t> dims, Format fm) :
+  TensorDim() {
   int shift_size = MAXDIM - dims.size();
 
   if (shift_size < 0) {
@@ -49,25 +52,33 @@ TensorDim::TensorDim(std::initializer_list<size_t> dims) : TensorDim() {
     setTensorDim(shift_size + cnt, i);
     cnt += 1;
   }
+  format = fm;
 }
 
-TensorDim::TensorDim(const std::array<size_t, 3> &shapes) :
-  TensorDim({shapes[0], shapes[1], shapes[2]}) {}
+TensorDim::TensorDim(const std::array<size_t, 3> &shapes, Format fm) :
+  TensorDim({shapes[0], shapes[1], shapes[2]}, fm) {}
 
-TensorDim::TensorDim(size_t b, size_t c, size_t h, size_t w,
+TensorDim::TensorDim(size_t b, size_t c, size_t h, size_t w, Format fm,
                      const std::bitset<MAXDIM> &eff_dim_flag_,
                      const std::bitset<MAXDIM> &dyn_dim_flag_) :
-  TensorDim(eff_dim_flag_, dyn_dim_flag_) {
+  TensorDim(fm, eff_dim_flag_, dyn_dim_flag_) {
+
   setTensorDim(0, b);
-  setTensorDim(1, c);
-  setTensorDim(2, h);
-  setTensorDim(3, w);
+  if (fm == Format::NHWC) {
+    setTensorDim(1, h);
+    setTensorDim(2, w);
+    setTensorDim(3, c);
+  } else {
+    setTensorDim(1, c);
+    setTensorDim(2, h);
+    setTensorDim(3, w);
+  }
   feature_len = c * h * w;
   len = b * feature_len;
 }
 
-TensorDim::TensorDim(const std::string &shape) : TensorDim() {
-  if (setTensorDim(shape) != ML_ERROR_NONE) {
+TensorDim::TensorDim(const std::string &shape, Format fm) : TensorDim() {
+  if (setTensorDim(shape, fm) != ML_ERROR_NONE) {
     throw std::invalid_argument("[TensorDim] Setting TensorDim failed");
   }
 }
@@ -119,7 +130,7 @@ void TensorDim::setTensorDim(unsigned int idx, size_t value) {
   resetLen();
 }
 
-int TensorDim::setTensorDim(const std::string &input_shape) {
+int TensorDim::setTensorDim(const std::string &input_shape, Format fm) {
   int status = ML_ERROR_NONE;
   static const std::regex words_regex("[^\\s.,:;!?]+");
   auto words_begin =
@@ -134,7 +145,7 @@ int TensorDim::setTensorDim(const std::string &input_shape) {
   for (std::sregex_iterator i = words_begin; i != words_end; ++i, ++cn) {
     setTensorDim(MAXDIM - cur_dim + cn, std::stoul((*i).str()));
   }
-
+  format = fm;
   return status;
 }
 
@@ -161,15 +172,22 @@ void swap(TensorDim &lhs, TensorDim &rhs) noexcept {
   std::swap(lhs.feature_len, rhs.feature_len);
   std::swap(lhs.eff_dim_flag, rhs.eff_dim_flag);
   std::swap(lhs.dyn_dim_flag, rhs.dyn_dim_flag);
+  std::swap(lhs.format, rhs.format);
 }
 
 size_t TensorDim::batch() const { return dim[0]; };
 
-size_t TensorDim::channel() const { return dim[1]; };
+size_t TensorDim::channel() const {
+  return format == Format::NCHW ? dim[1] : dim[3];
+};
 
-size_t TensorDim::height() const { return dim[2]; };
+size_t TensorDim::height() const {
+  return format == Format::NCHW ? dim[2] : dim[1];
+};
 
-size_t TensorDim::width() const { return dim[3]; };
+size_t TensorDim::width() const {
+  return format == Format::NCHW ? dim[3] : dim[2];
+};
 
 size_t TensorDim::getDataLen() const { return len; };
 
@@ -177,11 +195,20 @@ size_t TensorDim::getFeatureLen() const { return feature_len; };
 
 void TensorDim::batch(size_t b) { setTensorDim(0, b); }
 
-void TensorDim::channel(size_t c) { setTensorDim(1, c); }
+void TensorDim::channel(size_t c) {
+  uint i = (format == Format::NCHW) ? 1 : 3;
+  setTensorDim(i, c);
+}
 
-void TensorDim::height(size_t h) { setTensorDim(2, h); }
+void TensorDim::height(size_t h) {
+  uint i = (format == Format::NCHW) ? 2 : 1;
+  setTensorDim(i, h);
+}
 
-void TensorDim::width(size_t w) { setTensorDim(3, w); }
+void TensorDim::width(size_t w) {
+  uint i = (format == Format::NCHW) ? 3 : 2;
+  setTensorDim(i, w);
+}
 
 const size_t *TensorDim::getDim() const { return dim; }
 
@@ -211,6 +238,9 @@ TensorDim TensorDim::transpose(const std::array<size_t, MAXDIM> &axes) const {
 }
 
 bool TensorDim::operator==(const TensorDim &rhs) const {
+  if (this->format != rhs.format)
+    return false;
+
   for (size_t i = 0; i < MAXDIM; ++i) {
     if (this->dim[i] != rhs.dim[i]) {
       return false;
index 007f218..bc76ec4 100644 (file)
@@ -158,7 +158,8 @@ void Exporter::saveTflResult(
     new_weights.push_back(filter);
 
     auto &bias_weight = *old_weights[1];
-    TensorDim bias_dim{std::bitset<4>(0b0001)};
+    TensorDim bias_dim{ml::train::TensorDim::Format::NCHW,
+                       std::bitset<4>(0b0001)};
     bias_dim.setTensorDim(
       3 /** index **/,
       bias_weight
@@ -201,7 +202,10 @@ void Exporter::saveTflResult(
     new_inputs.reserve(inputs.size() + 1 /** perm **/);
     new_inputs.push_back(*inputs[0]);
     // create "perm" tensor for Transpose operator
-    TensorDim perm_dim{std::bitset<4>(0b0001)};
+    // @todo : This NCHW format setting is just temporal, it needs to be set by
+    //  global configuration
+    TensorDim perm_dim{ml::train::TensorDim::Format::NCHW,
+                       std::bitset<4>(0b0001)};
     perm_dim.setTensorDim(3 /** index **/,
                           4 /** value **/); // effective dimension = {4}
     new_inputs.emplace_back(perm_dim);