[ 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 2da4f9b3b49d0a67677a195cf7ae22694d8d3c2f..54858a87e7a498c7858f136a2cb11f4854c1af7e 100644 (file)
@@ -33,6 +33,11 @@ class TensorDim {
 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
    *
@@ -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 f3f18a238da4ec05da2d2ad0aa84bd8b8a9653f3..a2044c8527740e549f6e55e67d36f66821fc1e10 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 7fdfa1510114efd4b8f5b674a5c2d41099e6989a..a37ef37f22453dc1e3d567be1769a694b80b78cf 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 007f2185773ec41a170115ea6a0be61ca035ff3b..bc76ec455984af941074818ba00ac67a4c56f8bc 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);