[nnc] Support asimmetrical paddings in tflite models in ACL backend (#2447)
authorEfimov Alexander/AI Tools Lab/./Samsung Electronics <a.efimov@samsung.com>
Fri, 30 Nov 2018 12:38:50 +0000 (15:38 +0300)
committerРоман Михайлович Русяев/AI Tools Lab /SRR/Staff Engineer/삼성전자 <r.rusyaev@samsung.com>
Fri, 30 Nov 2018 12:38:50 +0000 (15:38 +0300)
Inference asimmetrical paddings for "Same" padding type on acl backend

Signed-off-by: Efimov Alexander <a.efimov@samsung.com>
contrib/nnc/include/core/modelIR/TensorUtil.h
contrib/nnc/include/passes/acl_soft_backend/AclCppOpGenerator.h
contrib/nnc/passes/acl_soft_backend/AclCppOpGenerator.cpp

index 683652d..d289b16 100644 (file)
@@ -31,9 +31,9 @@ namespace mir
 {
 
 // TODO: This is potentialy unsafe. Consider how to improve the transposition concept.
-template<unsigned int... Ints>
+template<int32_t... Ints>
 Shape transposeShape(const Shape& shape) {
-  std::vector<unsigned int> permutes{Ints...};
+  std::vector<int32_t> permutes{Ints...};
   Shape result(shape);
     int32_t nof_permutes = std::min<int32_t>(shape.rank(), permutes.size());
 
index 53ea6f6..86b2220 100644 (file)
@@ -133,7 +133,7 @@ private:
    *                   elements to the left of and including the in2 term, or the operation out if
    *                   in2 was the last term in the sequence.
    */
-  std::shared_ptr<ArtifactId> genMultiplication(const std::string& prefix, int index,
+  std::shared_ptr<ArtifactId> genMultiplication(const std::string& prefix, size_t index,
                                                 const mir::Shape& ir_shape,
                                                 std::shared_ptr<ArtifactId> in1,
                                                 std::shared_ptr<ArtifactId> in2,
index e79db22..051f317 100644 (file)
@@ -59,7 +59,7 @@ const ArtifactModule& AclCppOpGenerator::generate(mir::Graph* g) {
   _parIn = _parInVar->use();
   string par_file_name = cli::artifactName + ".par";
   _constrBlock->call("open", {AF::lit("\"" + par_file_name + "\""),
-                        AF::lit("std::ios_base::in | std::ios_base::binary")}, _parIn);
+                              AF::lit("std::ios_base::in | std::ios_base::binary")}, _parIn);
   auto file_fail = _constrBlock->ifCond(AF::call("fail", {}, _parIn));
   auto file_fail_block = file_fail->getBlock();
   file_fail_block->addStatement(AF::lit("throw std::string(\"Failed to open file: " +
@@ -181,8 +181,89 @@ void AclCppOpGenerator::visit(ops::SoftmaxOp& op) {
   }
 }
 
+template<class Oper>
+static Shape getWindowShape(const Oper& op) {
+  const Shape& kshape = op.getKernel().getShape();
+  return Shape{1, kshape.dim(0), kshape.dim(1), kshape.dim(2)};
+}
+
+template<>
+Shape getWindowShape<ops::PoolOp>(const ops::PoolOp& op) {
+  const Shape& wshape = op.getWindowShape();
+  return Shape{1, wshape.dim(-3), wshape.dim(-2), wshape.dim(-1)};
+}
+
+/**
+ * @brief Generate DOM for PadStrideInfo object
+ * @tparam Oper Class of operation with pad and stride properties
+ * @param op Operation entity to generate variable for
+ * @param prefix First part of generated variable name
+ * @param block Code block where insert variable declaration
+ * @return generated variable
+ */
+template<class Oper>
+static shared_ptr<ArtifactVariable>
+    genPadStrideInfo(const Oper& op, const string& prefix, ArtifactBlock* block) {
+  using AF = ArtifactFactory;
+
+  const Shape& strides = transposeShape<1, 0>(op.getStrides());
+  assert(strides.rank() == 3 && strides.dim(2) == 1);
+
+  // array of paddings
+  // order is following: [(top, bottom), (left, right)]
+  std::array<std::pair<int32_t, int32_t>, 2> pads;
+
+  // TODO this is workaround to overcome limitations of Model IR
+  // replace when left and right paddings introduced
+  if (op.getPaddingType() == mir::ops::PaddingType::Same) {
+    // If padding type is Same we need to recompute paddigns,
+    // to cover case with unequal "left" and "right" paddings
+    const int32_t h_axis = -3;
+    const int32_t w_axis = -2;
+    // This is offset to work correctly with bare padding and strides arrays
+    const int32_t axis_offset = 3;
+
+    const Shape window_shape = getWindowShape(op);
+
+    for (int32_t axis : {h_axis, w_axis}) {
+      int32_t full_pad;
+      const int32_t n = op.getInputShape(0).dim(axis);
+      const int32_t k = window_shape.dim(axis);
+      const int32_t s = op.getStrides().dim(axis);
+      if (n % s == 0)
+        full_pad = max(k - s, 0);
+      else
+        full_pad = max(k - n % s, 0);
+      pads[axis + axis_offset].first = full_pad / 2;
+      pads[axis + axis_offset].second = full_pad - pads[axis + axis_offset].first;
+    }
+  } else {
+    pads[0].first = pads[0].second = op.getPadding(0);
+    pads[1].first = pads[1].second = op.getPadding(1);
+  }
+
+  assert(op.getPadding(2) == 0);
+
+  string type_name = "arm_compute::PadStrideInfo";
+
+  string var_name = prefix + "_pad_stride_info";
+
+  list<std::shared_ptr<ArtifactExpr>> var_init_params =
+      {AF::lit(to_string(op.getStrides().dim(1))),
+       AF::lit(to_string(op.getStrides().dim(0))),
+       AF::lit(to_string(pads[1].first)),
+       AF::lit(to_string(pads[1].second)),
+       AF::lit(to_string(pads[0].first)),
+       AF::lit(to_string(pads[0].second)),
+       AF::lit("arm_compute::DimensionRoundingType::FLOOR")};
+
+  auto pad_stride_info_var = block->var(type_name, var_name, {}, var_init_params);
+
+  return pad_stride_info_var;
+}
+
 void AclCppOpGenerator::visit(ops::PoolOp& op) {
-  const char* pooling_type;
+  const char* pooling_type = nullptr;
 
   switch (op.getPoolingType()) {
     case ops::PoolOp::PoolingType::MAX:
@@ -203,12 +284,8 @@ void AclCppOpGenerator::visit(ops::PoolOp& op) {
   auto out = genTensor(op, transposeShape<1, 0, 2>(op.getOutputShape(0)));
   auto prefix = out->name() + "_pooling_layer";
 
-  auto pad_stride_info_var = _constrBlock->var("arm_compute::PadStrideInfo",
-                                               prefix + "_pad_stride_info",
-                                               {}, {AF::lit(to_string(op.getStrides().dim(1))),
-                                                    AF::lit(to_string(op.getStrides().dim(0))),
-                                                    AF::lit(to_string(op.getPadding(1))),
-                                                    AF::lit(to_string(op.getPadding(0)))});
+  auto pad_stride_info_var = genPadStrideInfo(op, prefix, _constrBlock);
+
   auto pad_stride_info = pad_stride_info_var->use();
   auto kernel_window_var = _constrBlock->var("arm_compute::Size2D", prefix + "_kernel_window", {},
                                              {AF::lit(to_string(op.getWindowShape().dim(1))),
@@ -439,7 +516,7 @@ void AclCppOpGenerator::visit(ops::ElementwiseOp& op) {
   // Get the identifier of the first input tensor in the DOM.
   auto in1 = AF::id(tensorName(in_op1));
 
-  for (int i = 1; i < prev_nodes.size(); ++i) {
+  for (size_t i = 1; i < prev_nodes.size(); ++i) {
     auto in_op2 = prev_nodes[i].op;
 
     // Get the identifier of the second input tensor in the DOM.
@@ -481,11 +558,6 @@ void AclCppOpGenerator::genConvolution(Op& op, const string& acl_func_name, cons
   const Shape& ir_weights_shape = ir_weights->getShape();
   assert(ir_weights_shape.rank() == 4);
   Shape ir_biases_shape({ir_weights_shape.dim(-1)});
-  const Shape& strides = transposeShape<1, 0>(op.getStrides());
-  assert(strides.rank() == 3 && strides.dim(2) == 1);
-  uint32_t pad_x = op.getPadding(1);
-  uint32_t pad_y = op.getPadding(0);
-  assert(op.getPadding(2) == 0);
 
   auto& prev_nodes = op.getPrevNodes();
   assert(prev_nodes.size() == 1);
@@ -503,12 +575,8 @@ void AclCppOpGenerator::genConvolution(Op& op, const string& acl_func_name, cons
 
   // Create a local variable of type PadStrideInfo in the artifact constructor:
   // PadStrideInfo pad_stride_info(stride_x, stride_y, pad_x, pad_y);
-  auto pad_stride_info_var = _constrBlock->var("arm_compute::PadStrideInfo",
-                                               operation_name + "_pad_stride_info",
-                                               {}, {AF::lit(to_string(strides.dim(0))),
-                                               AF::lit(to_string(strides.dim(1))),
-                                               AF::lit(to_string(pad_x)),
-                                               AF::lit(to_string(pad_y))});
+  auto pad_stride_info_var = genPadStrideInfo(op, operation_name, _constrBlock);
+
   auto pad_stride_info = pad_stride_info_var->use();
 
   // The parameter for the conv_layer.config(&in, &weights, nullptr, &out, pad_stride_info)
@@ -517,7 +585,7 @@ void AclCppOpGenerator::genConvolution(Op& op, const string& acl_func_name, cons
                                                AF::ref(out), pad_stride_info};
 
   // Add to additional parameters for deconvolution.
-  if (dynamic_cast<ops::DeConv2DOp*>(&op)) {
+  if (op.getType() == Operation::Type::deConv2D) {
     config_params.push_back(AF::lit("0"));
     config_params.push_back(AF::lit("0"));
   }
@@ -584,7 +652,7 @@ shared_ptr<ArtifactId> AclCppOpGenerator::genAddition(const string& prefix, int
   return out;
 }
 
-shared_ptr<ArtifactId> AclCppOpGenerator::genMultiplication(const string& prefix, int index,
+shared_ptr<ArtifactId> AclCppOpGenerator::genMultiplication(const string& prefix, size_t index,
                                                             const Shape& ir_shape,
                                                             shared_ptr<ArtifactId> in1,
                                                             shared_ptr<ArtifactId> in2,